`
ydbc
  • 浏览: 722984 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

浅议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. //Section1
  4. intmain(intargc,char*argv[])
  5. {
  6. QApplicationapp(argc,argv);
  7. Widgetwindow;//Widget继承自QWidget
  8. window.show();
  9. returnapp.exec();//进入Qpplication事件循环,见section2
  10. }
  11. //Section2:
  12. intQApplication::exec()
  13. {
  14. //skipcodes
  15. //简单的交给QCoreApplication来处理事件循环=〉section3
  16. returnQCoreApplication::exec();
  17. }
  18. //Section3
  19. intQCoreApplication::exec()
  20. {
  21. //得到当前Thread数据
  22. QThreadData*threadData=self->d_func()->threadData;
  23. if(threadData!=QThreadData::current()){
  24. qWarning("%s::exec:Mustbecalledfromthemainthread",self->metaObject()->className());
  25. return-1;
  26. }
  27. //检查eventloop是否已经创建
  28. if(!threadData->eventLoops.isEmpty()){
  29. qWarning("QCoreApplication::exec:Theeventloopisalreadyrunning");
  30. return-1;
  31. }
  32. ...
  33. QEventLoopeventLoop;
  34. self->d_func()->in_exec=true;
  35. self->d_func()->aboutToQuitEmitted=false;
  36. //委任QEventLoop处理事件队列循环==>Section4
  37. intreturnCode=eventLoop.exec();
  38. ....
  39. }
  40. returnreturnCode;
  41. }
  42. //Section4
  43. intQEventLoop::exec(ProcessEventsFlagsflags)
  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. //Section5
  54. boolQEventLoop::processEvents(ProcessEventsFlagsflags)
  55. {
  56. Q_D(QEventLoop);
  57. if(!d->threadData->eventDispatcher)
  58. returnfalse;
  59. if(flags&DeferredDeletion)
  60. QCoreApplication::sendPostedEvents(0,QEvent::DeferredDelete);
  61. //将事件派发给与平台相关的QAbstractEventDispatcher子类=>Section6
  62. returnd->threadData->eventDispatcher->processEvents(flags);
  63. }

  1. //Section6,QTDIR/src/corelib/kernel/qeventdispatcher_win.cpp
  2. //这段代码是完成与windows平台相关的windowsc++。以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
  3. //其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
  4. //QEventDispatcherWin32派生自QAbstractEventDispatcher.
  5. boolQEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlagsflags)
  6. {
  7. Q_D(QEventDispatcherWin32);
  8. if(!d->internalHwnd)
  9. createInternalHwnd();
  10. d->interrupt=false;
  11. emitawake();
  12. boolcanWait;
  13. boolretVal=false;
  14. boolseenWM_QT_SENDPOSTEDEVENTS=false;
  15. boolneedWM_QT_SENDPOSTEDEVENTS=false;
  16. do{
  17. DWORDwaitRet=0;
  18. HANDLEpHandles[MAXIMUM_WAIT_OBJECTS-1];
  19. QVarLengthArray<MSG>processedTimers;
  20. while(!d->interrupt){
  21. DWORDnCount=d->winEventNotifierList.count();
  22. Q_ASSERT(nCount<MAXIMUM_WAIT_OBJECTS-1);
  23. MSGmsg;
  24. boolhaveMessage;
  25. if(!(flags&QEventLoop::ExcludeUserInputEvents)&&!d->queuedUserInputEvents.isEmpty()){
  26. //processqueueduserinputevents
  27. haveMessage=true;
  28. //从处理用户输入队列中取出一条事件
  29. msg=d->queuedUserInputEvents.takeFirst();
  30. }elseif(!(flags&QEventLoop::ExcludeSocketNotifiers)&&!d->queuedSocketEvents.isEmpty()){
  31. //从处理socket队列中取出一条事件
  32. haveMessage=true;
  33. msg=d->queuedSocketEvents.takeFirst();
  34. }else{
  35. haveMessage=PeekMessage(&msg,0,0,0,PM_REMOVE);
  36. if(haveMessage&&(flags&QEventLoop::ExcludeUserInputEvents)
  37. &&((msg.message>=WM_KEYFIRST
  38. &&msg.message<=WM_KEYLAST)
  39. ||(msg.message>=WM_MOUSEFIRST
  40. &&msg.message<=WM_MOUSELAST)
  41. ||msg.message==WM_MOUSEWHEEL
  42. ||msg.message==WM_MOUSEHWHEEL
  43. ||msg.message==WM_TOUCH
  44. #ifndefQT_NO_GESTURES
  45. ||msg.message==WM_GESTURE
  46. ||msg.message==WM_GESTURENOTIFY
  47. #endif
  48. ||msg.message==WM_CLOSE)){
  49. //用户输入事件入队列,待以后处理
  50. haveMessage=false;
  51. d->queuedUserInputEvents.append(msg);
  52. }
  53. if(haveMessage&&(flags&QEventLoop::ExcludeSocketNotifiers)
  54. &&(msg.message==WM_QT_SOCKETNOTIFIER&&msg.hwnd==d->internalHwnd)){
  55. //socket事件入队列,待以后处理
  56. haveMessage=false;
  57. d->queuedSocketEvents.append(msg);
  58. }
  59. }
  60. ....
  61. if(!filterEvent(&msg)){
  62. TranslateMessage(&msg);
  63. //将事件打包成message调用WindowsAPI派发出去
  64. //分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数=>section7
  65. DispatchMessage(&msg);
  66. }
  67. }
  68. }
  69. }while(canWait);
  70. ...
  71. returnretVal;
  72. }

  1. //Section7windows窗口回调函数定义在QTDIR/src/gui/kernel/qapplication_win.cpp
  2. extern"C"LRESULTQT_WIN_CALLBACKQtWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)
  3. {
  4. ...
  5. //将消息重新封装成QEvent的子类QMouseEvent==>Section8
  6. result=widget->translateMouseEvent(msg);
  7. ...
  8. }

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

在下文中,我们将进一步讨论当我们收到来在Windows的回调后,事件又是怎么一步步打包成QEvent并通过QApplication分发给最终事件的接受和处理者QObject::event.

在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. //Section1
  4. intmain(intargc,char*argv[])
  5. {
  6. QApplicationapp(argc,argv);
  7. Widgetwindow;//Widget继承自QWidget
  8. window.show();
  9. returnapp.exec();//进入Qpplication事件循环,见section2
  10. }
  11. //Section2:
  12. intQApplication::exec()
  13. {
  14. //skipcodes
  15. //简单的交给QCoreApplication来处理事件循环=〉section3
  16. returnQCoreApplication::exec();
  17. }
  18. //Section3
  19. intQCoreApplication::exec()
  20. {
  21. //得到当前Thread数据
  22. QThreadData*threadData=self->d_func()->threadData;
  23. if(threadData!=QThreadData::current()){
  24. qWarning("%s::exec:Mustbecalledfromthemainthread",self->metaObject()->className());
  25. return-1;
  26. }
  27. //检查eventloop是否已经创建
  28. if(!threadData->eventLoops.isEmpty()){
  29. qWarning("QCoreApplication::exec:Theeventloopisalreadyrunning");
  30. return-1;
  31. }
  32. ...
  33. QEventLoopeventLoop;
  34. self->d_func()->in_exec=true;
  35. self->d_func()->aboutToQuitEmitted=false;
  36. //委任QEventLoop处理事件队列循环==>Section4
  37. intreturnCode=eventLoop.exec();
  38. ....
  39. }
  40. returnreturnCode;
  41. }
  42. //Section4
  43. intQEventLoop::exec(ProcessEventsFlagsflags)
  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. //Section5
  54. boolQEventLoop::processEvents(ProcessEventsFlagsflags)
  55. {
  56. Q_D(QEventLoop);
  57. if(!d->threadData->eventDispatcher)
  58. returnfalse;
  59. if(flags&DeferredDeletion)
  60. QCoreApplication::sendPostedEvents(0,QEvent::DeferredDelete);
  61. //将事件派发给与平台相关的QAbstractEventDispatcher子类=>Section6
  62. returnd->threadData->eventDispatcher->processEvents(flags);
  63. }

  1. //Section6,QTDIR/src/corelib/kernel/qeventdispatcher_win.cpp
  2. //这段代码是完成与windows平台相关的windowsc++。以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持
  3. //其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX
  4. //QEventDispatcherWin32派生自QAbstractEventDispatcher.
  5. boolQEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlagsflags)
  6. {
  7. Q_D(QEventDispatcherWin32);
  8. if(!d->internalHwnd)
  9. createInternalHwnd();
  10. d->interrupt=false;
  11. emitawake();
  12. boolcanWait;
  13. boolretVal=false;
  14. boolseenWM_QT_SENDPOSTEDEVENTS=false;
  15. boolneedWM_QT_SENDPOSTEDEVENTS=false;
  16. do{
  17. DWORDwaitRet=0;
  18. HANDLEpHandles[MAXIMUM_WAIT_OBJECTS-1];
  19. QVarLengthArray<MSG>processedTimers;
  20. while(!d->interrupt){
  21. DWORDnCount=d->winEventNotifierList.count();
  22. Q_ASSERT(nCount<MAXIMUM_WAIT_OBJECTS-1);
  23. MSGmsg;
  24. boolhaveMessage;
  25. if(!(flags&QEventLoop::ExcludeUserInputEvents)&&!d->queuedUserInputEvents.isEmpty()){
  26. //processqueueduserinputevents
  27. haveMessage=true;
  28. //从处理用户输入队列中取出一条事件
  29. msg=d->queuedUserInputEvents.takeFirst();
  30. }elseif(!(flags&QEventLoop::ExcludeSocketNotifiers)&&!d->queuedSocketEvents.isEmpty()){
  31. //从处理socket队列中取出一条事件
  32. haveMessage=true;
  33. msg=d->queuedSocketEvents.takeFirst();
  34. }else{
  35. haveMessage=PeekMessage(&msg,0,0,0,PM_REMOVE);
  36. if(haveMessage&&(flags&QEventLoop::ExcludeUserInputEvents)
  37. &&((msg.message>=WM_KEYFIRST
  38. &&msg.message<=WM_KEYLAST)
  39. ||(msg.message>=WM_MOUSEFIRST
  40. &&msg.message<=WM_MOUSELAST)
  41. ||msg.message==WM_MOUSEWHEEL
  42. ||msg.message==WM_MOUSEHWHEEL
  43. ||msg.message==WM_TOUCH
  44. #ifndefQT_NO_GESTURES
  45. ||msg.message==WM_GESTURE
  46. ||msg.message==WM_GESTURENOTIFY
  47. #endif
  48. ||msg.message==WM_CLOSE)){
  49. //用户输入事件入队列,待以后处理
  50. haveMessage=false;
  51. d->queuedUserInputEvents.append(msg);
  52. }
  53. if(haveMessage&&(flags&QEventLoop::ExcludeSocketNotifiers)
  54. &&(msg.message==WM_QT_SOCKETNOTIFIER&&msg.hwnd==d->internalHwnd)){
  55. //socket事件入队列,待以后处理
  56. haveMessage=false;
  57. d->queuedSocketEvents.append(msg);
  58. }
  59. }
  60. ....
  61. if(!filterEvent(&msg)){
  62. TranslateMessage(&msg);
  63. //将事件打包成message调用WindowsAPI派发出去
  64. //分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数=>section7
  65. DispatchMessage(&msg);
  66. }
  67. }
  68. }
  69. }while(canWait);
  70. ...
  71. returnretVal;
  72. }

  1. //Section7windows窗口回调函数定义在QTDIR/src/gui/kernel/qapplication_win.cpp
  2. extern"C"LRESULTQT_WIN_CALLBACKQtWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)
  3. {
  4. ...
  5. //将消息重新封装成QEvent的子类QMouseEvent==>Section8
  6. result=widget->translateMouseEvent(msg);
  7. ...
  8. }

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

在下文中,我们将进一步讨论当我们收到来在Windows的回调后,事件又是怎么一步步打包成QEvent并通过QApplication分发给最终事件的接受和处理者QObject::event.

分享到:
评论

相关推荐

    QT的事件处理机制

    事件是窗口系统或者qt对不同情况的响应...QWidget::event()虚函数是各种事件的一个大管家,负责把大多数常用类型的事件传递给特定的事件处理器(事件处理函数,也都是虚拟函数,便于其继承子类对于该事件处理的编程),

    第9章 Qt事件机制与原理

    类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责( 内存管理、内省intropection、事件处理制)之一。 任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的...

    Qt事件机制浅析

    Qt事件机制浅析

    Qt事件处理之鼠标处理事件

    这是一个Qt事件处理系统,目前只实现了鼠标事件的处理,后续将添加键盘事件,绘图事件,定时事件等等的处理过程。程序比较简单,适合初学者。程序开发环境:win7+qt5.7.0

    qt的消息处理机制

    查看了很多资料,对qt的消息处理机制做了整理,相信对qt学习者会有很大的帮助

    Qt中的事件机制

    这是一本描写Qt中的事件机制的PDF文档。

    Qt 事件处理机制 (上篇)

    1. #include 5. { 6. QApplication app(argc, argv)

    QT信号机制.

    qt用预编译器和宏来保证强大的跨平台能力,信号机制则是其中最精妙之处。本文分析了几种常见的信号处理机制,然后详细介绍了Qt的Signal/Slot机制。

    QT信号与槽机制浅析

    QT信号与槽机制浅析Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的 说法,简单点说就是如何在一个类...

    Qt事件处理,定时器

    关于Qt事件处理和定时器的详细内容,参考博客:http://blog.csdn.net/rl529014/article/details/53440211

    QT的显示机制

    QT的显示机制,可以看看,比较不错 QT的显示机制,可以看看,比较不错 QT的显示机制,可以看看,比较不错

    Qt Quick事件处理

    在 Qt Quick 中,事件处理通常涉及到对输入事件(如鼠标点击、键盘输入、触摸事件等)的响应。这些事件可以通过 Qt 的事件系统或信号与槽机制进行处理。鼠标、键盘、触摸……每一种用户可能使用的与计算机或手机交互...

    Qt 事件处理机制 (下篇) - 51CTO.COM1

    2. QApplication::exec() 3. QCoreApplication::exec() 4. QEventLoop::exec(ProcessE

    Qt核心机制、Qt元对象系统、Qt信号槽原理

    个人吐血整理,从源码剖析Qt框架,绝对给力!

    Qt事件处理及实例

    它也是目前流行的Linux桌面环境KDE的基础,KDE是Linux发行版的主要一个标准组件。2008年,奇趣科技被诺基亚公司收购,Qt成为诺基亚旗下的编程语言工具。2012年,Qt又被Digia公司收购。2014年,Digia宣布成立“The Qt...

    QT Event 事件处理

    QT 中 QEvent 和 QKeyEvent事件的处理,其中QEvent包含QKeyEvent和QMouseEvent事件处理,在event中处理QKeyEvent事件将影响单独的QKeyEvent的事件处理函数

    第11章 Qt 5事件处理.ppt

    qt事件处理qt事件处理qt事件处理

    Qt 使用QMetaObject实现反射机制代码demo

    Qt 使用QMetaObject实现反射机制代码demo

    QT内部工作机制

    QT内部工作机制,深入进入QT底层实现细节. 对于想深入了解QT工作原理的,值得一看!

    Qt Oracle 连接机制

    使用Qt的相关机制,保障Oracle数据库的链接机制

Global site tag (gtag.js) - Google Analytics