詳解 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 文檔以獲取更多細節.