一、Qt中事件處理的方式 1、事件處理模式一
首先是事件來源產生事件,最後是事件處理器對這些事件進行處理。然而也許大家會問,
Qt中有這麼多類的事件,我們怎麼樣比較簡便的處理每個事件呢?設想,如果是每個事件都對應同一個事件處理器,在該事件處理器中對不同的事件進行分類處理,這樣的弊端有兩點:第一,導致該事件處理器過於臃腫複雜;第二,這樣不便於擴充,當系統新增加事件類型或者是我們需要使用到自訂事件時,就不得不修改Qt的源碼來達到目的。所以Qt設計者的做法是針對不同類型的事件提供不同的事件處理器與之對應。這裡又有一個問題了,Qt中是怎麼讓不同類型事件與之對應的事件處理器相關聯的呢?我們不難猜想在事件和事件處理器中間必定有一個橋樑。這個橋樑就是QObject::event()函數,該函數是虛函數,QObject的子類例如QWidget都實現了該函數。該函數的主要功能是進行事件的分發,也就是將不同類型的事件與之相對應的事件處理器相關聯,該函數並不對事件進行處理,真正的事件處理是在事件處理器中進行的。
例如:當QWidget產生QPaintEvent事件後,QWidget的event函數會將該事件分發給QWidget::paintEvent()事件處理器,這樣該事件就得到處理了。
以上內容用一個圖形表示就是:
2、事件處理模式二
很多時候,我們只對某些特定的事件比較關心,例如:按一下滑鼠或者鍵盤按下等事件。其它的事件我們並不關心它是否發生,也無需對它們進行處理,這個時候最直接的想法就是將這些事件過濾掉,這樣做既可以免去對它們進行處理,也可以避免它們對程式其它部分產生影響。因此,處理模式二中我們引入了事件過濾器這個概念。
如果對象安裝了事件過濾器,則事件在到達目標對象之前先被事件過濾器截獲,進行一些處理之後再交給目標對象,該模式總結為一個圖如下:
注意:這裡需要區別對待,如果你是使用installEventFilter()函數給目標對象註冊事件過濾器,那麼該事件過濾器只對該目標對象有效,只有該對象的事件需要先傳遞給eventFilter()函數進行過濾,然後調用相應的事件處理器進行處理,非目標對象則不受影響。如果你是給程式中唯一的QApplication對象註冊事件過濾器,那麼該過濾器對程式中的每一個對象都有效,任何對象的事件都是先傳給eventFilter()函數,然後再使用事件處理器進行處理。
3、事件處理模式三
Qt調用QApplicaton來發送一個事件。所以我們可以重新實現QApplication中的notify()函數來擷取事件並進行處理,而且使用該函數擷取事件的時間要早於事件過濾器擷取事件的時間。但是使用事件過濾器較為簡便,因為我們可以有多個事件過濾器,但是只能有一個notify()函數。
用一個圖來總結該模式就是:
二、Qt中事件處理的方法
從以上三個處理模式我們可以看出,這是一個不斷完善的過程,從3個模式的討論中我們不難找到可以進行事件處理的地方,而這幾個地方就是我們在編寫程式的時候可以控制事件處理的地方。
1、event()函數
首先是控制事件分發的event()函數,我們可以改寫該函數,改變事件的分發方式,這樣就可以改變事件處理的結果。
2、notify()函數
實現該函數可以截獲事件,並對事件加以處理,但是該方法很少用,這裡不做介紹。
3、事件過濾器
實現自己的事件過濾器就可以改變事件處理的方法和結果,這個方法比較常用。
4、事件處理器
事件處理的最後一步,也是最重要的一步就是事件處理器,因為它才是真正進行事件處理的地方,我們可以改寫以有的事件處理器,以此改變已有事件的處理方法和處理結果,我們也可以定義自己的事件類型和相應的事件處理器。
注意:以上四種方法中最常用的是後兩者:事件過濾器和事件處理器。
補充內容:
1、事件的傳遞
包括滑鼠和鍵盤事件在內的很多事件都可以被傳遞。如果事件在到達目標對象之前沒有被截獲處理,或者已經傳遞給了它的目標對象但目標對象並沒有進行處理,那麼此時,目標對象的父物件將變成新的目標對象,整個事件處理的過程將重複進行,直到該事件被處理或者到達最頂層對象為止。
2、event執行個體解析
下面的代碼是QWidget::event()函數的代碼,從代碼中可以看出event()函數確實只進行事件的分發而不負責事件的處理。由於函數代碼過多,且都是一類型的用switch語句進行處理的,這裡只貼出一部分代碼:
bool QWidget::event(QEvent *event){ Q_D(QWidget); // ignore mouse events when disabled if (!isEnabled()) { switch(event->type()) { case QEvent::TabletPress: case QEvent::TabletRelease: case QEvent::TabletMove: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::ContextMenu:#ifndef QT_NO_WHEELEVENT case QEvent::Wheel:#endif return false; default: break; } } switch (event->type()) { case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); break; case QEvent::MouseButtonPress: // Don't reset input context here. Whether reset or not is // a responsibility of input method. reset() will be // called by mouseHandler() of input method if necessary // via mousePressEvent() of text widgets.#if 0 resetInputContext();#endif mousePressEvent((QMouseEvent*)event); break; case QEvent::MouseButtonRelease: mouseReleaseEvent((QMouseEvent*)event); break; case QEvent::MouseButtonDblClick: mouseDoubleClickEvent((QMouseEvent*)event); break;#ifndef QT_NO_WHEELEVENT case QEvent::Wheel: wheelEvent((QWheelEvent*)event); break;#endif#ifndef QT_NO_TABLETEVENT case QEvent::TabletMove: case QEvent::TabletPress: case QEvent::TabletRelease: tabletEvent((QTabletEvent*)event); break;#endif