【转】解读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处理。

 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,  至此事件的分发与处理完成了一半的路程。

 

posted on 2016-06-15 21:06  阿珩  阅读(518)  评论(0编辑  收藏  举报