1. 程式人生 > >Qt 學習之路 2(19):事件的接受與忽略(當重寫事件回撥函式時,時刻注意是否需要通過呼叫父類的同名函式來確保原有實現仍能進行!有好幾個例子。為什麼要這麼做?而不是自己去手動呼叫這兩個函式呢?因為我們無法確認父類中的這個處理函式有沒有額外的操作)

Qt 學習之路 2(19):事件的接受與忽略(當重寫事件回撥函式時,時刻注意是否需要通過呼叫父類的同名函式來確保原有實現仍能進行!有好幾個例子。為什麼要這麼做?而不是自己去手動呼叫這兩個函式呢?因為我們無法確認父類中的這個處理函式有沒有額外的操作)

版本:

  1. 2012-09-29
  2. 2013-04-23 更新有關accept()ignore()函式的相關內容。
  3. 2013-12-02 增加有關accept()ignore()函式的示例。

上一章我們介紹了有關事件的相關內容。我們曾經提到,事件可以依情況接受和忽略。現在,我們就來了解下有關事件的更多的知識。

 

首先來看一段程式碼:

C/C++ 35 lines   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       //!!! 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(); }    

這是一段簡單的程式碼,經過我們前面一段時間的學習,我們已經能夠知道這段程式碼的執行結果:點選按鈕,會在控制檯打印出“You clicked this!”字串。這是我們前面介紹過的內容。下面,我們向CustomButton類新增一個事件函式:

C/C++ 17 lines   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17       // CustomButton ... protected: void mousePressEvent(QMouseEvent *event); ...   // ---------- custombutton.cpp ---------- // ... void CustomButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { qDebug() << "left"; } else { QPushButton::mousePressEvent(event); } } ...    

我們重寫了CustomButtonmousePressEvent()函式,也就是滑鼠按下。在這個函式中,我們判斷如果滑鼠按下的是左鍵,則打印出來“left”字串,否則,呼叫父類的同名函式。編譯執行這段程式碼,當我們點選按鈕時,“You clicked this!”字串不再出現,只有一個“left”。也就是說,我們把父類的實現覆蓋掉了。由此可以看出,父類QPushButtonmousePressEvent()函式中肯定發出了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的預設實現。這一點我們前面已經說明。下面可以從程式碼級別來理解這一點,我們可以檢視一下QWidgetmousePressEvent()函式的實現:

C/C++ 17 lines   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17       //!!! 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 中基本一致(區別在於activePopupWidget()一行,Qt4 的版本是qApp->activePopupWidget())。注意函式的第一個語句:event->ignore(),如果子類都沒有重寫這個函式,Qt 會預設忽略這個事件,繼續尋找下一個事件接收者。如果我們在子類的mousePressEvent()函式中直接呼叫了accept()或者ignore(),而沒有呼叫父類的同名函式,QWidget::mousePressEvent()函式中關於Popup判斷的那段程式碼就不會被執行,因此可能會出現預設其妙的怪異現象。

針對accept()ignore(),我們再來看一個例子:

C/C++ 64 lines   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       class CustomButton : public QPushButton { Q_OBJECT public: CustomButton(QWidget *parent) : QPushButton(parent) { } protected: void mousePressEvent(QMouseEvent *event) { qDebug() << "CustomButton"; } };   class CustomButtonEx : public CustomButton { Q_OBJECT public: CustomButtonEx(QWidget *parent) : CustomButton(parent) { } protected: void mousePressEvent(QMouseEvent *event) { qDebug() << "CustomButtonEx"; } };   class CustomWidget : public QWidget { Q_OBJECT public: CustomWidget(QWidget *parent) : QWidget(parent) { } protected: void mousePressEvent(QMouseEvent *event) { qDebug() << "CustomWidget"; } };   class MainWindow : public QMainWindow { Q_OBJECT public: 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,裡面有兩個按鈕物件:CustomButtonCustomButtonEx。每一個類都重寫了mousePressEvent()函式。執行程式點選 CustomButtonEx,結果是

Plain Text 2 lines   1 2       CustomButtonEx      

這是因為我們重寫了滑鼠按下的事件,但是並沒有呼叫父類函式或者顯式設定accept()ignore()。下面我們在CustomButtonExmousePressEvent()第一行增加一句event->accept(),重新執行,發現結果不變。正如我們前面所說,QEvent預設是accept的,呼叫這個函式並沒有什麼區別。然後我們將CustomButtonExevent->accept()改成event->ignore()。這次執行結果是

Plain Text 3 lines   1 2 3       CustomButtonEx CustomWidget      

ignore()說明我們想讓事件繼續傳播,於是CustomButtonEx的父元件CustomWidget也收到了這個事件,所以輸出了自己的結果。同理,CustomWidget又沒有呼叫父類函式或者顯式設定accept()ignore(),所以事件傳播就此打住。這裡值得注意的是,CustomButtonEx的事件傳播給了父元件CustomWidget,而不是它的父類CustomButton事件的傳播是在元件層次上面的,而不是依靠類繼承機制。

接下來我們繼續測試,在CustomWidgetmousePressEvent()中增加QWidget::mousePressEvent(event)。這次的輸出是

Plain Text 4 lines   1 2 3 4       CustomButtonEx CustomWidget MainWindow      

如果你把QWidget::mousePressEvent(event)改成event->ignore(),結果也是一樣的。這正如我們前面說的,QWidget的預設是呼叫event->ignore()

在一個特殊的情形下,我們必須使用accept()ignore()函式,那就是視窗關閉的事件。對於視窗關閉QCloseEvent事件,呼叫accept()意味著 Qt 會停止事件的傳播,視窗關閉;呼叫ignore()則意味著事件繼續傳播,即阻止視窗關閉。回到我們前面寫的簡單的文字編輯器。我們在建構函式中新增如下程式碼:

C/C++ 28 lines   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       //!!! 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

相關推薦

Qt 學習之路 2(19):事件接受與忽略(當重寫事件回撥函式時,時刻注意是否需要通過呼叫父類的同名函式來確保原有實現仍能進行!好幾個例子。為什麼要這麼做?而不是自己去手動呼叫這兩個函式呢?因為我們無法確認父類中這個處理函式有沒有額外操作)

版本: 2012-09-29 2013-04-23 更新有關accept()和ignore()函式的相關內容。 2013-12-02 增加有關accept()和ignore()函式的示例。 上一章我們介紹了有關事件的相關內容。我們曾經提到,事件可以依情況接受和忽略。現在,我們就

Qt 學習 240隱式數據共享

深拷貝和淺拷貝 != 這樣的 pointer map painter pos 轉載 多線程 博客轉載自:https://www.devbean.net/2013/01/qt-study-road-2-implicit-sharing/ Qt 中許多 C++ 類使用了隱式數據

Qt 學習 242QListWidget、QTreeWidget 和 QTableWidget

上一章我們瞭解了 model/view 架構的基本概念。現在我們從最簡單的QListWidget、QTreeWidget和QTableWidget三個類開始瞭解最簡單的 model/view 的使用。這部分內容的確很難組織。首先,從最標準的 model/view 開始,往往會糾結於複雜的程式碼;但是

Qt 學習 226反走樣

我們在光柵圖形顯示器上繪製非水平、非垂直的直線或多邊形邊界時,或多或少會呈現鋸齒狀外觀。這是因為直線和多邊形的邊界是連續的,而光柵則是由離散的點組成。在光柵顯示裝置上表現直線、多邊形等,必須在離散位置取樣。由於取樣不充分重建後造成的資訊失真,就叫走樣;用於減少或消除這種效

Qt 學習 286QML檢視代理

與 Qt model/view 架構類似,在自定義使用者介面中,代理扮演著重要的角色。模型中的每一個數據項都要通過一個代理向用戶展示,事實上,使用者看到的可視部分就是代理。 每一個代理都可以訪問一系列屬性和附加屬性。這些屬性及附加屬性中,有些來自於資料模

Qt 學習 289Canvas

變換 Canvas中的“變形”,主要指的是座標系的變換,而不是路徑的變換。這與 QML 元素變換非常相似,都可以實現座標系統的scale(縮放)、rotate(旋轉)和translate(平移);不同的是,變換的原點是畫布原點。例如,如果以一個路徑的中心點為定點

Qt 學習 238儲存容器

儲存容器(containers)有時候也被稱為集合(collections),是能夠在記憶體中儲存其它特定型別的物件,通常是一些常用的資料結構,一般是通用模板類的形式。C++ 提供了一套完整的解決方案,作為標準模板庫(Standard Template Library

QT學習資料部落格Qt 實戰一二三》和《Qt 學習 2》等

參考原貼 https://blog.csdn.net/dpsying/article/details/80615320 目的:僅供自己學習,並無他用。 參考書目: 1《Qt5開發及例項》(Qt 5.8為平臺)         

《C++primer(第五版)》學習-第十九章特殊工具技術

【宣告:版權所有,轉載請標明出處,請勿用於商業用途。聯絡信箱:[email protected]】 19.1 控制記憶體分配 1. 當我們使用一條new表示式時: string *sp

Qt學習十三—— 再談TCP/IP多客戶端連線伺服器

一、TCP和UDP的區別這裡我會用一個表格來顯示這兩者的區別比較項TCPUDP是否連線面向連線無連線傳輸是否可靠可靠不可靠流量控制提供不提供工作方式全雙工可以是全雙工應用場合大量資料少量資料速度慢快二、incomingConnection函式這個函式和之前講過的newConn

Qt學習一個簡單的時鐘

  今天開始接觸了qt,剛開始為了搭環境,找qt create找了好久,網上不怎麼好找,找了一個晚上才找到的,先把資源分享一下給大家。   QTSDK分享     http://pan.baidu.c

QT學習十三基於Linux qt的聊天室

  前天將Windows下的聊天室改版了,但是我昨天才發現那個版本有一個缺陷,一個我一開始沒有注意到的錯誤,直到昨天除錯的時候才發現,就是我資料傳輸的時候是用std::string型別的,一開始是為了

QT學習十六QWebView實現簡易瀏覽器

QtWebkit 模組介紹   QtWebkit 模組提供了一個在Qt中使用web browser的engine,這使得我們在QT的應用程式中使用全球資訊網上的內容變得很容易,而且對其網頁內容的控制也可以通過native controls 實現  。   QtWebkit具

Qt學習12--Qt標準對話方塊字型對話方塊、進度條對話方塊和列印對話方塊

使用預定義對話方塊的三板斧原則:定義物件後設置父元件和屬性、模態呼叫exec()、根據結果判斷執行流程。 字型對話方塊 字型對話方塊屬於Qt預定義的對話方塊型別,它的作用就是通過使用者得到字型型別並返回。 QFontDialog dia(this)

QT學習十二基於Windows qt的聊天室

 這幾天一直在將之前的聊天室改版,客戶端用qt寫,伺服器用Windows API和c++寫,期間遇到很多挫折,很多bug,甚至有過想要放棄的念頭,幸好,上天眷顧,經過了4、5天的時間,終於找到了錯誤所

wepy學習2-初始化專案及額外元件的使用

文章目錄 基礎專案生產 1、使用wepy指令生成一個空wepy專案 2、安裝npm依賴包 3、關閉eslint程式碼檢查 4、配置完成 額外元件

QT學習 2彈簧和按鈕控制元件

二.彈簧和按鈕控制元件 2.1 關於SizePolicy Fixed:固定大小,大小取自sizeHint,不可伸展和收縮 Minimum: widget的最小值就是sizeHint,可伸展,不能

Qt學習十二——利用UDP進行通訊

一、UDP的特點UDP(使用者資料報協議)是一種簡單輕量級、不可靠、面向資料報,無連線的傳輸層協議。而TCP/IP協議卻是有連線的二、UDP適合應用的幾種情況1、網路資料大多為短訊息2、擁有大量客戶端3、對資料安全性無特殊要求4、網路負擔非常重,但對響應速度要求高。三、利用U

Python學習2 - 列表和元組

copy cto .com 會有 pen 輸入 插入元素 http 數據類型 列表 概念:Python內置的一種數據類型是列表:list。list是一種有序的集合,可以隨時添加和刪除其中的元素。 列表的使用 names = [‘zhangsan‘,‘lisi‘,‘

0.0 開啟自己QT學習

2008年左右接觸過一段時間的QT,感覺到了QT的方便快捷,之前我是做JAVA的,不過C++也還能看得懂,一共用QT開發了3個月左右的時間,一直是公司的同事帶著做。N年過去了,QT忘得差不多了,QT也有了很大的發展,現在重新學習是為了之後的工作。 打算用一個周的時間學完QT,達到能開發和看懂QT