上一节分析了qt和windows系统之间的消息的传递,本节着重看一下,qt内部的事件是如何传递的?
1.sendEvent函数
在使用的自定义事件时,有时需要手动抛出一个事件,常用的方式有2种,其一时阻塞式的sendEvent函数;其二是postEvent函数;sendEvent函数定义如下:
其主要是将spont设置为fasle,然后转入到notifyInternal2函数中执行处理;
其中self在QCoreApplicationPrivate构造函数中已被指定,所以不为空,if(!self && selfRequired)不会执行;
QInternal::activateCallbacks函数,会检测是否注册了事件回调函数,如果注册了对应的事件函数,会截获此事件;其对应的定义如下:
由于在实践项目中没有用过此种方式,此处略过
接下来会通过QScopedScopeLevelCounter类将接收对象的scopeLevel变量加1,并在函数退出时,减一;此处猜测是qt标识循环层级使用的,实际是做什么用的,还不清楚;
接下来会转入doNotifity函数处理,notify函数最终也是转入doNotify函数中处理;
此处会检测接收对象和发送对象是否在同一个线程;并最终转入notify_helper函数中处理;
此处会判断接收的对象是否在主线程中,如果是在主线程中,会检测QCoreApplication的对象是否安装了事件过滤器,如果此事件被QCoreApplication事件过滤器检测到并被处理,并不会向下传递;
接下来会判断此接收对象上的事件过滤器是否处理此事件,如果此事件被处理则退出;
最后,进入接收对象自己的event函数,进行事件处理;
通过上述流程可以发现:1、event对象在事件传递过程中,没有执行删除等释放动作,其内存需要用户自己管理;2、sendEvent函数一直在执行函数的调用,并最终转到notify_helper函数中处理,所以其是阻塞式调用;3、事件的传递顺序,先被QCoreApplication对象拦截,接着被对象注册的事件过滤器拦截,最后执行自己的event函数;
2、postEvent函数
首先判断接收对象是否为空,如果为空,删除event对象,并退出;
判断接收对象的线程是否被释放,如果接收对象所在的线程已被删除,则删除event对象,并退出;
如果接收对象被移动到其他线程中,再次检测新的线程是否被删除,如果新的线程已被删除,则删除event对象,并退出;
判断事件是否可以被压缩,即删除已经在postEventList中重复的事件,主要针对QTimerEvent事件、QDeferredDeleteEvent、QQuitEvent等事件;
接下来,执行:
将QEvent对象放入QScopedPointer中,将receiver、event对象拼装成QPostEvent对象,并放入到QPostEventList中,如果中间出现异常,则event对象内存有QScopedPointer对象回收;
QPostEventList是根据priority优先级进行排序执行的;
接下来将event->posted置为true,postedEvents数量加1,并将canWait设置为false;
最后调用QAbstractEventDispatcher的wakeUp函数;wakeUp函数会调用WinAPI的PostMessage函数,投递一个WM_QT_SENDPOSTEDEVENTS事件,其会被processEvent函数捡取到,并最终转入QCoreApplicationPrivate中的sendPostedEvents函数处理(具体可以参考上一节);
首先判断receiver对象是否为空,是否跨线程执行?由于通过QAbstractEventDispatcher对象调用,其receiver对象为空,所以此处会跳过执行;
将recursion加1,防止递归调用;并判断postEventList列表中是否有事件,如果没有事件需要处理则退出;
接下来主要是从QPostEventList中取出事件,并调用QCoreApplication::sendEvent函数处理;
并且会在此处删除event对象,由QScopedPointer对象负责处理;
另外qt在处理取事件的过程中非常小心,考虑了很多异常情况
期间定义了CleanUp的类,主要是用来处理事件对象被处理后,清空事件列表中已经处理过的事件;
总结:
sendEvent函数:
1、event对象在事件传递过程中,没有执行删除等释放动作,其内存需要用户自己管理;
2、sendEvent函数一直在执行函数的调用,并最终转到notify_helper函数中处理,所以其是阻塞式调用;
3、事件的传递顺序,先被QCoreApplication对象拦截,接着被对象注册的事件过滤器拦截,最后执行自己的event函数;
postEvnet函数:
1、event对象会被保存在全局的postEventList列表中,由事件在处理时被释放;
2、postevent函数会将事件、receiver对象放到全局的QPostEventList列表中,并通过WINAPI的postmessage系统函数,通知QAbstractEventDispatcher类检索到,最终转入到sendPostedEvents函数中处理;
3、上一节已提到windows消息及qt自定义消息如何被检索的过程,所以可以判断出postevent函数的处理是异步的过程;
QCoreApplication类的主要内容已基本结束,下一节会着重介绍下定时器事件;