基于QT的C++中小项目软件开发架构源码

news2024/9/23 20:11:58
描述

基于QT信号槽机制实现类之间的交互调用通信,适用于使用不同枚举作为消息交互的类型场景,支持附带任意参数,代码使用方式参考前一篇文章

特性
  1. 仅需包含一个头文件Communicator.h,需要通信的业务类继承Communicator,界面类继承VCommunicatorMVCommunicator
  2. 消息通过枚举类型定义,支持任意枚举消息,添加枚举无需改动代码。枚举名称区分消息类型,枚举值区分具体消息
  3. 使用便捷,只需注册消息和消息处理函数
  4. 提供发送消息时,附带any传递任意额外信息
  5. 支持多线程,同QT一致的信号槽触发方式,如需控制信号槽触发方式,需要额外补充代码
对比QT信号槽
  1. 适用于使用不同枚举作为消息类型场景,支持附带任意参数
  2. 使用同QT一样便捷,只需注册消息和消息回调函数。且如果使用QT的信号槽进行通信,不同的消息的类型则需要定义多个connect连接函数,如ELoginMsg、ERunMsg,多一种消息就要多一个connect

Communicator.h

#pragma once
#include <QMainWindow>
#include <functional>
#include <unordered_map>
#if __cplusplus >= 201703L
#include <any>
#else 
#include <typeinfo>
#include <memory>
namespace std {
class any
{
	class AnyHelperBase
	{
	public:
		virtual const std::type_info& type()const = 0;
		virtual AnyHelperBase* clone()const = 0;
	};

	template<typename T>
	class AnyHelper :public AnyHelperBase
	{
	public:
		T data;
		template<typename ...Args>
		AnyHelper(Args&&... args) :data(std::forward<Args>(args)...) {}
		AnyHelper(const AnyHelper& rhs) :data(rhs.data) {}
		AnyHelper(const T& value) :data(value) {}
		virtual const std::type_info& type() const
		{
			return typeid(T);
		}
		virtual AnyHelper* clone() const
		{
			return new AnyHelper(*this);
		}
	};
	template<typename T>
	friend T any_cast(const any& a);
private:
	std::unique_ptr<AnyHelperBase> pdata{};
public:
	any() :pdata(nullptr) {}
	template<typename T>
	any(T&& value) : pdata(new AnyHelper<std::decay_t<T>>(value)) {}
	any(const any& rhs) {
		if (rhs.pdata != nullptr) {
			pdata.reset(rhs.pdata->clone());
		}
	}
	any(any& rhs) {
		if (rhs.pdata != nullptr) {
			pdata.reset(rhs.pdata->clone());
		}
	}
	any(any&& rhs) :pdata(rhs.pdata.release()) {}

	const std::type_info& type() const
	{
		return pdata->type();
	}
	bool has_value() const {
		return pdata != nullptr;
	}
	void reset() {
		pdata.reset();
	}
	template<typename T>
	any& operator=(T value) {
		pdata.reset(new AnyHelper<std::decay_t<T>>(value));
		return *this;
	}
	any& operator=(any rhs)
	{
		pdata.reset(rhs.pdata->clone());
		return *this;
	}
};

template<typename T>
T any_cast(const any& a)
{
	auto p = dynamic_cast<any::AnyHelper<std::decay_t<T>>*>(a.pdata.get());
	if (p == nullptr)
		throw std::runtime_error("Bad any cast!");
	return p->data;
}
}
#endif

class MsgHandler {
public:
	using MsgID = size_t;
	using MsgHandleFunc = std::function<void(std::any)>;
	using MsgHandleFuncNoPara = std::function<void()>;
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void HandleMsg(T msg, std::any info = std::any{}) {
		m_msgHandlers[HashID(msg)](info);
	}
protected:
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	MsgID HashID(T msg) {
		return std::hash<std::string>()(typeid(T).name()) ^ std::hash<size_t>()(static_cast<size_t>(msg));
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RegisterMsgHandler(T msg, MsgHandleFuncNoPara func) {
		m_msgHandlers.insert({ HashID(msg),[=](std::any) {func(); } });
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RegisterMsgHandler(T msg, MsgHandleFunc func) {
		m_msgHandlers.insert({ HashID(msg), func });
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void RemoveMsgHandler(T msg) {
		if (auto findit = m_msgHandlers.find(msg); findit != m_msgHandlers.end()) {
			m_msgHandlers.erase(findit);
		}
	}
protected:
	std::unordered_map<MsgID, MsgHandleFunc> m_msgHandlers;
};

class Communicator : public QObject, public MsgHandler {
	Q_OBJECT
public:
	Communicator(QObject* parent = nullptr) : QObject(parent) {
		qRegisterMetaType<MsgID>("MsgID");
		qRegisterMetaType<std::any>("std::any");
	}
signals:
	void SendMsg(MsgID, std::any);
public:
	void HandleHashMsg(MsgID msgID, std::any info = std::any{}) {
		if (bool findHandler = m_msgHandlers.find(msgID) != m_msgHandlers.end()) {
			m_msgHandlers[msgID](info);
		}
	}
public:
	template<typename Communicator1>
	void Connect(Communicator1* receiver) {
		connect(this, &Communicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
	template<typename Communicator1>
	void Disconnect(Communicator1* receiver) {
		disconnect(this, &Communicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void Emit(T msg, std::any info = nullptr) {
		emit SendMsg(HashID(msg), info);
	}
};

class ViewCommunicator : public QWidget, public MsgHandler {
	Q_OBJECT
public:
	ViewCommunicator(QWidget* parent = nullptr) : QWidget(parent) {
		qRegisterMetaType<MsgID>("MsgID");
		qRegisterMetaType<std::any>("std::any");
	}
signals:
	void SendMsg(MsgID, std::any);
public:
	void HandleHashMsg(MsgID msgID, std::any info = std::any{}) {
		if (bool findHandler = m_msgHandlers.find(msgID) != m_msgHandlers.end()) {
			m_msgHandlers[msgID](info);
		}
	}
public:
	template<typename Communicator1>
	void Connect(Communicator1* receiver) {
		connect(this, &ViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
	template<typename Communicator1>
	void Disconnect(Communicator1* receiver) {
		disconnect(this, &ViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
protected:
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void Emit(T msg, std::any info = nullptr) {
		emit SendMsg(HashID(msg), info);
	}
};

class MViewCommunicator : public QMainWindow, public MsgHandler {
	Q_OBJECT
public:
	MViewCommunicator(QWidget* parent = nullptr) :QMainWindow(parent) {
		qRegisterMetaType<MsgID>("MsgID");
		qRegisterMetaType<std::any>("std::any");
	}
signals:
	void SendMsg(MsgID, std::any);
public:
	void HandleHashMsg(MsgID msgID, std::any info = std::any{}) {
		if (bool findHandler = m_msgHandlers.find(msgID) != m_msgHandlers.end()) {
			m_msgHandlers[msgID](info);
		}
	}
public:
	template<typename Communicator1>
	void Connect(Communicator1* receiver) {
		connect(this, &MViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
	template<typename Communicator1>
	void Disconnect(Communicator1* receiver) {
		disconnect(this, &MViewCommunicator::SendMsg, receiver, &Communicator1::HandleHashMsg);
	}
protected:
	template<typename T, typename = std::enable_if_t<std::is_enum<T>::value, void>>
	void Emit(T msg, std::any info = std::any{}) {
		emit SendMsg(HashID(msg), info);
	}
};

namespace InterConnection {
template<typename Communicator1, typename Communicator2>
void Connect(Communicator1* c1, Communicator2* c2)
{
	c1->Connect(c2);
	c2->Connect(c1);
}

template<typename Communicator1, typename Communicator2>
void Disconnect(Communicator1* c1, Communicator2* c2)
{
	c1->Disconnect(c2);
	c2->Disconnect(c1);
}
}
补充

由于现在大多软件都是基于QT进行开发,QT又有自己的事件循环机制,所以只能进行拓展通信方式,也可以自行实现事件循环,但是跟QT结合起来用就不太适宜了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2158356.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

老照片修复软件有哪些?6个工具轻松搞定

在回忆的长廊中&#xff0c;老照片承载着岁月的痕迹和珍贵的记忆。 然而&#xff0c;时间的流逝往往让这些宝贵的瞬间变得模糊不清。幸运的是&#xff0c;现代科技赋予了我们修复这些老照片的能力。 面对市场上众多的老照片自动修复软件&#xff0c;选择一个合适的工具变得尤…

Apache APISIX学习(1):介绍、docker启动

一、介绍 Apache APISIX 是一个动态、实时、高性能的 API 网关&#xff0c; 提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。你可以把 Apache APISIX 当做流量入口&#xff0c;来处理所有的业务数据&#xff0c;包括动态路由、动态上游…

得物自建 Redis 无人值守资源均衡调度设计与实现

目录&#xff1a; 一、为什么要做资源均衡调度 二、为什么要做自动化资源均衡调度 三、如何合理选择迁移节点 四、如何保障迁移过程中可靠性1. 添加从节点2. 检查同步数据正常3. 执行主从切换4. 检查主从切换正常5. 删除待迁移节点6. 消息通知 五、迁移任务管理展示 六、总结 …

户用光伏项目难管理,到底该怎么办?

一、鹧鸪云光伏业务管理软件&#xff1a;一站式管理利器 鹧鸪云光伏业务管理软件&#xff0c;作为一款专为光伏行业量身定制的智能化管理工具&#xff0c;集成了项目管理、运维管理、数据分析、用户服务等多功能模块于一体&#xff0c;旨在通过数字化手段&#xff0c;实现户用…

Nature Genetics|三代测序微量建库技术:媲美WGBS的直接甲基化检测

DNA修饰和甲基化是理解基因调控机制的关键。以往&#xff0c;我们的经验表明&#xff0c;使用三代测序从未经扩增的长DNA模板中同时读取序列信息和碱基修饰&#xff0c;需要投入大量的DNA样本来构建文库。 今天&#xff0c;小编带大家看一篇2024年发表于《Nature Genetics》的…

【MAUI】FlexLayout

文章目录 概述属性方向和对齐方式DirectionWrapJustifyContentAlignItemsAlignContent 圣杯布局来源 概述 FlexLayout弹性布局&#xff0c;和前端的Flex弹性布局&#xff0c;几乎一样。FlexLayout是容器&#xff0c;可以定义Direction/主轴方向、Wrap/子元素在主轴方向上是否换…

Vue使用Vue Router路由:开发单页应用

1、路由基础 在单页 Web 应用中&#xff0c;整个项目只有一个 HTML 文件&#xff0c;不同视图&#xff08;组件的模块&#xff09;的内容都是在同一个页面中渲染的。当用户切换页面时&#xff0c;页面之前的跳转都是在浏览器端完成的&#xff0c;这时就需要使用前端路由。 路…

蒙古语有方言差异吗?

蒙古语存在方言差异&#xff0c;主要分为西部方言和东部方言两大类。西部方言&#xff0c;即蒙古方言或喀尔喀方言&#xff0c;主要在蒙古国使用&#xff0c;是该国的官方语言。东部方言&#xff0c;又称布里亚特方言或巴尔虎-布里亚特方言&#xff0c;主要在中国内蒙古自治区和…

deepin桌面版连接windows远程桌面

在Linux系统中&#xff0c;要登录到Windows系统&#xff0c;通常可以使用远程桌面协议(RDP)。你需要在Linux系统上安装RDP客户端。 使用如下命令安装rdp协议&#xff1a; sudo apt-get install xrdp 安装成功后&#xff0c;启动rdp服务。 sudo systemctl start xrdp 有了r…

vscode缩进 和自动格式化

如下图&#xff0c;缩进太大了。 检查2个地方 prettierrc.cjs文件。此处决定缩进几个tab vscode 的设置。 保存的时候 格式化。

Apache Druid命令执行(CVE-2021-25646)

漏洞详情&#xff1a; Apache Druid 是用Java编写的面向列的开源分布式数据存储系统&#xff0c;旨在快速获取大量事件数据&#xff0c;并在数据之上提供低延迟查询。 Apache Druid含有能够执行嵌入在各种类型请求中由用户提供的JavaScript代码功能。此功能适用于高度信任环境…

Java_Day04学习

类继承实例 package com.dx.test03; public class extendsTest {public static void main(String args[]) {// 实例化一个Cat对象&#xff0c;设置属性name和age&#xff0c;调用voice()和eat()方法&#xff0c;再打印出名字和年龄信息/********* begin *********/Cat cat ne…

李飞飞创业公司World Labs:引领AI新方向的“大世界模型”

引言 随着人工智能的不断进步&#xff0c;AI领域涌现了许多新兴技术和研究方向。在这其中&#xff0c;李飞飞创办的World Labs凭借其独特的“空间智能”和“大世界模型”&#xff08;Large World Model, LWM&#xff09;理念&#xff0c;迅速成为焦点。尤其是在获得了2.3亿美元…

python 斑马打印模板

打印代码逻辑如下&#xff1b; 包括样式、表格 import win32printdef print_zpl_from_usb_printer(printer_name, zpl_content):# 打开打印机hPrinter win32print.OpenPrinter(printer_name)if hPrinter is None:print(f"Failed to open printer: {printer_name}")…

淘宝商品评论数据获取API接口响应参数列表展示(可测key)

item_review-获得淘宝商品评论 在电商领域&#xff0c;商品评论数据是商家和消费者都极为关注的重要信息。通过这些数据&#xff0c;商家可以了解产品的市场反馈&#xff0c;优化产品和服务&#xff1b;而消费者则可以参考其他用户的评价&#xff0c;做出更明智的购买决策。然…

Vulkan 学习(9)---- vkSuraceKHR 创建

目录 OverView创建窗口表面参考代码 OverView Vulkan 是一个平台无关的图形API&#xff0c;这意味着它不能直接与特定的窗口系统(Windows&#xff0c;linux 和 macOS 的窗口系统)进行交互 为了解决这个问题&#xff0c;Vulkan 引入了窗口系统集成(Window System Intergration …

Flutter为Android添加签名并打包

前言 我们需要将App进行数字签名才能发布到商店里。在这里就具体描述一下如果给App添加签名 为App签名 创建一个用户上传的秘钥库 如果你已经有一个秘钥库了&#xff0c;可以直接跳到下一步&#xff0c;如果没有则按照下面的指令创建一个 keytool 可能不在我们的系统路径中…

Vxe UI vue vxe-table 实现自适应列宽,根据内容自适应列的宽度

Vxe UI vue vxe-table 实现自适应列宽&#xff0c;根据内容自适应列的宽度 之前老版本是通过计算字符数量&#xff0c;然后给动态给每一列设置宽度&#xff0c;不仅麻烦&#xff0c;还不好复用。 看了 API 发现 v4.7 和 v3.9 版本已经直接就能支持了&#xff0c;只需加上 widt…

英飞凌TC3xx -- Bootstrap Loader分析

目录 1.Bootstrap Loaders作用 2.CAN BSL详解 2.1 CAN BSL的时钟系统 2.2 CAN BSL流程 3.小结 英飞凌TC3xx的Platform Firmware章节里&#xff0c;提供了多种启动模式&#xff1a; Internal start from Flash&#xff1a;b111Alternate Boot Mode&#xff1a;b110Generic …

宠物鱼油补充剂行业调研:未来几年年复合增长率CAGR为7.8%

宠物鱼油补充剂是一种营养补充剂&#xff0c;富含从鱼类中提取的欧米伽-3 脂肪酸&#xff08;主要是 EPA 和 DHA&#xff09;&#xff0c;专为宠物设计&#xff0c;以改善其健康状况。鱼油补充剂富含奥米加-3 脂肪酸&#xff0c;已被证明对宠物健康有诸多益处&#xff0c;包括改…