原文地址:http://mobile.51cto.com/symbian-272812.htmc++
在Qt中,事件被封裝成一個個對象,全部的事件均繼承自抽象類QEvent. 接下來依次談談Qt中有誰來產生、分發、接受和處理事件。windows
本篇來介紹Qt 事件處理機制。深刻了解事件處理系統對於每一個學習Qt人來講很是重要,能夠說,Qt是以事件驅動的UI工具集。 你們熟知Signals/Slots在多線程的實現也依賴於Qt的事件處理機制多線程
在Qt中,事件被封裝成一個個對象,全部的事件均繼承自抽象類QEvent. 接下來依次談談Qt中有誰來產生、分發、接受和處理事件:app
一、誰來產生事件: 最容易想到的是咱們的輸入設備,好比鍵盤、鼠標產生的框架
keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他們被封裝成QMouseEvent和QKeyEvent),這些事件來自於底層的操做系統,它們以異步的形式通知Qt事件處理系統,後文會仔細道來。固然Qt本身也會產生不少事件,好比QObject::startTimer()會觸發QTimerEvent.
用戶的程序可還以本身定製事件異步
二、誰來接受和處理事件:答案是QObject。在Qt的內省機制剖析一文已經介紹QObject
類是整個Qt對象模型的心臟,事件處理機制是QObject三大職責(內存管理、內省(intropection)與事件處理制)之一。任何一個想要接受並處理事件的對象均須繼承自QObject,能夠選擇重載QObject::event()函數或事件的處理權轉給父類。socket
三、誰來負責分發事件:對於non-GUI的Qt程序,是由QCoreApplication負責將QEvent分發給QObject的子類-Receiver.
對於Qt GUI程序,由QApplication來負責函數
接下來,將經過對代碼的解析來看看QT是利用event loop從事件隊列中獲取用戶輸入事件,又是如何將事件轉義成QEvents,並分發給相應的QObject處理。工具
section1oop
1 #include <QApplication> 2 #include "widget.h" 3 int main(int argc, char *argv[]) 4 { 5 QApplication app(argc, argv); 6 Widget window; // Widget 繼承自QWidget 7 window.show(); 8 return app.exec(); // 進入Qpplication事件循環,見section 2 9 }
section2
1 int QApplication::exec() 2 { 3 #ifndef QT_NO_ACCESSIBILITY 4 QAccessible::setRootObject(qApp); 5 #endif //簡單的交給QCoreApplication來處理事件循環=〉section 3 6 return QCoreApplication::exec(); 7 }
section3
1 int QCoreApplication::exec() 2 { 3 if (!QCoreApplicationPrivate::checkInstance("exec")) 4 return -1; 5 //獲得當前Thread數據 6 QThreadData *threadData = self->d_func()->threadData; 7 if (threadData != QThreadData::current()) { 8 qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className()); 9 return -1; 10 }
//檢查event loop是否已經建立 11 if (!threadData->eventLoops.isEmpty()) { 12 qWarning("QCoreApplication::exec: The event loop is already running"); 13 return -1; 14 } 15 16 threadData->quitNow = false; 17 QEventLoop eventLoop; 18 self->d_func()->in_exec = true; 19 self->d_func()->aboutToQuitEmitted = false;
//委任QEventLoop 處理事件隊列循環 ==> Section 4 20 int returnCode = eventLoop.exec(); 21 threadData->quitNow = false; 22 if (self) { 23 self->d_func()->in_exec = false; 24 if (!self->d_func()->aboutToQuitEmitted) 25 emit self->aboutToQuit(); 26 self->d_func()->aboutToQuitEmitted = true; 27 sendPostedEvents(0, QEvent::DeferredDelete); 28 } 29 30 return returnCode; 31 }
section4
1 int QEventLoop::exec(ProcessEventsFlags flags) 2 { 3 Q_D(QEventLoop); //訪問QEventloop私有類實例d 4 //we need to protect from race condition with QThread::exit 5 QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex); 6 if (d->threadData->quitNow) 7 return -1; 8 9 if (d->inExec) { 10 qWarning("QEventLoop::exec: instance %p has already called exec()", this); 11 return -1; 12 } 13 d->inExec = true; 14 d->exit = false; 15 ++d->threadData->loopLevel; 16 d->threadData->eventLoops.push(this); 17 locker.unlock(); 18 19 // remove posted quit events when entering a new event loop 20 QCoreApplication *app = QCoreApplication::instance(); 21 if (app && app->thread() == thread()) 22 QCoreApplication::removePostedEvents(app, QEvent::Quit); 23 //這裏的實現代碼很多,最爲重要的是如下幾行 24 #if defined(QT_NO_EXCEPTIONS) 25 while (!d->exit) 26 processEvents(flags | WaitForMoreEvents | EventLoopExec); 27 #else 28 try { 29 while (!d->exit) //只要沒有碰見exit,循環派發事件 30 processEvents(flags | WaitForMoreEvents | EventLoopExec); 31 } catch (...) { 32 qWarning("Qt has caught an exception thrown from an event handler. Throwing\n" 33 "exceptions from an event handler is not supported in Qt. You must\n" 34 "reimplement QApplication::notify() and catch all exceptions there.\n"); 35 36 // copied from below 37 locker.relock(); 38 QEventLoop *eventLoop = d->threadData->eventLoops.pop(); 39 Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); 40 Q_UNUSED(eventLoop); // --release warning 41 d->inExec = false; 42 --d->threadData->loopLevel; 43 44 throw; 45 } 46 #endif 47 48 // copied above 49 locker.relock(); 50 QEventLoop *eventLoop = d->threadData->eventLoops.pop(); 51 Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); 52 Q_UNUSED(eventLoop); // --release warning 53 d->inExec = false; 54 --d->threadData->loopLevel; 55 56 return d->returnCode; 57 }
section5
1 bool QEventLoop::processEvents(ProcessEventsFlags flags) 2 { 3 Q_D(QEventLoop); 4 if (!d->threadData->eventDispatcher) 5 return false; 6 if (flags & DeferredDeletion) 7 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); 8 return d->threadData->eventDispatcher->processEvents(flags); //將事件派發給與平臺相關的QAbstractEventDispatcher子類 =>Section 6 9 }
1 bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) 2 { 3 Q_D(QEventDispatcherWin32); 4 5 if (!d->internalHwnd) 6 createInternalHwnd(); 7 8 d->interrupt = false; 9 emit awake(); 10 11 bool canWait; 12 bool retVal = false; 13 bool seenWM_QT_SENDPOSTEDEVENTS = false; 14 bool needWM_QT_SENDPOSTEDEVENTS = false; 15 do { 16 DWORD waitRet = 0; 17 HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; 18 QVarLengthArray<MSG> processedTimers; 19 while (!d->interrupt) { 20 DWORD nCount = d->winEventNotifierList.count(); 21 Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); 22 23 MSG msg; 24 bool haveMessage; 25 26 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { 27 // process queued user input events 28 haveMessage = true; 29 msg = d->queuedUserInputEvents.takeFirst(); //從處理用戶輸入隊列中取出一條事件,處理隊列裏面的用戶輸入事件 30 } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { 31 // process queued socket events 32 haveMessage = true; 33 msg = d->queuedSocketEvents.takeFirst(); // 從處理socket隊列中取出一條事件,處理隊列裏面的socket事件 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 #ifndef QT_NO_GESTURES 45 || msg.message == WM_GESTURE 46 || msg.message == WM_GESTURENOTIFY 47 #endif 48 || msg.message == WM_CLOSE)) { 49 // queue user input events for later processing 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 // queue socket events for later processing 56 haveMessage = false; 57 d->queuedSocketEvents.append(msg); // socket 事件入隊列,待之後處理 58 } 59 } 60 if (!haveMessage) { 61 // no message - check for signalled objects 62 for (int i=0; i<(int)nCount; i++) 63 pHandles[i] = d->winEventNotifierList.at(i)->handle(); 64 waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); 65 if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { 66 // a new message has arrived, process it 67 continue; 68 } 69 } 70 if (haveMessage) { 71 #ifdef Q_OS_WINCE 72 // WinCE doesn't support hooks at all, so we have to call this by hand :( 73 (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg); 74 #endif 75 76 if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { 77 if (seenWM_QT_SENDPOSTEDEVENTS) { 78 // when calling processEvents() "manually", we only want to send posted 79 // events once 80 needWM_QT_SENDPOSTEDEVENTS = true; 81 continue; 82 } 83 seenWM_QT_SENDPOSTEDEVENTS = true; 84 } else if (msg.message == WM_TIMER) { 85 // avoid live-lock by keeping track of the timers we've already sent 86 bool found = false; 87 for (int i = 0; !found && i < processedTimers.count(); ++i) { 88 const MSG processed = processedTimers.constData()[i]; 89 found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); 90 } 91 if (found) 92 continue; 93 processedTimers.append(msg); 94 } else if (msg.message == WM_QUIT) { 95 if (QCoreApplication::instance()) 96 QCoreApplication::instance()->quit(); 97 return false; 98 } 99 100 if (!filterEvent(&msg)) { 101 TranslateMessage(&msg); //將事件打包成message調用Windows API派發出去 102 DispatchMessage(&msg); //分發一個消息給窗口程序。消息被分發到回調函數,將消息傳遞給windows系統,windows處理完畢,會調用回調函數 => section 7 103 } 104 } else if (waitRet < WAIT_OBJECT_0 + nCount) { 105 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); 106 } else { 107 // nothing todo so break 108 break; 109 } 110 retVal = true; 111 } 112 113 // still nothing - wait for message or signalled objects 114 canWait = (!retVal 115 && !d->interrupt 116 && (flags & QEventLoop::WaitForMoreEvents)); 117 if (canWait) { 118 DWORD nCount = d->winEventNotifierList.count(); 119 Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); 120 for (int i=0; i<(int)nCount; i++) 121 pHandles[i] = d->winEventNotifierList.at(i)->handle(); 122 123 emit aboutToBlock(); 124 waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); 125 emit awake(); 126 if (waitRet < WAIT_OBJECT_0 + nCount) { 127 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); 128 retVal = true; 129 } 130 } 131 } while (canWait); 132 133 if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) { 134 // when called "manually", always send posted events 135 QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); 136 } 137 138 if (needWM_QT_SENDPOSTEDEVENTS) 139 PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); 140 141 return retVal; 142 }
// Section 7 windows窗口回調函數 定義在QTDIR\src\gui\kernel\qapplication_win.cpp
1 extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 2 { 3 ... 4 //將消息從新封裝成QEvent的子類QMouseEvent ==> Section 8 5 result = widget->translateMouseEvent(msg); 6 ... 7 }
從Section 1~Section7, Qt進入QApplication的event loop,通過層層委任,最終QEventloop的processEvent將經過與平臺相關的QAbstractEventDispatcher的子類QEventDispatcherWin32得到用戶的用戶輸入事件,並將其打包成message後,經過標準Windows API ,把消息傳遞給了Windows OS,Windows OS獲得通知後回調QtWndProc, 至此事件的分發與處理完成了一半的路程。
在下文中,咱們將進一步討論當咱們收到來在Windows的回調後,事件又是怎麼一步步打包成QEvent並經過QApplication分發給最終事件的接受和處理者QObject::event
事件的產生、分發、接受和處理,並以視窗系統鼠標點擊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) 2 bool QETWidget::translateMouseEvent(const MSG &msg) 3 bool QApplicationPrivate::sendMouseEvent(...) 4 inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event) 5 bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) 6 bool QApplication::notify(QObject *receiver, QEvent *e) 7 bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) 8 bool QWidget::event(QEvent *event)
section7 == section2-1
1 QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 2 { 3 ... 4 //檢查message是否屬於Qt可轉義的鼠標事件 5 if (qt_is_translatable_mouse_event(message)) { 6 if (QApplication::activePopupWidget() != 0) { // in popup mode 7 POINT curPos = msg.pt; 8 //取得鼠標點擊座標所在的QWidget指針,它指向咱們在main建立的widget實例 9 QWidget* w = QApplication::widgetAt(curPos.x, curPos.y); 10 if (w) 11 widget = (QETWidget*)w; 12 } 13 14 if (!qt_tabletChokeMouse) { 15 //對,就在這裏。Windows的回調函數將鼠標事件分發回給了Qt Widget 16 // => Section 2-2 17 result = widget->translateMouseEvent(msg); // mouse event 18 ... 19 }
1 bool QETWidget::translateMouseEvent(const MSG &msg) 2 { 3 //.. 這裏很長的代碼給以忽略 4 // 讓咱們看一下sendMouseEvent的聲明 5 // widget是事件的接受者; e是封裝好的QMouseEvent 6 // ==> Section 2-3 7 res = QApplicationPrivate::sendMouseEvent(target, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); 8 }
// Section 2-3 $QTDIR\src\gui\kernel\qapplication.cpp
1 bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, 2 QWidget *alienWidget, QWidget *nativeWidget, 3 QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver, 4 bool spontaneous) 5 { 6 ... 7 //至此與平臺相關代碼處理完畢 8 //MouseEvent默認的發送方式是spontaneous, 因此將執行 9 //sendSpontaneousEvent。 sendSpontaneousEvent() 與 sendEvent的代碼實現幾乎相同 10 //除了將QEvent的屬性spontaneous標記不一樣。 這裏是解釋什麼spontaneous事件:若是事件由應用程序以外產生的,好比一個系統事件。 11 //顯然MousePress事件是由視窗系統產生的一個的事件(詳見上文Section 1~ Section 7),所以它是 spontaneous事件 12 if (spontaneous) 13 result = QApplication::sendSpontaneousEvent(receiver, event); 14 else 15 result = QApplication::sendEvent(receiver, event); 16 17 ... 18 19 return result; 20 }
// Section 2-4 C:\Qt\4.7.1-Vs\src\corelib\kernel\qcoreapplication.h
1 inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event) 2 { 3 //將event標記爲自發事件 4 //進一步調用 2-5 QCoreApplication::notifyInternal 5 if (event) 6 event->spont = true; 7 return self ? self->notifyInternal(receiver, event) : false; 8 }
// Section 2-5: $QTDIR\gui\kernel\qapplication.cpp
1 bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) 2 { 3 // 幾行代碼對於Qt Jambi (QT Java綁定版本) 和QSA (QT Script for Application)的支持 4 5 ... 6 7 // 如下代碼主要意圖爲Qt強制事件只可以發送給當前線程裏的對象,也就是說receiver->d_func()->threadData應該等於QThreadData::current()。 8 //注意,跨線程的事件須要藉助Event Loop來派發 9 QObjectPrivate *d = receiver->d_func(); 10 QThreadData *threadData = d->threadData; 11 ++threadData->loopLevel; 12 13 //哇,終於來到大名鼎鼎的函數QCoreApplication::nofity()了 ==> Section 2-6 14 QT_TRY { 15 returnValue = notify(receiver, event); 16 } QT_CATCH (...) { 17 --threadData->loopLevel; 18 QT_RETHROW; 19 } 20 21 ... 22 23 return returnValue; 24 }
1 bool QCoreApplication::notify(QObject *receiver, QEvent *event) 2 { 3 Q_D(QCoreApplication); 4 // no events are delivered after ~QCoreApplication() has started 5 if (QCoreApplicationPrivate::is_app_closing) 6 return true; 7 8 if (receiver == 0) { // serious error 9 qWarning("QCoreApplication::notify: Unexpected null receiver"); 10 return true; 11 } 12 13 #ifndef QT_NO_DEBUG 14 d->checkReceiverThread(receiver); 15 #endif 16 17 return receiver->isWidgetType() ? false : d->notify_helper(receiver, event); 18 }
notify 調用 notify_helper()
// Section 2-7: $QTDIR\gui\kernel\qapplication.cpp
1 bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) 2 { 3 // send to all application event filters 4 if (sendThroughApplicationEventFilters(receiver, event)) 5 return true; 6 // 向事件過濾器發送該事件,這裏介紹一下Event Filters. 事件過濾器是一個接受即將發送給目標對象全部事件的對象。 7 //如代碼所示它開始處理事件在目標對象行動以前。過濾器的QObject::eventFilter()實現被調用,能接受或者丟棄過濾 8 //容許或者拒絕事件的更進一步的處理。若是全部的事件過濾器容許更進一步的事件處理,事件將被髮送到目標對象自己。 9 //若是他們中的一箇中止處理,目標和任何後來的事件過濾器不能看到任何事件。 10 if (sendThroughObjectEventFilters(receiver, event)) 11 return true; 12 // deliver the event 13 // 遞交事件給receiver => Section 2-8 14 return receiver->event(event); 15 }
// Section 2-8 $QTDIR\gui\kernel\qwidget.cpp
// QApplication經過notify及其私有類notify_helper,將事件最終派發給了QObject的子類- QWidget.
1 bool QWidget::event(QEvent *event) 2 { 3 ... 4 5 switch (event->type()) { 6 case QEvent::MouseMove: 7 mouseMoveEvent((QMouseEvent*)event); 8 break; 9 10 case QEvent::MouseButtonPress: 11 // Don't reset input context here. Whether reset or not is 12 // a responsibility of input method. reset() will be 13 // called by mouseHandler() of input method if necessary 14 // via mousePressEvent() of text widgets. 15 #if 0 16 resetInputContext(); 17 #endif 18 mousePressEvent((QMouseEvent*)event); 19 break; 20 21 ... 22 23 }