Qt事件過濾器
技術標籤:Qt
1、事件過濾器
用於攔截傳遞到目標物件的事件,這樣可以實現監視目標物件事件的作用。
2、Qt 實現事件過濾器的步驟如下:
①、 Qt 呼叫
void QObject::installEventFilter (QObject* filterObj)
把 filterObj 物件設定安裝(或註冊)為事件過濾器, filterObj 也稱為過濾器物件。 事件
過濾器通常在建構函式中進行註冊。
②、 在上一步註冊的 filterObj 物件,通過呼叫
bool QObject::eventFilter(QObject* obj, QEvent* e);
來接收攔截到的事件。也就是說攔截到的事件在 filterObj 物件中的 eventFilter 函式中
③、 使用 QObject::removeEventFilter(QObject *obj)函式可以刪除事件過濾器。
3、 事件過濾器處理事件的規則
①、 過濾器物件的 eventFilter()函式可以接受或拒絕攔截到的事件,若該函式返回 false,
則表示事件需要作進一步處理,此時事件會被髮送到目標物件本身進行處理(注意:
這裡並未向父物件進行傳遞),若 eventFilter()返回 true,則表示停止處理該事件,此
時目標物件和後面安裝的事件過濾器就無法獲得該事件。
demo:
#include <QApplication> #include<QWidget> #include<QMouseEvent> #include<QPushButton> #include<QObject> #include <iostream> using namespace std; class A:public QObject{ public: //該類的物件用作過濾器物件,使用事件過濾器需繼承 QObject bool eventFilter(QObject *w, QEvent *e) { if(e->type()==QEvent::MouseButtonPress) { cout<<w->objectName().toStdString(); //驗證 w 為事件本應到達的目標物件 cout<<"=Ak"<<endl; return 1; //返回 1 表示該事件不再進一步處理 } return 0; } /*返回 0 表示其餘事件交還給目標物件處理, 本例應返回 0,否則添加了該 過濾器的安鈕會無法顯示。 */ }; class B:public A{ public: //繼承自類 A bool eventFilter(QObject *w, QEvent *e) { if(e->type()==QEvent::MouseButtonPress) { cout<<w->objectName().toStdString()<<"=Bk"<<endl; return 0; } return 0; } }; class C:public QWidget { public: void mousePressEvent(QMouseEvent *e) { cout<<"Ck"<<endl; } }; class D:public QPushButton { public: void mousePressEvent(QMouseEvent *e) { cout<<"DK"<<endl; } }; int main(int argc, char *argv[]) { QApplication a(argc,argv); //建立物件,注意:本例父物件應先建立,以避免生命期過早結束 A ma; B mb; C mc; D *pd=new D; D *pd1=new D; pd->setText("AAA"); pd->move(22,22); pd1->setText("BBB"); pd1->move(99,22); //設定物件名 ma.setObjectName("ma"); mb.setObjectName("mb"); mc.setObjectName("mc"); pd->setObjectName("pd"); pd1->setObjectName("pd1"); //設定父物件 pd->setParent(&mc); pd1->setParent(&mc); mb.setParent(&ma); //當用滑鼠按下按鈕 AAA 時,輸出 pd=Bk 和Dk。 因為按鈕 AAA 安裝的過濾器物件為 mb, //因此由 mb 的 eventFilter 函式處理該事件,輸出 pd=BK //註冊過濾器物件 //此時 mb::eventFilter()返回 0,表示此事件需作進一步處理, //於是把該事件傳遞給目標物件處理(即 pd 所指向的物件), // 注意:本例雖然為 mb 設定了父物件 ma,但事件並不會傳遞給父物件處理,而是返回給目標物件 //此時呼叫 D::mousePressEvent 函式,輸出 Dk,至此事件處理結束 pd->installEventFilter(&mb); pd1->installEventFilter(&ma); mc.resize(333,222); mc.show(); a.exec(); return 0; }
②、 若同一物件安裝了多個事件過濾器,則最後安裝的過濾器首先被啟用。
多個物件過濾器demo:
#include <QApplication> #include<QWidget> #include<QMouseEvent> #include<QPushButton> #include<QObject> #include <iostream> using namespace std; class A:public QObject{public: bool eventFilter(QObject *w, QEvent *e) { if(e->type()==QEvent::MouseButtonPress) { cout<<"A"<<endl; return 0; }//此處應返回 0,注意,返回 1 事件將停止傳遞。 return 0; } }; class B:public QObject{public: bool eventFilter(QObject *w, QEvent *e) { if(e->type()==QEvent::MouseButtonPress) { cout<<"B"<<endl;return 0; } return 0; } }; class C:public QObject{public: bool eventFilter(QObject *w, QEvent *e) { static int i=0; if(e->type()==QEvent::MouseButtonPress) { cout<<"C="<<i++<<endl;return 0; } return 0; } }; int main(int argc, char *argv[]) { QApplication a(argc,argv); //在 Qt 中 QApplication 型別的物件只能有一個 //建立物件 A ma; B mb; C mc,mc1; //事件過濾器物件 QWidget w; QPushButton *pb=new QPushButton(); pb->setText("AAA"); pb->move(220,22); pb->setParent(&w);//設定父物件 //註冊過濾器物件 pb->installEventFilter(&ma); pb->installEventFilter(&mb); pb->installEventFilter(&mc); pb->installEventFilter(&mc1); w.resize(333,222); w.show(); a.exec(); return 0; }
4、 為什麼使用事件過濾器:
①使用事件過濾器可以簡化程式程式碼。比如按鈕 1 和標籤 1,對按
下 A 鍵的事件響應相同的操作,若不使用事件過濾器, 則需要分別子類化按鈕和標籤部
件,並重新實現各自的事件處理函式。 再如使用同一個子類化按鈕的類 C 建立的按鈕 1
和按鈕 2,對按下鍵 A, 按鈕 1 和按鈕 2 需要作不同的響應,若不使用事件過濾器,則他
們的響應是相同的,若使用事件過濾器,則可以攔截按鈕1或按鈕2的事件並作特殊處理。
②Qt開發中,有些元件的事件往往沒有,比如QMenu並沒有click事件,那麼我們便可以通過事件過濾,來給QMenu處理click事件,包括QLabel,QLineEdit等等
class MainWindow : public QMainWindow
{
public:
MainWindow();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
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);
}
}
5、理解事件過濾器
觀察者模式:其原理為,設有一目標物件 S,它有多個觀察該物件的物件 G1, G2, G3,
當 S 發生變化時, S 的觀察者會依情形改變自身。應用於 Qt 事件過濾器,則是,首先使
用 S 的成員函式 installEventFilter 函式把 G1, G2, G3 設定為 S 的觀察者,所有本應傳遞
給 S 的事件 E,則先傳遞給觀察者 G1, G2, G3, 然後觀察者呼叫其成員函式 eventFilter
對傳遞進來的事件進行處理,若 eventFilter 返回 true 表示事件處理完畢,返回 false 則返
回給被觀察者 S 進行處理。 見下圖。