Qt之事件处理机制
思維導(dǎo)讀
一、事件簡(jiǎn)介
QT程序是事件驅(qū)動(dòng)的, 程序的每個(gè)動(dòng)作都是由內(nèi)部某個(gè)事件所觸發(fā)。QT事件的發(fā)生和處理成為程序運(yùn)行的主線,存在于程序整個(gè)生命周期。
常見的QT事件類型如下:
???? 鍵盤事件: 按鍵按下和松開
???? 鼠標(biāo)事件: 鼠標(biāo)移動(dòng),鼠標(biāo)按鍵的按下和松開
???? 拖放事件: 用鼠標(biāo)進(jìn)行拖放
???? 滾輪事件: 鼠標(biāo)滾輪滾動(dòng)
???? 繪屏事件: 重繪屏幕的某些部分
???? 定時(shí)事件: 定時(shí)器到時(shí)
???? 焦點(diǎn)事件: 鍵盤焦點(diǎn)移動(dòng)
???? 進(jìn)入和離開事件: 鼠標(biāo)移入widget之內(nèi),或是移出
???? 移動(dòng)事件: widget的位置改變
???? 大小改變事件: widget的大小改變
???? 顯示和隱藏事件: widget顯示和隱藏
???? 窗口事件: 窗口是否為當(dāng)前窗口
? QT將系統(tǒng)產(chǎn)生的消息轉(zhuǎn)化為QT事件,QT事件被封裝為對(duì)象,所有的QT事件均繼承抽象類QEvent,用于描述程序內(nèi)部或外部發(fā)生的動(dòng)作,任意的QObject對(duì)象都具備處理QT事件的能力。
二、QT事件產(chǎn)生
(1)操作系統(tǒng)事件
操作系統(tǒng)將獲取的事件,比如鼠標(biāo)按鍵,鍵盤按鍵等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系統(tǒng)的消息隊(duì)列中,Qt事件循環(huán)的時(shí)候讀取消息隊(duì)列中的消息,轉(zhuǎn)化為QEvent并被分發(fā)到相應(yīng)的QWidget對(duì)象,相應(yīng)的QWidget中的event(QEvent *)進(jìn)行事件處理會(huì)對(duì)事件進(jìn)行處理,event(QEvent *)會(huì)根據(jù)事件類型調(diào)用不同的事件處理函數(shù),在事件處理函數(shù)中發(fā)送QT預(yù)定義的信號(hào),最終調(diào)用信號(hào)關(guān)聯(lián)的槽函數(shù)。
GUI應(yīng)用程序的事件處理:
A、QT事件產(chǎn)生后會(huì)被立即發(fā)送到相應(yīng)的QWidget對(duì)象
B、相應(yīng)的QWidget中的event(QEvent *)進(jìn)行事件處理
C、event(QEvent *)根據(jù)事件類型調(diào)用不同的事件處理函數(shù)
D、在事件處理函數(shù)中發(fā)送QT預(yù)定義的信號(hào)
E、調(diào)用信號(hào)關(guān)聯(lián)的槽函數(shù)
(2)Qt應(yīng)用程序自己產(chǎn)生
程序產(chǎn)生事件有兩種方式, 一種是調(diào)用QApplication::postEvent(),?例如QWidget::update()函數(shù),當(dāng)需要重新繪制屏幕時(shí),程序調(diào)用update()函數(shù),new出來一個(gè)paintEvent,調(diào)用 QApplication::postEvent(),將其放入Qt的消息隊(duì)列中,等待依次被處理;
另一種方式是調(diào)用sendEvent()函數(shù),事件不會(huì)放入隊(duì)列, 而是直接被派發(fā)和處理, QWidget::repaint()函數(shù)用的就是阻塞型的。
???? sendEvent()中事件對(duì)象的生命期由Qt程序管理,支持分配在棧上和堆上的事件對(duì)象;postEvent()中事件對(duì)象的生命期由Qt平臺(tái)管理,只支持分配在堆上的事件對(duì)象,事件被處理后由Qt平臺(tái)銷毀。
三、Qt事件處理
(1)事件調(diào)度
事件有兩種調(diào)度方式,同步和異步。
Qt的事件循環(huán)是異步的,當(dāng)調(diào)用QApplication::exec()時(shí),就進(jìn)入了事件循環(huán),先處理Qt事件隊(duì)列中的事件, 直至為空,再處理系統(tǒng)消息隊(duì)列中的消息, 直至為空, 處理系統(tǒng)消息的時(shí)候會(huì)產(chǎn)生新的Qt事件, 需要對(duì)其再次進(jìn)行處理。
???? 調(diào)用QApplication::sendEvent的時(shí)候, 消息會(huì)立即被處理,是同步的。實(shí)際上QApplication::sendEvent()是通過調(diào)用QApplication::notify(), 直接進(jìn)入了事件的派發(fā)和處理環(huán)節(jié)。
(2)事件通知、派發(fā)
事件過濾器是Qt中一個(gè)獨(dú)特的事件處理機(jī)制, 功能強(qiáng)大而且使用起來靈活方便。通過事件過濾器, 可以讓一個(gè)對(duì)象偵聽攔截另外一個(gè)對(duì)象的事件。事件過濾器實(shí)現(xiàn)如下: 在所有Qt對(duì)象的基類QObject中有一個(gè)類型為QObjectList的成員變量,名字為eventFilters,當(dāng)某個(gè)QObject(A)給另一個(gè)QObject(B)安裝了事件過濾器后, B會(huì)把A的指針保存在eventFilters中。在B處理事件前,會(huì)先去檢查eventFilters列表, 如果非空, 就先調(diào)用列表中對(duì)象的eventFilter()函數(shù)。一個(gè)對(duì)象可以給多個(gè)對(duì)象安裝過濾器,一個(gè)對(duì)象能同時(shí)被安裝多個(gè)過濾器, 在事件到達(dá)之后,?事件過濾器以安裝次序的反序被調(diào)用。事件過濾器函數(shù)( eventFilter() ) 返回值是bool型, 如果返回true, 則表示事件已經(jīng)被處理完畢, Qt將直接返回, 進(jìn)行下一事件的處理。如果返回false, 事件將接著被送往剩下的事件過濾器或是目標(biāo)對(duì)象進(jìn)行處理。
QT中,事件的派發(fā)是從 QApplication::notify()開始的, 因?yàn)镼Appliction也是繼承自QObject, 所以先檢查QAppliation對(duì)象, 如果有事件過濾器安裝在qApp上, 先調(diào)用事件過濾器,接下來QApplication::notify() 會(huì)過濾或合并一些事件(比如失效widget的鼠標(biāo)事件會(huì)被過濾掉, 而同一區(qū)域重復(fù)的繪圖事件會(huì)被合并),事件被送到reciver::event()處理。
???? 在reciver::event()中, 先檢查有無事件過濾器安裝在reciever上。若有, 則調(diào)用之。然后根據(jù)QEvent的類型, 調(diào)用相應(yīng)的特定事件處理函數(shù)。常見的事件都有特定事件處理函數(shù), 比如:mousePressEvent(), focusOutEvent(),? resizeEvent(), paintEvent(), resizeEvent()等等。在實(shí)際應(yīng)用中, 經(jīng)常需要重載特定事件處理函數(shù)處理事件。對(duì)于不常見的事件, 沒有相對(duì)應(yīng)的特定事件處理函數(shù),如果要處理這些事件, 就需要使用別的辦法, 比如重載event() 函數(shù), 或是安裝事件過濾器。
(3)事件的轉(zhuǎn)發(fā)
對(duì)于某些類別的事件,如果在整個(gè)事件的派發(fā)過程結(jié)束后還沒有被處理, 那么這個(gè)事件將會(huì)向上轉(zhuǎn)發(fā)給它的父widget, 直到最頂層窗口。Qt中和事件相關(guān)的函數(shù)通過兩種方式相互通信,一種是QApplication::notify(), QObject::eventFilter(), QObject::event()通過返回bool值來表示是否已處理;另一種是調(diào)用QEvent::ignore() 或 QEvent::accept() 對(duì)事件進(jìn)行標(biāo)識(shí),只用于event()函數(shù)和特定事件處理函數(shù)之間的溝通,而且只有用在某些類別事件上是有意義的, 這些事件就是上面提到的那些會(huì)被轉(zhuǎn)發(fā)的事件, 包括: 鼠標(biāo), 滾輪, 按鍵等事件。
(4)事件的處理和過濾
QT提供了五種不同級(jí)別的事件處理和過濾:
???? A、重寫特定事件處理函數(shù).
???? 最常見的事件處理辦法就是重寫mousePressEvent(), keyPressEvent(), paintEvent()?等特定事件處理函數(shù)。
? B、重寫event()函數(shù).
???? 重寫event()函數(shù)時(shí), 需要調(diào)用父類的event()函數(shù)來處理不需要處理或是不清楚如何處理的事件。
???? return QWidget::event(event);
???? C、在Qt對(duì)象上安裝事件過濾器
???? 安裝事件過濾器有兩個(gè)步驟: (假設(shè)要用A來監(jiān)視過濾B的事件)
???? 首先調(diào)用B的installEventFilter( const QOject *obj ), 以A的指針作為參數(shù),所有發(fā)往B的事件都將先由A的eventFilter()處理。然后, A要重寫QObject::eventFilter()函數(shù), 在eventFilter() 中對(duì)事件進(jìn)行處理。
???? D、給QAppliction對(duì)象安裝事件過濾器
如果給QApplication對(duì)象裝上過濾器,那么所有的事件在發(fā)往任何其他的過濾器時(shí),都要先經(jīng)過當(dāng)前eventFilter()。在QApplication::notify() 中, 是先調(diào)用qApp的過濾器, 再對(duì)事件進(jìn)行分析, 以決定是否合并或丟棄。
???? E、繼承QApplication類,并重載notify()函數(shù)
???? Qt是用QApplication::notify()函數(shù)來分發(fā)事件的,要在任何事件過濾器查看任何事件之前先得到這些事件,重寫notify()函數(shù)是唯一的辦法。通常來說事件過濾器更好用一些, 因?yàn)椴恍枰ダ^承QApplication類,而且可以給QApplication對(duì)象安裝任意個(gè)數(shù)的事件過濾器。
四、自定義事件和eventFilter示例
class DefineEvent : public QEvent { public:const static QEvent::Type DefineType = static_cast<QEvent::Type>(QEvent::User + 0xFF);explicit DefineEvent(QString data) : QEvent(DefineType){m_data = data;}QString data() {return m_data;}private:QString m_data; };?
#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QLineEdit> #include "StringEvent.h" #include <QMouseEvent> #include <QDebug> #include <QApplication>class Widget : public QWidget {Q_OBJECTQLineEdit m_edit; public:Widget(QWidget *parent = 0): QWidget(parent), m_edit(this){m_edit.installEventFilter(this);}bool event(QEvent* evt){if( evt->type() == QMouseEvent::MouseButtonDblClick ){qDebug() << "event: Before sentEvent";StringEvent e("D.T.Software");QApplication::sendEvent(&m_edit, &e);qDebug() << "event: After sentEvent";}return QWidget::event(evt);}bool eventFilter(QObject* obj, QEvent* evt){if( (obj == &m_edit) && (evt->type() == StringEvent::TYPE) ){StringEvent* se = dynamic_cast<StringEvent*>(evt);qDebug() << "Receive: " << se->data();m_edit.insert(se->data());return true;}return QWidget::eventFilter(obj, evt);}~Widget(){} };#endif // WIDGET_H 補(bǔ)充:自定義事件類型可以使用registerEventTypeQEvent::Type ImageLoadedEvent::evType(){ if(s_evType == QEvent::None) { s_evType = (QEvent::Type)registerEventType(); } return s_evType; }
轉(zhuǎn)載于:https://www.cnblogs.com/xiaobingqianrui/p/9547924.html
總結(jié)
- 上一篇: Array and ArrayList
- 下一篇: 重定向与页面转发