专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

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】总结
  到此为止。

股市中稳挣的方法运用进入股市重要的是什么?来了就是为了挣钱的,稳赚不赔是我们的理想,想象着不但稳赚不赔,而且还能够赚更多的钱。随时随地无时不刻的赚钱,这是我们进入股市的初心。选一只k线在低位的票,均线美媒科尔向普尔说了刻薄的话库里他们都在努力赢得比赛直播吧1月21日讯勇士今日在客场120114力克骑士。第三节时,普尔跟裁判抱怨吃到技犯。吃到技犯之后,场边的科尔也对普尔一直抱怨感到不满了,他直接对普尔大喊乔丹!你不该这么做的!专超越水花兄弟!普尔打爆骑士创纪录!641712,勇士4大奇兵诞生今天勇士客场挑战骑士,由于是背靠背的比赛,水花兄弟维金斯追梦4大主力全部轮休,所以首发阵容有了很大的改变,普尔单核带队。首节比赛勇士反客为主,在普尔和杰罗姆的带领下很快建立起领先优德约科维奇麻烦了!前世界第一澳网上演大逆转,曾11次终结德约1月20日,2023年澳大利亚网球公开赛结束了第二轮的争夺,结果35岁的英国网球巨星安迪穆雷制造了职业生涯又一场惊天大逆转,经过5小时45分钟鏖战,他以4667(4)76(5)63组图丨来谢子龙影像艺术馆,用照片定格春天的气息1月24日,谢子龙影像艺术馆门口,等待打卡的游客已经排起了长队,大家都饶有兴致地看着保安大哥定格美丽瞬间。红网时刻新闻1月24日讯(记者李丹陈杰李璐通讯员丁婷婷)1月24日(大年初塔图姆1代谍照都说丑,发售抢成狗!从传闻到谍照,一年的等待,终于迎来了塔图姆一代的曝光!看到谍照不知道你们有何感想?说实话第一眼有点丑,再细看会觉得有点意思,通常谍照曝光都是这样,先让你感到失望,然后在发售时惊艳全输越南惨案一周年,国足继续解散中去年今日,也是大年初一,在12强赛第8轮,国足客场1比3不敌越南,彻底无缘2022年卡塔尔世界杯。而越南也历史性获得本届他们在12强赛中的首场胜利,这也是东南亚球队首次在亚洲区外围东部排名榜篮网难得一胜,老鹰5连胜有望争第6,哈登开始拉票了20222023赛季NBA常规赛正在如火如荼的进行中,1月21日比赛结束后,东部排名榜再次变化篮网难得一胜,暂且独居第4名老鹰5连胜,有望冲击第6名哈登全明星首发不稳,76人开始拉印度公开赛半决赛战报,国羽5战3胜,3项双打决赛争金,献礼春节各位看官大人新年好!癸卯春节吉祥!!北京时间1月22日凌晨1时,2023世界羽联职业巡回赛印度公开赛半决赛的战事在德里决出胜负,中国军团5组球员出战,取得三胜两负,三项双打全部闯入小罗我是足坛历史上唯一的个人技术天花板,能和我接近的仅3人尽管小罗早已淡出球迷视线,不过有时也会对足坛重要事件和人物发表观点,他认为,从个人技术上来讲,足坛历史上自己是唯一的天花板,能和他接近的仅3名球员,梅西老马大罗。如实说,从人球结合麦迪认真讲,现役能有机会超越詹姆斯总得分的仅三人,库里不行麦迪认为随着詹姆斯越来越接近40000分,预估詹姆斯能打到40岁,詹姆斯至少可以拿到42000分才会退役,未来有机会超越詹姆斯的现役球员只有3个人能做到,他们分别东契奇,字母哥及维
明星红毯争艳秀性感丁字裤红毯上穿性感丁字裤的名人肯德尔詹娜,蕾哈娜当涉及到高调的活动时,明星们往往会穿着最经典的服装走上红毯,以获得完美的照片。然而,这并不意味着他们的着装一定要朴素!从海莉比伯(neBa20182019微博之夜明星穿搭回顾,大咖云集高定满天飞今年的微博之夜即将上演,从目前公布的名单看,热门明星几乎全部都会亮相,估计超过百人一点都没问题,不知道到时候走啥红毯的明星们会选择什么品牌带来何种造型,好期待呀来个小调查,你最期待海通国际助力固生堂完成港股上市后首次配售中证网讯(王珞)海通国际日前作为联席配售代理完成固生堂控股有限公司(以下简称固生堂,股票代码2273。HK)的先旧后新配售交易。此次配售交易创下了今年以来由中资投行主导的医疗健康配第十届国际残疾人职业技能竞赛闭幕中国代表团取得佳绩中新社巴黎3月26日电(记者李洋)第十届国际残疾人职业技能竞赛于当地时间3月25日晚在法国东部城市梅斯闭幕,中国代表团获6金7银7铜。中国代表团29名选手参加了9个类别20个项目的阿里巴巴国际站运营岗位的工作定位根据我这些年的工作经验,我个人对阿里巴巴国际站运营这个岗位,或者说对外贸公司的运营岗位有一些不同于同行的了解。我所在的这家公司刚入职时也是初创公司,所以也可能是因为前期分工不明确,全国滑雪障碍追逐冠军赛云南队夺得一银一铜来源云南日报云南小将李佳鸿(右一)在比赛中。20222023赛季全国自由式滑雪及单板滑雪障碍追逐冠军赛于2023年3月13日至20日在黑龙江省亚布力滑雪场举行。云南代表队共选派12CBA全明星周末结束各单项冠军和赛事MVP出炉北区全明星117113南区全明星3月24日至26日在厦门奥林匹克体育中心凤凰体育馆进行的2023年CBA全明星周末在今晚正式落下帷幕,这是疫情结束后第一次在主场进行比赛,北区全明星早安,黄山黄山毛峰全面开园采摘中国黄山(黟县)国际自行车公开赛鸣枪26hr202303黄山毛峰全面开园采摘中国黄山(黟县)国际自行车公开赛鸣枪海外华文媒体集中采访走进屯溪区休宁县九三学社专家工作站(灾害防治)签约挂牌影像力网红点游人如织安徽疾控发历史性突破!上海选手吴愉加冕女子拳击世锦赛冠军3月26日,上海健儿吴愉在2023女子世界拳击锦标赛5052公斤级的决赛中夺冠。这是上海拳击队首次在世界拳击锦标赛中获得奖牌,创历史最佳战绩,这也是女子拳击项目的重大突破。5052最坏的排名,最新的开始记中国男子单人滑名将金博洋新华社日本埼玉3月25日电(记者王子江)金博洋是以男子单人滑短节目第19名的成绩进入25日晚的自由滑比赛的,他第六个出场,埼玉的超级体育场响起英文歌声这是个美丽的开始,也是个崭新的(体育)村BA总决赛季军出炉冠军争夺战因雨择期再战(2)当日,贵州省首届美丽乡村篮球联赛总决赛季军争夺战在黔东南州台江县台盘村举行,铜仁队以57比50战胜毕节队获得季军。因当地出现降雨天气,主办方现场宣布,原计划当晚进行的冠军争夺战将择
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网