詳解 QT Event 以及 Event Filter 事件處理
QT Event 以及 Event Filter 事件處理是本文要介紹的內(nèi)容,詳細(xì)內(nèi)容如下,先來看內(nèi)容。Event 和 Event Filters:
1、手動(dòng)發(fā)送事件流程:
(1)構(gòu)造自己的事件對象:
- QEvent *evt = new QEvent( QEvent::Close );
(2)發(fā)送給指定的對象:
- QApplication::sendEvent(this, evt );
2、定制某個(gè)控件的事件處理:
(1)確定需要對哪些控件的哪些事件, 通常的 close以及 key 和 keyboard 事件;
(2)重寫該對象的 event() 函數(shù);
3、事件過濾流程:
(1)確定自己需要過濾處理哪些對象的那些事件;
(2)構(gòu)造自己的事件過濾類: 重寫該類的 eventFilter 函數(shù);
(3)在主程序中實(shí)例化一個(gè)過濾類對象;
(4)調(diào)用該過濾類對象的 installEventFilter( receiver, QEvent *event),
以在目標(biāo)對象上安裝該過濾器。
在 Qt 中, event 被建模成派生自abstract QEvent 類的對象, 用來表示在應(yīng)用程序中發(fā)生的事件,或是應(yīng)用程序需要處理的外部活動(dòng)產(chǎn)生的事件.
Events 可以被任一 QObject 派生的子類實(shí)例對象接收和處理, 但他們是關(guān)聯(lián)到特定控件的. 本文檔描述 event 在典型應(yīng)用程序中是如何發(fā)送及處理的.
- How Events are Delivered
- Event Types
- Event Handlers
- Event Filters
- Sending Events
event 如何發(fā)送
通常情況下,當(dāng)一個(gè)事件發(fā)生的時(shí)候, Qt 通過構(gòu)造一個(gè)合適的 QEvent子類對象來表示事件的發(fā)生, 然后將該事件對象發(fā)送給特定的 QObject對象( 或其子類實(shí)例對象), 通過調(diào)用該 QObject 的 event() 函數(shù). 這個(gè) event() 函數(shù)不會(huì)對事件本身進(jìn)行處理, 而是首先檢查所接受到event 的類型, 然后根據(jù) event 的類型來調(diào)用相應(yīng)的 event handler, event handler 在處理完 event 之后會(huì)返回一個(gè)bool值表示 該 event是被接受了,還是被忽略了。
某些事件, 例如 QMouseEvent 和 QKeyEvent, 來自于窗口系統(tǒng); 某些, 例如 QTimerEvent, 來自于其他的事件源; 某些, 來自于應(yīng)用程序本身.
Event 類型
Qt為多數(shù) Event 類型建立了相應(yīng)的類, 常見有QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent, QCloseEvent.
每一個(gè)特定的 event 類都是繼承自 QEvent 基類,添加特定的事件函數(shù). 例如, QResizeEvent 添加了 size() 和 oldSize() 讓控件可以發(fā)現(xiàn)他們的尺度,發(fā)生了的怎么改變.
某些類實(shí)際支持不止一種事件類型. QMouseEvent 就支持鼠標(biāo)按鍵按下事件,雙擊事件, 移動(dòng)事件, 以及其他相關(guān)操作所引發(fā)的事件.
每一個(gè)事件都有它的類型, 由 QEvent::Type 定義, 運(yùn)行時(shí)可以很方便的檢測每個(gè)事件對象的事件類型,以快速的判斷該事件對象構(gòu)造自哪個(gè)事件類.
由于程序需要和又多樣又復(fù)雜的事件進(jìn)行交互, 所以 Qt 的 event 發(fā)送機(jī)制設(shè)計(jì)非常有彈性.
QCoreApplication::notify() 的文檔簡潔的說明了整個(gè)機(jī)制:
- bool QCoreApplication::notify ( QObject * receiver, QEvent * event ) [virtual]
發(fā)送 event 給 接收者: receiver->event(event). 返回從 receiver 的 event handler 返回的值. 注意這個(gè)函數(shù)適用于該應(yīng)用程序中的任何線程中的任何對象. 對于特定類型的事件 (例如, 鼠標(biāo)和鍵盤事件), 該事件將被傳送到 receiver 的 parent 并這樣逐級上傳
直到傳到 top-level object, 如果這些 receiver 都沒有對該事件進(jìn)行處理的話(比如, 它返回 false ).
共五種處理 event 的方法; 重寫(重實(shí)現(xiàn)) QCoreApplication::notify() 這個(gè) virtual 函數(shù)只是其 中的一種. 以下列出了這五種方法:
1、重寫 paintEvent(), mousePressEvent() 等. 這是最常用, 最簡單但也是最有限的方式.
2、重寫 QCoreApplication::notify(). 這非常強(qiáng)大, 可以完全控制事件處理; 但一次只可用于一個(gè)子類.
3、給 QCoreApplication::instance() 安裝一個(gè) event filter .這個(gè) event filter 就能處理所有控件的所有事件, 因此這與重寫 notify() 一樣強(qiáng)大; 此外, 可以有不止一個(gè)應(yīng)用程序全局級的 event filter. 應(yīng)用程序全局級 event filter 甚至可以收到已禁用控件的鼠標(biāo)事件.
注意: 應(yīng)用程序級 event filter 僅能用于存活在主線程中的對象.
4、 重寫 QObject::event()( 像 QWidget 那樣 ). 如果你重寫了 QObject::event(), 當(dāng) Tab 鍵按下時(shí), 你就可以在任何控件級 event filter 捕獲這個(gè) Tab 鍵按下事件之前處理這個(gè)事件.
5、給相應(yīng)的接收對象安裝一個(gè) event filter. 例如一個(gè)捕獲所有事件的 event filter, 包含 Tab 和Shift+Tab 鍵按下事件, 在它們沒有改變焦點(diǎn)控件之前.
另請參考 QObject::event() 以及 installEventFilter().
Event Handlers
處理 event 的標(biāo)準(zhǔn)方式是調(diào)用一個(gè) virtual 函數(shù). 例如, QPaintEvent 是通過調(diào)用 QWidget::paintEvent() 來處理的. 這個(gè) virtual 函數(shù)負(fù)責(zé)進(jìn)行相應(yīng)的處理, 通常就是重畫該控件. 如果你在自己實(shí)現(xiàn)的 virtual 函數(shù)中沒有做所有必要的工作, 你就有必要調(diào)用它的基類實(shí)現(xiàn).
例如, 下面的代碼處理一個(gè)定制 checkbox 控件的鼠標(biāo)左鍵點(diǎn)擊事件, 并將所有其他點(diǎn)擊事件轉(zhuǎn)發(fā)給它的基類 QCheckBox 類:
- void MyCheckBox::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- // handle left mouse button here
- } else {
- // pass on other buttons to base class
- QCheckBox::mousePressEvent(event);
- }
- }
如果你需要替換基類的函數(shù), 你應(yīng)該自己實(shí)現(xiàn)所有相關(guān)的處理. 但是, 如果你只想擴(kuò)展基類的功能, 那么你就只需實(shí)現(xiàn)需要實(shí)現(xiàn)的部分, 然后調(diào)用基類處理函數(shù)來處理你不想處理的情況.
偶爾, 你要處理沒有相應(yīng)處理函數(shù)的特定事件, 或遇到事件處理函數(shù)不夠用情況. 最常見的例子是 Tab 鍵按下事件. 通常, QWidget 截獲到 Tab 鍵按下事件后,會(huì)移動(dòng)鍵盤焦點(diǎn), 但是少數(shù)控件需要自己來處理這個(gè)事件. 這些對想可以重寫 QObject::event() 函數(shù), 通用的 event handler, 然后在通常處理過程之前或之后寫自己的事件處理過程, 或完全替代原處理過程. 下面是這樣一個(gè)很常見的控件:
一個(gè)既自己處理 Tab 事件又自己處理某些按鍵事件, 然后將其它不需自己處理的事件轉(zhuǎn)發(fā)給基類處理:
- bool MyWidget::event(QEvent *event)
- {
- if (event->type() == QEvent::KeyPress) {
- QKeyEvent *ke = static_cast<QKeyEvent *>(event);
- if (ke->key() == Qt::Key_Tab) {
- // special tab handling here
- return true;
- }
- } else if (event->type() == MyCustomEventType) {
- MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
- // custom event handling here
- return true;
- }
- return QWidget::event(event);
- }
值得注意的是對沒有處理的事件仍調(diào)用 QWidget::event(), 并返回該基類調(diào)用的返回值以指示事件是否被處理了; 若返回 true 值則將會(huì)禁止將該事件再發(fā)往其他對象.
Event Filters
有時(shí)候一個(gè)對象需要檢查, 還可能截取發(fā)往其它對象的事件.例如, 對話框通常需要過濾發(fā)往某些控件的事件, 比如 更改 Enter 鍵按下的事件處理.
通過調(diào)用過濾器對象的 QObject::installEventFilter() 函數(shù), 為目標(biāo)對象設(shè)置一個(gè) event filter, 就可在過濾器對象的QObject::eventFilter() 函數(shù)中處理發(fā)往目標(biāo)對象的事件. 一個(gè) event filter 在目標(biāo)對象收到事件之前處理事件, 這使得過濾器對象在需要的時(shí)候可以檢查并丟棄事件. 一個(gè)現(xiàn)有的 event filter 可以調(diào)用 QObject::removeEventFilter() 來移除已經(jīng)安裝的 event filter .
當(dāng)過濾器的 eventFilter() 實(shí)現(xiàn)被調(diào)用的時(shí)候, 它就可以選擇是處理該事件,還是轉(zhuǎn)發(fā)該事件, 或禁止該事件繼續(xù)被其它對象處理. 若所有的事件過濾器都允許一個(gè)事件可被繼續(xù)處理( 每個(gè)過濾器處理后都返回 false ), 該事件最終將被發(fā)送到目標(biāo)對象. 如果其中一個(gè)中止了這個(gè)流程(通過返回 TRUE),
則后面的過濾器對象以及目標(biāo)對象將不會(huì)收到該事件.
- bool FilterObject::eventFilter(QObject *object, QEvent *event)
- {
- if (object == target && event->type() == QEvent::KeyPress) {
- QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
- if (keyEvent->key() == Qt::Key_Tab) {
- // Special tab handling
- return true;
- } else
- return false;
- }
- return false;
- }
上面代碼演示了另外一種截取發(fā)往特定對象 Tab 鍵事件的方法. 在這個(gè)例子里, 該過濾器處理 Tab 事件后返回 true 來阻止它們被繼續(xù)處理. 所有其他的按鍵事件將被忽略掉,然后過濾器返回 false 來允許該事件被已安裝的后續(xù)過濾器處理, 最終發(fā)往目標(biāo)控件.
當(dāng)然也可以過濾整個(gè)應(yīng)用程序的所有事件, 只需將過濾器對象安裝到 QApplication 對象或QCoreApplication 對象上. 這樣的全局事件過濾器會(huì)在任何對象級過濾器()調(diào)用之前調(diào)用.
這是非常強(qiáng)大的, 但它也拖慢了整個(gè)應(yīng)用程序范圍內(nèi)每個(gè)事件的每次處理過程; 通常使用其他的技術(shù)來實(shí)現(xiàn)應(yīng)用程序全局的事件過濾.
發(fā)送事件
許多應(yīng)用程序需要?jiǎng)?chuàng)建并發(fā)送自己的事件. 你完全可以模仿 Qt 自有的 event loop 機(jī)制, 先構(gòu)造合適的事件對象, 然調(diào)用 QCoreApplication::sendEvent() QCoreApplication::postEvent() 來把這些構(gòu)造好的事件發(fā)送給指定的接收者.
sendEvent() 立即同步處理要發(fā)送的 event . 當(dāng)它返回的時(shí)候, 表示相關(guān)的事件過濾器 和/或目標(biāo)對象就處理完了該 event. 對于多數(shù)的 event 類, 有一個(gè)成員函數(shù) isAccepted() 可以用來判別該事件是已被接受處理了,還是被拒絕處理了.
postEvent() 將 event 提交到一個(gè)隊(duì)列中等待調(diào)度. 在下一次 Qt 的主 event loop 運(yùn)行的時(shí)候,主 event loop 就會(huì)調(diào)度所有提交到隊(duì)列中的 event, 以某種優(yōu)化的方式. 例如, 如果有幾個(gè) resize event, 他們就會(huì)被壓縮成一個(gè)事件. 同樣適用于 paint events: QWidget::update() 調(diào)用postEvent(), 以避免多次重畫來避免閃爍以及提高速度.
postEvent() 也被用于對象的初始化過程, 因?yàn)樘峤贿^的 event 通常在相應(yīng)對象初始化完畢后極短的 時(shí)間內(nèi)就會(huì)被調(diào)度. 在實(shí)現(xiàn)一個(gè)控件的時(shí)候, 在自定義控件的 constructor 中盡早支持事件機(jī)制是非常重要的, 在可能接受到任何事件之前,確保盡早初始化成員變量.
要?jiǎng)?chuàng)建一個(gè)定制類型的 event, 你需要定義一個(gè)事件號( event number ), 這個(gè)事件號應(yīng)該大于QEvent::User, 并且你可能需要繼承 QEvent 以傳遞關(guān)于你定制的 event 類型的特定信息.
參考 QEvent 文檔以獲取更多細(xì)節(jié).
小結(jié):關(guān)于詳解 QT Event 以及 Event Filter 事件處理的內(nèi)容介紹完了,希望本文對你有所幫助。更多請參考編輯推薦。