Qt事件处理机制整个流程--以鼠标在一个窗口中点击为例

来源:互联网 发布:人工智能能超越人脑么 编辑:程序博客网 时间:2024/05/23 13:51

原文地址::http://blog.csdn.net/zgrjkflmkyc/article/details/44240729


相关文章

1、QT显示机制(转)_一滴水里的海_百度空间----http://www.360doc.com/content/11/1216/09/6828497_172621488.shtml





转载自:http://mobile.51cto.com/symbian-272812.htm,在此谢谢原作者的分享!

------------------------第一部分----------------------

本篇来介绍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.    
  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 = 0;     
  82.         HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];     
  83.         QVarLengthArray<MSG> processedTimers;     
  84.         while (!d->interrupt) {     
  85.             DWORD nCount = d->winEventNotifierList.count();     
  86.             Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);     
  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, 0, 0, 0, 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.  
  138. // Section 7 windows窗口回调函数 定义在QTDIR\src\gui\kernel\qapplication_win.cpp     
  139. extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)     
  140. {     
  141.    ...     
  142.    //将消息重新封装成QEvent的子类QMouseEvent ==> Section 8     
  143.     result = widget->translateMouseEvent(msg);         
  144.    ...     
  145. }     
  146.  

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

小结:Qt 事件处理机制 (上篇)的内容介绍完了,在下文中,我们将进一步讨论当我们收到来在Windows的回调后,事件又是怎么一步步打包成QEvent并通过QApplication分发给最终事件的接受和处理者QObject::event.请继续看Qt 事件处理机制 (下篇)。最后希望本文能帮你解决问题!


---------------------------第二部分----------------------------

继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生、分发、接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的函数获取、打包用户输入事件交给视窗系统处理,函数调用栈如下:

  1. main(int, char **)   
  2. QApplication::exec()   
  3. QCoreApplication::exec()   
  4. QEventLoop::exec(ProcessEventsFlags )   
  5. QEventLoop::processEvents(ProcessEventsFlags )   
  6. QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)  

本文将介绍Qt app在视窗系统回调后,事件又是怎么一步步通过QApplication分发给最终事件的接受和处理者QWidget::event, (QWidget继承Object,重载其虚函数event),以下所有的讨论都将嵌入在源码之中。

  1. QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) bool QETWidget::translateMouseEvent(const MSG &msg)   
  2. bool QApplicationPrivate::sendMouseEvent(...)   
  3. inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)   
  4. bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)   
  5. bool QApplication::notify(QObject *receiver, QEvent *e)   
  6. bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)   
  7. bool QWidget::event(QEvent *event)   
  8.  
  9. // (续上文Section 7) Section 2-1:     
  10. QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)      
  11. {     
  12.    ...     
  13.    //检查message是否属于Qt可转义的鼠标事件     
  14.    if (qt_is_translatable_mouse_event(message)) {     
  15.         if (QApplication::activePopupWidget() != 0) {                 
  16.             POINT curPos = msg.pt;     
  17.             //取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例     
  18.             QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);     
  19.             if (w)     
  20.                 widget = (QETWidget*)w;     
  21.         }     
  22.         if (!qt_tabletChokeMouse) {     
  23.             //对,就在这里。Windows的回调函数将鼠标事件分发回给了Qt Widget      
  24.             // => Section 2-2     
  25.             result = widget->translateMouseEvent(msg);            
  26.      ...     
  27. }     
  28. // Section 2-2  $QTDIR\src\gui\kernel\qapplication_win.cpp     
  29. //该函数所在与Windows平台相关,主要职责就是把已windows格式打包的鼠标事件解包、翻译成QApplication可识别的QMouseEvent,QWidget.     
  30. bool QETWidget::translateMouseEvent(const MSG &msg)     
  31. {     
  32.      //.. 这里很长的代码给以忽略       
  33.       // 让我们看一下sendMouseEvent的声明     
  34.      // widget是事件的接受者; e是封装好的QMouseEvent     
  35.      // ==> Section 2-3     
  36.      res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver);     
  37. }     
  38. // Section 2-3 $QTDIR\src\gui\kernel\qapplication.cpp     
  39. bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,     
  40.                                          QWidget *alienWidget, QWidget *nativeWidget,     
  41.                                          QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver,     
  42.                                          bool spontaneous)     
  43. {     
  44.      //至此与平台相关代码处理完毕     
  45.      //MouseEvent默认的发送方式是spontaneous, 所以将执行sendSpontaneousEvent。 sendSpontaneousEvent() 与 sendEvent的代码实现几乎相同,
  46. 除了将QEvent的属性spontaneous标记不同。 这里是解释什么spontaneous事件:如果事件由应用程序之外产生的,比如一个系统事件。 
  47. 显然MousePress事件是由视窗系统产生的一个的事件(详见上文Section 1~ Section 7),因此它是   spontaneous事件     
  48.        
  49.     if (spontaneous)     
  50.         result = QApplication::sendSpontaneousEvent(receiver, event);  ==〉Section 2-4     
  51.     else    
  52.         result = QApplication::sendEvent(receiver, event);     
  53. }    

  1. // Section 2-4 C:\Qt\4.7.1-Vs\src\corelib\kernel\qcoreapplication.h     
  2. inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)     
  3. {      
  4.     //将event标记为自发事件     
  5.      //进一步调用 2-5 QCoreApplication::notifyInternal     
  6.     if (event) event->spont = true; return self ? self->notifyInternal(receiver, event) : false;      
  7. }     
  8. // Section 2-5:  $QTDIR\gui\kernel\qapplication.cpp     
  9. bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)     
  10. {     
  11.          
  12.     // 几行代码对于Qt Jambi (QT Java绑定版本) 和QSA (QT Script for Application)的支持     
  13.      ...     
  14.      // 以下代码主要意图为Qt强制事件只能够发送给当前线程里的对象,也就是说receiver->d_func()->threadData应该等于QThreadData::current()。
  15.  注意,跨线程的事件需要借助Event Loop来派发     
  16.      QObjectPrivate *d = receiver->d_func();     
  17.     QThreadData *threadData = d->threadData;     
  18.     ++threadData->loopLevel;     
  19.     bool returnValue;     
  20.     QT_TRY {     
  21.         //哇,终于来到大名鼎鼎的函数QCoreApplication::nofity()了 ==> Section 2-6     
  22.         returnValue = notify(receiver, event);     
  23.     } QT_CATCH (...) {     
  24.         --threadData->loopLevel;     
  25.         QT_RETHROW;     
  26.     }     
  27. }     
  28. // Section 2-6:  $QTDIR\gui\kernel\qapplication.cpp     
  29. // QCoreApplication::notify和它的重载函数QApplication::notify在Qt的派发过程中起到核心的作用,Qt的官方文档时这样说的:
  30. 任何线程的任何对象的所有事件在发送时都会调用notify函数。     
  31. bool QApplication::notify(QObject *receiver, QEvent *e)     
  32. {     
  33.    //代码很长,最主要的是一个大大的Switch,Case     
  34.    ..     
  35.    switch ( e->type())     
  36.    {     
  37.     ...     
  38.     case QEvent::MouseButtonPress:     
  39.     case QEvent::MouseButtonRelease:     
  40.     case QEvent::MouseButtonDblClick:     
  41.     case QEvent::MouseMove:     
  42.      ...     
  43.         //让自己私有类(d是私有类的句柄)来进一步处理 ==> Section 2-7     
  44.         res = d->notify_helper(w, w == receiver ? mouse : &me);     
  45.         e->spont = false;     
  46.         break;     
  47.     }     
  48.     ...     
  49. }     
  50. // Section 2-7:  $QTDIR\gui\kernel\qapplication.cpp     
  51. bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)     
  52. {     
  53.     ...     
  54.     // 向事件过滤器发送该事件,这里介绍一下Event Filters. 事件过滤器是一个接受即将发送给目标对象所有事件的对象。     
  55.    //如代码所示它开始处理事件在目标对象行动之前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤,
  56. 允许或者拒绝事件的更进一步的处理。如果所有的事件过滤器允许更进一步的事件处理,事件将被发送到目标对象本身。
  57. 如果他们中的一个停止处理,目标和任何后来的事件过滤器不能看到任何事件。     
  58.     if (sendThroughObjectEventFilters(receiver, e))     
  59.         return true;     
  60.      // 递交事件给receiver  => Section 2-8     
  61.     bool consumed = receiver->event(e);     
  62.     e->spont = false;     
  63. }     
  64. // Section 2-8  $QTDIR\gui\kernel\qwidget.cpp     
  65. // QApplication通过notify及其私有类notify_helper,将事件最终派发给了QObject的子类- QWidget.     
  66. bool QWidget::event(QEvent *event)     
  67. {     
  68.     ...     
  69.     switch(event->type()) {     
  70.     case QEvent::MouseButtonPress:     
  71.         // Don't reset input context here. Whether reset or not is     
  72.         // a responsibility of input method. reset() will be     
  73.         // called by mouseHandler() of input method if necessary     
  74.         // via mousePressEvent() of text widgets.     
  75. #if 0     
  76.         resetInputContext();     
  77. #endif     
  78.         //mousePressEvent是虚函数,QWidget的子类可以通过重载重新定义mousePress事件的行为     
  79.         mousePressEvent((QMouseEvent*)event);     
  80.         break;        
  81. }   

小结:Qt 事件处理机制 (下篇)的内容介绍完了,希望本文对你 有所帮助!更多相关资料请参考编辑推荐!

注:转载时删除了原文中一些重复的地方。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 喉咙里总是有痰怎么办 1岁宝宝喉咙有痰怎么办 嗓子总感觉有痰怎么办 嗓子痒感觉有痰怎么办 1岁宝宝嗓子有痰怎么办 3岁宝宝嗓子有痰怎么办 喉咙总感觉有痰怎么办 感冒了喉咙有痰怎么办 咽喉有异物感是怎么办 老感觉喉咙有痰怎么办 感冒有痰怎么办最有效 感冒快好了有痰怎么办 喉咙里一直有痰怎么办 一到晚上就咳嗽怎么办 1岁宝宝咳嗽痰多怎么办 3岁宝宝咳嗽痰多怎么办 六岁儿童咳嗽有痰怎么办 很多白痰在喉咙怎么办 我喉咙总是有痰怎么办 喉咙老感觉有痰怎么办 喉咙痒老是有痰怎么办 抽烟多了嗓子疼怎么办 抽烟多了老咳嗽怎么办 抽烟抽多了咳嗽怎么办 嗓子咳出异物臭怎么办 鼻子有鼻涕喉咙有痰怎么办 怀孕39周感冒了怎么办 一口痰卡在喉咙怎么办 鼻塞黄鼻涕黄痰怎么办 小孩咳嗽流黄鼻涕怎么办 小孩鼻塞怎么办最简单方法 有黄鼻涕黄痰怎么办 咳嗽有泡沫白痰怎么办 痰多咳嗽老不好怎么办 5岁儿童咳嗽有痰怎么办 感冒后一直有痰怎么办 感冒吐绿色的痰怎么办 孕妇咳嗽有痰怎么办啊 没结婚的人死了怎么办 金花鼠尾巴断了怎么办 辞职交了不批怎么办