安裝事件過濾器(installEventFilter) ,過濾子控件事件,截獲控件按鍵、鼠標事件

文章來自:http://blog.csdn.net/iamdbl/article/details/1630043#


Qt的事件模型一個強大的功能是一個QObject對象能夠監視發送其他QObject對象的事件,在事件到達之前對其進行處理。

假設我們有一個CustomerInfoDialog控件,由一些QLineEdit控件組成。我們希望使用Space鍵得到下一個QLineEdit的輸入焦點。一個最直接的方法是繼承QLineEdit重寫keyPressEvent()函數,當點擊了Space鍵時,調用focusNextChild():
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space) {
        focusNextChild();
    } else {
        QLineEdit::keyPressEvent(event);
    }
}
這個方法有一個最大的缺點:如果我們在窗體中使用了很多不同類型的控件(QComboBox,QSpinBox等等),我們也要繼承這些控件,重寫它們的keyPressEvent()。一個更好的解決方法是讓CustomerInfoDialog監視其子控件的鍵盤事件,在監視代碼處實現以上功能。這就是事件過濾的方法。實現一個事件過濾包括兩個步驟:
1.      在目標對象上調用installEventFilter(),註冊監視對象。
2.      在監視對象的eventFilter()函數中處理目標對象的事件。
註冊監視對象的位置是在CustomerInfoDialog的構造函數中:
CustomerInfoDialog::CustomerInfoDialog(QWidget *parent)
    : QDialog(parent)
{
    ...
    firstNameEdit->installEventFilter(this);
    lastNameEdit->installEventFilter(this);
    cityEdit->installEventFilter(this);
    phoneNumberEdit->installEventFilter(this);
}
事件過濾器註冊後,發送到firstNameEdit,lastNameEdit,cityEdit,phoneNumberEdit控件的事件首先到達CustomerInfoDialog::eventFilter()函數,然後在到達最終的目的地。
下面是eventFilter()函數的代碼:
bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)
{
    if (target == firstNameEdit || target == lastNameEdit
            || target == cityEdit || target == phoneNumberEdit) {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Space) {
                focusNextChild();
                return true;
            }
        }
    }
    return QDialog::eventFilter(target, event);
}
首先,我們看是目標控件是否爲QLineEdit,如果事件爲鍵盤事件,把QEvent轉換爲QKeyEvent,確定被敲擊的鍵。如果爲Space鍵,調用focusNextChild(),把焦點交給下一個控件,返回true通知Qt已經處理了這個事件,如果返回false,Qt將會把事件傳遞給目標控件,把一個空格字符插入到QLineEdit中。
如果目標控件不是QLineEdit,或者事件不是Space敲擊事件,把控制權交給基類QDialog的eventFilter()。目標控件也可以是基類QDialog正在監視的控件。(在Qt4.1中,QDialog沒有監視的控件,但是Qt的其他控件類,如QScrollArea,監視一些它們的子控件)
Qt的事件處理有5中級別:
1.      重寫控件的事件處理函數:如重寫keyPressEvent(),mousePressEvent()和paintEvent(),這是最常用的事件處理方法,我們已經看到過很多這樣的例子了。
2.      重寫QObject::event(),在事件到達事件處理函數時處理它。在需要改變Tab鍵的慣用法時這樣做。也可以處理那些沒有特定事件處理函數的比較少見的事件類型(例如,QEvent::HoverEnter)。我們重寫event()時,必須要調用基類的event(),由基類處理我們不需要處理的那些情況。
3.      給QObject對象安裝事件過濾器:對象用installEventFilter()後,所有達到目標控件的事件都首先到達監視對象的eventFilter()函數。如果一個對象有多個事件過濾器,過濾器按順序激活,先到達最近安裝的監視對象,最後到達最先安裝的監視對象。
4.      給QApplication安裝事件過濾器,如果qApp(唯一的QApplication對象)安裝了事件過濾器,程序中所有對象的事件都要送到eventFilter()函數中。這個方法在調試的時候非常有用,在處理非活動狀態控件的鼠標事件時這個方法也很常用。
5.      繼承QApplication,重寫notify()。Qt調用QApplication::nofity()來發送事件。重寫這個函數是在其他事件過濾器處理事件前得到所有事件的唯一方法。通常事件過濾器是最有用的,因爲在同一時間,可以有任意數量的事件過濾器,但是notify()函數只有一個。
許多事件類型,包括鼠標,鍵盤事件,是能夠傳播的。如果事件在到達目標對象的途中或者由目標對象處理掉,事件處理的過程會重新開始,不同的是這時的目標對象是原目標對象的父控件。這樣從父控件再到父控件,知道有控件處理這個事件或者到達了最頂級的那個控件。
圖7.2顯示了一個鍵盤事件在一個對話框中從子控件到父控件的傳播過程。當用戶敲擊一個鍵盤,時間首先發送到有焦點的控件上(這個例子中是QCheckBox)。如果QCheckBox沒有處理這個事件,Qt把事件發送到QGroupBox中,如果仍然沒有處理,則最後發送到QDialog中。
Figure 7.2. Event propagation in a dialog

發佈了30 篇原創文章 · 獲贊 106 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章