• 【转】解读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,QTDIRsrccorelibkernelqeventdispatcher_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,QTDIRsrccorelibkernelqeventdispatcher_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窗口回调函数 定义在QTDIRsrcguikernelqapplication_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窗口回调函数 定义在QTDIRsrcguikernelqapplication_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,  至此事件的分发与处理完成了一半的路程。

  • 相关阅读:
    如何在 Knative 中部署 WebSocket 和 gRPC 服务?
    全球首个开放应用模型 OAM 开源 | 云原生生态周报 Vol. 23
    从零开始入门 K8s | Kubernetes 网络概念及策略控制
    重磅发布 | 全球首个云原生应用标准定义与架构模型 OAM 正式开源
    成都,我们来啦 | Dubbo 社区开发者日
    一文读懂分布式架构知识体系(内含超全核心知识大图)
    阿里巴巴开源 Dragonwell JDK 最新版本 8.1.1-GA 发布
    可能是国内第一篇全面解读 Java 现状及趋势的文章
    从零开始入门 K8s | 可观测性:监控与日志
    阿里巴巴的云原生与开发者
  • 原文地址:https://www.cnblogs.com/aheng123/p/5588895.html
Copyright © 2020-2023  润新知