总线模型"/>
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
该类是事件类,主要内容有:
- 事件名称
- 事件是否带有参数
- 事件参数的设置和获取
- 事件的发布(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 之 事件总线模型
发布评论