QT-事件详解2
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() 函數進行派發
事件在傳遞到對象之前(調用obj->event()函數之前),要先能通過 Applicaton 和 obj 安裝的過濾器,那么過濾器是怎么安裝的:
- 首先QObject中有一個類型為QObjectList的成員變量,名字為eventFilters
- 當某個QObject安裝了事件過濾器之后, 它會將filterObj的指針保存在eventFilters中
- 在事件到達QObject::event()函數之前,會先查看該對象的eventFilters列表, 如果非空, 就先調用列表中對象的eventFilter()函數.
- 事件過濾器函數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
- QDragMoveEvent
- 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
總結
- 上一篇: springCloud - 第9篇 -
- 下一篇: 《小狗钱钱》:理财首先应该有一种强烈的意