Qt之事件處理機制

思惟導讀

  

1、事件簡介

  QT程序是事件驅動的, 程序的每一個動做都是由內部某個事件所觸發。QT事件的發生和處理成爲程序運行的主線,存在於程序整個生命週期。異步

  常見的QT事件類型以下:函數

     鍵盤事件: 按鍵按下和鬆開post

     鼠標事件: 鼠標移動,鼠標按鍵的按下和鬆開this

     拖放事件: 用鼠標進行拖放spa

     滾輪事件: 鼠標滾輪滾動操作系統

     繪屏事件: 重繪屏幕的某些部分指針

     定時事件: 定時器到時code

     焦點事件: 鍵盤焦點移動對象

     進入和離開事件: 鼠標移入widget以內,或是移出blog

     移動事件: widget的位置改變

     大小改變事件: widget的大小改變

     顯示和隱藏事件: widget顯示和隱藏

     窗口事件: 窗口是否爲當前窗口

   QT將系統產生的消息轉化爲QT事件,QT事件被封裝爲對象,全部的QT事件均繼承抽象類QEvent,用於描述程序內部或外部發生的動做,任意的QObject對象都具有處理QT事件的能力。

wKioL1gZ-degD-AyAAB-9l0vK0g875.png

2、QT事件產生

(1)操做系統事件

  操做系統將獲取的事件,好比鼠標按鍵,鍵盤按鍵等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、調用信號關聯的槽函數

(2)Qt應用程序本身產生

  程序產生事件有兩種方式, 一種是調用QApplication::postEvent(), 例如QWidget::update()函數,當須要從新繪製屏幕時,程序調用update()函數,new出來一個paintEvent,調用 QApplication::postEvent(),將其放入Qt的消息隊列中,等待依次被處理;

  另外一種方式是調用sendEvent()函數,事件不會放入隊列, 而是直接被派發和處理, QWidget::repaint()函數用的就是阻塞型的。

     sendEvent()中事件對象的生命期由Qt程序管理,支持分配在棧上和堆上的事件對象;postEvent()中事件對象的生命期由Qt平臺管理,只支持分配在堆上的事件對象,事件被處理後由Qt平臺銷燬。

3、Qt事件處理

(1)事件調度

  事件有兩種調度方式,同步和異步。

  Qt的事件循環是異步的,當調用QApplication::exec()時,就進入了事件循環,先處理Qt事件隊列中的事件, 直至爲空,再處理系統消息隊列中的消息, 直至爲空, 處理系統消息的時候會產生新的Qt事件, 須要對其再次進行處理。

     調用QApplication::sendEvent的時候, 消息會當即被處理,是同步的。實際上QApplication::sendEvent()是經過調用QApplication::notify(), 直接進入了事件的派發和處理環節。

(2)事件通知、派發

  事件過濾器是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() 函數, 或是安裝事件過濾器。

(3)事件的轉發

  對於某些類別的事件,若是在整個事件的派發過程結束後尚未被處理, 那麼這個事件將會向上轉發給它的父widget, 直到最頂層窗口。Qt中和事件相關的函數經過兩種方式相互通訊,一種是QApplication::notify(), QObject::eventFilter(), QObject::event()經過返回bool值來表示是否已處理;另外一種是調用QEvent::ignore() 或 QEvent::accept() 對事件進行標識,只用於event()函數和特定事件處理函數之間的溝通,並且只有用在某些類別事件上是有意義的, 這些事件就是上面提到的那些會被轉發的事件, 包括: 鼠標, 滾輪, 按鍵等事件。

(4)事件的處理和過濾

  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對象安裝任意個數的事件過濾器。

4、自定義事件和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_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;
}
相關文章
相關標籤/搜索