【转】解读Qt 事件处理机制(上篇)

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

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

时间: 2024-08-08 13:59:24

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

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

转自:http://mobile.51cto.com/symbian-272816.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分发.接受和处理事件. 继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生.分发.接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的

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

QT开发(十二)--QT事件处理机制 一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 鼠标事件: 鼠标移动,鼠标按键的按下和松开 拖放事件: 用鼠标进行拖放 滚轮事件: 鼠标滚轮滚动 绘屏事件: 重绘屏幕的某些部分 定时事件: 定时器到时 焦点事件: 键盘焦点移动 进入和离开事件: 鼠标移入widget之内,或是移出 移动事件: widget的

9、Qt 事件处理机制

原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分发.接受和处理事件. 本篇来介绍Qt 事件处理机制.深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集. 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 

Qt 事件处理机制

Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.Qt是以事件驱动UI工具集.Signals/Slots在多线程中的实现也是依赖于Qt的事件处理机制.在Qt中,事件被封装成一个个对象,所有的事件都继承抽象基类QEvent. Qt事件处理机制 产生事件:输入设备,键盘鼠标等.keyPr

QT开发(六十三)——QT事件机制分析

QT开发(六十三)--QT事件机制分析 一.事件机制 事件是由系统或者QT平台本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应时发出,如键盘事件等:另一些事件则是由系统自动发出,如计时器事件. 事件的出现,使得程序代码不会按照原始的线性顺序执行.线性顺序的程序设计风格不适合处理复杂的用户交互,如用户交互过程中,用户点击"打开文件"将开始执行打开文件的操作,用户点击"保存文件"将开始执

从零开始理解JAVA事件处理机制(2)

第一节中的示例过于简单<从零开始理解JAVA事件处理机制(1)>,简单到让大家觉得这样的代码简直毫无用处.但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码. 一:事件驱动模型初窥 我们要说事件驱动模型是观察者模式的升级版本,那我们就要说说其中的对应关系: 观察者对应监听器(学生) 被观察者对应事件源(教师) 事件源产生事件,监听器监听事件.爱钻牛角尖的朋友可能会说,我擦,什么叫产生事件,监听事件,事件事件到底什么? 莫慌,如果我们用代码来说事,事件源它就是个类,事件就是

Qt事件机制浅析

Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事件: 按键按下和松开. 鼠标事件: 鼠标移动,鼠标按键的按下和松开. 拖放事件: 用鼠标进行拖放. 滚轮事件: 鼠标滚轮滚动. 绘屏事件: 重绘屏幕的某些部分. 定时事件: 定时器到时. 焦点事件: 键盘焦点移动. 进入和离开事件: 鼠标移入widget之内,或是移出. 移动事件: widget的位

Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)

Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事件: 按键按下和松开. 鼠标事件: 鼠标移动,鼠标按键的按下和松开. 拖放事件: 用鼠标进行拖放. 滚轮事件: 鼠标滚轮滚动. 绘屏事件: 重绘屏幕的某些部分. 定时事件: 定时器到时. 焦点事件: 键盘焦点移动. 进入和离开事件: 鼠标移入widget之内,或是移出. 移动事件: widget的位

Qt事件机制---信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。

Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事件: 按键按下和松开. 鼠标事件: 鼠标移动,鼠标按键的按下和松开. 拖放事件: 用鼠标进行拖放. 滚轮事件: 鼠标滚轮滚动. 绘屏事件: 重绘屏幕的某些部分. 定时事件: 定时器到时. 焦点事件: 键盘焦点移动. 进入和离开事件: 鼠标移入widget之内,或是移出. 移动事件: widget的位