QT事件的接受与忽略
轉載:http://blog.csdn.net/gusgao/article/details/48862391
首先來看一段代碼:
//!!! Qt5 // ---------- custombutton.h ---------- // class CustomButton : public QPushButton {Q_OBJECT public:CustomButton(QWidget *parent = 0); private:void onButtonCliecked(); };// ---------- custombutton.cpp ---------- // CustomButton::CustomButton(QWidget *parent) :QPushButton(parent) {connect(this, &CustomButton::clicked,this, &CustomButton::onButtonCliecked); }void CustomButton::onButtonCliecked() {qDebug() << "You clicked this!"; }// ---------- main.cpp ---------- // int main(int argc, char *argv[]) {QApplication a(argc, argv);CustomButton btn;btn.setText("This is a Button!");btn.show();return a.exec(); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
這是一段簡單的代碼,經過我們前面一段時間的學習,我們已經能夠知道這段代碼的運行結果:點擊按鈕,會在控制臺打印出“You clicked this!”字符串。這是我們前面介紹過的內容。下面,我們向CustomButton類添加一個事件函數:
// CustomButton ... protected:void mousePressEvent(QMouseEvent *event); ...// ---------- custombutton.cpp ---------- // ... void CustomButton::mousePressEvent(QMouseEvent *event) {if (event->button() == Qt::LeftButton) {qDebug() << "left";} else {QPushButton::mousePressEvent(event);} } ...- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
我們重寫了CustomButton的mousePressEvent()函數,也就是鼠標按下。在這個函數中,我們判斷如果鼠標按下的是左鍵,則打印出來“left”字符串,否則,調用父類的同名函數。編譯運行這段代碼,當我們點擊按鈕時,“You clicked this!”字符串不再出現,只有一個“left”。也就是說,我們把父類的實現覆蓋掉了。由此可以看出,父類QPushButton的mousePressEvent()函數中肯定發出了clicked()信號,否則的話,我們的槽函數怎么會不執行了呢?這暗示我們一個非常重要的細節:當重寫事件回調函數時,時刻注意是否需要通過調用父類的同名函數來確保原有實現仍能進行!比如我們的CustomButton了,如果像我們這么覆蓋函數,clicked()信號永遠不會發生,你連接到這個信號的槽函數也就永遠不會被執行。這個錯誤非常隱蔽,很可能會浪費你很多時間才能找到。因為這個錯誤不會有任何提示。這一定程度上說,我們的組件“忽略”了父類的事件,但這更多的是一種違心之舉,一種錯誤。
通過調用父類的同名函數,我們可以把 Qt 的事件傳遞看成鏈狀:如果子類沒有處理這個事件,就會繼續向其父類傳遞。Qt 的事件對象有兩個函數:accept()和ignore()。正如它們的名字一樣,前者用來告訴 Qt,這個類的事件處理函數想要處理這個事件;后者則告訴 Qt,這個類的事件處理函數不想要處理這個事件。在事件處理函數中,可以使用isAccepted()來查詢這個事件是不是已經被接收了。具體來說:如果一個事件處理函數調用了一個事件對象的accept()函數,這個事件就不會被繼續傳播給其父組件;如果它調用了事件的ignore()函數,Qt 會從其父組件中尋找另外的接受者。
事實上,我們很少會使用accept()和ignore()函數,而是像上面的示例一樣,如果希望忽略事件(所謂忽略,是指自己不想要這個事件),只要調用父類的響應函數即可。記得我們曾經說過,Qt 中的事件都是 protected 的,因此,重寫的函數必定存在著其父類中的響應函數,所以,這個方法是可行的。為什么要這么做,而不是自己去手動調用這兩個函數呢?因為我們無法確認父類中的這個處理函數有沒有額外的操作。如果我們在子類中直接忽略事件,Qt 會去尋找其他的接收者,該子類的父類的操作會被忽略(因為沒有調用父類的同名函數),這可能會有潛在的危險。為了避免自己去調用accept()和ignore()函數,而是盡量調用父類實現,Qt 做了特殊的設計:事件對象默認是 accept 的,而作為所有組件的父類QWidget的默認實現則是調用ignore()。這么一來,如果你自己實現事件處理函數,不調用QWidget的默認實現,你就等于是接受了事件;如果你要忽略事件,只需調用QWidget的默認實現。這一點我們前面已經說明。下面可以從代碼級別來理解這一點,我們可以查看一下QWidget的mousePressEvent()函數的實現:
//!!! Qt5 void QWidget::mousePressEvent(QMouseEvent *event) {event->ignore();if ((windowType() == Qt::Popup)) {event->accept();QWidget* w;while ((w = QApplication::activePopupWidget()) && w != this){w->close();if (QApplication::activePopupWidget() == w)w->hide(); // hide at least}if (!rect().contains(event->pos())){close();}} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
這段代碼在 Qt4 和 Qt5 中基本一致(區別在于activePopupWidget()一行,Qt4 的版本是qApp->activePopupWidget())。注意函數的第一個語句:event->ignore(),如果子類都沒有重寫這個函數,Qt 會默認忽略這個事件,繼續尋找下一個事件接收者。如果我們在子類的mousePressEvent()函數中直接調用了accept()或者ignore(),而沒有調用父類的同名函數,QWidget::mousePressEvent()函數中關于Popup判斷的那段代碼就不會被執行,因此可能會出現默認其妙的怪異現象。
針對accept()和ignore(),我們再來看一個例子:
class CustomButton : public QPushButton {Q_OBJECT public:CustomButton::CustomButton(QWidget *parent): QPushButton(parent){} protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomButton";} };class CustomButtonEx : public CustomButton {Q_OBJECT public:CustomButtonEx::CustomButtonEx(QWidget *parent): CustomButton(parent){} protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomButtonEx";} };class CustomWidget : public QWidget {Q_OBJECT public:CustomWidget::CustomWidget(QWidget *parent): QWidget(parent){} protected:void mousePressEvent(QMouseEvent *event){qDebug() << "CustomWidget";} };class MainWindow : public QMainWindow {Q_OBJECT public:MainWindow::MainWindow(QWidget *parent = 0): QMainWindow(parent){CustomWidget *widget = new CustomWidget(this);CustomButton *cbex = new CustomButton(widget);cbex->setText(tr("CustomButton"));CustomButtonEx *cb = new CustomButtonEx(widget);cb->setText(tr("CustomButtonEx"));QVBoxLayout *widgetLayout = new QVBoxLayout(widget);widgetLayout->addWidget(cbex);widgetLayout->addWidget(cb);this->setCentralWidget(widget);} protected:void mousePressEvent(QMouseEvent *event){qDebug() << "MainWindow";} };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
這段代碼在一個MainWindow中添加了一個CustomWidget,里面有兩個按鈕對象:CustomButton和CustomButtonEx。每一個類都重寫了mousePressEvent()函數。運行程序點擊 CustomButtonEx,結果是`CustomButtonEx
這是因為我們重寫了鼠標按下的事件,但是并沒有調用父類函數或者顯式設置accept()或ignore()。下面我們在CustomButtonEx的mousePressEvent()第一行增加一句event->accept(),重新運行,發現結果不變。正如我們前面所說,QEvent默認是accept的,調用這個函數并沒有什么區別。然后我們將CustomButtonEx的event->accept()改成event->ignore()。這次運行結果是CustomButtonEx
CustomWidget`?
ignore()說明我們想讓事件繼續傳播,于是CustomButtonEx的父組件CustomWidget也收到了這個事件,所以輸出了自己的結果。同理,CustomWidget又沒有調用父類函數或者顯式設置accept()或ignore(),所以事件傳播就此打住。這里值得注意的是,CustomButtonEx的事件傳播給了父組件CustomWidget,而不是它的父類CustomButton。事件的傳播是在組件層次上面的,而不是依靠類繼承機制。
接下來我們繼續測試,在CustomWidget的mousePressEvent()中增加QWidget::mousePressEvent(event)。這次的輸出是CustomButtonEx?
CustomWidget?
MainWindow
如果你把QWidget::mousePressEvent(event)改成event->ignore(),結果也是一樣的。這正如我們前面說的,QWidget的默認是調用event->ignore()。
在一個特殊的情形下,我們必須使用accept()和ignore()函數,那就是窗口關閉的事件。對于窗口關閉QCloseEvent事件,調用accept()意味著 Qt 會停止事件的傳播,窗口關閉;調用ignore()則意味著事件繼續傳播,即阻止窗口關閉。回到我們前面寫的簡單的文本編輯器。我們在構造函數中添加如下代碼:
“`?
//!!! Qt5?
…?
textEdit = new QTextEdit(this);?
setCentralWidget(textEdit);?
connect(textEdit, &QTextEdit::textChanged,?=?{?
this->setWindowModified(true);?
});
setWindowTitle(“TextPad [*]”);?
…
void MainWindow::closeEvent(QCloseEvent *event)?
{?
if (isWindowModified()) {?
bool exit = QMessageBox::question(this,?
tr(“Quit”),?
tr(“Are you sure to quit this application?”),?
QMessageBox::Yes | QMessageBox::No,?
QMessageBox::No) == QMessageBox::Yes;?
if (exit) {?
event->accept();?
} else {?
event->ignore();?
}?
} else {?
event->accept();?
}?
}?
“`setWindowTitle()函數可以使用 [] 這種語法來表明,在窗口內容發生改變時(通過setWindowModified(true)函數通知),Qt 會自動在標題上面的 [] 位置替換成 * 號。我們使用 Lambda 表達式連接QTextEdit::textChanged()信號,將windowModified設置為 true。然后我們需要重寫closeEvent()函數。在這個函數中,我們首先判斷是不是有過修改,如果有,則彈出詢問框,問一下是否要退出。如果用戶點擊了“Yes”,則接受關閉事件,這個事件所在的操作就是關閉窗口。因此,一旦接受事件,窗口就會被關閉;否則窗口繼續保留。當然,如果窗口內容沒有被修改,則直接接受事件,關閉窗口。
總結
以上是生活随笔為你收集整理的QT事件的接受与忽略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端工程化之FaaS SSR方案
- 下一篇: strncat()