QT程序是事件驅動的, 程序的每一個動做都是由內部某個事件所觸發。QT事件的發生和處理成爲程序運行的主線,存在於程序整個生命週期。異步
常見的QT事件類型以下:函數
鍵盤事件: 按鍵按下和鬆開post
鼠標事件: 鼠標移動,鼠標按鍵的按下和鬆開this
拖放事件: 用鼠標進行拖放spa
滾輪事件: 鼠標滾輪滾動操作系統
繪屏事件: 重繪屏幕的某些部分指針
定時事件: 定時器到時code
焦點事件: 鍵盤焦點移動對象
進入和離開事件: 鼠標移入widget以內,或是移出blog
移動事件: widget的位置改變
大小改變事件: widget的大小改變
顯示和隱藏事件: widget顯示和隱藏
窗口事件: 窗口是否爲當前窗口
QT將系統產生的消息轉化爲QT事件,QT事件被封裝爲對象,全部的QT事件均繼承抽象類QEvent,用於描述程序內部或外部發生的動做,任意的QObject對象都具有處理QT事件的能力。
操做系統將獲取的事件,好比鼠標按鍵,鍵盤按鍵等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系統的消息隊列中,Qt事件循環的時候讀取消息隊列中的消息,轉化爲QEvent並被分發到相應的QWidget對象,相應的QWidget中的event(QEvent *)進行事件處理會對事件進行處理,event(QEvent *)會根據事件類型調用不一樣的事件處理函數,在事件處理函數中發送QT預約義的信號,最終調用信號關聯的槽函數。
GUI應用程序的事件處理:
A、QT事件產生後會被當即發送到相應的QWidget對象
B、相應的QWidget中的event(QEvent *)進行事件處理
C、event(QEvent *)根據事件類型調用不一樣的事件處理函數
D、在事件處理函數中發送QT預約義的信號
E、調用信號關聯的槽函數
程序產生事件有兩種方式, 一種是調用QApplication::postEvent(), 例如QWidget::update()函數,當須要從新繪製屏幕時,程序調用update()函數,new出來一個paintEvent,調用 QApplication::postEvent(),將其放入Qt的消息隊列中,等待依次被處理;
另外一種方式是調用sendEvent()函數,事件不會放入隊列, 而是直接被派發和處理, QWidget::repaint()函數用的就是阻塞型的。
sendEvent()中事件對象的生命期由Qt程序管理,支持分配在棧上和堆上的事件對象;postEvent()中事件對象的生命期由Qt平臺管理,只支持分配在堆上的事件對象,事件被處理後由Qt平臺銷燬。
事件有兩種調度方式,同步和異步。
Qt的事件循環是異步的,當調用QApplication::exec()時,就進入了事件循環,先處理Qt事件隊列中的事件, 直至爲空,再處理系統消息隊列中的消息, 直至爲空, 處理系統消息的時候會產生新的Qt事件, 須要對其再次進行處理。
調用QApplication::sendEvent的時候, 消息會當即被處理,是同步的。實際上QApplication::sendEvent()是經過調用QApplication::notify(), 直接進入了事件的派發和處理環節。
事件過濾器是Qt中一個獨特的事件處理機制, 功能強大並且使用起來靈活方便。經過事件過濾器, 可讓一個對象偵聽攔截另一個對象的事件。事件過濾器實現以下: 在全部Qt對象的基類QObject中有一個類型爲QObjectList的成員變量,名字爲eventFilters,當某個QObject(A)給另外一個QObject(B)安裝了事件過濾器後, B會把A的指針保存在eventFilters中。在B處理事件前,會先去檢查eventFilters列表, 若是非空, 就先調用列表中對象的eventFilter()函數。一個對象能夠給多個對象安裝過濾器,一個對象能同時被安裝多個過濾器, 在事件到達以後, 事件過濾器以安裝次序的反序被調用。事件過濾器函數( eventFilter() ) 返回值是bool型, 若是返回true, 則表示事件已經被處理完畢, Qt將直接返回, 進行下一事件的處理。若是返回false, 事件將接着被送往剩下的事件過濾器或是目標對象進行處理。
QT中,事件的派發是從 QApplication::notify()開始的, 由於QAppliction也是繼承自QObject, 因此先檢查QAppliation對象, 若是有事件過濾器安裝在qApp上, 先調用事件過濾器,接下來QApplication::notify() 會過濾或合併一些事件(好比失效widget的鼠標事件會被過濾掉, 而同一區域重複的繪圖事件會被合併),事件被送到reciver::event()處理。
在reciver::event()中, 先檢查有無事件過濾器安裝在reciever上。如有, 則調用之。而後根據QEvent的類型, 調用相應的特定事件處理函數。常見的事件都有特定事件處理函數, 好比:mousePressEvent(), focusOutEvent(), resizeEvent(), paintEvent(), resizeEvent()等等。在實際應用中, 常常須要重載特定事件處理函數處理事件。對於不常見的事件, 沒有相對應的特定事件處理函數,若是要處理這些事件, 就須要使用別的辦法, 好比重載event() 函數, 或是安裝事件過濾器。
對於某些類別的事件,若是在整個事件的派發過程結束後尚未被處理, 那麼這個事件將會向上轉發給它的父widget, 直到最頂層窗口。Qt中和事件相關的函數經過兩種方式相互通訊,一種是QApplication::notify(), QObject::eventFilter(), QObject::event()經過返回bool值來表示是否已處理;另外一種是調用QEvent::ignore() 或 QEvent::accept() 對事件進行標識,只用於event()函數和特定事件處理函數之間的溝通,並且只有用在某些類別事件上是有意義的, 這些事件就是上面提到的那些會被轉發的事件, 包括: 鼠標, 滾輪, 按鍵等事件。
QT提供了五種不一樣級別的事件處理和過濾:
A、重寫特定事件處理函數.
最多見的事件處理辦法就是重寫mousePressEvent(), keyPressEvent(), paintEvent() 等特定事件處理函數。
B、重寫event()函數.
重寫event()函數時, 須要調用父類的event()函數來處理不須要處理或是不清楚如何處理的事件。
return QWidget::event(event);
C、在Qt對象上安裝事件過濾器
安裝事件過濾器有兩個步驟: (假設要用A來監視過濾B的事件)
首先調用B的installEventFilter( const QOject *obj ), 以A的指針做爲參數,全部發往B的事件都將先由A的eventFilter()處理。而後, A要重寫QObject::eventFilter()函數, 在eventFilter() 中對事件進行處理。
D、給QAppliction對象安裝事件過濾器
若是給QApplication對象裝上過濾器,那麼全部的事件在發往任何其餘的過濾器時,都要先通過當前eventFilter()。在QApplication::notify() 中, 是先調用qApp的過濾器, 再對事件進行分析, 以決定是否合併或丟棄。
E、繼承QApplication類,並重載notify()函數
Qt是用QApplication::notify()函數來分發事件的,要在任何事件過濾器查看任何事件以前先獲得這些事件,重寫notify()函數是惟一的辦法。一般來講事件過濾器更好用一些, 由於不須要去繼承QApplication類,並且能夠給QApplication對象安裝任意個數的事件過濾器。
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_OBJECT QLineEdit 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
補充:自定義事件類型可使用registerEventType
QEvent::Type ImageLoadedEvent::evType(){if(s_evType == QEvent::None){s_evType = (QEvent::Type)registerEventType();}return s_evType;}