Qt的事件機制 事件過濾器: 可以讓一個對象偵聽攔截另外一個對象的事件。 實現原理: 在所有Qt對象的基類:QObject中有一個 類型為:QObjectList 名字為:eventFilters 的成員變數,當A給B安裝了事件過濾器後 B的eventList中就會保存A對象的指針, 在B處理事件之 ...
Qt的事件機制
事件過濾器:
可以讓一個對象偵聽攔截另外一個對象的事件。
實現原理:
在所有Qt對象的基類:QObject中有一個
類型為:QObjectList
名字為:eventFilters
的成員變數,當A給B安裝了事件過濾器後
B的eventList中就會保存A對象的指針,
在B處理事件之前,會先檢查eventList是
否為空,如果不為空,就會調用事件過濾器函數eventFilter(),如果eventFilter()返回true,表示事件已經被處理完畢,Qt將直接返回進行下一事件處理,如果為false,事件將接著被送往剩下的事件過濾器或者是目標對象進行處理。
按照事件的起源將事件分為三類:
Spontaneous事件-----自發事件
由視窗系統產生,被放到系統隊列中,通過事件迴圈逐個處理。
Posted事件
由Qt或是應用程式產生,被Qt組成隊列,再通過事件迴圈處理。
sent事件
由Qt或是應用程式產生,但他們被直接發送到目標對象。
Qt事件迴圈的過程
在調用QApplication::exec()時,程式進入了Qt的事件迴圈,
事件迴圈的大致示意
1 while (!exit_was_called) 2 { 3 while(!posted_event_queue_is_empty) 4 { 5 process_next_posted_event(); 6 } 7 while(!spontaneous_event_queue_is_empty) 8 { 9 process_next_spontaneous_event(); 10 } 11 while(!posted_event_queue_is_empty) 12 { 13 process_next_posted_event(); 14 } 15 }
可以看出,程式首先處理所有的posted事件,知道隊列空,再處理Spontaneous事件,再處理因Spontaneous事件產生的posted事件。
send事件不在事件迴圈內,因為他們不進入事件隊列而是直接發送給目標對象
實例paint()事件:
當一個widget第一次可見,或者是被遮擋後可見,視窗產生一個(Spontaneous)paint事件,要求程式重繪widget,事件迴圈最宗從事件隊列中揀選這個事件並把他們分發到那個徐奧重畫的widget對象。
並不是所有paint事件都是視窗系統產生,當你調用update()去強行重畫widget,這個widget會post一個paint事件給自己,這個paint事件被放入隊列,最終被事件迴圈分發。
而當你等不及事件迴圈時,本來應該調用paintEvent()強制立即重畫,但是實際上不可行因為paintEvent()是受保護的函數,因此Qt提供了一個機制直接sending事件給對象,repaint()就使用了這個機制來進行立即重畫。(這是update()更新和repaint()更新的區別)。
posting相對於sending的一個優勢就是給了Qt一個壓縮事件的機會,假如在一個widget上連續調用update()十次,因update()而產生的這十個事件,會被自動地合併成一個單獨的事件,
人工合成的事件
Qt應用程式可以產生他們自己的事件,或是預定義類型,或是自定義類型。這可以通過創建QEvent類或它的子類的實例,並且調用QApplication::postEvent()或QApplication::sendEvent()來實現。
這兩個函數需要一個QObject* 與一個QEvent作為參數,如果使用postEvent(),要使用new操作符來創建事件對象,如
QApplication::postEvent(mainWin,new QKeyEvent(QEvent::KeyPress,Key_X,'X',0));
如果使用sendEvent(),應該使用棧來創建事件
QKeyEvent event(QEvent::KeyPress,Key_X,'X',0);
QApplication::sendEvent(mainWin,&event);
定製事件類型
Qt允許創建自己的事件類型,可以作為對象間的一種通訊機制。是因為這個可以是非同步的,函數調用或槽調用總是同步的。另一個好處是可以被過濾
post一個定製事件:
const QEvent::Type MyEvent = (QEvent::Type)1234;
...
QApplication::postEvent(obj,new QCustomEvent(MyEvent));
事件必須是QCustomEvent類型(或子類)的。構造函數的參數是事件的類型,
為了處理定製事件類型,要重新實現customEvent()函數:
void MyLineEdit::customEvent(QCustomEvent *event)
{
if(event->type() == MyEvent){
myEvent();
}else{
QLineEdit::customEvent(event);
}
}
可以子類化QCustomEvent,加上別的成員,但是需要在customEvent()中轉換QCustomEvent到特有的類型
事件的處理與過濾
Qt的事件可以在五個不同的層次上被處理
1.重新實現一個特定的事件handler
QObject與QWidget提供了許多特定的事件handlers,分別對應於不同的事件類型。(如paintEvent()對應paint()事件)
2.重新實現QObject::event()
event()函數是所有對象事件的入口,QObject和QWidget中預設的實現是簡單地把事件推入特定的事件handlers。
3.在QObject上安裝事件過濾器
事件過濾器是一個對象,它在事件到達指定目標之前接收這些事件。
4.在aApp上安裝一個事件過濾器
它會監視程式中發送到所有對象的所有事件
5.重新實現QApplication::notify()
Qt的事件迴圈與sendEvent()調用這個函數來分發事件,
特定對象的事件處理
一些事件類型可以被傳遞。這意味著加入目標對象不處理一個事件,Qt會試著尋找另外的事件接收者。用新的目標來調用QApplication::notify()。舉例來講,key事件是傳遞的,假如擁有焦點的Widget不處理特定鍵,Qt會分發相同的事件給父widget,然後是父親的父親,直到最頂層widget。
什麼時候該接收事件,什麼時候該忽略
通過accept()函數和ignore()函數。可被傳遞的事件有一個accept()函數和一個ignore()函數,可以用他們來告訴Qt,你"接收"或是"忽略"這個事件。假如事件handler調用accept(),這個事件將不會再被傳遞。假如事件handler調用ignore(),Qt會試著查找另外的事件接收者。
來源:http://www.cnblogs.com/li-hao/archive/2011/11/13/2247662.html