CEGUI的事件系统分析-阿里云开发者社区

开发者社区> 杨粼波> 正文

CEGUI的事件系统分析

简介: 当客户向事件系统发送了一个事件之后,即是执行EventSet::fireEvent. EventSet::fireEvent首先执行了GlobalEventSet:: fireEvent,而后才执行其自身的一个方法EventSet::fireEvent_impl,该方法才是真正进行事件处理的方法,由该方法的后缀impl即可得知了.
+关注继续查看

什么是GUI?

       GUI就是图形用户界面(Graphics User Interface),是指采用图形方式显示的计算机操作用户接口.

 

 什么是GUI的事件?

       所谓事件就是指发送给GUI系统的消息,该消息通知GUI系统某种事情已发生,要求作出响应.简言之就是:用户将自己的一个或多个回调函数挂钩到某个“事件”上,一旦“事件”被触发,所有挂钩的函数都被调用。

      显然,事件机制是一个非常有用的并且常用的机制,C#已经将之在语言层面进行了实现,但是C++并无实现.不过很多库都有实现比如:boost::signal. 

 

CEGUI的事件机制是什么?

       CEGUI的事件机制采用的是观察者模式(Observer pattern)的一种实现.很有点像QtSignal/Slot机制,但是又没有那么复杂.

对事件的封装上虽然用到了仿函数,但其实却是为了统一接口,其实质可以是回调函数也可以是仿函数.


CEGUI事件系统的静态类图



向事件系统注册事件的流程

首先客户调用EventSet::subscribeEvent方法,传入的参数为参数名和回调方法.实际上第二个参数的传入过程中有一个创建临时变量SubscriberSlot,其实质上是一个绑定的回调函数(函数指针,成员函数指针,仿函数等)的过程,在图中并没有表现出来.

       接着, EventSet::subscribeEvent方法中会首先调用EventSet::getEventObject方法获取事件,EventSet::getEventObject,如果没有查找到事件,则会创建之.当获取到事件之后, EventSet::subscribeEvent会调用Event::subscribe方法.

       Event::subscribe方法中,会先创建一个BoundSlot的实例,而在BoundSlot的构造方法当中,它会创建一个SubscriberSlot的实例,并将EventSet::subscribeEvent传入的第二个参数保存到SubscriberSlot的实例里面去.



事件系统响应事件的流程

       首先要说明的是,客户首先必须要从EventSet继承下来才行,CEGUI里面有System,Renderer,Window,MouseCursor以及GlobalEventSet.其中GlobalEventSet是单件,系统创建以后就要创建它,EventSet对它是有依赖的,由上图就可以得知.

       EventSet是什么呢?EventSet它是一个事件的容器,它是一个事件处理中心.可以说是事件系统的接口.

       当客户向事件系统发送了一个事件之后,即是执行EventSet::fireEvent. EventSet::fireEvent首先执行了GlobalEventSet:: fireEvent,而后才执行其自身的一个方法EventSet::fireEvent_impl,该方法才是真正进行事件处理的方法,由该方法的后缀impl即可得知了. EventSet::fireEvent_impl先是执行了getEventObject方法查找到事件,然后调用该事件Event的仿函数,该仿函数调用BoundSlot:: m_pSubscriber的仿函数,它最终将会调用到SlotFunctorBase接口的实现的仿函数,而这个仿函数内部调用到的是真正的回调方法.

       SlotFunctorBase这一块实际上是Gof模式之一的适配器模式(adapter pattern)的应用.

 

测试代码:

None.gifusing namespace CEGUI;
None.gif
ExpandedBlockStart.gif/// 事件参数
None.gifclass TestEventArgs : public EventArgs
ExpandedBlockStart.gif{
InBlock.gifpublic:
ExpandedSubBlockStart.gif    TestEventArgs(const int& _n) : n(_n) {}
InBlock.gif
InBlock.gif    int n;    
ExpandedBlockEnd.gif}
;
None.gif
ExpandedBlockStart.gif/// 事件
None.gifclass testEvent : public EventSet
ExpandedBlockStart.gif{
InBlock.gifpublic:
ExpandedSubBlockStart.gif    testEvent(){}
InBlock.gif
InBlock.gif    static const String EventNamespace;
InBlock.gif    static const String EventTest;
InBlock.gif
InBlock.gif    void injectTest(int n)
ExpandedSubBlockStart.gif    {
InBlock.gif        TestEventArgs e(n);
InBlock.gif        fireEvent(EventTest, e, EventNamespace);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}
;
None.gifconst String testEvent::EventTest("test");
None.gifconst String testEvent::EventNamespace("testEvent");
None.gif
ExpandedBlockStart.gif/// 客户
None.gifclass testClient
ExpandedBlockStart.gif{
InBlock.gifpublic:
InBlock.gif    testClient()
ExpandedSubBlockStart.gif    {
InBlock.gif        Init();
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    void Init()
ExpandedSubBlockStart.gif    {
InBlock.gif        new GlobalEventSet();
InBlock.gif        mEvent.subscribeEvent(testEvent::EventTest, Event::Subscriber(&testClient::handleTest, this) );
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    bool handleTest(const CEGUI::EventArgs& e)
ExpandedSubBlockStart.gif    {
InBlock.gif        int val = static_cast<const TestEventArgs&>(e).n;
InBlock.gif        std::cout << val << std::endl;
InBlock.gif        return true;
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    void exe()
ExpandedSubBlockStart.gif    {
InBlock.gif        mEvent.injectTest(99);
InBlock.gif        mEvent.injectTest(5555);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gifprivate:
InBlock.gif    testEvent mEvent;
ExpandedBlockEnd.gif}
;
None.gif
None.gifint main()
ExpandedBlockStart.gif{
InBlock.gif    testClient client;
InBlock.gif    client.exe();
InBlock.gif
InBlock.gif    system("pause");
InBlock.gif    return 0;
ExpandedBlockEnd.gif}

下面对代码进行讲解.
   首先,我们需要声明一个事件参数,在CEGUI主模块里面都是一些键盘鼠标的输入事件参数.
   然后,我们需要声明一个事件集,它由EventSet继承而来.
   在测试代码里面,我们声明了一个testClient的类,代表着客户在里面我们声明一个回调方法:testClient::handleTest.testClient::Init方法作为客户的初始代码,在这里面,我们注册事件.在testClient::exe里面执行触发事件的代码.而后,事件将会被触发,继而testClient::handleTest方法将会被回调执行.


代码下载:
testEventSystem.rar
需要注意的是,这份代码里面的事件系统不是原汁原味的CEGUI的事件系统,是经过我略加修改之后的东西.


文档和图片下载:
CEGUI事件系统分析.rar
静态类图貌似过大了,上传到博客上面它自动给我缩小了--!那根本就看不清楚,Word和PDF文档里面也勉强能看清楚,没办法只好都打包进去了.

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
针对巴基斯坦的某APT活动事件分析
本文讲的是针对巴基斯坦的某APT活动事件分析,2017年6月,360威胁情报中心发现了一份可疑的利用漏洞执行恶意代码的Word文档,经过分析后,我们发现这有可能是一起针对巴基斯坦的政府官员的APT攻击事件,
1614 0
Scheduled SQL: SLS 大规模日志上的全局分析与调度
本文总结了大规模日志全局分析的需求,讨论SLS上现有的典型分析方案,并延伸到 SLS 原生数据处理方案,介绍 Schedueld SQL 功能与最佳实践。
1058 0
snk
Angular vs React vs Vue vs UISYS 的事件绑定方式对比(新手必看)
web三大框架和 airoot uisys 都给大家介绍了一番。在事件绑定上都很棒,除了angluar 有些小伙伴会疑问,angluar为啥那么复杂,其实 angluar 设计之初就为大型企业项目考虑了很多,而且他的组件是最成熟的,React 和 Vue 毕竟不是做成google 那么复杂,所以angluar开始学的时候,感觉有点“脱裤子放屁的感觉”,但是你学深入了,你就明白作者的困境了。
1082 0
+关注
杨粼波
网游的老兵
1135
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载