有時候,對象須要查看、甚至要攔截髮送到另外對象的事件。例如,對話框可能想要攔截按鍵事件,不讓別的組件接收到;或者要修改回車鍵的默認處理。函數
經過前面的章節,咱們已經知道,Qt 建立了QEvent
事件對象以後,會調用QObject
的event()
函數處理事件的分發。顯然,咱們能夠在event()
函數中實現攔截的操做。因爲event()
函數是 protected 的,所以,須要繼承已有類。若是組件不少,就須要重寫不少個event()
函數。這固然至關麻煩,更不用說重寫event()
函數還得當心一堆問題。好在 Qt 提供了另一種機制來達到這一目的:事件過濾器。this
QObject
有一個eventFilter()
函數,用於創建事件過濾器。這個函數的簽名以下:線程
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
這個函數正如其名字顯示的那樣,是一個「事件過濾器」。所謂事件過濾器,能夠理解成一種過濾代碼。想一想作化學實驗時用到的過濾器,能夠將雜質留到濾紙上,讓過濾後的液體溜走。事件過濾器也是如此:它會檢查接收到的事件。若是這個事件是咱們感興趣的類型,就進行咱們本身的處理;若是不是,就繼續轉發。這個函數返回一個 bool 類型,若是你想將參數 event 過濾出來,好比,不想讓它繼續轉發,就返回 true,不然返回 false。事件過濾器的調用時間是目標對象(也就是參數裏面的watched
對象)接收到事件對象以前。也就是說,若是你在事件過濾器中中止了某個事件,那麼,watched
對象以及之後全部的事件過濾器根本不會知道這麼一個事件。code
咱們來看一段簡單的代碼:對象
class MainWindow : public QMainWindow { public: MainWindow(); protected: bool eventFilter(QObject *obj, QEvent *event); private: QTextEdit *textEdit; }; MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->installEventFilter(this); } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (obj == textEdit) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); qDebug() << "Ate key press" << keyEvent->key(); return true; } else { return false; } } else { // pass the event on to the parent class return QMainWindow::eventFilter(obj, event); } }
MainWindow
是咱們定義的一個類。咱們重寫了它的eventFilter()
函數。爲了過濾特定組件上的事件,首先須要判斷這個對象是否是咱們感興趣的組件,而後判斷這個事件的類型。在上面的代碼中,咱們不想讓textEdit
組件處理鍵盤按下的事件。因此,首先咱們找到這個組件,若是這個事件是鍵盤事件,則直接返回 true,也就是過濾掉了這個事件,其餘事件仍是要繼續處理,因此返回 false。對於其它的組件,咱們並不保證是否是還有過濾器,因而最保險的辦法是調用父類的函數。繼承
eventFilter()
函數至關於建立了過濾器,而後咱們須要安裝這個過濾器。安裝過濾器須要調用QObject::installEventFilter()
函數。這個函數的簽名以下:事件
void QObject::installEventFilter ( QObject * filterObj )
這個函數接受一個QObject *
類型的參數。記得剛剛咱們說的,eventFilter()
函數是QObject
的一個成員函數,所以,任意QObject
均可以做爲事件過濾器(問題在於,若是你沒有重寫eventFilter()
函數,這個事件過濾器是沒有任何做用的,由於默認什麼都不會過濾)。已經存在的過濾器則能夠經過QObject::removeEventFilter()
函數移除。rem
咱們能夠向一個對象上面安裝多個事件處理器,只要調用屢次installEventFilter()
函數。若是一個對象存在多個事件過濾器,那麼,最後一個安裝的會第一個執行,也就是後進先執行的順序。get
還記得咱們前面的那個例子嗎?咱們使用event()
函數處理了 Tab 鍵:it
bool CustomWidget::event(QEvent *e) { if (e->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e); if (keyEvent->key() == Qt::Key_Tab) { qDebug() << "You press tab."; return true; } } return QWidget::event(e); }
如今,咱們能夠給出一個使用事件過濾器的版本:
bool FilterObject::eventFilter(QObject *object, QEvent *event) { if (object == target && event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab) { qDebug() << "You press tab."; return true; } else { return false; } } return false; }
事件過濾器的強大之處在於,咱們能夠爲整個應用程序添加一個事件過濾器。記得,installEventFilter()
函數是QObject
的函數,QApplication
或者QCoreApplication
對象都是QObject
的子類,所以,咱們能夠向QApplication
或者QCoreApplication
添加事件過濾器。這種全局的事件過濾器將會在全部其它特性對象的事件過濾器以前調用。儘管很強大,但這種行爲會嚴重下降整個應用程序的事件分發效率。所以,除非是不得不使用的狀況,不然的話咱們不該該這麼作。
注意,若是你在事件過濾器中 delete 了某個接收組件,務必將函數返回值設爲 true。不然,Qt 仍是會將事件分發給這個接收組件,從而致使程序崩潰。
事件過濾器和被安裝過濾器的組件必須在同一線程,不然,過濾器將不起做用。另外,若是在安裝過濾器以後,這兩個組件到了不一樣的線程,那麼,只有等到兩者從新回到同一線程的時候過濾器纔會有效。