【转自】:http://mobile.51cto.com/symbian-272812.htm

在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件。

本篇来介绍Qt 事件处理机制 。深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集。 大家熟知Signals/Slots在多线程的实现也依赖于Qt事件处理机制。

Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.  接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件

1、谁来产生事件: 最容易想到的是我们的输入设备,比如键盘、鼠标产生的

keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent),这些事件来自于底层的操作系统,它们以异步的形式通知Qt事件处理系统,后文会仔细道来。当然Qt自己也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent. 用户的程序可还以自己定制事件。

2、谁来接受和处理事件:答案是QObject。在Qt的内省机制剖析一文已经介绍QObject 类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的处理权转给父类。

3、谁来负责分发事件:对于non-GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类-Receiver. 对于Qt GUI程序,由QApplication来负责。

接下来,将通过对代码的解析来看看QT是利用event loop从事件队列中获取用户输入事件,又是如何将事件转义成QEvents,并分发给相应的QObject处理。

  1. #include <QApplication>
  2. #include "widget.h"
  3. //Section 1
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication app(argc, argv);
  7. Widget window; // Widget 继承自QWidget
  8. window.show();
  9. return app.exec(); // 进入Qpplication事件循环,见section 2
  10. }
  11. // Section 2:
  12. int QApplication::exec()
  13. {
  14. //skip codes
  15. //简单的交给QCoreApplication来处理事件循环=〉section 3
  16. return QCoreApplication::exec();
  17. }
  18. // Section 3
  19. int QCoreApplication::exec()
  20. {
  21. //得到当前Thread数据
  22. QThreadData *threadData = self->d_func()->threadData;
  23. if (threadData != QThreadData::current()) {
  24. qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
  25. return -;
  26. }
  27. //检查event loop是否已经创建
  28. if (!threadData->eventLoops.isEmpty()) {
  29. qWarning("QCoreApplication::exec: The event loop is already running");
  30. return -;
  31. }
  32. ...
  33. QEventLoop eventLoop;
  34. self->d_func()->in_exec = true;
  35. self->d_func()->aboutToQuitEmitted = false;
  36. //委任QEventLoop 处理事件队列循环 ==> Section 4
  37. int returnCode = eventLoop.exec();
  38. ....
  39. }
  40. return returnCode;
  41. }
  42. // Section 4
  43. int QEventLoop::exec(ProcessEventsFlags flags)
  44. {
  45. //这里的实现代码不少,最为重要的是以下几行
  46. Q_D(QEventLoop); // 访问QEventloop私有类实例d
  47. try {
  48. //只要没有遇见exit,循环派发事件
  49. while (!d->exit)
  50. processEvents(flags | WaitForMoreEvents | EventLoopExec);
  51. } catch (...) {}
  52. }
  53. // Section 5
  54. bool QEventLoop::processEvents(ProcessEventsFlags flags)
  55. {
  56. Q_D(QEventLoop);
  57. if (!d->threadData->eventDispatcher)
  58. return false;
  59. if (flags & DeferredDeletion)
  60. QCoreApplication::sendPostedEvents(, QEvent::DeferredDelete);
  61. //将事件派发给与平台相关的QAbstractEventDispatcher子类 =>Section 6
  62. return d->threadData->eventDispatcher->processEvents(flags);
  63. }
  64.  
  65. // Section 6,QTDIR\src\corelib\kernel\qeventdispatcher_win.cpp
  66. // 这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
  67. // 其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
  68. // QEventDispatcherWin32派生自QAbstractEventDispatcher.
  69. bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
  70. {
  71. Q_D(QEventDispatcherWin32);
  72. if (!d->internalHwnd)
  73. createInternalHwnd();
  74. d->interrupt = false;
  75. emit awake();
  76. bool canWait;
  77. bool retVal = false;
  78. bool seenWM_QT_SENDPOSTEDEVENTS = false;
  79. bool needWM_QT_SENDPOSTEDEVENTS = false;
  80. do {
  81. DWORD waitRet = ;
  82. HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - ];
  83. QVarLengthArray<MSG> processedTimers;
  84. while (!d->interrupt) {
  85. DWORD nCount = d->winEventNotifierList.count();
  86. Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
  87. MSG msg;
  88. bool haveMessage;
  89. if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
  90. // process queued user input events
  91. haveMessage = true;
  92. //从处理用户输入队列中取出一条事件
  93. msg = d->queuedUserInputEvents.takeFirst();
  94. } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
  95. // 从处理socket队列中取出一条事件
  96. haveMessage = true;
  97. msg = d->queuedSocketEvents.takeFirst();
  98. } else {
  99. haveMessage = PeekMessage(&msg, , , , PM_REMOVE);
  100. if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
  101. && ((msg.message >= WM_KEYFIRST
  102. && msg.message <= WM_KEYLAST)
  103. || (msg.message >= WM_MOUSEFIRST
  104. && msg.message <= WM_MOUSELAST)
  105. || msg.message == WM_MOUSEWHEEL
  106. || msg.message == WM_MOUSEHWHEEL
  107. || msg.message == WM_TOUCH
  108. #ifndef QT_NO_GESTURES
  109. || msg.message == WM_GESTURE
  110. || msg.message == WM_GESTURENOTIFY
  111. #endif
  112. || msg.message == WM_CLOSE)) {
  113. // 用户输入事件入队列,待以后处理
  114. haveMessage = false;
  115. d->queuedUserInputEvents.append(msg);
  116. }
  117. if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
  118. && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
  119. // socket 事件入队列,待以后处理
  120. haveMessage = false;
  121. d->queuedSocketEvents.append(msg);
  122. }
  123. }
  124. ....
  125. if (!filterEvent(&msg)) {
  126. TranslateMessage(&msg);
  127. //将事件打包成message调用Windows API派发出去
  128. //分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7
  129. DispatchMessage(&msg);
  130. }
  131. }
  132. }
  133. } while (canWait);
  134. ...
  135. return retVal;
  136. }
  137. // Section 6,QTDIR\src\corelib\kernel\qeventdispatcher_win.cpp
  138. // 这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
  139. // 其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
  140. // QEventDispatcherWin32派生自QAbstractEventDispatcher.
  141. bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
  142. {
  143. Q_D(QEventDispatcherWin32);
  144. if (!d->internalHwnd)
  145. createInternalHwnd();
  146. d->interrupt = false;
  147. emit awake();
  148. bool canWait;
  149. bool retVal = false;
  150. bool seenWM_QT_SENDPOSTEDEVENTS = false;
  151. bool needWM_QT_SENDPOSTEDEVENTS = false;
  152. do {
  153. DWORD waitRet = ;
  154. HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - ];
  155. QVarLengthArray<MSG> processedTimers;
  156. while (!d->interrupt) {
  157. DWORD nCount = d->winEventNotifierList.count();
  158. Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
  159. MSG msg;
  160. bool haveMessage;
  161. if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
  162. // process queued user input events
  163. haveMessage = true;
  164. //从处理用户输入队列中取出一条事件
  165. msg = d->queuedUserInputEvents.takeFirst();
  166. } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
  167. // 从处理socket队列中取出一条事件
  168. haveMessage = true;
  169. msg = d->queuedSocketEvents.takeFirst();
  170. } else {
  171. haveMessage = PeekMessage(&msg, , , , PM_REMOVE);
  172. if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
  173. && ((msg.message >= WM_KEYFIRST
  174. && msg.message <= WM_KEYLAST)
  175. || (msg.message >= WM_MOUSEFIRST
  176. && msg.message <= WM_MOUSELAST)
  177. || msg.message == WM_MOUSEWHEEL
  178. || msg.message == WM_MOUSEHWHEEL
  179. || msg.message == WM_TOUCH
  180. #ifndef QT_NO_GESTURES
  181. || msg.message == WM_GESTURE
  182. || msg.message == WM_GESTURENOTIFY
  183. #endif
  184. || msg.message == WM_CLOSE)) {
  185. // 用户输入事件入队列,待以后处理
  186. haveMessage = false;
  187. d->queuedUserInputEvents.append(msg);
  188. }
  189. if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
  190. && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
  191. // socket 事件入队列,待以后处理
  192. haveMessage = false;
  193. d->queuedSocketEvents.append(msg);
  194. }
  195. }
  196. ....
  197. if (!filterEvent(&msg)) {
  198. TranslateMessage(&msg);
  199. //将事件打包成message调用Windows API派发出去
  200. //分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7
  201. DispatchMessage(&msg);
  202. }
  203. }
  204. }
  205. } while (canWait);
  206. ...
  207. return retVal;
  208. }
  209.  
  210. // Section 7 windows窗口回调函数 定义在QTDIR\src\gui\kernel\qapplication_win.cpp
  211. extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  212. {
  213. ...
  214. //将消息重新封装成QEvent的子类QMouseEvent ==> Section 8
  215. result = widget->translateMouseEvent(msg);
  216. ...
  217. }
  218.  
  219. // Section 7 windows窗口回调函数 定义在QTDIR\src\gui\kernel\qapplication_win.cpp
  220. extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  221. {
  222. ...
  223. //将消息重新封装成QEvent的子类QMouseEvent ==> Section 8
  224. result = widget->translateMouseEvent(msg);
  225. ...
  226. }

从Section 1~Section7, Qt进入QApplication的event loop,经过层层委任,最终QEventloop的processEvent将通过与平台相关的QAbstractEventDispatcher的子类QEventDispatcherWin32获得用户的用户输入事件,并将其打包成message后,通过标准Windows API ,把消息传递给了Windows OS,Windows OS得到通知后回调QtWndProc,  至此事件的分发与处理完成了一半的路程。

【转】解读Qt 事件处理机制(上篇)的更多相关文章

  1. Qt 事件处理机制 (上篇)

    本篇来介绍Qt 事件处理机制 .深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集. 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制. ...

  2. 【转】Qt 事件处理机制 (下篇)

    转自:http://mobile.51cto.com/symbian-272816.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分 ...

  3. Qt 事件处理机制 (下篇)

    继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生.分发.接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何 ...

  4. Qt事件处理机制

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...

  5. 9、Qt 事件处理机制

    原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...

  6. Qt 事件处理机制

    Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt ...

  7. QT开发(十二)——QT事件处理机制

    一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 ...

  8. 【QT学习】QT事件处理机制

    GUI应用程序由 事件驱动. 键盘.鼠标.拖放.滚动.绘屏.定时事件. connect

  9. Qt之事件处理机制

    思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...

随机推荐

  1. poi简单案例

    //poi api 操作 public static void main(String[] args) {  // TODO Auto-generated method stub  // 创建一个工作 ...

  2. 转:微博CacheService架构浅析

    文章来自于:http://www.infoq.com/cn/articles/weibo-cacheservice-architecture 微博作为国内最大的社交媒体网站之一,每天承载着亿万用户的服 ...

  3. MSSQL BACKUP WEBSHELL

    Title:MSSQL BACKUP WEBSHELL -- Long Long Ago. Version:MSSQL 2005 && + ';alter/**/database/** ...

  4. at命令

    在windows系统中,windows提供了计划任务这一功能,在控制面板 -> 性能与维护 -> 任务计划, 它的功能就是安排自动运行的任务. 通过'添加任务计划'的一步步引导,则可建立一 ...

  5. 【转】Android中EditText中的InputType类型含义与如何定义

    原文网址:http://www.crifan.com/summary_android_edittext_inputtype_values_and_meaning_definition/ 经过一些And ...

  6. (转载)PHP array_merge() 函数

    (转载)http://www.w3school.com.cn/php/func_array_merge.asp PHP Array 函数 定义和用法 array_merge() 函数把两个或多个数组合 ...

  7. CSS伪元素before和after

    今天发现很多国外的网站和框架设计都用到了before和after,之前使用的比较少,今天试了下觉得还是很有意思的~ 说明 1. :before 和 :after将在内容元素的前后插入额外的元素::be ...

  8. 项目管理模式之如何去除SVN标记

    原问地址:http://blog.csdn.net/djcken/article/details/7916986      当项目不需要SVN标志的时候,我们一般怎么办哪??可能很多人设置Window ...

  9. 【2013杭州区域赛】部分题解 hdu4770—4780

    1008: 题意: 有20W个数,每个数都在20W以内,现在有20W个询问,每次询问L,R区间内不会打架的数有多少个 定义两个数不互质 就会打架 解法: 脑洞很大的一道题,先要进行预处理,对每一个数预 ...

  10. [LeetCode] 205. Isomorphic Strings 解题思路 - Java

    Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the chara ...