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_subscriptionsmessageType.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_subscriptionsmessageType;

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_subscriptionsmessageType;

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 || slot0 == '\0') {

return;

}

// 验证格式

if (slot0 != '1' && slot0 != '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);

}

相关推荐
大丑哥13 小时前
QT QML 中实现高亮功能
qt
小白不想画工图16 小时前
银河麒麟V10系统安装QT5.12
linux·开发语言·qt·银河麒麟
mengzhi啊17 小时前
QEventLoop loop配合loop.exec();替代while(1)。电脑cpu占用为0,使用while(1)cpu占用率100%
c++·qt
@福者20 小时前
C++ QT 入门
开发语言·c++·qt
长友cy20 小时前
Qt官方示例BluetoothLowEnergyGame源码阅读
开发语言·qt
Quz20 小时前
QML 日期解析:字符串转日期与时间戳互转
qt
Coodor20 小时前
信创系统基于QT5操作NFC读写器
开发语言·qt·智能卡·nfc读写器·yw-607hc
励志不掉头发的内向程序员20 小时前
【LibreCAD 2D架构】从鼠标点击到屏幕像素:LibreCAD绘图架构全链路解析之Action与命令系统
linux·开发语言·c++·qt·学习·系统架构
沧海一笑-dj21 小时前
【Qt】Qt6.11安装教程详解
qt·qt creator·qt6·qt安装·qt安装教程
似水এ᭄往昔1 天前
【QT】--常用控件(QWidget的核心属性)
服务器·开发语言·qt