Qt 之 事件总线模型

编程入门 行业动态 更新时间:2024-10-18 06:05:31

Qt 之 事件<a href=https://www.elefans.com/category/jswz/34/1769217.html style=总线模型"/>

Qt 之 事件总线模型

文章目录

  • Qt 之 开源事件总线模块
    • libgitlevtbus
    • 模型
    • 模块
      • Event
      • EventBus
      • Module
      • ModuleDelegate
    • TestCase

Qt 之 开源事件总线模块

libgitlevtbus

用到了libgitlevtbus
(libgitlevtbus)[]

  • 介绍
    libgitlevtbus 是一个基于Qt的开源的事件总线(消息总线 ) , BSD lisence.
  • 特征
1. Easy to use (c++11 feature supported: lambda expression, member function callback, ...) //C++11新特性,lambda表达式, 成员函数回调
2. Custom event support (carry custom parameters)										   //用户事件支持(可携带用户自定义的参数)
3. Events can be deliverd across threads												   //事件可以跨线程
  • Demo
#include "gitlmodule.h"
#include <QDebug>int main(int argc, char *argv[])
{GitlModule cModule;/// subscribe to an eventcModule.subscribeToEvtByName("I am a test event",[](GitlEvent& rcEvt)->bool{qDebug() << "Hello GitlEvtBus!";return true;});GitlEvent cEvent("I am a test event");              ///< create an eventcEvent.dispatch();                                  ///< dispatch/// output: "Hello GitlEvtBus!"*/return 0;
}

模型

模块

该事件总线模型主要有以下几个类构成:

  • Event
  • EventBus
  • Module
  • ModuleDelegate

Event

该类是事件类,主要内容有:

  1. 事件名称
  2. 事件是否带有参数
  3. 事件参数的设置和获取
  4. 事件的发布(dispatch):调用Bus的Post接口,将事件派发到总线上,所有订阅该事件的module都会收到消息
  • gitlevent.h
#ifndef GITLEVENT_H
#define GITLEVENT_H
#include <QString>
#include <QMap>
#include <QVariant>
#include "gitldef.h"
#include "gitleventparam.h"class GitlModule;
class GitlEventBus;/*!* \brief The GitlEvent class represents an event.*  If you want to create an custom event by inherit GitlEvent, you ***MUST****  reimplement the 'clone' method in this class. This can be done by adding*  VIRTUAL_COPY_PATTERN(subclassname) in the subclass. Otherwise the application*  may crash*/
class GitlEvent
{/// virtual copy pattern, please add this macro to all the subclassCLONABLE(GitlEvent)public:GitlEvent( const QString& strEvtName );GitlEvent();virtual ~GitlEvent() {}/*!* \brief hasParameter if this event carries a specific parameter* \param strParam parameter name* \return*/bool hasParameter(QString strParam) const;/*!* \brief getParameter get the value of a specific parameter* \param strParam parameter name* \return parameter value, if it does not exist, return a default-constructed QVariant*/QVariant getParameter(const QString& strParam ) const;/*!* \brief setParameter set the value of a  specific parameter* \param strParam parameter name* \param rvValue parameter value* \return*/bool setParameter(const QString& strParam, const QVariant& rvValue);/*!* \brief dispatch dispatch this event to event bus, all module subscribed to this event name will be notified.* \param pcEventBus If pcEventBus is NULL, it will find a global (default) event bus and post the event onto the bus.*                   Or you can specify another event bus.*/void dispatch(GitlEventBus *pcEventBus = NULL) const;protected:    ADD_CLASS_FIELD(QString, strEvtName, getEvtName, setEvtName)            ///< event nameADD_CLASS_FIELD_NOSETTER(GitlEventParam, cParameters, getParameters)    ///< event parameters-value pair};
  • gitlevent.cpp
#include "gitlevent.h"
#include "gitlmodule.h"
#include <QDebug>
#include "gitleventbus.h"
#include <QSharedPointer>
GitlEvent::GitlEvent( const QString& strEvtName )
{this->m_strEvtName = strEvtName;
}GitlEvent::GitlEvent()
{this->m_strEvtName = "UNKNOWN";
}bool GitlEvent::hasParameter(QString strParam) const
{return m_cParameters.hasParameter(strParam);
}QVariant GitlEvent::getParameter(const QString& strParam ) const
{return m_cParameters.getParameter(strParam);
}bool GitlEvent::setParameter(const QString& strParam, const QVariant& rvValue)
{m_cParameters.setParameter(strParam, rvValue);return true;
}void GitlEvent::dispatch(GitlEventBus* pcEventBus) const
{if(pcEventBus == NULL)GitlEventBus::getInstance()->post(*this);elsepcEventBus->post(*this);
}

EventBus

事件总线,在实际应用中,可以有多条事件总线,每条总线挂接不同的Module.

  • 单例模式,在该类中,存在该类型的单例模式,当module未指定某个Bus时, 默认情况都使用该单例
  • register: 将ModuleDelegate 的denotate关联至该总线的post接口发出的eventTriggered信号
  • post: 将Event传递至总线。上述的Event中的dispatch接口,最终调用的某个Bus对象的post接口。

EventBus的核心: 注册ModuleDelegate, 发布Event 消息

  • gitleventbus.h
#ifndef GITLEVENTBUS_H
#define GITLEVENTBUS_H#include <QList>
#include <QObject>
#include <QMutex>
#include <QMutexLocker>
#include <QSharedPointer>
#include "gitldef.h"
#include "gitlevent.h"
#include "gitlmodule.h"class GitlModuleDelegate;
/*!* \brief The GitlEventBus class represents the event bus*/
class GitlEventBus : public QObject
{Q_OBJECT
private:GitlEventBus();public:/*!* \brief create The safe way to explictly create a new event bus* \return*/static GitlEventBus *create();/*!* \brief registerModule connect a module to the event bus* \param pcModule* \return*/bool registerModule(GitlModuleDelegate *pcModule);/*!* \brief unregisterModule disconncet a module from the event bus* \param pcModule* \return*/bool unregisterModule(GitlModuleDelegate *pcModule);public slots:/*! send event to event bus*/void post(const GitlEvent &rcEvt) const;signals:/*! message to send*/void eventTriggered( QSharedPointer<GitlEvent> pcEvt ) const;///SINGLETON design patternSINGLETON_PATTERN_DECLARE(GitlEventBus)};#endif // GITLEVTBUS_H
  • gitleventbus.cpp
#include "gitleventbus.h"
#include <QDebug>SINGLETON_PATTERN_IMPLIMENT(GitlEventBus)GitlEventBus::GitlEventBus()
{
}GitlEventBus *GitlEventBus::create()
{return new GitlEventBus();
}/*! connect a module to the event bus*/
Q_DECLARE_METATYPE( QSharedPointer<GitlEvent> )
bool GitlEventBus::registerModule(GitlModuleDelegate* pcModule)
{    qRegisterMetaType< QSharedPointer<GitlEvent> >("QSharedPointer<GitlEvent>");connect(this,     SIGNAL(eventTriggered(QSharedPointer<GitlEvent>) ),pcModule, SLOT  (detonate      (QSharedPointer<GitlEvent>) ),Qt::AutoConnection );return true;
}bool GitlEventBus::unregisterModule(GitlModuleDelegate *pcModule)
{return disconnect(this, NULL, pcModule, NULL);
}/*! send event to event bus*/
void GitlEventBus::post(const GitlEvent& rcEvt) const
{    QSharedPointer<GitlEvent> pcEvtCopy( rcEvt.clone() );/// notify modulesemit eventTriggered(pcEvtCopy);}

Module

Module 和 ModuleDelegate 使用了委托模式, 将实际的工作都交给了ModuleDelegate处理了。

委托模式:
一个对象接收到了请求,但是自己不处理,交给另外的对象处理,就是委托模式,例如 老板接到了活,
然后把活转手给了工人去做。

这里的Module有一个私有成员: ModuleDelegate, Event其实并不知道这一层关系的存在,在它眼里,只有BusEvent。
Module的行为(动作), 最后都转化成ModuleDelegate去执行了, 仿佛只是套了个壳。

  • gitlmodule.h
#include <QSharedPointer>#include "gitldef.h"
#include "gitlevent.h"#include "gitlmoduledelegate.h"class GitlEventBus;/*!* \brief The GitlModule class represents a module*/class GitlModule
{
public:/*** @brief GitlModule Represents a module in the event bus. It will keep listening to events*                   in the event bus and catch those it is interested in.* @param pcEventBus If pcEventBus it will find a gloabl event bus using singleton pattern.*                   Or you can specify an exsiting event bus.*/GitlModule(GitlEventBus* pcEventBus = NULL);/*!* \brief subscribeToEvtByName Subscribe to an event* \param strEvtName event name* \param pfListener listener callback function*/void subscribeToEvtByName(const QString& strEvtName,const GitlCallBack& pfListener  );/*!* \brief unsubscribeToEvtByName Unsubscribe to an event* \param strEvtName event name*/void unsubscribeToEvtByName( const QString& strEvtName );/*!* \brief dispatchEvt Dispatch an event* \param rcEvt event*/void dispatchEvt(GitlEvent &rcEvt );/*!* \brief setModuleName Set the name of this module. That's ok if you do not* give a name to this module. But for better debugging, we recommend you name it.* \param strModuleName name for this module*/void setModuleName(QString strModuleName );/*** @brief getEventBus Get the event bus that this module is attached to* @return*/GitlEventBus* getEventBus();/*!* \brief detach Detach the module*/void detach();/*!* \brief attach Attach the module to a new event bus* \param pcEventBus*/void attach(GitlEventBus *pcEventBus);/// Delegate pattern/// Avoiding this class becoming a subclass of QObject/// (GUI class is based on QOBject, but QObject doesn't support virtual inheritance).ADD_CLASS_FIELD_PRIVATE( GitlModuleDelegate, cDelegate )
};#endif // GITLMODULE_H
  • gitlmodule.cpp
#include "gitlmodule.h"
#include "gitleventbus.h"
#include <QDebug>GitlModule::GitlModule(GitlEventBus *pcEventBus) :m_cDelegate(this, pcEventBus)
{    
}void GitlModule::subscribeToEvtByName( const QString& strEvtName,const GitlCallBack& pfListener )
{return m_cDelegate.subscribeToEvtByName(strEvtName, pfListener);
}void GitlModule::unsubscribeToEvtByName( const QString& strEvtName )
{return m_cDelegate.unsubscribeToEvtByName(strEvtName);
}void GitlModule::dispatchEvt( GitlEvent& rcEvt )
{m_cDelegate.dispatchEvt(rcEvt);
}void GitlModule::setModuleName( QString strModuleName )
{m_cDelegate.setModuleName(strModuleName);
}GitlEventBus *GitlModule::getEventBus()
{return m_cDelegate.getGitlEvtBus();
}void GitlModule::detach()
{m_cDelegate.detach();
}void GitlModule::attach(GitlEventBus *pcEventBus)
{m_cDelegate.attach(pcEventBus);
}

ModuleDelegate

由于真正干活的是这位老兄,所以,它除了和Module具有类似的接口,还需要额外维护一些私有成员,比如Module的名字,Event事件和回调接口的QMap关系表。

  • gitlmoduledelegate.h
#ifndef GITLMODULEDELEGATE_H
#define GITLMODULEDELEGATE_H#include <QObject>
#include <QMap>
#include <QMutex>
#include <QMutexLocker>
#include <QSharedPointer>
#include <functional>
#include "gitldef.h"
#include "gitlevent.h"class GitlModule;
class GitlEventBus;///
/// \brief GitlCallBack gitl event callback function
///
typedef std::function<bool (GitlEvent&)> GitlCallBack;class GitlModuleDelegate : public QObject
{Q_OBJECTfriend class GitlModule;  //can access the GitlModule
private:explicit GitlModuleDelegate(GitlModule *pcDelegator, GitlEventBus *pcEventBus = NULL);public:/*!* \brief subscribeToEvtByName listening to an event by name* \param strEvtName event name*/void subscribeToEvtByName( const QString& strEvtName,GitlCallBack pfListener );/*!* \brief subscribeToEvtByName not listening to an event by name* \param strEvtName event name*/void unsubscribeToEvtByName( const QString& strEvtName );/*!* \brief dispatchEvt dispatch an event to subscribers* \param pcEvt event*/void dispatchEvt(const GitlEvent &rcEvt  ) const;/*!* \brief detach Detach the module*/void detach();/*!* \brief attach Attach the module to a new event bus* \param pcEventBus*/void attach(GitlEventBus *pcEventBus);public slots:/*!* \brief detonate notifyed by event bus* \param cEvt* \return*/bool detonate( QSharedPointer<GitlEvent> pcEvt );protected:bool xIsListenToEvt(const QString& strEvtName);ADD_CLASS_FIELD( QString, strModuleName, getModuleName, setModuleName )ADD_CLASS_FIELD_PRIVATE( CONCATE(QMap<QString, GitlCallBack>), cListeningEvts )ADD_CLASS_FIELD_NOSETTER( GitlEventBus*, pcGitlEvtBus, getGitlEvtBus )ADD_CLASS_FIELD_PRIVATE(GitlModule*, pcDelegator)};#endif // GITLMODULEDELEGATE_H
  • gitlmoduledelegate.cpp
#include "gitlmoduledelegate.h"
#include "gitleventbus.h"
#include <QDebug>
#include <iostream>
using namespace std;
GitlModuleDelegate::GitlModuleDelegate(GitlModule *pcDelegator, GitlEventBus* pcEventBus)
{m_pcDelegator = pcDelegator;if(pcEventBus == NULL)m_pcGitlEvtBus = GitlEventBus::getInstance();elsem_pcGitlEvtBus = pcEventBus;m_pcGitlEvtBus->registerModule(this);      //调用eventBus注册moduleDelegatem_strModuleName = "undefined_module_name";}void GitlModuleDelegate::subscribeToEvtByName(const QString& strEvtName, GitlCallBack pfListener )
{m_cListeningEvts.insert(strEvtName, pfListener);return;
}void GitlModuleDelegate::unsubscribeToEvtByName( const QString& strEvtName )
{m_cListeningEvts.remove(strEvtName);
}bool GitlModuleDelegate::detonate(QSharedPointer<GitlEvent> pcEvt )
{QMap<QString, std::function<bool (GitlEvent&)>>::iterator p =m_cListeningEvts.find(pcEvt->getEvtName());if( p != m_cListeningEvts.end() ){(p.value())(*pcEvt.data());}return true;
}bool GitlModuleDelegate::xIsListenToEvt( const QString& strEvtName )
{return m_cListeningEvts.contains(strEvtName);
}void GitlModuleDelegate::dispatchEvt( const GitlEvent& rcEvt ) const
{if(m_pcGitlEvtBus != NULL)m_pcGitlEvtBus->post(rcEvt);
}void GitlModuleDelegate::detach()
{if(m_pcGitlEvtBus != NULL)m_pcGitlEvtBus->unregisterModule(this);m_pcGitlEvtBus = NULL;
}void GitlModuleDelegate::attach(GitlEventBus *pcEventBus)
{if(pcEventBus == NULL)return;detach();m_pcGitlEvtBus = pcEventBus;m_pcGitlEvtBus->registerModule(this);
}

TestCase

#include <QCoreApplication>
#include <iostream>
#include <QtTest/QtTest>
#include <QTest>
#include <QSharedPointer>
#include <QString>
#include <functional>
#include "gitldef.h"
#include "gitlmodule.h"
#include "gitleventbus.h"
using namespace std;/// test event bus
class TestModule : public GitlModule    //继承自GitlModule
{
public:TestModule(GitlEventBus* pcEventBus = NULL):GitlModule(pcEventBus){this->m_bNotified = false;}void subscribeInsideClass(){subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK(TestModule::callback));  //绑定内部的callback函数}bool callback( GitlEvent& rcEvt){Q_UNUSED(rcEvt)this->m_bNotified = true;return true;}ADD_CLASS_FIELD(bool, bNotified, getNotified, setNotified)
};/// custom event, 用户自定义事件,集成GitlEvent
class CustomEvent : public GitlEvent     
{CLONABLE(CustomEvent)
public:CustomEvent( const QString& strEvtName ) : GitlEvent(strEvtName) { m_strCustomVar = "Custom String";  //自定义私有成员}ADD_CLASS_FIELD(QString, strCustomVar, getCustomVar, setCustomVar)
};/// 用户自定义事件监听模块,继承自GitlModule
class CustomEventListener : public GitlModule
{
public:CustomEventListener(){this->m_bNotified = false;}bool callback( GitlEvent& rcEvt){CustomEvent& pcCusEvt = static_cast<CustomEvent&>(rcEvt);this->m_bNotified = true;this->m_strCustomVar = pcCusEvt.getCustomVar();return true;}ADD_CLASS_FIELD(bool, bNotified, getNotified, setNotified)ADD_CLASS_FIELD(QString, strCustomVar, getCustomVar, setCustomVar)
};/// test case
class TestCase : public QObject
{Q_OBJECTprivate slots:void lamdaListening(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1",[&](GitlEvent& e)->bool{Q_UNUSED(e)cModule.setNotified(true);return true;});QVERIFY(!cModule.getNotified());  //QVERIFY(condition)GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(cModule.getNotified());}void listenInsideClass(){TestModule cModule;cModule.subscribeInsideClass();QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cEvt.dispatch();QVERIFY(cModule.getNotified());}void listenOutsideClass(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule, TestModule::callback));QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(cModule.getNotified());}void unsubscribe(){TestModule cModule;cModule.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule, TestModule::callback));cModule.unsubscribeToEvtByName("TEST_EVENT_1");QVERIFY(!cModule.getNotified());GitlEvent cEvt("TEST_EVENT_1");cModule.dispatchEvt(cEvt);QVERIFY(!cModule.getNotified());}/// 一对多,一个事件,多个Module关注void oneToMany(){TestModule cSender;TestModule cModule1;TestModule cModule2;TestModule cModule3;cModule1.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule1, TestModule::callback));cModule2.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule2, TestModule::callback));cModule3.subscribeToEvtByName("TEST_EVENT_2", MAKE_CALLBACK_OBJ(cModule3, TestModule::callback));GitlEvent cEvt1("TEST_EVENT_1");cSender.dispatchEvt(cEvt1);  //某个Module 派发Event, 而其他Module 关注该Event的后进行处理QVERIFY(cModule1.getNotified());QVERIFY(cModule2.getNotified());QVERIFY(!cModule3.getNotified());GitlEvent cEvt2("TEST_EVENT_2");cSender.dispatchEvt(cEvt2);QVERIFY(cModule3.getNotified());}/// 用户自定义事件测试void customEventTest(){CustomEventListener cModule;cModule.subscribeToEvtByName("TEST_EVENT_1",MAKE_CALLBACK_OBJ(cModule, CustomEventListener::callback));CustomEvent cEvt("TEST_EVENT_1");cEvt.dispatch();qDebug()<<__FUNCTION__<<cModule.getNotified();qDebug()<<__FUNCTION__<<cModule.getCustomVar();QVERIFY(cModule.getNotified());QVERIFY(cModule.getCustomVar() == QString("Custom String"));  //用户自定义的参数,  需要关注CModule的getCustomVar}///多个EventBus和多个Module对象void multiplyEventBus(){GitlEventBus* pcBus1 = GitlEventBus::create(); TestModule cModule1(pcBus1); TestModule cModule2(pcBus1);GitlEventBus* pcBus2 = GitlEventBus::create(); TestModule cModule3(pcBus2); TestModule cModule4(pcBus2);/// all module are listening to the same events, but on different event buses.cModule1.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule1, TestModule::callback));cModule2.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule2, TestModule::callback));cModule3.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule3, TestModule::callback));cModule4.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule4, TestModule::callback));/// eventCustomEvent cEvt("TEST_EVENT_1");/// no one get notified because no module is attached to the default event buscEvt.dispatch();QVERIFY(!cModule1.getNotified());QVERIFY(!cModule2.getNotified());QVERIFY(!cModule3.getNotified());QVERIFY(!cModule4.getNotified());/// this will only notify module 1 & 2cEvt.dispatch(pcBus1);QVERIFY(cModule1.getNotified());QVERIFY(cModule2.getNotified());QVERIFY(!cModule3.getNotified());QVERIFY(!cModule4.getNotified());/// this will notify module 3 & 4cEvt.dispatch(cModule3.getEventBus());QVERIFY(cModule3.getNotified());QVERIFY(cModule4.getNotified());/// make sure everyone is attached to the correct event busQVERIFY(cModule1.getEventBus() == pcBus1);QVERIFY(cModule2.getEventBus() == pcBus1);QVERIFY(cModule3.getEventBus() == pcBus2);QVERIFY(cModule4.getEventBus() == pcBus2);/// create cModule5TestModule cModule5(pcBus1);cModule5.subscribeToEvtByName("TEST_EVENT_1", MAKE_CALLBACK_OBJ(cModule5, TestModule::callback));cEvt.dispatch(pcBus2);QVERIFY(!cModule5.getNotified());cModule5.attach(pcBus2);cEvt.dispatch(pcBus2);QVERIFY(cModule5.getNotified());}
};/// test main
QTEST_MAIN(TestCase)
#include "testcase.moc"
  • 运行测试结果
********* Start testing of TestCase *********
Config: Using QtTest library 5.14.2, Qt 5.14.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 5.3.1 20160406 (Red Hat 5.3.1-6))
PASS   : TestCase::initTestCase()
PASS   : TestCase::lamdaListening()
PASS   : TestCase::listenInsideClass()
PASS   : TestCase::listenOutsideClass()
PASS   : TestCase::unsubscribe()
PASS   : TestCase::oneToMany()
QDEBUG : TestCase::customEventTest() customEventTest true
QDEBUG : TestCase::customEventTest() customEventTest "Custom String"
PASS   : TestCase::customEventTest()
PASS   : TestCase::multiplyEventBus()
PASS   : TestCase::cleanupTestCase()
Totals: 9 passed, 0 failed, 0 skipped, 0 blacklisted, 1ms
********* Finished testing of TestCase *********

更多推荐

Qt 之 事件总线模型

本文发布于:2023-06-28 04:04:26,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/921485.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:总线   模型   事件   Qt

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!