1. 程式人生 > >Qt的Event Filter(轉)

Qt的Event Filter(轉)

event filter是什麼?
EventFilter即所謂事件過濾器, 在Qt中是一個比較重要的概念, 它的功能是把所有事件在到達watchee(被監控者)之前全部傳遞給另一個watcher(監控者), 由watcher先行處理並決定是否繼續傳遞該事件, 如果繼續傳遞, 則事件將回傳給watchee來處理。 可能很多人已經知道怎麼用event filter處理事件了, 不過你也別嫌本文太簡單, 畢竟總是不斷的有新人提出類似的問題, 就當是複習一下吧。

event filter的常見應用場合
用來處理熱鍵 -- 比如一個介面上可以由使用者熱鍵來觸發的多個按鈕。 由於只有得到焦點的控制元件才能獲得鍵盤的事件, 如果不用event filter就需要給每個button都加上鍵盤事件的處理, 還要在button裡去訪問兄弟button的指標, 邏輯非常混亂。 如果由主窗體做各個按鈕的eventFilter, 則只需要在主窗體裡去處理鍵盤事件就好, 而且主窗體可以很容易的訪問到各個button的指標, 很方便。

用來代替派生和重寫虛擬函式 -- Qt裡的鍵盤滑鼠事件基本上都是以虛擬函式的方式來處理, 要想重寫虛擬函式則必須派生一個子類, 這樣的話如果只是一個簡單的事件處理也去派生子類代價未免大了些, 這時候就值得用用eventFilter。 比如我的MDI介面想在每個子窗體關閉的時候做一些統一的操作, 一般的做法是處理子窗體的closeEvent。 但顯然給每個子窗體都去派生個子類太不現實, 最好的方法是把mainwindow作為子窗體的eventFilter去處理CloseEvent事件。

這裡只舉了兩個例子, 相信聰明的同學們能在自己的程式中找到適合eventFilter發揮的位置。

如何使用event filter處理事件?


在前面的一篇blog中, 作者bug介紹了Qt中事件傳遞的順序(如果沒看過的先來掃掃盲), 其中就提到了兩個級別的event filter, 一個是為QApplication類安裝一個eventFilter, 另一個是針對某個控制元件來安裝eventFilter。 本質上這兩個級別的event filter呼叫的都是QObject類提供的API:“installEventFilter”。

event filter的使用有點曲折, 有兩個步驟要做。 一是要呼叫watchee的installEventFilter以watcher指標為引數

watchee->installEventFilter

(watcher);

二是在watcher的類中實現bool eventFilter(QObject*, QEvent*) 這個虛擬函式, 在此函式中處理事件。

 class KeyPressEater : public QObject
 {
     Q_OBJECT
     ...

 protected:
     bool eventFilter(QObject *obj, QEvent *event);
 };

 bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
 {
     if (event->type() == QEvent::KeyPress) {
         QKeyEvent *keyEvent = static_cast(event);
         qDebug("Ate key press %d", keyEvent->key());
         return true;//返回true表示當前事件已經被處理(遮蔽),不需要進一步分發
     } else {
         // standard event processing
         return QObject::eventFilter(obj, event);
     }
 }

上面的例子程式碼是直接從Qt文件裡抄的, 呵呵, 其實文件說的實在是很明白, 不知道為啥還是有很多人不懂這個怎麼用。

其中幾點需要注意的地方:
- watchee和watcher必須都是從QObject派生的子類例項
- watcher的類必須是一個自定義的子類(因為需要重新實現虛擬函式, 用現成的類是不行的。)
- watcher類的eventFilter函式返回true時表示該事件處理完畢, 不再繼續傳遞;返回false表示該事件仍然傳遞
- eventFilter的實現的最後必須呼叫watcher基類的eventFilter函式以傳遞事件。 如果不調的話watcher的所有事件都將丟失。
- 注意eventFilter的宣告必須和文件裡的一模一樣, 寫的時候注意返回值、大小寫和引數型別

其他
在Qt中還有一些應用程式級別的eventFilter函式, 是和平臺相關的, 如x11EventFilter可以截獲程式得到的所有X事件。 相應的Windows平臺下用winEventFilter… 大家不要被函式的名字給誤導了, 這些函式並不是全域性級別的, 充其量只能拿到應用程式內部的事件, 想做系統級別的事件過濾還是差著檔次…