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

}

相关推荐
Quz20 小时前
QML 信号与槽:直接绑定与跨文件通信
qt·编程语言·设计
GIS阵地20 小时前
QgsSingleBandPseudoColorRenderer 完整详解(QGIS 3.40.13 C++)
开发语言·前端·c++·qt·qgis
丰锋ff1 天前
1.2 Qt常用基础类型
开发语言·qt
nianniannnn1 天前
Qt QMessageBox知识点
开发语言·数据库·qt
J_yyy1 天前
【Qt核心组件类框架】
开发语言·qt
Drone_xjw1 天前
双网卡下Qt程序数据包“乱飞”之谜:为什么必须断开再连接
开发语言·qt
Quz1 天前
QML 网格与流式布局:Grid/GridLayout 与 Flow
qt
qq_401700411 天前
Qt容器性能优化:QVector、QHash、QMap到底应该怎么选?
开发语言·qt·性能优化
Quz1 天前
QML 线性布局:Row/RowLayout 与 Column/ColumnLayout
前端·qt
imatt2 天前
Qt Creator 在Windows下编译信息乱码
windows·qt