Qt事件過濾器原理(installEventFilter函數)
事件過濾器用於攔截傳遞到目標對象的事件,這樣可以實現監視目標對象事件的作用。
1、Qt實現事件過濾器的步驟如下:
①、Qt調用
void QObject::installEventFilter (QObject* filterObj)
把filterObj對象安裝(或注冊)為事件過濾器,filterObj也稱為過濾器對象。事件過濾器通常在構造函數中進行注冊。
②、在上一步注冊的filterObj對象,通過調用
bool QObject::eventFilter(QObject* obj, QEvent* e);
來接收攔截到的事件。也就是說攔截到的事件在filterObj對象中的eventFilter函數中處理。eventFilter的第一個參數obj指向的是事件本應傳遞到的目標對象。
③、使用QObject::removeEventFilter(QObject *obj)函數可以刪除事件過濾器。
2、事件過濾器處理事件的規則
①、過濾器對象的eventFilter()函數可以接受或拒絕攔截到的事件,若該函數返回false,則表示事件需要作進一步處理,此時事件會被發送到目標對象本身進行處理(注意:這里並未向父對象進行傳遞),若evetnFilter()返回true,則表示停止處理該事件,此時目標對象和后面安裝的事件過濾器就無法獲得該事件。
②、若同一對象安裝了多個事件過濾器,則最后安裝的過濾器首先被激活。
3、為什么使用事件過濾器
使用事件過濾器可以簡化程序代碼。比如按鈕1和標簽1,對按下A鍵的事件響應相同的操作,若不使用事件過濾器,則需要分別子類化按鈕和標簽部件,並重新實現各自的事件處理函數。再如使用同一個子類化按鈕的類C創建的按鈕1和按鈕2,對按下鍵A,按鈕1和按鈕2需要作不同的響應,若不使用事件過濾器,則他們的響應是相同的,若使用事件過濾器,則可以攔截按鈕1或按鈕2的事件並作特殊處理。
4、理解事件過濾器
觀察者模式:其原理為,設有一目標對象S,它有多個觀察該對象的對象G1,G2,G3,當S發生變化時,S的觀察者會依情形改變自身。應用於Qt事件過濾器,則是,首先使用S的成員函數installEventFilter函數把G1,G2,G3設置為S的觀察者,所有本應傳遞給S的事件E,則先傳遞給觀察者G1,G2,G3,然后觀察者調用其成員函數eventFilter對傳遞進來的事件進行處理,若eventFilter返回true表示事件處理完畢,返回false則返回給被觀察者S進行處理。見圖2-13。
示例2.22:事件過濾器的使用
1 #include <QApplication>
2 #include<QWidget>
3 #include<QMouseEvent>
4 #include<QPushButton>
5 #include<QObject>
6 #include <iostream>
7 using namespace std; 8 class A:public QObject{public: //該類的對象用作過濾器對象,使用事件過濾器需繼承QObject
9 bool eventFilter(QObject *w, QEvent *e){ 10 if(e->type()==QEvent::MouseButtonPress) 11 {cout<<w->objectName().toStdString(); //驗證w為事件本應到達的目標對象
12 cout<<"=Ak"<<endl; 13 return 1; //返回1表示該事件不再進一步處理
14 } 15 return 0;} }; /*返回0表示其余事件交還給目標對象處理,本例應返回0,否則添加了該過濾器的安鈕會無法顯示。*/
16 class B:public A{public: //繼承自類A
17 bool eventFilter(QObject *w, QEvent *e){ 18 if(e->type()==QEvent::MouseButtonPress){ 19 cout<<w->objectName().toStdString()<<"=Bk"<<endl; 20 return 0;} 21 return 0;} }; 22 class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}}; 23 class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}}; 24
25 int main(int argc, char *argv[]){ 26 QApplication a(argc,argv); 27 //創建對象,注意:本例父對象應先創建,以避免生命期過早結束
28 A ma; B mb; C mc; D *pd=new D; D *pd1=new D; 29 pd->setText("AAA"); pd->move(22,22); pd1->setText("BBB"); pd1->move(99,22); 30 //設置對象名
31 ma.setObjectName("ma"); mb.setObjectName("mb"); mc.setObjectName("mc"); 32 pd->setObjectName("pd"); pd1->setObjectName("pd1"); 33 //設置父對象
34 pd->setParent(&mc); pd1->setParent(&mc); 35 mb.setParent(&ma); //① 36 //注冊過濾器對象
37 pd->installEventFilter(&mb); //②
38 pd1->installEventFilter(&ma); //③
39
40 mc.resize(333,222); mc.show(); a.exec(); 41 return 0; }
程序運行結果及說明(見圖2-14)
當用鼠標按下按鈕AAA時,輸出pd=Bk和Dk。因為按鈕AAA安裝的過濾器對象為mb,因此由mb的eventFilter函數處理該事件,輸出pd=BK,此時mb::eventFilter()返回0,表示此事件需作進一步處理,於是把該事件傳遞給目標對象處理(即pd所指向的對象),注意:本例雖然為mb設置了父對象ma,但事件並不會傳遞給父對象處理,而是返回給目標對象。此時調用D::mousePressEvent函數,輸出Dk,至此事件處理結束。用鼠標按下按鈕BBB輸出pd1=Ak的原理略(較簡單)
示例2.23:添加多個事件過濾器
1 #include <QApplication>
2 #include<QWidget>
3 #include<QMouseEvent>
4 #include<QPushButton>
5 #include<QObject>
6 #include <iostream>
7 using namespace std; 8 class A:public QObject{public: //該類的對象用作過濾器對象,使用事件過濾器需繼承QObject
9 bool eventFilter(QObject *w, QEvent *e){ 10 if(e->type()==QEvent::MouseButtonPress) 11 {cout<<w->objectName().toStdString(); //驗證w為事件本應到達的目標對象
12 cout<<"=Ak"<<endl; 13 return 1; //返回1表示該事件不再進一步處理
14 } 15 return 0;} }; /*返回0表示其余事件交還給目標對象處理,本例應返回0,否則添加了該過濾器的安鈕會無法顯示。*/
16 class B:public A{public: //繼承自類A
17 bool eventFilter(QObject *w, QEvent *e){ 18 if(e->type()==QEvent::MouseButtonPress){ 19 cout<<w->objectName().toStdString()<<"=Bk"<<endl; 20 return 0;} 21 return 0;} }; 22 class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}}; 23 class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}}; 24
25 int main(int argc, char *argv[]){ 26 QApplication a(argc,argv); 27 //創建對象,注意:本例父對象應先創建,以避免生命期過早結束
28 A ma; B mb; C mc; D *pd=new D; D *pd1=new D; 29 pd->setText("AAA"); pd->move(22,22); pd1->setText("BBB"); pd1->move(99,22); 30 //設置對象名
31 ma.setObjectName("ma"); mb.setObjectName("mb"); mc.setObjectName("mc"); 32 pd->setObjectName("pd"); pd1->setObjectName("pd1"); 33 //設置父對象
34 pd->setParent(&mc); pd1->setParent(&mc); 35 mb.setParent(&ma); //① 36 //注冊過濾器對象
37 pd->installEventFilter(&mb); //②
38 pd1->installEventFilter(&ma); //③
39
40 mc.resize(333,222); mc.show(); a.exec(); 41 return 0; }
程序運行結果及說明(見圖2-15)
按鈕AAA安裝的過濾器對象依次為ma,mb,mc,mc1,
因此按下鼠標時,依次調用對象mc1,mc,mb,ma(即逆序)
的eventFilter函數,需要注意的是:當安裝了多個事件過濾
器之后,eventFilter函數返回0並不會使事件返回給目標對
象,而是傳遞給下一個過濾器對象,當所有過濾器對象都不處理該事件時才會傳遞給目標對象。