qt中 accept()和ignore()函数
首先來看一段代碼:
//!!! 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(); }這是一段簡單的代碼,經(jīng)過我們前面一段時間的學習,我們已經(jīng)能夠知道這段代碼的運行結(jié)果:點擊按鈕,會在控制臺打印出“You clicked this!”字符串。這是我們前面介紹過的內(nèi)容。下面,我們向CustomButton類添加一個事件函數(shù):
// CustomButton ... protected:void mousePressEvent(QMouseEvent *event); ...// ---------- custombutton.cpp ---------- // ... void CustomButton::mousePressEvent(QMouseEvent *event) {if (event->button() == Qt::LeftButton) {qDebug() << "left";} else {QPushButton::mousePressEvent(event);} } ...我們重寫了CustomButton的mousePressEvent()函數(shù),也就是鼠標按下。在這個函數(shù)中,我們判斷如果鼠標按下的是左鍵,則打印出來“l(fā)eft”字符串,否則,調(diào)用父類的同名函數(shù)。編譯運行這段代碼,當我們點擊按鈕時,“You clicked this!”字符串不再出現(xiàn),只有一個“l(fā)eft”。也就是說,我們把父類的實現(xiàn)覆蓋掉了。槽函數(shù)沒有執(zhí)行,說明沒有發(fā)出相應的信號。之所以沒有發(fā)出相應的信號,是因為我們覆蓋了父類的mousePressEvent()函數(shù)。原來的QPushButton會發(fā)出信號,我們的實現(xiàn)則不發(fā)出信號:由此可以看出,父類QPushButton的原始實現(xiàn)mousePressEvent()函數(shù)中,發(fā)出了clicked()信號。這暗示我們一個非常重要的細節(jié):當重寫事件回調(diào)函數(shù)時,時刻注意是否需要通過調(diào)用父類的同名函數(shù)來確保原有實現(xiàn)仍能進行!比如我們的CustomButton類,如果像我們這么覆蓋函數(shù),clicked()信號永遠不會發(fā)生,你連接到這個信號的槽函數(shù)也就永遠不會被執(zhí)行。這個錯誤非常隱蔽,很可能會浪費你很多時間才能找到。因為這個錯誤不會有任何提示。這一定程度上說,我們的組件“忽略”了父類的事件,但這更多的是一種違心之舉,一種錯誤。
通過調(diào)用父類的同名函數(shù),我們可以把 Qt 的事件傳遞看成鏈狀:如果子類沒有處理這個事件,就會繼續(xù)向其父類傳遞。Qt 的事件對象有兩個函數(shù):accept()和ignore()。正如它們的名字一樣,前者用來告訴 Qt,這個類的事件處理函數(shù)想要處理這個事件;后者則告訴 Qt,這個類的事件處理函數(shù)不想要處理這個事件。在事件處理函數(shù)中,可以使用isAccepted()來查詢這個事件是不是已經(jīng)被接收了。具體來說:如果一個事件處理函數(shù)調(diào)用了一個事件對象的accept()函數(shù),這個事件就不會被繼續(xù)傳播給其父組件;如果它調(diào)用了事件的ignore()函數(shù),Qt 會從其父組件中尋找另外的接受者。
事實上,我們很少會使用accept()和ignore()函數(shù),而是像上面的示例一樣,如果希望忽略事件(所謂忽略,是指自己不想要這個事件),只要調(diào)用父類的響應函數(shù)即可。記得我們曾經(jīng)說過,Qt 中的事件都是 protected 的,因此,重寫的函數(shù)必定存在著其父類中的響應函數(shù),所以,這個方法是可行的。為什么要這么做,而不是自己去手動調(diào)用這兩個函數(shù)呢?因為我們無法確認父類中的這個處理函數(shù)有沒有額外的操作。如果我們在子類中直接忽略事件,Qt 會去尋找其他的接收者,該子類的父類的操作會被忽略(因為沒有調(diào)用父類的同名函數(shù)),這可能會有潛在的危險。為了避免自己去調(diào)用accept()和ignore()函數(shù),而是盡量調(diào)用父類實現(xiàn),Qt 做了特殊的設計:事件對象默認是 accept 的,而作為所有組件的父類QWidget的默認實現(xiàn)則是調(diào)用ignore()。這么一來,如果你自己實現(xiàn)事件處理函數(shù),不調(diào)用QWidget的默認實現(xiàn),你就等于是接受了事件;如果你要忽略事件,只需調(diào)用QWidget的默認實現(xiàn)。這一點我們前面已經(jīng)說明。下面可以從代碼級別來理解這一點,我們可以查看一下QWidget的mousePressEvent()函數(shù)的實現(xiàn):
//!!! 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();}} }這段代碼在 Qt4 和 Qt5 中基本一致(區(qū)別在于activePopupWidget()一行,Qt4 的版本是qApp->activePopupWidget())。注意函數(shù)的第一個語句:event->ignore(),如果子類都沒有重寫這個函數(shù),Qt 會默認忽略這個事件,繼續(xù)尋找下一個事件接收者。如果我們在子類的mousePressEvent()函數(shù)中直接調(diào)用了accept()或者ignore(),而沒有調(diào)用父類的同名函數(shù),QWidget::mousePressEvent()函數(shù)中關于Popup判斷的那段代碼就不會被執(zhí)行,因此可能會出現(xiàn)默認其妙的怪異現(xiàn)象。
針對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";} };這段代碼在一個MainWindow中添加了一個CustomWidget,里面有兩個按鈕對象:CustomButton和CustomButtonEx。每一個類都重寫了mousePressEvent()函數(shù)。運行程序點擊 CustomButtonEx,結(jié)果是
CustomButtonEx這是因為我們重寫了鼠標按下的事件,但是并沒有調(diào)用父類函數(shù)或者顯式設置accept()或ignore()。下面我們在CustomButtonEx的mousePressEvent()第一行增加一句event->accept(),重新運行,發(fā)現(xiàn)結(jié)果不變。正如我們前面所說,QEvent默認是accept的,調(diào)用這個函數(shù)并沒有什么區(qū)別。然后我們將CustomButtonEx的event->accept()改成event->ignore()。這次運行結(jié)果是
CustomButtonEx CustomWidgetignore()說明我們想讓事件繼續(xù)傳播,于是CustomButtonEx的父組件CustomWidget也收到了這個事件,所以輸出了自己的結(jié)果。同理,CustomWidget又沒有調(diào)用父類函數(shù)或者顯式設置accept()或ignore(),所以事件傳播就此打住。這里值得注意的是,CustomButtonEx的事件傳播給了父組件CustomWidget,而不是它的父類CustomButton。事件的傳播是在組件層次上面的,而不是依靠類繼承機制。
接下來我們繼續(xù)測試,在CustomWidget的mousePressEvent()中增加QWidget::mousePressEvent(event)。這次的輸出是
CustomButtonEx CustomWidget MainWindow如果你把QWidget::mousePressEvent(event)改成event->ignore(),結(jié)果也是一樣的。這正如我們前面說的,QWidget的默認是調(diào)用event->ignore()。
在一個特殊的情形下,我們必須使用accept()和ignore()函數(shù),那就是窗口關閉的事件。對于窗口關閉QCloseEvent事件,調(diào)用accept()意味著 Qt 會停止事件的傳播,窗口關閉;調(diào)用ignore()則意味著事件繼續(xù)傳播,即阻止窗口關閉。回到我們前面寫的簡單的文本編輯器。我們在構(gòu)造函數(shù)中添加如下代碼:
//!!! 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()函數(shù)可以使用 [ ] 這種語法來表明,在窗口內(nèi)容發(fā)生改變時(通過setWindowModified(true)函數(shù)通知),Qt 會自動在標題上面的 [] 位置替換成 * 號。我們使用 Lambda 表達式連接 QTextEdit::textChanged()信號,將 windowModified設置為 true。然后我們需要重寫 closeEvent()函數(shù)。在這個函數(shù)中,我們首先判斷是不是有過修改,如果有,則彈出詢問框,問一下是否要退出。如果用戶點擊了“Yes”,則接受關閉事件,這個事件所在的操作就是關閉窗口。因此,一旦接受事件,窗口就會被關閉;否則窗口繼續(xù)保留。當然,如果窗口內(nèi)容沒有被修改,則直接接受事件,關閉窗口。
總結(jié)
以上是生活随笔為你收集整理的qt中 accept()和ignore()函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA --BYTECODE
- 下一篇: JAVA POI 应用系列(2)--读取