【Qt筆記】自定義事件

儘管 Qt 已經提供了不少事件,但對於更加變幻無窮的需求來講,有限的事件都是不夠的。例如,我要支持一種新的設備,這個設備提供一種嶄新的交互方式,那麼,這種事件如何處理呢?因此,容許建立本身的事件 類型也就勢在必行。即使是不說那種很是極端的例子,在多線程的程序中,自定義事件也是尤爲有用。固然,事件也並非侷限在多線程中,它能夠用在單線程的程序中,做爲一種對象間通信的機制。那麼,爲何我須要使用事件,而不是信號槽呢?主要緣由是,事件的分發既能夠是同步的,又能夠是異步的,而函數的調用或者說是槽的回調老是同步的。事件的另一個好處是,它可使用過濾器。安全

 

Qt 自定義事件很簡單,同其它類庫的使用很類似,都是要繼承一個類進行擴展。在 Qt 中,你須要繼承的類是QEvent多線程

繼承QEvent類,最重要的是提供一個QEvent::Type類型的參數,做爲自定義事件的類型值。回憶一下,這個 type 是咱們在處理事件時用於識別事件類型的代號。好比在event()函數中,咱們使用QEvent::type()得到這個事件類型,而後與咱們定義的實際類型對比。異步

QEvent::TypeQEvent定義的一個枚舉。所以,咱們能夠傳遞一個 int 值。可是須要注意的是,咱們的自定義事件類型不能和已經存在的 type 值重複,不然會有不可預料的錯誤發生。由於系統會將你新增長的事件當作系統事件進行派發和調用。在 Qt 中,系統保留 0 – 999 的值,也就是說,你的事件 type 要大於 999。這種數值固然很是難記,因此 Qt 定義了兩個邊界值:QEvent::UserQEvent::MaxUser。咱們的自定義事件的 type 應該在這兩個值的範圍之間。其中,QEvent::User的值是 1000,QEvent::MaxUser的值是 65535。從這裏知道,咱們最多能夠定義 64536 個事件。經過這兩個枚舉值,咱們能夠保證咱們本身的事件類型不會覆蓋系統定義的事件類型。可是,這樣並不能保證自定義事件相互之間不會被覆蓋。爲了解決這個問題,Qt 提供了一個函數:registerEventType(),用於自定義事件的註冊。該函數簽名以下:函數

static int QEvent::registerEventType ( int hint = -1 );

這個函數是 static 的,所以可使用QEvent類直接調用。函數接受一個 int 值,其默認值是 -1;函數返回值是向系統註冊的新的 Type 類型的值。若是 hint 是合法的,也就是說這個 hint 不會發生任何覆蓋(系統的以及其它自定義事件的),則會直接返回這個值;不然,系統會自動分配一個合法值並返回。所以,使用這個函數便可完成 type 值的指定。這個函數是線程安全的,沒必要另外添加同步。post

咱們能夠在QEvent子類中添加本身的事件所須要的數據,而後進行事件的發送。Qt 中提供了兩種事件發送方式:線程

static bool QCoreApplication::sendEvent(QObject *receiver,
                                        QEvent *event);

1. 直接將event事件發送給receiver接受者,使用的是QCoreApplication::notify()函數。函數返回值就是事件處理函數的返回值。在事件被髮送的時候,event對象並不會被銷燬。一般咱們會在棧上建立event對象,例如:code

QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
QApplication::sendEvent(mainWindow, &event);
static void QCoreApplication::postEvent(QObject *receiver,
                                        QEvent *event);

2. 將event事件及其接受者receiver一同追加到事件隊列中,函數當即返回。orm

由於 post 事件隊列會持有事件對象,而且在其 post 的時候將其 delete 掉,所以,咱們必須在堆上建立event對象。當對象被髮送以後,再試圖訪問event對象就會出現問題(由於 post 以後,event對象就會被 delete)。對象

當控制權返回到主線程循環時,保存在事件隊列中的全部事件都經過notify()函數發送出去。繼承

事件會根據 post 的順序進行處理。若是你想要改變事件的處理順序,能夠考慮爲其指定一個優先級。默認的優先級是Qt::NormalEventPriority

這個函數是線程安全的。

Qt 還提供了一個函數:

static void QCoreApplication::sendPostedEvents(QObject *receiver,
                                               int event_type);

這個函數的做用是,將事件隊列中的接受者爲receiver,事件相似爲 event_type 的全部事件當即發送給 receiver 進行處理。須要注意的是,來自窗口系統的事件並不禁這個函數進行處理,而是processEvent()。詳細信息請參考 Qt API 手冊。

如今,咱們已經可以自定義事件對象,已經可以將事件發送出去,還剩下最後一步:處理自定義事件。處理自定義事件,同前面咱們講解的那些處理方法沒有什麼區別。咱們能夠重寫QObject::customEvent()函數,該函數接收一個QEvent對象做爲參數:

void QObject::customEvent(QEvent *event);

咱們能夠經過轉換 event 對象類型來判斷不一樣的事件:

void CustomWidget::customEvent(QEvent *event) {
    CustomEvent *customEvent = static_cast<CustomEvent *>(event);
    // ...
}

固然,咱們也能夠在event()函數中直接處理:

bool CustomWidget::event(QEvent *event) {
    if (event->type() == MyCustomEventType) {
        CustomEvent *myEvent = static_cast<CustomEvent *>(event);
        // processing...
        return true;
    }
    return QWidget::event(event);
}
相關文章
相關標籤/搜索