1. 程式人生 > >淺議Qt的事件處理機制 一

淺議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. //Section 1
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QApplication app(argc, argv);  
  7.     Widget window;  // Widget 繼承自QWidget
  8.     window.show();  
  9.     return
     app.exec(); // 進入Qpplication事件迴圈,見section 2
  10. }  
  11. // Section 2: 
  12. int QApplication::exec()  
  13. {  
  14.    //skip codes
  15.    //簡單的交給QCoreApplication來處理事件迴圈=〉section 3
  16.    return QCoreApplication::exec();  
  17. }  
  18. // Section 3
  19. int QCoreApplication::exec()  
  20. {  
  21.     //得到當前Thread資料
  22.     QThreadData *threadData = self->d_func()->threadData;  
  23.     if (threadData != QThreadData::current()) {  
  24.         qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());  
  25.         return -1;  
  26.     }  
  27.     //檢查event loop是否已經建立
  28.     if (!threadData->eventLoops.isEmpty()) {  
  29.         qWarning("QCoreApplication::exec: The event loop is already running");  
  30.         return -1;  
  31.     }  
  32.     ...  
  33.     QEventLoop eventLoop;  
  34.     self->d_func()->in_exec = true;  
  35.     self->d_func()->aboutToQuitEmitted = false;  
  36.     //委任QEventLoop 處理事件佇列迴圈 ==> Section 4
  37.     int returnCode = eventLoop.exec();  
  38.     ....  
  39.     }  
  40.     return returnCode;  
  41. }  
  42. // Section 4
  43. int QEventLoop::exec(ProcessEventsFlags flags)  
  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. // Section 5
  54. bool QEventLoop::processEvents(ProcessEventsFlags flags)  
  55. {  
  56.     Q_D(QEventLoop);  
  57.     if (!d->threadData->eventDispatcher)  
  58.         returnfalse;  
  59.     if (flags & DeferredDeletion)  
  60.         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);  
  61.     //將事件派發給與平臺相關的QAbstractEventDispatcher子類 =>Section 6
  62.     return d->threadData->eventDispatcher->processEvents(flags);  
  63. }  
  1. // Section 6,QTDIR/src/corelib/kernel/qeventdispatcher_win.cpp
  2. // 這段程式碼是完成與windows平臺相關的windows c++。 以跨平臺著稱的Qt同時也提供了對Symiban,Unix等平臺的訊息派發支援
  3. // 其事現分別封裝在QEventDispatcherSymbian和QEventDispatcherUNIX
  4. // QEventDispatcherWin32派生自QAbstractEventDispatcher.
  5. bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)  
  6. {  
  7.     Q_D(QEventDispatcherWin32);  
  8.     if (!d->internalHwnd)  
  9.         createInternalHwnd();  
  10.     d->interrupt = false;  
  11.     emit awake();  
  12.     bool canWait;  
  13.     bool retVal = false;  
  14.     bool seenWM_QT_SENDPOSTEDEVENTS = false;  
  15.     bool needWM_QT_SENDPOSTEDEVENTS = false;  
  16.     do {  
  17.         DWORD waitRet = 0;  
  18.         HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];  
  19.         QVarLengthArray<MSG> processedTimers;  
  20.         while (!d->interrupt) {  
  21.             DWORD nCount = d->winEventNotifierList.count();  
  22.             Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);  
  23.             MSG msg;  
  24.             bool haveMessage;  
  25.             if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {  
  26.                 // process queued user input events
  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. #ifndef QT_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呼叫Windows API派發出去
  64.                        //分發一個訊息給視窗程式。訊息被分發到回撥函式,將訊息傳遞給windows系統,windows處理完畢,會呼叫回撥函式 => section 7                    
  65.                   DispatchMessage(&msg);  
  66.                 }  
  67.             }               
  68.         }  
  69.     } while (canWait);  
  70.       ...  
  71.     return retVal;  
  72. }  
  1. // Section 7 windows視窗回撥函式 定義在QTDIR/src/gui/kernel/qapplication_win.cpp
  2. extern"C"LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
  3. {  
  4.    ...  
  5.    //將訊息重新封裝成QEvent的子類QMouseEvent ==> Section 8
  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人來說非常重要,可以說,Qt是以事件驅動的UI工具集。 大家熟知Signals/Slots在多執行緒的實現也依賴於Qt的事件處理機制。     在Qt中,事件被封裝成一個個物件,所有的事件均繼承自抽象類QEvent.  接下來

Qt ------ 事件處理機制

後處理 分發 異步 ant ont 事件循環 tar rpo 基類 簡介 在Qt中,事件被封裝成一個個對象,所有的事件均繼承自抽象類QEvent。Qt是以事件驅動UI工具集。Signals/Slots在多線程中的實現也是依賴於Qt的事件處理機制。在Qt中,事件被封裝成一個個

QT開發——QT事件處理機制

ref:https://blog.csdn.net/A642960662/article/details/66473871 一、QT事件簡介     QT程式是事件驅動的, 程式的每個動作都是由內部某個事件所觸發。QT事件的發生和處理成為程式執行的主線

Qt事件處理機制整個流程--以滑鼠在一個視窗中點選為例

轉載自:http://mobile.51cto.com/symbian-272812.htm,在此謝謝原作者的分享! ------------------------第一部分----------------------   本篇來介紹Qt 事件處理機制 。深入瞭解事件處理系統對

Qt中的事件處理機制(event)

Qt事件也就是Qt程式中出現的一系列“事情”,包括對使用者操作做出反應時發出的滑鼠或鍵盤事件等;以及系統內部自動發出的定時器事件等。總之,出現了這些事件後就需要對這些事件進行處理,處理的方法便是“事件處理機制”。 圖1 Qt事件產生 以

控制元件巢狀中的QT滑鼠事件處理機制

想實現在層層佈局的控制元件中,對最外層的QLabel上的滑鼠事件實現tracking,即觸發mouseMoveEvent的時候,不需要一直按著 前提:自定義控制元件,繼承QLabel重寫滑鼠事件相應的方法 可參考:https://wiki.qt.io/Clickable_QLabel

談Android 事件分發機制

get touch 需要 傳遞 cimage android ges 常見 滑動 在上一篇文章中,淺談Android 事件分發機制(一),簡要分析了一下事件分發機制的原理,總結一下就是事件層層傳遞,直到被消費,原理看似簡單,但是在實際使用過程中,場景各不相同,復雜程度也就因

事件處理

blog lis 監聽 上下 strong 單擊 長按 view 處理方法 事件處理步驟: 1.註冊事件監聽器 2.根據指定的事件中編寫事件處理的代碼 3.在事件處理的代碼證完成對事件的處理 事件處理方法 1.setOnClickListener(View.OnC

從零開始理解JAVA事件處理機制(2)

extend nds 接下來 htm ref param 簡單 tostring ansi 第一節中的示例過於簡單《從零開始理解JAVA事件處理機制(1)》,簡單到讓大家覺得這樣的代碼簡直毫無用處。但是沒辦法,我們要繼續寫這毫無用處的代碼,然後引出下一階段真正有益的代碼。

事件處理機制--瀏覽器流程處理分析

style chan 一件事 cli con open() -h response xhr 事件處理機制--瀏覽器流程處理分析 js的運行是單線程的,單線程即一個時間只能做一件事。瀏覽器的運行是多線程的。 如下三種情況會進入事件隊列(任務隊列)中,但不立即執行:   1.定

初步剖析QT事件處理全過程(Windows)

職責 spa message alt cati 窗口 初步 負責 啟動 一、說起Qt事件處理,在windows平臺下,當然離不開Win32: Win32程序的基本結構:  1.註冊窗口;  2.創建窗口;  3.啟動由GetMessage和DispatchMessage構成

關於表單form元素中onsubmit事件處理機制的認識

讓我 clas 教程 是否 它的 默認方法 對象實例 action 事件處理機制   博主目前處於Js學習的初期,遇到了很多問題,比如今天的關於表單form元素中onsubmit事件問題,根據教程所述,onsubmit事件是在表單提交的時候觸發的,但是我看到教程上的onsu

wxPython事件處理機制

參數 過程 Coding vtt nbu roc pre 是我 說明 一直沒有看明白wxPython的自定義事件是如何工作的。如果要自定義事件,那麽需要明白事件源,事件類型,事件處理函數,以及事件綁定器; 但是我在整個過程中沒有看到這幾個概念是如何關聯起來的。下面舉例說明一

3.1.1 基於監聽的事件處理機制

講解 基於 example this apk imp cal href eat 3.1.1 基於監聽的事件處理機制 http://www.runoob.com/w3cnote/android-tutorial-listen-event-handle.html 本節引言:

3.2 基於回調的事件處理機制

return lan 依賴 架構 順序 們的 oncreate 兩個 運行 3.2 基於回調的事件處理機制 http://www.runoob.com/w3cnote/android-tutorial-callback-event-handle.html 1.

17.QT-事件處理分析、事件過濾器、拖放事件

期待 使用 lis 相關 事件處理 [] 支持 實現 endif Qt事件處理介紹 Qt平臺會將系統產生的消息轉換為Qt事件 Qt事件是一個QEvent的對象 Qt事件用來描述程序內部或外部發生的動作 任意的QObject對象都具備事件處理的能力 Qt常見

Node.js知識點整理之----基礎知識----事件處理機制事件機制

node 應用程序 nod eat 回調函數 clas 對象 繼續 知識 在event模塊中,定義了EventEmitter類,所有觸發事件的對象都是繼承了這個類的子類的實例對象。 addListener(event,listener) 對指定事件綁定事件處理函數 on

Qt事件處理

Qt事件處理 Qt事件處理的五個層次 Qt 應用程式 事件處理 的五個層次,同時也是時間流程如下圖所示: 說明如下: sendEvent()會把event直接傳送給QCoreApplication的notify(),postEvent()把event追加到事件佇列中最終也要呼叫notify(

JAVA異常處理機制()

異常處理機制try-catch 語法定義: try{ //可能出現異常的程式碼片段 }catch(XXXException e){ //捕獲try中出現的XXXException後的處理操作程式碼 } try-catch演示 public class Try_CatchDe

這篇文章對於瞭解Javascript的事件處理機制非常好,將它全文轉載於此,以備不時之需。

什麼是事件? 事件(Event)是JavaScript應用跳動的心臟 ,也是把所有東西粘在一起的膠水。當我們與瀏覽器中 Web 頁面進行某些型別的互動時,事件就發生了。事件可能是使用者在某些內容上的點選、滑鼠經過某個特定元素或按下鍵盤上的某些按鍵。事件還可能是 Web