Qt 事件系統 Qt是事件驅動的, 程序每個動作都是由某個事件所觸發。 Qt事件的類型很多,我們可以通過查看Qt的 manual中的Event System 和 QEvent 來獲得各個事件的詳細信息。
為了完整起見,一份Qt4.6的事件列表附在本文后面。
事件來源
Spontaneous events(自發事件)
從系統得到的消息,比如鼠標按鍵,鍵盤按鍵等。Qt事件循環的時候讀取這些事件,轉化為QEvent后依次處理
Posted events
有Qt或應用程序產生,放入消息隊列
QCoreApplication::postEvent()
Sent events
由Qt或應用程序產生,不放入隊列,直接被派發和處理
QCoreApplication::sendEvent()
比如考慮重繪事件處理函數 paintEvent(),3種事件都能使得該函數被調用:
當窗口被其他窗口覆蓋后,再次重新顯示時,系統將產生 spontaneous 事件來請求重繪
當我們調用 update() 時,產生的是 Posted 事件
當我們調用 repaint() 時,產生的是 Sent 事件
事件派發事件循環 while (!exit_was_called) {
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
while (!spontaneous_event_queue_is_empty) {
process_next_spontaneous_event();
}
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
}
先處理Qt事件隊列中的事件,直至為空
再處理系統消息隊列中的消息,直至為空
在處理系統消息的時候會產生新的Qt事件,需要對其再次進行處理
不通過事件循環
sendEvent的事件派發不通過事件循環。QApplication::sendEvent()是通過調用QApplication::notify(),直接進入了事件的派發和處理環節,是同步的。
sendEvent與postEvent的使用
兩個函數都是接受一個 QObject * 和一個 QEvent * 作為參數。
postEvent 的 event 必須分配在 heep 上。用完后會被Qt自動刪除
sendEvent 的 event 必須分配在 stack 上。
例子(發送X按鍵事件到mainWin):
QApplication::postEvent(mainWin, new QKeyEvent(QEvent::KeyPress, Key_X, 'X', 0));
QKeyEvent event(QEvent::KeyPress, Key_X, 'X', 0);
QApplication::sendEvent(mainWin, &event);
notify
所有的事件都最終通過 notify 派發到相應的對象中。
bool QCoreApplication::notify ( QObject * receiver, QEvent * event )事件過濾
看看notify()調用的內部函數notify_helper()的源碼部分:
先通過 Applicaton 安裝的過濾器
如果未被過濾,再通過 receiver 安裝的過濾器
如果仍未被過濾,才調用 receiver->event() 函數進行派發
/*!\internal
Helper function called by notify()
*/
bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
// send to all application event filters
if (sendThroughApplicationEventFilters(receiver, event))
return true;
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, event))
return true;
// deliver the event
return receiver->event(event);
}
事件在傳遞到對象之前(調用obj->event()函數之前),要先能通過 Applicaton 和 obj 安裝的過濾器,那么過濾器是怎么安裝的:
首先QObject中有一個類型為QObjectList的成員變量,名字為eventFilters
當某個QObject安裝了事件過濾器之后, 它會將filterObj的指針保存在eventFilters中
monitoredObj->installEventFilter(filterObj);
在事件到達QObject::event()函數之前,會先查看該對象的eventFilters列表, 如果非空, 就先調用列表中對象的eventFilter()函數.
bool QObject::eventFilter ( QObject * watched, QEvent * event )
事件過濾器函數eventFilter()返回值是bool型
如果返回true, 則表示該事件已經被處理完畢, Qt將直接返回, 進行下一事件的處理
如果返回false, 事件將接着被送往剩下的事件過濾器或是目標對象進行處理
對於 QCoreApplication ,由於也是QObject 派生類,安裝過濾器方式與前述相同。
事件轉發
對於某些類別的事件, 如果在整個事件的派發過程結束后還沒有被處理, 那么這個事件將會向上轉發給它的父widget, 直到最頂層窗口.
如何判斷一個事件是否被處理了呢? (有兩個層次)
QApplication::notify(), QObject::eventFilter(), QObject::event() 通過返回bool值來表示是否已處理. “真”表示已經處理, “假”表示事件需要繼續傳遞
另一種是調用QEvent::ignore() 或 QEvent::accept() 對事件進行標識,accept表示事件被處理
為清楚起見,貼一點Qt的源碼(來自 QApplication::notify()):
case QEvent::ToolTip:
case QEvent::WhatsThis:
case QEvent::QueryWhatsThis:
{
QWidget* w = static_cast<QWidget *>(receiver);
QHelpEvent *help = static_cast<QHelpEvent*>(e);
QPoint relpos = help->pos();
bool eventAccepted = help->isAccepted();
while (w) {
QHelpEvent he(help->type(), relpos, help->globalPos());
he.spont = e->spontaneous();
res = d->notify_helper(w, w == receiver ? help : &he);
e->spont = false;
eventAccepted = (w == receiver ? help : &he)->isAccepted();
if ((res && eventAccepted) || w->isWindow())
break;
relpos += w->pos();
w = w->parentWidget();
}
help->setAccepted(eventAccepted);
}
break;
這兒顯示了對 WhatsThis 事件的處理:先派發給 w,如果事件被accepted 或已經是頂級窗口,則停止;否則獲取w的父對象,繼續派發。
事件處理
重新實現一個特定的事件handler
QObject與QWidget提供了許多特定的事件handlers,分別對應於不同的事件類型。(如paintEvent()對應paint事件)
重新實現QObject::event()
event()函數是所有對象事件的入口,QObject和QWidget中缺省的實現是簡單地把事件推入特定的事件handlers。
在QObject安裝上事件過濾器
事件過濾器是一個對象,它接收別的對象的事件,在這些事件到達指定目標之間。
在aApp上安裝一個事件過濾器,它會監視程序中發送到所有對象的所有事件
重新實現QApplication:notify(),Qt的事件循環與sendEvent()調用這個函數來分發事件,通過重寫它,你可以在別人之前看到事件。
事件列表
Qt4.6的事件列表:
QAccessibleEvent
QActionEvent
QChildEvent
QCloseEvent
QCustomEvent
QDragLeaveEvent
QDropEvent
QDragMoveEvent
QDragEnterEvent
QDynamicPropertyChangeEvent
QFileOpenEvent
QFocusEvent
QGestureEvent
QGraphicsSceneEvent
QGraphicsSceneContextMenuEvent
QGraphicsSceneDragDropEvent
QGraphicsSceneHelpEvent
QGraphicsSceneHoverEvent
QGraphicsSceneMouseEvent
QGraphicsSceneMoveEvent
QGraphicsSceneResizeEvent
QGraphicsSceneWheelEvent.
QHelpEvent
QHideEvent
QHoverEvent
QIconDragEvent
QInputEvent
QContextMenuEvent
QKeyEvent
QMouseEvent
QTabletEvent
QTouchEvent
QWheelEvent
QInputMethodEvent
QMoveEvent
QPaintEvent
QResizeEvent
QShortcutEvent
QShowEvent
QStatusTipEvent
QTimerEvent
QWhatsThisClickedEvent
QWindowStateChangeEvent
原文鏈接:http://bbs.csdn.net/topics/390316264