Qt 中使用消息中心,一处发送,其他订阅处处理逻辑

使用 Q_GLOBAL_STATIC 实现(线程安全)

全局消息中心

// GlobalMessageCenter.h

#pragma once

#include <QObject>

#include <QHash>

#include <QVector>

#include <QVariant>

#include <functional>

#include <QMutex>

class GlobalMessageCenter : public QObject

{

Q_OBJECT

public:

// 单例访问

static GlobalMessageCenter* instance();

// 订阅消息

void subscribe(const QString& messageType, QObject* receiver,

const char* slot, Qt::ConnectionType type = Qt::AutoConnection);

// 取消订阅

void unsubscribe(const QString& messageType, QObject* receiver);

void unsubscribeAll(QObject* receiver);

// 发布消息

void publish(const QString& messageType, const QVariant& data = QVariant());

// 同步发布(阻塞直到所有处理器完成)

void publishSync(const QString& messageType, const QVariant& data = QVariant());

signals:

// 全局信号

void messagePublished(const QString& messageType, const QVariant& data);

private:

explicit GlobalMessageCenter(QObject* parent = nullptr);

~GlobalMessageCenter();

struct Subscription {

QObject* receiver;

const char* slot;

Qt::ConnectionType connectionType;

};

QHash<QString, QVector<Subscription>> m_subscriptions;

QMutex m_mutex;

};

// GlobalMessageCenter.cpp

#include "GlobalMessageCenter.h"

Q_GLOBAL_STATIC(GlobalMessageCenter, globalMessageCenter)

GlobalMessageCenter::GlobalMessageCenter(QObject* parent)

: QObject(parent)

{

}

GlobalMessageCenter::~GlobalMessageCenter()

{

QMutexLocker locker(&m_mutex);

m_subscriptions.clear();

}

GlobalMessageCenter* GlobalMessageCenter::instance()

{

return globalMessageCenter;

}

void GlobalMessageCenter::subscribe(const QString& messageType, QObject* receiver,

const char* slot, Qt::ConnectionType type)

{

QMutexLocker locker(&m_mutex);

Subscription sub{receiver, slot, type};

m_subscriptions[messageType].append(sub);

// 连接全局信号

connect(this, &GlobalMessageCenter::messagePublished,

receiver, slot, type);

qDebug() << "订阅消息:" << messageType

<< "接收者:" << receiver

<< "槽函数:" << slot;

}

void GlobalMessageCenter::unsubscribe(const QString& messageType, QObject* receiver)

{

QMutexLocker locker(&m_mutex);

if (m_subscriptions.contains(messageType)) {

auto& subs = m_subscriptions[messageType];

for (auto it = subs.begin(); it != subs.end(); ) {

if (it->receiver == receiver) {

disconnect(this, &GlobalMessageCenter::messagePublished,

receiver, it->slot);

it = subs.erase(it);

} else {

++it;

}

}

if (subs.isEmpty()) {

m_subscriptions.remove(messageType);

}

}

}

void GlobalMessageCenter::unsubscribeAll(QObject* receiver)

{

QMutexLocker locker(&m_mutex);

for (auto it = m_subscriptions.begin(); it != m_subscriptions.end(); ) {

auto& subs = it.value();

for (auto subIt = subs.begin(); subIt != subs.end(); ) {

if (subIt->receiver == receiver) {

disconnect(this, &GlobalMessageCenter::messagePublished,

receiver, subIt->slot);

subIt = subs.erase(subIt);

} else {

++subIt;

}

}

if (subs.isEmpty()) {

it = m_subscriptions.erase(it);

} else {

++it;

}

}

}

void GlobalMessageCenter::publish(const QString& messageType, const QVariant& data)

{

qDebug() << "发布异步消息:" << messageType << "数据:" << data;

emit messagePublished(messageType, data);

}

void GlobalMessageCenter::publishSync(const QString& messageType, const QVariant& data)

{

QMutexLocker locker(&m_mutex);

qDebug() << "发布同步消息:" << messageType << "数据:" << data;

if (m_subscriptions.contains(messageType)) {

const auto& subs = m_subscriptions[messageType];

for (const auto& sub : subs) {

if (sub.receiver) {

// 直接调用槽函数

QMetaObject::invokeMethod(sub.receiver, sub.slot + 1, // 去掉 SIGNAL() 的括号

Qt::DirectConnection,

Q_ARG(QString, messageType),

Q_ARG(QVariant, data));

}

}

}

}

/*

slot + 1 可能会有风险 替换为以下方法

// 推荐

void callSlotSafely(QObject* obj, const char* slot) {

if (!slot || slot[0] == '\0') {

return;

}

// 验证格式

if (slot[0] != '1' && slot[0] != '2') {

qWarning() << "Invalid signature:" << slot;

return;

}

// 使用方法名

const char* methodName = slot + 1;

// 或者使用 QMetaMethod

QString signature = QString::fromUtf8(slot + 1);

const QMetaObject* meta = obj->metaObject();

int methodIndex = meta->indexOfMethod(signature.toUtf8().constData());

if (methodIndex >= 0) {

QMetaMethod method = meta->method(methodIndex);

method.invoke(obj, Qt::QueuedConnection);

}

}

*/

使用 示例

// ReceiverWidget.cpp

#include "ReceiverWidget.h"

#include <QVBoxLayout>

#include <QHBoxLayout>

#include <QDateTime>

#include <QJsonObject>

#include <QJsonDocument>

ReceiverWidget::ReceiverWidget(QWidget* parent)

: QWidget(parent)

{

setupUI();

registerMessages();

}

ReceiverWidget::~ReceiverWidget()

{

GlobalMessageCenter::instance()->unsubscribeAll(this);

}

void ReceiverWidget::registerMessages()

{

auto globalMsgCenter = GlobalMessageCenter::instance();

// 使用 GlobalMessageCenter

globalMsgCenter->subscribe(MessageType::USER_LOGIN, this,

SLOT(onGlobalMessage(QString, QVariant)));

}

void ReceiverWidget::onUserLogin(const QVariant& data)

{

}

void ReceiverWidget::onGlobalMessage(const QString& messageType, const QVariant& data)

{

QString log = QString("[%1] 全局消息中心收到: %2")

.arg(QDateTime::currentDateTime().toString("hh:mm:ss"))

.arg(messageType);

m_logText->append(log);

}

相关推荐
MR.P_H_2 小时前
QT创建新工程,无法正常编译(Kit套件无法正常配置)
开发语言·qt
森G3 小时前
28、视图基类 QAbstractItemView---------Model/View模型视图
c++·qt
不想看见4043 小时前
C++/Qt 使用 Tushare 获取股票信息
c++·qt·信息可视化
liwenzhuola15 小时前
解决 Ubuntu 上 Qt Creator 项目编译失败的问题
数据库·qt·ubuntu
娇娇yyyyyy15 小时前
QT编程(18): Qt QItemSelectionModel介绍
开发语言·qt
sthnyph16 小时前
QT开发:事件循环与处理机制的概念和流程概括性总结
开发语言·qt
机器视觉知识推荐、就业指导20 小时前
LVGL真能动摇Qt的地位吗?
开发语言·qt·系统架构
羊小猪~~1 天前
【QT】--QWIdget与QDialog
开发语言·数据库·c++·后端·qt·求职招聘
Blasit1 天前
Qt 程序打包,运行提示找不到或无法加载平台插件 qwindows.dll
开发语言·windows·qt