应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

Qt事件机制

  【1】事件
  事件是可以被控件识别的操作。如按下确定按钮、选择某个单选按钮或复选框。
  每种控件有自己可识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事件等等。
  事件就是用户对窗口上各种组件的操作。
  【2】Qt事件
  由窗口系统或Qt自身产生的,用以响应所发生各类事情的操作。具体点,Qt事件是一个QEvent对象,用于描述程序内部或外部发生的动作。
  【3】Qt事件产生类型
  1、键盘或鼠标事件:用户按下或松开键盘或鼠标上的按键时,就可以产生一个键盘或者鼠标事件。
  2、绘制事件:某个窗口第一次显示的时候,就会产生一个绘制事件,用来告诉窗口需要重新绘制它本身,从而使得该窗口可见。
  3、QT事件:Qt自己也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent。
  【4】Qt事件分类
  基于事件如何被产生与分发,可以把事件分为三类:
  1、Spontaneous事件
  由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理。
  本类事件通常是WindowsSystem把从系统得到的消息,比如鼠标按键、键盘按键等,放入系统的消息队列中。Qt事件循环的时候读取这些事件,转化为QEvent,再依次逐个处理。
  2、Posted事件
  由Qt或应用程序产生,它们被Qt组成队列,再通过事件循环处理。
  调用QApplication::postEvent()来产生一个posted类型事件。例如:QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数。
  其实现的原理是new出一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理。
  3、Send事件
  由Qt或应用程序产生,但它们被直接发送到目标对象。
  调用QApplication::sendEvent()函数来产生一个send类型事件。
  send类型事件不会放入队列,而是直接被派发和处理,QWidget::repaint()函数用的就是这种方式。
  【5】QObject类
  QObject三大职责
  1、内存管理
  2、内省(intropection)
  3、事件处理机制
  任何一个想要接受并处理事件的对象均须继承自QObject,可以重写QObject::event()来处理事件,也可以由父类处理。
  【6】事件处理与过滤
  Qt提供了5个级别来处理和过滤事件。
  1、我们可以重新实现特定的eventhandler。
  重新实现像mousePressEvent(),keyPressEvent()和paintEvent()这样的eventHandler是目前处理event最普通的方式。
  2、我们可以重新实现QObject::event()。
  通过重新实现event(),我们可以在事件到达特定的eventhandler之前对它们作出处理。
  这个方法主要是用来覆写Tab键的缺省实现,也可以用来处理不同发生的事件类型,对它们,就没有特定的eventhandler。
  当重新实现event()的时候,我们必须调用基类的event()来处理我们不显式处理的情况。
  3、我们可以安装一个eventfilter到一个单独的QObject。
  一旦一个对象用installEventFilter注册了,发到目标对象的所有事件都会先发到监测对象的eventFilter()。
  如果同一个object安装了多个eventfilter,filter会依次被激活,从最近安装的回到第一个。
  4、我们可以在QApplication对象上安装eventfilter。
  一旦一个eventfilter被注册到qApp(唯一的QApplication对象),程序里发到每个对象的每个事件在发到其他eventfilter之前,都要首先发到eventFilter()。
  这个方法对debugging非常有用,也可以用来处理发到disable的widget上的事件,QApplication通常会丢弃它们。
  5、我们可以子类化QApplication并重新实现notify()。
  Qt调用QApplication::notify()来发出事件,在任何eventfilter得到之前,重新实现这个函数是得到所有事件的唯一方法。
  eventfilter通常更有用,因为可以有任意数目且同时存在的eventfilter,但是只有一个notify()函数。
  【7】事件过滤器
  Qt创建QEvent事件对象后,会调用QObject的event()函数来分发事件。
  但有时,你可能需要在调用event()函数之前做一些自己的操作,比如,对话框上某些组件可能并不需要响应回车键按下的事件,此时,你就需要重新定义组件的event()函数。
  如果组件很多,就需要重写很多次event()函数,这显然没有效率。为此,你可以使用一个事件过滤器,来判断是否需要调用组件的event()函数。
  QOjbect有一个eventFilter()函数,用于建立事件过滤器。这个函数的声明如下:
  virtualboolQObject::eventFilter(QObjectwatched,QEventevent)
  在创建了过滤器之后,下面要做的是安装这个过滤器。安装过滤器需要调用installEventFilter()函数。这个函数的声明如下:
  voidQObject::installEventFilter(QObjectfilterObj)
  这个函数是QObject的一个函数,因此可以安装到任何QObject的子类,并不仅仅是UI组件。
  这个函数接收一个QObject对象,调用了这个函数安装事件过滤器的组件会调用filterObj定义的eventFilter()函数。
  例如,textField。installEventFilter(obj),则如果有事件发送到textField组件时,会先调用objeventFilter()函数,然后才会调用textField。event()。
  当然,你也可以把事件过滤器安装到QApplication上面,这样就可以过滤所有的事件,已获得更大的控制权。不过,这样做的后果就是会降低事件分发的效率。
  我们可以把Qt的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递。其实,Qt的事件对象都有一个accept()函数和ignore()函数。
  正如它们的名字,前者用来告诉Qt,事件处理函数接收了这个事件,不要再传递;后者则告诉Qt,事件处理函数忽略了这个事件,需要继续传递,寻找另外的接受者。
  在事件处理函数中,可以使用isAccepted()来查询这个事件是不是已经被接收了。
  事实上,我们很少使用accept()和ignore()函数,而是像上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可。
  记得我们曾经说过,Qt中的事件大部分是protected的,因此,重写的函数必定存在着其父类中的响应函数,这个方法是可行的。
  为什么要这么做呢?因为我们无法确认父类中的这个处理函数没有操作,如果我们在子类中直接忽略事件,Qt不会再去寻找其他的接受者,那么父类的操作也就不能进行,这可能会有潜在的危险。
  不过,事情也不是绝对的。在一个情形下,我们必须使用accept()和ignore()函数,那就是在窗口关闭的时候。
  如果你在窗口关闭时需要有个询问对话框,那么就需要这么去写:1voidMainWindow::closeEvent(QCloseEventevent)2{3if(continueToClose())4{5eventaccept();6}7else8{9eventignore();10}11}
  nonGUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类Receiver。QtGUI程序,由QApplication来负责。
  【8】事件和信号的区别
  Qt的事件很容易和信号槽混淆。signal由具体对象发出,然后会马上交给由connect函数连接的slot进行处理;
  而对于事件,Qt使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部,前一个事件完成后,取出后面的事件接着再进行处理。
  但是,必要的时候,Qt的事件也是可以不进入事件队列,而是直接处理的。并且,事件还可以使用事件过滤器进行过滤。
  比如一个按钮对象,我们使用这个按钮对象的时候,我们只关心它被按下的信号,至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的。
  但是如果我们要重载一个按钮的时候,我们就要面对event了。比如我们可以改变它的行为,在鼠标按键按下的时候(mousepressevent)就触发clicked()的signal而不是通常在释放的(mousereleaseevent)时候。
  总结的说,Qt的事件和Qt中的signal不一样。后者通常用来使用widget,而前者用来实现widget。如果我们使用系统预定义的控件,那我们关心的是信号,如果自定义控件我们关心的是事件。
  【9】自定义事件
  为什么需要自定义事件?
  事件既可用于同步也可用于异步(依赖于你是调用sendEvent()或是postEvents()),函数调用或是槽调用总是同步的。事件的另外一个好处是它可以被过滤。
  阻塞型事件:事件发送后需要等待处理完成
  〔static〕boolQCoreApplication::sendEvent(QObjectreceiver,QEventevent)
  事件生命周期由应用程序自身管理,同时支持栈事件对象和堆事件对象的发送。
  非阻塞型发送:事件发送后立刻返回,事件被发送到事件队列等待处理
  〔static〕voidQCoreApplication::postEvent(QObjectreceiver,QEventevent,intpriorityQt::NormalEventPriority)
  只能发送堆事件对象,事件被处理后由Qt平台销毁
  当我们在main()函数的末尾调用QApplication::exec()时,程序进入了Qt的事件循环,大概来讲,事件循环如下面所示:1while(!exitwascalled)2{3while(!postedeventqueueisempty)4{5processnextpostedevent();6}7while(!spontaneouseventqueueisempty)8{9processnextspontaneousevent();10}11while(!postedeventqueueisempty)12{13processnextpostedevent();14}15}
  Qt事件循环的过程
  首先,事件循环处理所有的posted事件,直到队列空。然后再处理所有spontaneous事件,最后它处理所有的因为处理spontaneous事件而产生的posted事件。
  send事件并不在事件循环内处理,它们都直接被发送到了目标对象。
  当一个widget第一次可见,或被遮挡后再次变为可见,窗口系统产生一个(spontaneous)paint事件,要求程序重绘widget。
  事件循环最终会从事件队列中捡选这个事件并把它分发到那个需要重画的widget。
  并不是所有的paint事件都是由窗口系统产生的。当你调用QWidget::update()去强行重画widget,这个widget会post一个paint事件给自己。这个paint事件被放入队列,最终被事件循环分发之。
  如果等不及事件循环去重画一个widget,理论上,应该直接调用paintEvent()强制进行立即的重画。但实际上这不总是可行的,因为paintEvent()函数是protected的(很可能访问不了)。
  它也绕开了任何存在的事件过滤器。因为这些原因,Qt提供了一个机制,直接sending事件而不是posting。QWidget::repaint()就使用了这个机制来强制进行立即重画。
  posting相对于sending的一个优势是,它给了Qt一个压缩(compress)事件的机会。
  假如你在一个widget上连续地调用update()十次,因update()而产生的这十个事件,将会自动地被合并为一个单独的事件,但是QPaintEvents事件附带的区域信息也合并了。
  可压缩的事件类型包括:paint、move、resize、layouthint、languagechange。
  最后要注意,你可以在任何时候调用QApplication::sendPostedEvent(),强制Qt产生一个对象的posted事件。
  【10】事件转发
  对于某些类别的事件,如果在整个事件的派发过程结束后还没有被处理,那么这个事件将会向上转发给它的父widget,直到最顶层窗口。
  比如:事件可能最先发送给QCheckBox,如果QCheckBox没有处理,那么由QGroupBox接着处理;
  如果QGroupBox仍然没有处理,再送到QDialog,因为QDialog已经是最顶层widget,所以如果QDialog再不处理,QEvent将停止转发。
  如何判断一个事件是否被处理了呢?Qt中和事件相关的函数通过两种方式相互通信。
  QApplication::notify(),QObject::eventFilter(),QObject::event()通过返回bool值来表示是否已处理。真表示已经处理,假表示事件需要继续传递。
  另一种是调用QEvent::ignore()或QEvent::accept()对事件进行标识。这种方式只用于event()函数和特定事件处理函数之间的沟通。
  而且只有用在某些类别事件上是有意义的,这些事件就是上面提到的那些会被转发的事件,包括:鼠标、滚轮、按键等事件。
  【11】事件的传播(propogation)
  如果事件在目标对象上得不到处理,事件向上一层进行传播,直到最顶层的widget为止。
  如果得到事件的对象,调用了accept(),则事件停止继续传播;如果调用了ignore(),事件向上一级继续传播。
  Qt对自定义事件处理函数的默认返回值是accept(),但默认的事件处理函数是ingore()。
  因此,如果要继续向上传播,调用QWidget的默认处理函数即可。到此为止的话,不必显式调用accept()。
  但在event处理函数里,返回true表示accept,返回false表示向上级传播。
  但closeEvent是个特殊情形,accept表示quit,ignore表示取消,所以最好在closeEvent显式调用accept和ignore。
  【12】事件产生
  事件产生详细过程:1section112intmain(intargc,charargv〔〕)3{4QApplicationa(argc,argv);5MainWindoww;6w。show();78returna。exec();9}1011section1212源码路径:(QTDIRSrcqtbasesrcwidgetskernelqapplication。cpp)13intQApplication::exec()14{15returnQGuiApplication::exec();16}1718section1319源码路径:(QTDIRSrcqtbasesrcguikernelqguiapplication。cpp)20intQGuiApplication::exec()21{22ifndefQTNOACCESSIBILITY23QAccessible::setRootObject(qApp);24endif25returnQCoreApplication::exec();26}2728section1429源码路径:(QTDIRSrcqtbasesrccorelibkernelqcoreapplication。cpp)30intQCoreApplication::exec()31{32if(!QCoreApplicationPrivate::checkInstance(exec))33return1;3435QThreadDatathreadDataselfdfunc()threadData;36if(threadData!QThreadData::current())37{38qWarning(s::exec:Mustbecalledfromthemainthread,selfmetaObject()className());39return1;40}41if(!threadDataeventLoops。isEmpty())42{43qWarning(QCoreApplication::exec:Theeventloopisalreadyrunning);44return1;45}4647threadDataquitNowfalse;48QEventLoopeventLoop;49selfdfunc()inexectrue;50selfdfunc()aboutToQuitEmittedfalse;51intreturnCodeeventLoop。exec();52threadDataquitNowfalse;53if(self)54{55selfdfunc()inexecfalse;56if(!selfdfunc()aboutToQuitEmitted)57{58emitselfaboutToQuit(QPrivateSignal());59}60selfdfunc()aboutToQuitEmittedtrue;61sendPostedEvents(0,QEvent::DeferredDelete);62}6364returnreturnCode;65}6667section1568源码路径:(QTDIRSrcqtbasesrccorelibkernelqeventloop。cpp)69声明:intexec(ProcessEventsFlagsflagsAllEvents);70intQEventLoop::exec(ProcessEventsFlagsflags)71{72QD(QEventLoop);73weneedtoprotectfromraceconditionwithQThread::exit74QMutexLockerlocker(staticcastQThreadPrivate(QObjectPrivate::get(dthreadDatathread))mutex);75if(dthreadDataquitNow)76{77return1;78}7980if(dinExec)81{82qWarning(QEventLoop::exec:instancephasalreadycalledexec(),this);83return1;84}8586structLoopReference87{88QEventLoopPrivated;89QMutexLockerlocker;9091boolexceptionCaught;92LoopReference(QEventLoopPrivated,QMutexLockerlocker):d(d),locker(locker),exceptionCaught(true)93{94dinExectrue;95dexit。storeRelease(false);96dthreadDataloopLevel;97dthreadDataeventLoops。push(dqfunc());98locker。unlock();99}100101LoopReference()102{103if(exceptionCaught)104{105qWarning(Qthascaughtanexceptionthrownfromaneventhandler。Throwing106exceptionsfromaneventhandlerisnotsupportedinQt。Youmust107reimplementQApplication::notify()andcatchallexceptionsthere。);108}109locker。relock();110QEventLoopeventLoopdthreadDataeventLoops。pop();111QASSERTX(eventLoopdqfunc(),QEventLoop::exec(),internalerror);112QUNUSED(eventLoop);releasewarning113dinExecfalse;114dthreadDataloopLevel;115}116};117LoopReferenceref(d,locker);118119removepostedquiteventswhenenteringaneweventloop120QCoreApplicationappQCoreApplication::instance();121if(appappthread()thread())122{123QCoreApplication::removePostedEvents(app,QEvent::Quit);124}125126while(!dexit。loadAcquire())127{128processEvents(flagsWaitForMoreEventsEventLoopExec);129}130131ref。exceptionCaughtfalse;132returndreturnCode。load();133}134135section16136源码路径:(QTDIRSrcqtbasesrccorelibkernelqeventloop。cpp)137boolQEventLoop::processEvents(ProcessEventsFlagsflags)138{139QD(QEventLoop);140if(!dthreadDataeventDispatcher。load())141{142returnfalse;143}144returndthreadDataeventDispatcher。load()processEvents(flags);145}146147section17148源码路径:(QTDIRSrcqtbasesrccorelibkernelqeventdispatcherwin。cpp)149这段代码是完成与windows平台相关的windowsc。150以跨平台著称的Qt同时也提供了对Symiban、Unix等平台的消息派发支持,151分别封装在QEventDispatcherSymbian和QEventDIspatcherUNIX。152QEventDispatcherWin32继承QAbstractEventDispatcher。153boolQEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlagsflags)154{155QD(QEventDispatcherWin32);156157if(!dinternalHwnd)158{159createInternalHwnd();160wakeUp();triggeracalltosendPostedEvents()161}162163dinterruptfalse;164emitawake();165166boolcanWait;167boolretValfalse;168boolseenWMQTSENDPOSTEDEVENTSfalse;169boolneedWMQTSENDPOSTEDEVENTSfalse;170do171{172DWORDwaitRet0;173HANDLEpHandles〔MAXIMUMWAITOBJECTS1〕;174QVarLengthArrayMSGprocessedTimers;175while(!dinterrupt)176{177DWORDnCountdwinEventNotifierList。count();178QASSERT(nCountMAXIMUMWAITOBJECTS1);179180MSGmsg;181boolhaveMessage;182183if(!(flagsQEventLoop::ExcludeUserInputEvents)!dqueuedUserInputEvents。isEmpty())184{185processqueueduserinputevents186haveMessagetrue;187msgdqueuedUserInputEvents。takeFirst();逐个处理用户输入队列中的事件188}189elseif(!(flagsQEventLoop::ExcludeSocketNotifiers)!dqueuedSocketEvents。isEmpty())190{191processqueuedsocketevents192haveMessagetrue;193msgdqueuedSocketEvents。takeFirst();逐个处理socket队列中的事件194}195else196{197haveMessagePeekMessage(msg,0,0,0,PMREMOVE);198if(haveMessage(flagsQEventLoop::ExcludeUserInputEvents)199((msg。messageWMKEYFIRSTmsg。messageWMKEYLAST)200(msg。messageWMMOUSEFIRSTmsg。messageWMMOUSELAST)201msg。messageWMMOUSEWHEEL202msg。messageWMMOUSEHWHEEL203msg。messageWMTOUCH204ifndefQTNOGESTURES205msg。messageWMGESTURE206msg。messageWMGESTURENOTIFY207endif208msg。messageWMCLOSE))209{210queueuserinputeventsforlaterprocessing211haveMessagefalse;212dqueuedUserInputEvents。append(msg);用户输入事件入队列,待以后处理213}214if(haveMessage(flagsQEventLoop::ExcludeSocketNotifiers)215(msg。messageWMQTSOCKETNOTIFIERmsg。hwnddinternalHwnd))216{217queuesocketeventsforlaterprocessing218haveMessagefalse;219dqueuedSocketEvents。append(msg);socket事件入队列,待以后处理220}221}222if(!haveMessage)223{224nomessagecheckforsignalledobjects225for(inti0;i(int)nCount;i)226{227pHandles〔i〕dwinEventNotifierList。at(i)handle();228}229waitRetMsgWaitForMultipleObjectsEx(nCount,pHandles,0,QSALLINPUT,MWMOALERTABLE);230if((haveMessage(waitRetWAITOBJECT0nCount)))231{232anewmessagehasarrived,processit233continue;234}235}236if(haveMessage)237{238WinCEdoesntsupporthooksatall,sowehavetocallthisbyhand:(239if(!dgetMessageHook)240{241(void)qtGetMessageHook(0,PMREMOVE,(LPARAM)msg);242}243244if(dinternalHwndmsg。hwndmsg。messageWMQTSENDPOSTEDEVENTS)245{246if(seenWMQTSENDPOSTEDEVENTS)247{248whencallingprocessEvents()manually,weonlywanttosendposted249eventsonce250needWMQTSENDPOSTEDEVENTStrue;251continue;252}253seenWMQTSENDPOSTEDEVENTStrue;254}255elseif(msg。messageWMTIMER)256{257avoidlivelockbykeepingtrackofthetimerswevealreadysent258boolfoundfalse;259for(inti0;!foundiprocessedTimers。count();i)260{261constMSGprocessedprocessedTimers。constData()〔i〕;262found(processed。wParammsg。wParamprocessed。hwndmsg。hwndprocessed。lParammsg。lParam);263}264if(found)265{266continue;267}268processedTimers。append(msg);269}270elseif(msg。messageWMQUIT)271{272if(QCoreApplication::instance())273{274QCoreApplication::instance()quit();275}276returnfalse;277}278279if(!filterNativeEvent(QByteArrayLiteral(windowsgenericMSG),msg,0))280{281将事件打包成message调用WindowsAPI派发出去282TranslateMessage(msg);283分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数。284DispatchMessage(msg);285}286}287elseif(waitRetWAITOBJECT0nCount)288{289dactivateEventNotifier(dwinEventNotifierList。at(waitRetWAITOBJECT0));290}291else292{293nothingtodosobreak294break;295}296retValtrue;297}298299stillnothingwaitformessageorsignalledobjects300canWait(!retVal301!dinterrupt302(flagsQEventLoop::WaitForMoreEvents));303if(canWait)304{305DWORDnCountdwinEventNotifierList。count();306QASSERT(nCountMAXIMUMWAITOBJECTS1);307for(inti0;i(int)nCount;i)308{309pHandles〔i〕dwinEventNotifierList。at(i)handle();310}311312emitaboutToBlock();313waitRetMsgWaitForMultipleObjectsEx(nCount,pHandles,INFINITE,QSALLINPUT,MWMOALERTABLEMWMOINPUTAVAILABLE);314emitawake();315if(waitRetWAITOBJECT0nCount)316{317dactivateEventNotifier(dwinEventNotifierList。at(waitRetWAITOBJECT0));318retValtrue;319}320}321}while(canWait);322323if(!seenWMQTSENDPOSTEDEVENTS(flagsQEventLoop::EventLoopExec)0)324{325whencalledmanually,alwayssendpostedevents326sendPostedEvents();327}328329if(needWMQTSENDPOSTEDEVENTS)330{331PostMessage(dinternalHwnd,WMQTSENDPOSTEDEVENTS,0,0);332}333334returnretVal;335}336337section17的过程:Qt进入QApplication的eventloop,经过层层委任,338最终QEventLoop的processEvent将通过与平台相关的AbstractEventDispatcher的子类QEventDispatcherWin32339获得用户的输入事件,并将其打包成message后,通过标准的WindowsAPI传递给WindowsOS。340WindowsOS得到通知后回调QtWndProc,至此事件的分发与处理完成了一半的路程。341事件的产生、分发、接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event342Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的函数获取、打包用户输入事件交给视窗系统处理,函数调用栈如下:3431。main(int,char)3442。QApplication::exec()3453。QCoreApplication::exec()3464。QEventLoop::exec(ProcessEventsFlags)3475。QEventLoop::processEvents(ProcessEventsFlags)3486。QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)
  【13】事件分发
  事件分发详细过程:11。QTWINCALLBACKQtWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)boolQETWidget::translateMouseEvent(constMSGmsg)22。boolQApplicationPrivate::sendMouseEvent(。。。)33。inlineboolQCoreApplication::sendSpontaneousEvent(QObjectreceiver,QEventevent)44。boolQCoreApplication::notifyInternal(QObjectreceiver,QEventevent)55。boolQApplication::notify(QObjectreceiver,QEvente)66。boolQApplicationPrivate::notifyhelper(QObjectreceiver,QEvente)77。boolQWidget::event(QEventevent)8下面介绍Qtapp在视窗系统回调后,事件又是怎么一步步通过QApplication分发给最终事件的接受和处理者QWidget::event,9QWidget继承自Object,重载其虚函数event。10section2111windows窗口回调函数12QTWINCALLBACKQtWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)13{14。。。15检查message是否属于Qt可转义的鼠标事件16if(qtistranslatablemouseevent(message))17{18if(QApplication::activePopupWidget()!0)19{inpopupmode20POINTcurPosmsg。pt;21取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例22QWidgetwQApplication::widgetAt(curPos。x,curPos。y);23if(w)24{25widget(QETWidget)w;26}27}2829if(!qttabletChokeMouse)30{31对,就在这里。Windows的回调函数将鼠标事件分发回给了QtWidget32Section2233resultwidgettranslateMouseEvent(msg);mouseevent34}35}3637。。。38}3940section22(QTDIRsrcguikernelqapplicationwin。cpp)41该函数所在与Windows平台相关,主要职责就是把已用windows格式打包的鼠标事件解包、翻译成QApplication可识别的QMouseEvent。42boolQETWidget::translateMouseEvent(constMSGmsg)43{44。。。这里有很长的一段代码可以忽略45让我们看一下sendMouseEvent的声明46widget是事件的接受者;e是封装好的QMouseEvent47Section2348resQApplicationPrivate::sendMouseEvent(target,49e,alienWidget,this,qtbuttondown,50qtlastmousereceiver);51}5253section2354源码路径:(QTDIRSrcqtbasesrcwidgetskernelqapplication。cpp)55boolQApplicationPrivate::sendMouseEvent(QWidgetreceiver,QMouseEventevent,56QWidgetalienWidget,QWidgetnativeWidget,57QWidgetbuttonDown,QPointerQWidgetlastMouseReceiver,58boolspontaneous)59{60。。。61至此与平台相关代码处理完毕62MouseEvent默认的发送方式是spontaneous,所以将执行sendSpontaneousEvent。63sendSpontaneousEvent()与sendEvent的代码实现几乎相同64除了将QEvent的属性spontaneous标记不同。这里是解释什么是spontaneous事件:事件由应用程序之外产生的,比如一个系统事件。65显然MousePress事件是由视窗系统产生的一个的事件(详见上文Section1Section7),因此它是spontaneous事件66if(spontaneous)67{68resultQApplication::sendSpontaneousEvent(receiver,event);69}70else71{72resultQApplication::sendEvent(receiver,event);TODO73}7475。。。7677returnresult;78}7980section2481源码路径:(QTDIRSrcqtbasesrccorelibkernelqcoreapplication。cpp)82inlineboolQCoreApplication::sendSpontaneousEvent(QObjectreceiver,QEventevent)83{84将event标记为自发事件85进一步调用25QCoreApplication::notifyInternal86if(event)87{88eventsponttrue;89}90returnself?selfnotifyInternal(receiver,event):false;91}9293section25:94源码路径:(QTDIRSrcqtbasesrccorelibkernelqcoreapplication。cpp)95boolQCoreApplication::notifyInternal(QObjectreceiver,QEventevent)96{97几行代码对于QtJambi(QTJava绑定版本)和QSA(QTScriptforApplication)的支持98。。。99以下代码主要意图为Qt强制事件只能够发送给当前线程里的对象,100也就是说receiverdfunc()threadData应该等于QThreadData::current()。101注意,跨线程的事件需要借助EventLoop来派发102QObjectPrivatedreceiverdfunc();103QThreadDatathreadDatadthreadData;104threadDataloopLevel;105106哇,终于来到大名鼎鼎的函数QCoreApplication::nofity()了Section26107QTTRY108{109returnValuenotify(receiver,event);110}111QTCATCH(。。。)112{113threadDataloopLevel;114QTRETHROW;115}116117。。。118119returnreturnValue;120}121122section26:123源码路径:(QTDIRSrcqtbasesrccorelibkernelqcoreapplication。cpp)124QCoreApplication::notify和它的重载函数QApplication::notify在Qt的派发过程中起到核心的作用,Qt的官方文档时这样说的:125任何线程的任何对象的所有事件在发送时都会调用notify函数。126boolQCoreApplication::notify(QObjectreceiver,QEventevent)127{128QD(QCoreApplication);129noeventsaredeliveredafterQCoreApplication()hasstarted130if(QCoreApplicationPrivate::isappclosing)131{132returntrue;133}134135if(receiver0)136{seriouserror137qWarning(QCoreApplication::notify:Unexpectednullreceiver);138returntrue;139}140141ifndefQTNODEBUG142dcheckReceiverThread(receiver);143endif144145returnreceiverisWidgetType()?false:dnotifyhelper(receiver,event);146}147148section27:149源码路径:(QTDIRSrcqtbasesrccorelibkernelqcoreapplication。cpp)150notify调用notifyhelper()151boolQCoreApplicationPrivate::notifyhelper(QObjectreceiver,QEventevent)152{153sendtoallapplicationeventfilters154if(sendThroughApplicationEventFilters(receiver,event))155{156returntrue;157}158向事件过滤器发送该事件,这里介绍一下EventFilters。事件过滤器是一个接受即将发送给目标对象所有事件的对象。159如代码所示它开始处理事件在目标对象行动之前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤160允许或者拒绝事件的更进一步的处理。如果所有的事件过滤器允许更进一步的事件处理,事件将被发送到目标对象本身。161如果他们中的一个停止处理,目标和任何后来的事件过滤器不能看到任何事件。162if(sendThroughObjectEventFilters(receiver,event))163{164returntrue;165}166delivertheevent167递交事件给receiverSection28168returnreceiverevent(event);169}170171section28172源码路径:(QTDIRSrcqtbasesrcwidgetskernelqwidget。cpp)173QApplication通过notify及其私有类notifyhelper,将事件最终派发给了QObject的子类QWidget。174boolQWidget::event(QEventevent)175{176。。。177switch(eventtype())178{179caseQEvent::MouseMove:180mouseMoveEvent((QMouseEvent)event);181break;182183caseQEvent::MouseButtonPress:184Dontresetinputcontexthere。Whetherresetornotis185aresponsibilityofinputmethod。reset()willbe186calledbymouseHandler()ofinputmethodifnecessary187viamousePressEvent()oftextwidgets。188if0189resetInputContext();190endif191mousePressEvent((QMouseEvent)event);192break;193}194。。。195}
  【14】Qt5。3。2版本事件机制源码调试
  事件产生于分发调试堆栈图如下:
  【15】总结
  到此为止。

谷歌宣布Android13正式开源源代码已公开!华为小米等厂Android是开源的系统,这是大家都知道的事情,不过每个版本什么时候开源,也要看谷歌的进度。现在,谷歌正式对外宣布,Android13已经开源,新系统源代码已经发布到A……星空还没发售,民间mod社区已经准备好修bug了据外媒PCGamesN报道,一群《星空》的粉丝和mod作者组建了一个名为星空社区补丁(StarfieldCommunityPatch,简称SCP)的团体,打算应对本作发售后可能……明日末伏,无论再忙,记得多吃3宝,少食1物,平安过秋冬一年之中最热的阶段,非三伏天莫属。之所以称之为三伏,是因为其由初伏、中伏、末伏这3伏构成。自7月16日入伏以来,眼下初伏已经过了,中伏也来到了尾声,马上就迎来的是最后一伏末伏。……vivo中国扛旗,海外拉垮,如何拿捏未来?图片来源视觉中国文财经故事荟,作者浮零,编辑天南海内海外表现大不同。近期,随着各大机构的数据公布,发现一个有意思的现象,近年来vivo在国内市场表现不俗,但在……学会拒绝,是一种了不起的才华哈佛大学曾经做过一项调查:如果一个人学会合理地拒绝,就能减少90以上不必要的麻烦,更能减少大量个人时间和精力上的浪费。其实,学会拒绝,是一种稀缺的能力。有的人……红米手机再好也别乱买,这三款红米手机堪称完美,闭眼买都行尽管现如今红米手机已经成为性价比市场一个举足轻重的大品牌,但是这并不意味着每一款红米机型都值得购买,今天就给大家推荐三款个人觉得综合表现最佳的三款红米手机,如果你有想换红米手机……酷比魔方iPlay50平板电脑发布,2K屏幕,售价599元起近日,国产酷比魔方发布了一款新的平板电脑iPlay50系列,提供了464GB以及6GB128GB两个版本,首发价分别为599元以及799元,属于入门低端的平板电脑,面向学生以及……有米云CTO蔡锐涛以中立全链路DaaS服务,赋能企业数字化增数据已经成为继土地、人力、资本、技术之后的新生产要素,并且在数字经济当中,数据要素的重要性与日俱增。如何获得更多的数据,用好数据,是企业数字化转型的必答题。在这个背景下,Daa……红军利物浦开始送温暖,拥有哈兰德曼城就是猛,曼联魔性平切尔西本轮英超最大的冷门莫过于红军利物浦阴沟翻船,上一轮成功阻击曼城以后,这一轮直接给副班长诺丁汉森林送温暖。利物浦成功刷新了各项纪录,本赛季5个英超客场1场没胜,英超11场比赛只获……抗老只知维A醇?三大抗老巨头教你怎么选,内附代表产品讲解抗老要趁早这个概念,00后的妹子们都喊得火热,早就深入人心了,但市面上大火的抗老单品大多主打维A醇,这就是国货护肤的通病,哪个成分火了就一起扎堆出哪个,真是无力吐槽。而大家熟知……F1新加坡大奖赛正赛又是雨战!又是街道赛!又是Checo北京时间10月2日,F1新加坡大奖赛正赛在滨海湾赛道全部结束。佩雷兹起跑阶段超越勒克莱尔,并且守到了最后,拿下赛季第二冠。勒克莱尔亚军,塞恩斯季军。赛前在周六的排位……百强房企销售环比回升中国基金报记者南深10月1日,地产第三方研究机构克而瑞发布了前9月典型房企销售及新增投资情况。传统的金九银十旺季行至中途,今年的房地产销售虽不及往年,但9月来看百强……
2022年末,大总结(太精辟了!)来源于云谷师慈怀读书会2022121206:30发表于上海兹心说不觉年末将至,只愿尘世冬安。佛曰:三千繁华,弹指刹那。岁月流转,时光匆匆。转眼,2022年就进……芒果台格局小了,强捧的综艺火不起来,被遗忘的节目却成了黑马芒果台可以说是综艺界的翘楚,遥遥领先其他卫视,无论是综艺的类型、原创能力还是后期制作,芒果台都是独一档。每个季度芒果台都会有许多综艺节目上线,这些节目不可能所有人都爱看,……力擒韩国狼,转眼败给小绵羊,中国棋手多次在日本阴沟里翻船昨天中国零零后小将李维清力擒元晟溱之后,我有点飘了,写了一篇文字《三国擂台赛硝烟弥漫,李维清会成为中华英雄吗》,寄语首次出战农心杯的李维清能够豪取五连胜,结果我惨遭打脸,今天他……阔别20年李湘回归湖南卫视,谢娜前途未卜2002年,湖南卫视王牌综艺节目《快乐大本营》调整主持人阵容。在何炅的极力推荐下,娜娜成为了幸福家庭的一员。两年后的2004年,李湘主动自愿离开《快乐大本营》,让娜娜成为节目中……同样是短视频爆红,把赵雷和李荣浩一对比,差距就出来了文令狐伯光2023年春节档结束的时候,伯光君写过文章感慨。去年年底刚放开的时候,谁都没有想到春节档会是一个爆发,电视剧有《去有风的地方》《平原上的摩西》《狂飙》《三体》《……买手机没头绪?推荐小米和vivo,好用不贵如果您喜欢,可以点击上面的关注二字。后续会为您提供更多有价值的内容。今天分享:买手机没头绪?推荐小米和vivo,好用不贵第一款:红米k40s参考价格:1799……理财市场回暖,机构密集增持债券备战权益投资今年一季度以来,理财市场回暖的迹象逐渐显露:部分理财公司产品规模开始企稳,新发产品重回扩张区间;产品净值持续修复反弹,业绩比较基准波动回升在2022年两轮破净潮后,银行理财终于……躺平升级,全服虚拟资源互换!搬砖党福音魔域手游2来了!现在的游戏好像都有大病,似乎不为难玩家就不会做游戏了!市面上大部分游戏更新后,玩家感受到的不是游戏更新的快乐,而是冗长的剧情,还有上蹿下跳的任务,有时候打了好久,主线一半都没完……三月樱花为谁留,携妹开心去旅游文生活如诗如画三月樱花为谁留携妹开心去旅游美好风光情义在一只蝴蝶驻心头图片来自网络,如侵权删除。2023。3。2作者简介:占小红,湖北……宇宙演化行星由来生命起源人类产生?看恩格斯的讲述宇宙的演化、行星的由来整个自然界,从最小的东西到最大的东西,从沙粒到太阳,从原生生物到人,都处于永恒的产生和消灭中,处于不断的流动中,处于无休止的运动和变化之中。从旋转的……站在距离地球2241光年的位置,能否直击秦始皇登基时的盛况?科技之巅如果仅仅从时间和光速上考虑,理想条件下确实是可以看到秦始皇登基的盛况!前提你有足够强大的天文空间望远镜,哈勃太空望远镜直径4。3米,估计你需要拿个直径4。3亿公里……北京成功举办冬奥一周年从春天走向未来来源:央视网央视网消息:今天(2月4日)是二十四节气的立春,意味着万物生长的春天重回大地。一年前的今天,北京冬奥会盛大开幕,以一束微火温暖了世界,在冰与雪的竞技中,把一起……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网