范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Qt事件机制

  【1】事件
  事件是可以被控件识别的操作。如按下确定按钮、选择某个单选按钮或复选框。
  每种控件有自己可识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事件等等。
  事件就是用户对窗口上各种组件的操作。
  【2】Qt事件
  由窗口系统或Qt自身产生的,用以响应所发生各类事情的操作。具体点,Qt事件是一个QEvent对象,用于描述程序内部或外部发生的动作。
  【3】Qt事件产生类型
  1、键盘或鼠标事件:用户按下或松开键盘或鼠标上的按键时,就可以产生一个键盘或者鼠标事件。
  2、绘制事件:某个窗口第一次显示的时候,就会产生一个绘制事件,用来告诉窗口需要重新绘制它本身,从而使得该窗口可见。
  3、QT事件:Qt自己也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent。
  【4】Qt事件分类
  基于事件如何被产生与分发,可以把事件分为三类:
  1、Spontaneous 事件
  由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理。
  本类事件通常是Windows System把从系统得到的消息,比如鼠标按键、键盘按键等, 放入系统的消息队列中。 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、我们可以重新实现特定的event handler。
  重新实现像mousePressEvent(), keyPressEvent()和paintEvent()这样的event Handler是目前处理event最普通的方式。
  2、我们可以重新实现QObject::event()。
  通过重新实现event(),我们可以在事件到达特定的event handler之前对它们作出处理。
  这个方法主要是用来覆写Tab键的缺省实现,也可以用来处理不同发生的事件类型,对它们,就没有特定的event handler。
  当重新实现event()的时候,我们必须调用基类的event()来处理我们不显式处理的情况。
  3、我们可以安装一个event filter到一个单独的QObject。
  一旦一个对象用installEventFilter注册了, 发到目标对象的所有事件都会先发到监测对象的eventFilter()。
  如果同一个object安装了多个event filter, filter会依次被激活, 从最近安装的回到第一个。
  4、我们可以在QApplication对象上安装event filter。
  一旦一个event filter被注册到qApp(唯一的QApplication对象), 程序里发到每个对象的每个事件在发到其他event filter之前,都要首先发到eventFilter()。
  这个方法对debugging非常有用,也可以用来处理发到disable的widget上的事件, QApplication通常会丢弃它们。
  5、我们可以子类化QApplication并重新实现notify()。
  Qt调用QApplication::notify()来发出事件,在任何event filter得到之前, 重新实现这个函数是得到所有事件的唯一方法。
  event filter通常更有用, 因为可以有任意数目且同时存在的event filter, 但是只有一个notify()函数。
  【7】事件过滤器
  Qt创建QEvent事件对象后,会调用QObject的event()函数来分发事件。
  但有时,你可能需要在调用event()函数之前做一些自己的操作,比如,对话框上某些组件可能并不需要响应回车键按下的事件,此时,你就需要重新定义组件的event()函数。
  如果组件很多,就需要重写很多次event()函数,这显然没有效率。为此,你可以使用一个事件过滤器,来判断是否需要调用组件的event()函数。
  QOjbect有一个eventFilter()函数,用于建立事件过滤器。这个函数的声明如下:
  virtual bool QObject::eventFilter (QObject * watched, QEvent * event)
  在创建了过滤器之后,下面要做的是安装这个过滤器。安装过滤器需要调用installEventFilter()函数。这个函数的声明如下:
  void QObject::installEventFilter ( QObject * filterObj)
  这个函数是QObject的一个函数,因此可以安装到任何QObject的子类,并不仅仅是UI组件。
  这个函数接收一个QObject对象,调用了这个函数安装事件过滤器的组件会调用filterObj定义的eventFilter()函数。
  例如,textField.installEventFilter(obj),则如果有事件发送到textField组件时,会先调用obj->eventFilter()函数,然后才会调用textField.event()。
  当然,你也可以把事件过滤器安装到QApplication上面,这样就可以过滤所有的事件,已获得更大的控制权。不过,这样做的后果就是会降低事件分发的效率。
  我们可以把Qt的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递。其实,Qt的事件对象都有一个accept()函数和ignore()函数。
  正如它们的名字,前者用来告诉Qt,事件处理函数"接收"了这个事件,不要再传递;后者则告诉Qt,事件处理函数"忽略"了这个事件,需要继续传递,寻找另外的接受者。
  在事件处理函数中,可以使用isAccepted()来查询这个事件是不是已经被接收了。
  事实上,我们很少使用accept()和ignore()函数,而是像上面的示例一样,如果希望忽略事件,只要调用父类的响应函数即可。
  记得我们曾经说过,Qt中的事件大部分是protected的,因此,重写的函数必定存在着其父类中的响应函数,这个方法是可行的。
  为什么要这么做呢?因为我们无法确认父类中的这个处理函数没有操作,如果我们在子类中直接忽略事件,Qt不会再去寻找其他的接受者,那么父类的操作也就不能进行,这可能会有潜在的危险。
  不过,事情也不是绝对的。在一个情形下,我们必须使用accept()和ignore()函数,那就是在窗口关闭的时候。
  如果你在窗口关闭时需要有个询问对话框,那么就需要这么去写:  1 void MainWindow::closeEvent(QCloseEvent * event)  2 {  3     if (continueToClose())  4     {  5         event->accept();  6     }  7     else  8     {  9         event->ignore(); 10     } 11 }
  non-GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类-Receiver. Qt GUI程序,由QApplication来负责。
  【8】事件和信号的区别
  Qt的事件很容易和信号槽混淆。signal由具体对象发出,然后会马上交给由connect函数连接的slot进行处理;
  而对于事件,Qt使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部,前一个事件完成后,取出后面的事件接着再进行处理。
  但是,必要的时候,Qt的事件也是可以不进入事件队列,而是直接处理的。并且,事件还可以使用"事件过滤器"进行过滤。
  比如一个按钮对象, 我们使用这个按钮对象的时候, 我们只关心它被按下的信号, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的。
  但是如果我们要重载一个按钮的时候,我们就要面对event了。 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候。
  总结的说,Qt的事件和Qt中的signal不一样。 后者通常用来使用widget, 而前者用来实现widget。 如果我们使用系统预定义的控件,那我们关心的是信号,如果自定义控件我们关心的是事件。
  【9】自定义事件
  为什么需要自定义事件?
  事件既可用于同步也可用于异步(依赖于你是调用sendEvent()或是postEvents()),函数调用或是槽调用总是同步的。事件的另外一个好处是它可以被过滤。
  阻塞型事件:事件发送后需要等待处理完成
  [static] bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
  事件生命周期由应用程序自身管理,同时支持栈事件对象和堆事件对象的发送。
  非阻塞型发送:事件发送后立刻返回,事件被发送到事件队列等待处理
  [static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
  只能发送堆事件对象,事件被处理后由Qt平台销毁
  当我们在main()函数的末尾调用QApplication::exec()时,程序进入了Qt的事件循环,大概来讲,事件循环如下面所示:  1 while (!exit_was_called)  2 {  3     while (!posted_event_queue_is_empty)  4     {  5         process_next_posted_event();  6     }  7     while (!spontaneous_event_queue_is_empty)  8     {  9         process_next_spontaneous_event(); 10     } 11     while (!posted_event_queue_is_empty) 12     { 13         process_next_posted_event(); 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、layout hint、language change。
  最后要注意,你可以在任何时候调用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】事件产生
  事件产生详细过程:   1 // section 1-1   2 int main(int argc, char *argv[])   3 {   4     QApplication a(argc, argv);   5     MainWindow w;   6     w.show();   7    8     return a.exec();   9 }  10   11 // section 1-2  12 // 源码路径:($QTDIRSrcqtbasesrcwidgetskernelqapplication.cpp)  13 int QApplication::exec()  14 {  15     return QGuiApplication::exec();  16 }  17   18 // section 1-3  19 // 源码路径:($QTDIRSrcqtbasesrcguikernelqguiapplication.cpp)  20 int QGuiApplication::exec()  21 {  22 #ifndef QT_NO_ACCESSIBILITY  23     QAccessible::setRootObject(qApp);  24 #endif  25     return QCoreApplication::exec();  26 }  27   28 // section 1-4  29 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqcoreapplication.cpp)  30 int QCoreApplication::exec()  31 {  32     if (!QCoreApplicationPrivate::checkInstance("exec"))  33         return -1;  34   35     QThreadData *threadData = self->d_func()->threadData;  36     if (threadData != QThreadData::current())  37     {  38         qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());  39         return -1;  40     }  41     if (!threadData->eventLoops.isEmpty())  42     {  43         qWarning("QCoreApplication::exec: The event loop is already running");  44         return -1;  45     }  46   47     threadData->quitNow = false;  48     QEventLoop eventLoop;  49     self->d_func()->in_exec = true;  50     self->d_func()->aboutToQuitEmitted = false;  51     int returnCode = eventLoop.exec();  52     threadData->quitNow = false;  53     if (self)  54     {  55         self->d_func()->in_exec = false;  56         if (!self->d_func()->aboutToQuitEmitted)  57         {  58             emit self->aboutToQuit(QPrivateSignal());  59         }  60         self->d_func()->aboutToQuitEmitted = true;  61         sendPostedEvents(0, QEvent::DeferredDelete);  62     }  63   64     return returnCode;  65 }  66   67 // section 1-5  68 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqeventloop.cpp)  69 // 声明:int exec(ProcessEventsFlags flags = AllEvents);  70 int QEventLoop::exec(ProcessEventsFlags flags)  71 {  72     Q_D(QEventLoop);  73     // we need to protect from race condition with QThread::exit  74     QMutexLocker locker(&static_cast(QObjectPrivate::get(d->threadData->thread))->mutex);  75     if (d->threadData->quitNow)  76     {  77         return -1;  78     }  79   80     if (d->inExec)  81     {  82         qWarning("QEventLoop::exec: instance %p has already called exec()", this);  83         return -1;  84     }  85   86     struct LoopReference  87     {  88         QEventLoopPrivate *d;  89         QMutexLocker &locker;  90   91         bool exceptionCaught;  92         LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)  93         {  94             d->inExec = true;  95             d->exit.storeRelease(false);  96             ++d->threadData->loopLevel;  97             d->threadData->eventLoops.push(d->q_func());  98             locker.unlock();  99         } 100  101         ~LoopReference() 102         { 103             if (exceptionCaught) 104             { 105                 qWarning("Qt has caught an exception thrown from an event handler. Throwing " 106                          "exceptions from an event handler is not supported in Qt. You must " 107                          "reimplement QApplication::notify() and catch all exceptions there. "); 108             } 109             locker.relock(); 110             QEventLoop *eventLoop = d->threadData->eventLoops.pop(); 111             Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error"); 112             Q_UNUSED(eventLoop); // --release warning 113             d->inExec = false; 114             --d->threadData->loopLevel; 115         } 116     }; 117     LoopReference ref(d, locker); 118  119     // remove posted quit events when entering a new event loop 120     QCoreApplication *app = QCoreApplication::instance(); 121     if (app && app->thread() == thread()) 122     { 123         QCoreApplication::removePostedEvents(app, QEvent::Quit); 124     } 125  126     while (!d->exit.loadAcquire()) 127     { 128         processEvents(flags | WaitForMoreEvents | EventLoopExec); 129     } 130  131     ref.exceptionCaught = false; 132     return d->returnCode.load(); 133 } 134  135 // section 1-6 136 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqeventloop.cpp) 137 bool QEventLoop::processEvents(ProcessEventsFlags flags) 138 { 139     Q_D(QEventLoop); 140     if (!d->threadData->eventDispatcher.load()) 141     { 142         return false; 143     } 144     return d->threadData->eventDispatcher.load()->processEvents(flags); 145 } 146  147 // section 1-7 148 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqeventdispatcher_win.cpp) 149 // 这段代码是完成与windows平台相关的windows c++。 150 // 以跨平台著称的Qt同时也提供了对Symiban、Unix等平台的消息派发支持, 151 // 分别封装在QEventDispatcherSymbian和QEventDIspatcherUNIX。 152 // QEventDispatcherWin32继承QAbstractEventDispatcher。 153 bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) 154 { 155     Q_D(QEventDispatcherWin32); 156  157     if (!d->internalHwnd) 158     { 159         createInternalHwnd(); 160         wakeUp(); // trigger a call to sendPostedEvents() 161     } 162  163     d->interrupt = false; 164     emit awake(); 165  166     bool canWait; 167     bool retVal = false; 168     bool seenWM_QT_SENDPOSTEDEVENTS = false; 169     bool needWM_QT_SENDPOSTEDEVENTS = false; 170     do 171     { 172         DWORD waitRet = 0; 173         HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; 174         QVarLengthArray processedTimers; 175         while (!d->interrupt) 176         { 177             DWORD nCount = d->winEventNotifierList.count(); 178             Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); 179  180             MSG msg; 181             bool haveMessage; 182  183             if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) 184             { 185                 // process queued user input events 186                 haveMessage = true; 187                 msg = d->queuedUserInputEvents.takeFirst(); // 逐个处理用户输入队列中的事件 188             } 189             else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) 190             { 191                 // process queued socket events 192                 haveMessage = true; 193                 msg = d->queuedSocketEvents.takeFirst(); // 逐个处理socket队列中的事件 194             } 195             else 196             { 197                 haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); 198                 if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) 199                     && ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) 200                         || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) 201                         || msg.message == WM_MOUSEWHEEL 202                         || msg.message == WM_MOUSEHWHEEL 203                         || msg.message == WM_TOUCH 204 #ifndef QT_NO_GESTURES 205                         || msg.message == WM_GESTURE 206                         || msg.message == WM_GESTURENOTIFY 207 #endif 208                         || msg.message == WM_CLOSE)) 209                 { 210                     // queue user input events for later processing 211                     haveMessage = false; 212                     d->queuedUserInputEvents.append(msg); // 用户输入事件入队列,待以后处理 213                 } 214                 if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) 215                     && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) 216                 { 217                     // queue socket events for later processing 218                     haveMessage = false; 219                     d->queuedSocketEvents.append(msg); // socket 事件入队列,待以后处理 220                 } 221             } 222             if (!haveMessage) 223             { 224                 // no message - check for signalled objects 225                 for (int i = 0; i < (int)nCount; i++) 226                 { 227                     pHandles[i] = d->winEventNotifierList.at(i)->handle(); 228                 } 229                 waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); 230                 if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) 231                 { 232                     // a new message has arrived, process it 233                     continue; 234                 } 235             } 236             if (haveMessage) 237             { 238                 // WinCE doesn"t support hooks at all, so we have to call this by hand :( 239                 if (!d->getMessageHook) 240                 { 241                     (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg); 242                 } 243  244                 if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) 245                 { 246                     if (seenWM_QT_SENDPOSTEDEVENTS) 247                     { 248                         // when calling processEvents() "manually", we only want to send posted 249                         // events once 250                         needWM_QT_SENDPOSTEDEVENTS = true; 251                         continue; 252                     } 253                     seenWM_QT_SENDPOSTEDEVENTS = true; 254                 } 255                 else if (msg.message == WM_TIMER) 256                 { 257                     // avoid live-lock by keeping track of the timers we"ve already sent 258                     bool found = false; 259                     for (int i = 0; !found && i < processedTimers.count(); ++i) 260                     { 261                         const MSG processed = processedTimers.constData()[i]; 262                         found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); 263                     } 264                     if (found) 265                     { 266                         continue; 267                     } 268                     processedTimers.append(msg); 269                 } 270                 else if (msg.message == WM_QUIT) 271                 { 272                     if (QCoreApplication::instance()) 273                     { 274                         QCoreApplication::instance()->quit(); 275                     } 276                     return false; 277                 } 278  279                 if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) 280                 { 281                     // 将事件打包成message调用Windows API派发出去 282                     TranslateMessage(&msg); 283                     // 分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数。 284                     DispatchMessage(&msg); 285                 } 286             } 287             else if (waitRet - WAIT_OBJECT_0 < nCount) 288             { 289                 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); 290             } 291             else 292             { 293                 // nothing todo so break 294                 break; 295             } 296             retVal = true; 297         } 298  299         // still nothing - wait for message or signalled objects 300         canWait = (!retVal 301                    && !d->interrupt 302                    && (flags & QEventLoop::WaitForMoreEvents)); 303         if (canWait) 304         { 305             DWORD nCount = d->winEventNotifierList.count(); 306             Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); 307             for (int i = 0; i < (int)nCount; i++) 308             { 309                 pHandles[i] = d->winEventNotifierList.at(i)->handle(); 310             } 311  312             emit aboutToBlock(); 313             waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); 314             emit awake(); 315             if (waitRet - WAIT_OBJECT_0 < nCount) 316             { 317                 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); 318                 retVal = true; 319             } 320         } 321     } while (canWait); 322  323     if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) 324     { 325         // when called "manually", always send posted events 326         sendPostedEvents(); 327     } 328  329     if (needWM_QT_SENDPOSTEDEVENTS) 330     { 331         PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); 332     } 333  334     return retVal; 335 } 336  337 // section1~7的过程:Qt进入QApplication的event loop,经过层层委任, 338 // 最终QEventLoop的processEvent将通过与平台相关的AbstractEventDispatcher的子类QEventDispatcherWin32 339 // 获得用户的输入事件,并将其打包成message后,通过标准的Windows API传递给Windows OS。 340 // Windows OS得到通知后回调QtWndProc,至此事件的分发与处理完成了一半的路程。 341 // 事件的产生、分发、接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event 342 // Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的函数获取、打包用户输入事件交给视窗系统处理,函数调用栈如下: 343 // 1.main(int, char **) 344 // 2.QApplication::exec() 345 // 3.QCoreApplication::exec() 346 // 4.QEventLoop::exec(ProcessEventsFlags ) 347 // 5.QEventLoop::processEvents(ProcessEventsFlags ) 348 // 6.QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)
  【13】事件分发
  事件分发详细过程:   1 // 1.QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) bool QETWidget::translateMouseEvent(const MSG &msg)   2 // 2.bool QApplicationPrivate::sendMouseEvent(...)   3 // 3.inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)   4 // 4.bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)   5 // 5.bool QApplication::notify(QObject *receiver, QEvent *e)   6 // 6.bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)   7 // 7.bool QWidget::event(QEvent *event)   8 // 下面介绍Qt app在视窗系统回调后,事件又是怎么一步步通过QApplication分发给最终事件的接受和处理者QWidget::event,   9 // QWidget继承自Object,重载其虚函数event。  10 // section 2-1  11 // windows窗口回调函数  12 QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  13 {  14     // ...  15     // 检查message是否属于Qt可转义的鼠标事件  16     if (qt_is_translatable_mouse_event(message))  17     {  18         if (QApplication::activePopupWidget() != 0)  19         { // in popup mode  20             POINT curPos = msg.pt;  21             // 取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例  22             QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);  23             if (w)  24             {  25                 widget = (QETWidget*)w;  26             }  27         }  28   29         if (!qt_tabletChokeMouse)  30         {  31             // 对,就在这里。Windows的回调函数将鼠标事件分发回给了Qt Widget  32             // => Section 2-2  33             result = widget->translateMouseEvent(msg);  // mouse event  34         }  35     }  36   37     // ...  38 }  39   40 // section 2-2 ($QTDIRsrcguikernelqapplication_win.cpp)  41 // 该函数所在与Windows平台相关,主要职责就是把已用windows格式打包的鼠标事件解包、翻译成QApplication可识别的QMouseEvent。  42 bool QETWidget::translateMouseEvent(const MSG &msg)  43 {  44     // ...这里有很长的一段代码可以忽略  45     // 让我们看一下sendMouseEvent的声明  46     // widget是事件的接受者;e是封装好的QMouseEvent  47     // ==> Section 2-3  48     res = QApplicationPrivate::sendMouseEvent(target,  49                                               &e, alienWidget, this, &qt_button_down,  50                                               qt_last_mouse_receiver);  51 }  52   53 // section 2-3  54 // 源码路径:($QTDIRSrcqtbasesrcwidgetskernelqapplication.cpp)  55 bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,  56                                          QWidget *alienWidget, QWidget *nativeWidget,  57                                          QWidget **buttonDown, QPointer &lastMouseReceiver,  58                                          bool spontaneous)  59 {  60     // ...  61     // 至此与平台相关代码处理完毕  62     // MouseEvent默认的发送方式是spontaneous, 所以将执行sendSpontaneousEvent。  63     // sendSpontaneousEvent() 与 sendEvent的代码实现几乎相同  64     // 除了将QEvent的属性spontaneous标记不同。 这里是解释什么是spontaneous事件:事件由应用程序之外产生的,比如一个系统事件。  65     // 显然MousePress事件是由视窗系统产生的一个的事件(详见上文Section 1~ Section 7),因此它是spontaneous事件  66     if (spontaneous)  67     {  68         result = QApplication::sendSpontaneousEvent(receiver, event);  69     }  70     else  71     {  72         result = QApplication::sendEvent(receiver, event);//TODO  73     }  74   75     ...  76   77     return result;  78 }  79   80 // section 2-4  81 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqcoreapplication.cpp)  82 inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)  83 {  84     // 将event标记为自发事件  85     // 进一步调用 2-5 QCoreApplication::notifyInternal  86     if (event)  87     {  88         event->spont = true;  89     }  90     return self ? self->notifyInternal(receiver, event) : false;  91 }  92   93 // section 2-5:  94 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqcoreapplication.cpp)  95 bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)  96 {  97     // 几行代码对于Qt Jambi (QT Java绑定版本) 和QSA (QT Script for Application)的支持  98     // ...  99     // 以下代码主要意图为Qt强制事件只能够发送给当前线程里的对象, 100     // 也就是说receiver->d_func()->threadData应该等于QThreadData::current()。 101     // 注意,跨线程的事件需要借助Event Loop来派发 102     QObjectPrivate *d = receiver->d_func(); 103     QThreadData *threadData = d->threadData; 104     ++threadData->loopLevel; 105  106     // 哇,终于来到大名鼎鼎的函数QCoreApplication::nofity()了 ==> Section 2-6 107     QT_TRY 108     { 109         returnValue = notify(receiver, event); 110     } 111     QT_CATCH (...) 112     { 113         --threadData->loopLevel; 114         QT_RETHROW; 115     } 116  117     ... 118  119     return returnValue; 120 } 121  122 // section 2-6: 123 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqcoreapplication.cpp) 124 // QCoreApplication::notify和它的重载函数QApplication::notify在Qt的派发过程中起到核心的作用,Qt的官方文档时这样说的: 125 // 任何线程的任何对象的所有事件在发送时都会调用notify函数。 126 bool QCoreApplication::notify(QObject *receiver, QEvent *event) 127 { 128     Q_D(QCoreApplication); 129     // no events are delivered after ~QCoreApplication() has started 130     if (QCoreApplicationPrivate::is_app_closing) 131     { 132         return true; 133     } 134  135     if (receiver == 0) 136     {                        // serious error 137         qWarning("QCoreApplication::notify: Unexpected null receiver"); 138         return true; 139     } 140  141 #ifndef QT_NO_DEBUG 142     d->checkReceiverThread(receiver); 143 #endif 144  145     return receiver->isWidgetType() ? false : d->notify_helper(receiver, event); 146 } 147  148 // section 2-7: 149 // 源码路径:($QTDIRSrcqtbasesrccorelibkernelqcoreapplication.cpp) 150 // notify 调用 notify_helper() 151 bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) 152 { 153     // send to all application event filters 154     if (sendThroughApplicationEventFilters(receiver, event)) 155     { 156         return true; 157     } 158     // 向事件过滤器发送该事件,这里介绍一下Event Filters. 事件过滤器是一个接受即将发送给目标对象所有事件的对象。 159     //如代码所示它开始处理事件在目标对象行动之前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤 160     //允许或者拒绝事件的更进一步的处理。如果所有的事件过滤器允许更进一步的事件处理,事件将被发送到目标对象本身。 161     //如果他们中的一个停止处理,目标和任何后来的事件过滤器不能看到任何事件。 162     if (sendThroughObjectEventFilters(receiver, event)) 163     { 164         return true; 165     } 166     // deliver the event 167     // 递交事件给receiver  => Section 2-8 168     return receiver->event(event); 169 } 170  171 // section 2-8 172 // 源码路径:($QTDIRSrcqtbasesrcwidgetskernelqwidget.cpp) 173 // QApplication通过notify及其私有类notify_helper,将事件最终派发给了QObject的子类- QWidget. 174 bool QWidget::event(QEvent *event) 175 { 176     // ... 177     switch (event->type()) 178     { 179     case QEvent::MouseMove: 180         mouseMoveEvent((QMouseEvent*)event); 181         break; 182  183     case QEvent::MouseButtonPress: 184         // Don"t reset input context here. Whether reset or not is 185         // a responsibility of input method. reset() will be 186         // called by mouseHandler() of input method if necessary 187         // via mousePressEvent() of text widgets. 188 #if 0 189         resetInputContext(); 190 #endif 191         mousePressEvent((QMouseEvent*)event); 192         break; 193     } 194     // ... 195 }
  【14】Qt5.3.2版本事件机制源码调试
  事件产生于分发调试堆栈图如下:
  【15】总结
  到此为止。

梦中的那片海陷入舆论风波,疑似被暂停拍摄,肖战又要背锅最近肖战一直忙于拍摄梦中的那片海,这部剧官宣之后一直备受关注,因为这部剧是一部年代剧,而且作品还受到了官方的认可和支持,剧中的两位主演肖战李沁已经合作过很多次了。然而最近关于这部剧度过低谷后的沉默因为熬过低谷后的人,虽然变得更强大了,但往往也更破碎了。在哲学史上有一个忒修斯之船的悖论如果一条船在大海上航行,每遇到船受损的时候就用新的木头进行替换修补,那么直到所有的木头都不是最适合孩子吃的食物排行榜,鸡翅倒数第1,牛肉只排第3,建议了解孩子正处于不断生长发育的阶段,对营养素的需要量较大,科学选择孩子的食物十分重要。对于正在生长发育时期的孩子来说,没有及时摄入身体所需的营养物质,抵抗力差,也会影响长个,还会影响学习伊朗高级军官被暗杀,幕后黑手或与美国有关,伊朗总统誓言报复当所有国家都在关注俄乌冲突时,伊朗传来了一个坏消息。莱希或许也没想到,光天化日之下,极端武装居然有胆量在德黑兰街头暗杀伊朗军官。22日,伊朗革命卫队表示,伊朗圣城旅高级指挥官科代伊1998年7名少女一夜惨死,唯一幸存者死里逃生,却被警方逮捕文影中纪实编辑影中纪实前言1998年天津警方接到报案,静海一中113号女生宿舍,一夜之间竟有七名女生离奇身亡。警察介入调查后,将嫌疑人锁定为113宿舍唯一幸存的女学生孙亚宇,随即将奋力续写时代文艺华章夏风轻轻掠过,田野的麦穗透着香甜。位于桥儿沟鲁艺旧址的延安文艺纪念馆,在蓝天白云下静静伫立。80年前的红色五月,革命进程中文艺领域响起的第一声春雷,开启了文艺为人民服务的崭新时代。德系豪门,集体塌方作者张生来源鸣金网传统汽车的王者,新能源浪潮下的看客。跟不上时代的,终将被时代所抛弃!一hr奥迪翻车,刘德华被坑惨!这两天,奥迪请刘德华拍摄的人生小满广告,一夜之间全网刷屏。一开始世界常任理事国将起新变化,拜登支持日本入常,日本能成功吗?导语NHK公共电视台周一表示,美国总统乔拜登周一表示,他支持日本成为联合国安理会常任理事国,那么日本真的可以成功入常吗?最近老美又在搞事情,刚刚国际新闻表示,美国拜登将支持日本,进清华博士生报考协警,网友热议近日,有网友爆料称有清华博士生报考湖南省长沙市岳麓区协警岗位引发广泛关注今天(5月23日)相关话题冲上微博热搜榜第一在长沙市岳麓区人民政府官网查询发现,该区于5月21日中午12点公免职一年半,张炳政落马据中央纪委国家监委5月23日消息,海关总署统计分析司原副司长张炳政涉嫌严重违纪违法,目前正在接受中央纪委国家监委驻海关总署纪检监察组纪律审查和山西省监察委员会监察调查。张炳政资料图英国专家感慨难怪中国能迅速发展,他们愿意花70年治理沙漠笔者杰鲁鲁衡量一个国家真正实力,一般都是从经济科技军事等维度去分析,然而我始终觉得,一个国家真正的内在实力体现在长远的战略眼光上。中国为何能够取得如此迅速的发展,归根结底是因为中国
乙类乙管实施2个月,目前出入境情况如何?1月8日起,我国对新型冠状病毒感染实施乙类乙管,距离今天已经过去2个月,目前出入境情况如何?对出入境人员的防控措施要求如何?乙类乙管实施2个月出入境流量大幅回升自1月8日我国实施新武威副市长姜保红,贪赃1400万,陪睡40余名官员,最后被情夫抛弃姜保红,官运长红姜保红是一个通过利用自己的美貌,将皮肉生意做到极致的睡美人。1997年,她才24岁,正值青春年华,如一朵娇艳欲滴的玫瑰,才一出学校,便在甘肃兰州市七里河区人民法院成收评丨原油鲍威尔鹰派发言,油价跌超301hr黑色板块焦炭基本面健康运行,旺季来临趋势未被破坏价格今日05合约弱势运行收于2894,港口准一级资源仓单成本2851元吨。现货第一轮提涨尚未落地,焦炭市场暂稳运行。供应端现如何防范重特大安全事故?部长通道给出了答案今天我们继续聚焦两会,应急管理部部长王祥喜在部长通道,就应急管理体制改革推动公共安全治理模式向事前预防转型防范和遏制重特大事故发生等热点问题回答媒体记者提问。我们将把防范重特大事故内蒙前公安厅长手持双枪闹市追杀情人,残忍杀害,被执行死刑赵黎平,曾经是内蒙古自治区公安厅的厅长,是一名严谨而敬业的警察。然而,一段爱情改变了他的人生轨迹。赵黎平的情人是一个年轻的女孩,名叫李琳。李琳来自一个普通的家庭,是一名警察局的文员黑龙江又一位高颜值女干部被查!拔出萝卜带出泥,她的伯乐也落马2023年1月10日,黑龙江省纪委监委发布了消息鹤岗市卫生健康委副主任曹晓宇涉嫌严重违违法,目前正接受纪律审查和监察调查。鹤岗市,属地级市,常住人口约90万人。对于这位美女干部,广人行员工做梦都能笑醒13万员工摇身变为公务员,撤销支行是好事两会期间,最受关注的莫过于第八次机构改革,也是近20年来,机构变动最大的一次,其中金融机构变动最大。而最大的受益者莫过于人民银行员工,他们做梦都能笑醒。人民银行三大变化金融改革成为曾经奋战一线的缉毒女警长,拥有权利的同时,却是堕落的开始又有人落马!这次这位不简单,她曾是株洲市公安局副局长,现任株洲市公安局二级高级警长,她就是凌娅,估计是坏事干太多,害怕秋后算账,夜不能寐,于是干脆主动投案,看来她是想争取宽大处理,新华网评他们,连起了万家灯火灌缝缝补坑坑修盖盖,这就是全国人大代表王润梅和她的团队的工作日常。她是山西省太原市的一名清掏工,用她自己的话来说,就是拿着洋镐叉子,在大街小巷掏泥。这样一份工作,她从18岁开始,一我第一个举手报名!一年四季春为首莫负春日好时光春天百花齐放万物苏醒在这个生机盎然的季节旅游经济也如春花绽放般加快复苏随着气温回升出行人数也日益增多不仅有文旅局长的火爆出圈各地也积极施策吸引客流为旅游校尉美术内蒙校区世界名画鉴赏什么是成全?RaffaelloSanti拉斐尔是意大利著名的画家,也是文艺复兴后的三杰中最年轻的一位艺术家。在文艺复兴时期,可以将理想美的事业到达巅峰的一位艺术家,代表作品有雅典学派西斯廷圣母