1. 程式人生 > 其它 >Qt事件過濾器

Qt事件過濾器

技術標籤:Qt

1、事件過濾器

用於攔截傳遞到目標物件的事件,這樣可以實現監視目標物件事件的作用。

2、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)函式可以刪除事件過濾器。

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 進行處理。 見下圖。