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

server/2024/9/23 22:35:46/
描述

基于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;
}
}
#endifclass 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.ppmy.cn/server/121017.html

相关文章

Android 命令行关机

在 Android 设备上&#xff0c;可以通过以下命令行命令来关机&#xff1a; adb shell reboot -p其中&#xff1a; adb shell&#xff1a;通过 ADB 进入设备的命令行环境。reboot -p&#xff1a;执行关机操作&#xff0c;-p 表示关机而不是重启。 如果你是在设备本地的终端上而…

Spring源码学习:SpringMVC(1)根容器初始化

目录 前言 Servlet介绍 Servlet 接口及其实现类结构 Servlet生命周期 初始化阶段 运行阶段 销毁阶段 SpringMVC源码环境构建 web.xml配置 applicationContext.xml spring-mvc.xml 相关bean以及静态页面 TestController TestService index.jsp 源码剖析-根容器初…

DNS是什么?怎么设置

NS是什么意思?有什么用呢?专业的说DNS就是域名系统 (Domain Name System)的简称&#xff0c;也就是IT人士常说的域名解析系统。主要是让用户在互联网上通过域名找到域名对应的IP地址&#xff0c;因为IP地址都是一串数字(例如&#xff1a;192.168.0.1)不方便记忆&#xff0c;便…

基于MaxScale搭建MariaDB读写分离集群的方法【2024年最新版】

1、什么是MaxScale MaxScale是MariaDB数据库的一个中间件&#xff0c;为MariaDB提供代理服务&#xff0c;主要可以实现读写分离和一定的负载均衡功能&#xff0c;其中读写分离可将读操作和写操作分离到不同的数据库服务器上&#xff0c;以提高系统的整体性能和扩展性&#xff…

Authentication plugin ‘caching _sha2_password’ cannot be loaded:

使用navicat连接mysql8.0以及以上版本的时候&#xff0c;出现这样的错误&#xff1a;“Authentication plugin ‘caching _sha2_password’ cannot be loaded:” 出现这个原因是MySQL8之前的版本中加密规则是mysql_native_password&#xff0c;而在MySQL8之后&#xff0c;加密规…

巴黎嫩事件对数据信息安全的影响及必要措施

2024年9月17日&#xff0c;黎巴嫩首都贝鲁特发生了多起小型无线电通信设备爆炸事件&#xff0c;导致伊朗驻黎巴嫩大使受轻伤。这一事件不仅引发了对安全的广泛关注&#xff0c;也对数据信息安全提出了新的挑战。 王工 18913263502 对数据信息安全的影响&#xff1a; 数据泄露风…

[leetcode刷题]面试经典150题之2移除元素(简单)

题目 移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作…

SpringBoot开发——集成Tess4j实现OCR图像文字识别

文章目录 1、准备工作2、配置Tess4j3、编写OCR服务4、创建控制器5、测试集成6、处理多语言与自定义字体7、总结 随着数字化转型的推进&#xff0c;光学字符识别&#xff08; OCR, Optical Character Recognition&#xff09;技术在各种应用场景中变得愈发重要。 OCR技术可以…