Qt Event 以及 Event Filter 事件處理


詳解 QT Event 以及 Event Filter 事件處理

本文介紹的是詳解 QT Event 以及 Event Filter 事件處理,不多說,先來看內容,更多相關內容參考文章末尾。

QT Event 以及 Event Filter 事件處理是本文要介紹的內容,詳細內容如下,先來看內容。Event 和 Event Filters:

1、手動發送事件流程:

(1)構造自己的事件對象:

QEvent *evt = new QEvent( QEvent::Close  );  

(2)發送給指定的對象:

QApplication::sendEvent(this, evt );  

2、定制某個控件的事件處理:

(1)確定需要對哪些控件的哪些事件, 通常的 close以及 key 和 keyboard 事件;

(2)重寫該對象的 event() 函數;


3、事件過濾流程: 
 
(1)確定自己需要過濾處理哪些對象的那些事件;   
(2)構造自己的事件過濾類: 重寫該類的 eventFilter 函數;

(3)在主程序中實例化一個過濾類對象;
(4)調用該過濾類對象的 installEventFilter( receiver, QEvent *event),
  
以在目標對象上安裝該過濾器。

在 Qt 中, event 被建模成派生自abstract QEvent 類的對象, 用來表示在應用程序中發生的事件,或是應用程序需要處理的外部活動產生的事件.

Events 可以被任一 QObject 派生的子類實例對象接收和處理, 但他們是關聯到特定控件的. 本文檔描述 event 在典型應用程序中是如何發送及處理的.

1 How Events are Delivered 2 Event Types 3 Event Handlers 4 Event Filters 5 Sending Events 

event 如何發送

通常情況下,當一個事件發生的時候, Qt 通過構造一個合適的 QEvent子類對象來表示事件的發生, 然后將該事件對象發送給特定的 QObject對象( 或其子類實例對象), 通過調用該 QObject 的 event() 函數. 這個 event() 函數不會對事件本身進行處理, 而是首先檢查所接受到event 的類型, 然后根據 event 的類型來調用相應的 event handler, event handler 在處理完 event 之后會返回一個bool值表示 該 event是被接受了,還是被忽略了。

某些事件, 例如 QMouseEvent 和 QKeyEvent, 來自於窗口系統; 某些, 例如 QTimerEvent, 來自於其他的事件源; 某些, 來自於應用程序本身.

Event 類型

Qt為多數 Event 類型建立了相應的類, 常見有QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent, QCloseEvent.

每一個特定的 event 類都是繼承自 QEvent 基類,添加特定的事件函數. 例如, QResizeEvent 添加了 size() 和 oldSize() 讓控件可以發現他們的尺度,發生了的怎么改變.

某些類實際支持不止一種事件類型. QMouseEvent 就支持鼠標按鍵按下事件,雙擊事件, 移動事件, 以及其他相關操作所引發的事件.

每一個事件都有它的類型, 由 QEvent::Type 定義, 運行時可以很方便的檢測每個事件對象的事件類型,以快速的判斷該事件對象構造自哪個事件類.

由於程序需要和又多樣又復雜的事件進行交互, 所以 Qt 的 event 發送機制設計非常有彈性.

QCoreApplication::notify() 的文檔簡潔的說明了整個機制:

bool QCoreApplication::notify ( QObject * receiver, QEvent * event )   [virtual]  

發送 event 給 接收者: receiver->event(event). 返回從 receiver 的 event handler 返回的值. 注意這個函數適用於該應用程序中的任何線程中的任何對象. 對於特定類型的事件 (例如, 鼠標和鍵盤事件), 該事件將被傳送到 receiver 的 parent 並這樣逐級上傳

直到傳到 top-level object, 如果這些 receiver 都沒有對該事件進行處理的話(比如, 它返回 false ).

共五種處理 event 的方法; 重寫(重實現) QCoreApplication::notify() 這個 virtual 函數只是其 中的一種. 以下列出了這五種方法:

1、重寫 paintEvent(), mousePressEvent() 等. 這是最常用, 最簡單但也是最有限的方式.

2、重寫 QCoreApplication::notify(). 這非常強大, 可以完全控制事件處理; 但一次只可用於一個子類.

3、給 QCoreApplication::instance() 安裝一個 event filter .這個 event filter 就能處理所有控件的所有事件, 因此這與重寫 notify() 一樣強大; 此外, 可以有不止一個應用程序全局級的 event filter. 應用程序全局級 event filter 甚至可以收到已禁用控件的鼠標事件.

注意:  應用程序級 event filter 僅能用於存活在主線程中的對象.

4、 重寫 QObject::event()( 像 QWidget 那樣 ). 如果你重寫了 QObject::event(), 當 Tab 鍵按下時, 你就可以在任何控件級 event filter 捕獲這個 Tab 鍵按下事件之前處理這個事件.

5、給相應的接收對象安裝一個 event filter. 例如一個捕獲所有事件的 event filter, 包含 Tab 和Shift+Tab 鍵按下事件, 在它們沒有改變焦點控件之前.

另請參考 QObject::event() 以及 installEventFilter().

Event Handlers

處理 event 的標准方式是調用一個 virtual 函數. 例如, QPaintEvent 是通過調用 QWidget::paintEvent() 來處理的. 這個 virtual 函數負責進行相應的處理, 通常就是重畫該控件. 如果你在自己實現的 virtual 函數中沒有做所有必要的工作, 你就有必要調用它的基類實現.

例如, 下面的代碼處理一個定制 checkbox 控件的鼠標左鍵點擊事件, 並將所有其他點擊事件轉發給它的基類 QCheckBox 類:

 1 void MyCheckBox::mousePressEvent(QMouseEvent *event)  2 {  3     if (event->button() == Qt::LeftButton)  4  {  5         // handle left mouse button here 
 6  }  7     else 
 8  {  9         // pass on other buttons to base class 
10         QCheckBox::mousePressEvent(event); 11  } 12 }  

如果你需要替換基類的函數, 你應該自己實現所有相關的處理. 但是, 如果你只想擴展基類的功能, 那么你就只需實現需要實現的部分, 然后調用基類處理函數來處理你不想處理的情況.

偶爾, 你要處理沒有相應處理函數的特定事件, 或遇到事件處理函數不夠用情況. 最常見的例子是 Tab 鍵按下事件. 通常, QWidget 截獲到 Tab 鍵按下事件后,會移動鍵盤焦點, 但是少數控件需要自己來處理這個事件. 這些對想可以重寫 QObject::event() 函數, 通用的 event handler, 然后在通常處理過程之前或之后寫自己的事件處理過程, 或完全替代原處理過程. 下面是這樣一個很常見的控件:

一個既自己處理 Tab 事件又自己處理某些按鍵事件, 然后將其它不需自己處理的事件轉發給基類處理:

 

 1 bool MyWidget::event(QEvent *event)  2 {  3     if (event->type() == QEvent::KeyPress) {  4         QKeyEvent *ke = static_cast<QKeyEvent *>(event);  5         if (ke->key() == Qt::Key_Tab) {  6             // special tab handling here 
 7             return true;  8  }  9     } else if (event->type() == MyCustomEventType) { 10         MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event); 11         // custom event handling here 
12         return true; 13  } 14  
15     return QWidget::event(event); 16 }  

 

值得注意的是對沒有處理的事件仍調用 QWidget::event(), 並返回該基類調用的返回值以指示事件是否被處理了; 若返回 true 值則將會禁止將該事件再發往其他對象.

Event Filters

有時候一個對象需要檢查, 還可能截取發往其它對象的事件.例如, 對話框通常需要過濾發往某些控件的事件, 比如 更改 Enter 鍵按下的事件處理.

通過調用過濾器對象的 QObject::installEventFilter() 函數, 為目標對象設置一個 event filter, 就可在過濾器對象的QObject::eventFilter() 函數中處理發往目標對象的事件. 一個 event filter 在目標對象收到事件之前處理事件, 這使得過濾器對象在需要的時候可以檢查並丟棄事件. 一個現有的 event filter 可以調用 QObject::removeEventFilter() 來移除已經安裝的 event filter .

當過濾器的 eventFilter() 實現被調用的時候, 它就可以選擇是處理該事件,還是轉發該事件, 或禁止該事件繼續被其它對象處理. 若所有的事件過濾器都允許一個事件可被繼續處理( 每個過濾器處理后都返回 false ), 該事件最終將被發送到目標對象. 如果其中一個中止了這個流程(通過返回 TRUE),

則后面的過濾器對象以及目標對象將不會收到該事件.

 1 bool FilterObject::eventFilter(QObject *object, QEvent *event)  2 {  3     if (object == target && event->type() == QEvent::KeyPress) {  4         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);  5         if (keyEvent->key() == Qt::Key_Tab) {  6             // Special tab handling 
 7             return true;  8         } else   
 9             return false; 10  } 11     return false; 12 }  

上面代碼演示了另外一種截取發往特定對象 Tab 鍵事件的方法. 在這個例子里, 該過濾器處理 Tab 事件后返回 true 來阻止它們被繼續處理. 所有其他的按鍵事件將被忽略掉,然后過濾器返回 false 來允許該事件被已安裝的后續過濾器處理, 最終發往目標控件.

當然也可以過濾整個應用程序的所有事件, 只需將過濾器對象安裝到 QApplication 對象或QCoreApplication 對象上. 這樣的全局事件過濾器會在任何對象級過濾器()調用之前調用.

這是非常強大的, 但它也拖慢了整個應用程序范圍內每個事件的每次處理過程; 通常使用其他的技術來實現應用程序全局的事件過濾.

發送事件

許多應用程序需要創建並發送自己的事件. 你完全可以模仿 Qt 自有的 event loop 機制, 先構造合適的事件對象, 然調用 QCoreApplication::sendEvent() QCoreApplication::postEvent() 來把這些構造好的事件發送給指定的接收者.

sendEvent() 立即同步處理要發送的 event . 當它返回的時候, 表示相關的事件過濾器 和/或目標對象就處理完了該 event. 對於多數的 event 類, 有一個成員函數 isAccepted() 可以用來判別該事件是已被接受處理了,還是被拒絕處理了.

postEvent() 將 event 提交到一個隊列中等待調度. 在下一次 Qt 的主 event loop 運行的時候,主 event loop 就會調度所有提交到隊列中的 event, 以某種優化的方式. 例如, 如果有幾個 resize event, 他們就會被壓縮成一個事件. 同樣適用於 paint events: QWidget::update() 調用postEvent(), 以避免多次重畫來避免閃爍以及提高速度.

postEvent() 也被用於對象的初始化過程, 因為提交過的 event 通常在相應對象初始化完畢后極短的 時間內就會被調度. 在實現一個控件的時候, 在自定義控件的 constructor 中盡早支持事件機制是非常重要的, 在可能接受到任何事件之前,確保盡早初始化成員變量.

要創建一個定制類型的 event, 你需要定義一個事件號( event number ), 這個事件號應該大於QEvent::User, 並且你可能需要繼承 QEvent 以傳遞關於你定制的 event 類型的特定信息. 
參考 QEvent 文檔以獲取更多細節.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM