Qt事件体系概述(The Event System)
本文譯自https://doc.qt.io/qt-5/eventsandfilters.html,是意譯。
目錄
事件的發(fā)送(How Events are delivered)
事件類型(Event Types)
事件處理器(Event Handlers)
事件過濾器(Event filters)
發(fā)送事件(Sending Events)
In Qt, events are objects, derived from the abstract?QEvent?class, that represent things that have happened either within an application or as a result of outside activity that the application needs to know about. Events can be received and handled by any instance of a?QObject?subclass, but they are especially relevant to widgets. This document describes how events are delivered and handled in a typical application.
Qt的事件都是派生自QEvent的對象。QEvent類代表應(yīng)用程序內(nèi)部發(fā)生的事情,或者代表程序需要知悉的一些外部活動的結(jié)果。事件可以被任何的QObject的派生類接收、處理,但是尤其事件都跟QWidget有關(guān)。本文介紹事件被程序發(fā)送、處理的典型流程。
事件的發(fā)送(How Events are delivered)
When an event occurs, Qt creates an event object to represent it by constructing an instance of the appropriate?QEvent?subclass, and delivers it to a particular instance of?QObject?(or one of its subclasses) by calling its?event()?function.
This function does not handle the event itself; based on the type of event delivered, it calls an event handler for that specific type of event, and sends a response based on whether the event was accepted or ignored.
Some events, such as?QMouseEvent?and?QKeyEvent, come from the window system; some, such as?QTimerEvent, come from other sources; some come from the application itself.
事件發(fā)生時,qt產(chǎn)生一個相應(yīng)的QEvent派生類實例,然后通過調(diào)用event()函數(shù)把事件發(fā)送給某個QObject或者其派生類。
event函數(shù)并不處理事件,而是根據(jù)事件類型調(diào)用相應(yīng)的event handler,然后根據(jù)事件被接受(accept)還是忽略(ignore)來發(fā)送一個響應(yīng)。
有的事件來自窗體系統(tǒng),如鼠標(biāo)事件、鍵盤事件。還有的如定時器事件來自其他源頭。還有的來自程序自身。
事件類型(Event Types)
大多數(shù)事件都有獨特的類與之對應(yīng),如鼠標(biāo)事件,重繪事件等。每一個類都繼承自QEvent,但是在其基礎(chǔ)上添加了函數(shù)。比如說,QResizeEvent添加了size() 和 oldSize()函數(shù)來描述窗體尺寸的變化。
有的事件可以描述多種不同的事件。如QMouseEvent,可以用來描述鼠標(biāo)按下、雙擊、光標(biāo)移動等。
所有事件的類型都通過QEvent::Type來描述。利用這個特性,可以在運(yùn)行時快速的確定該事件屬于哪個類型。
由于程序的行為復(fù)雜多變,qt的事件發(fā)送機(jī)制也很有彈性。發(fā)送機(jī)制的描述在QCoreApplication::notify()的文檔中有具體描述。而另一篇文章<<Another Look At Events>>則差一些。下面的關(guān)于事件發(fā)送機(jī)制的描述將覆蓋95%的情況。
事件處理器(Event Handlers)
The normal way for an event to be delivered is by calling a virtual function. For example,?QPaintEvent?is delivered by calling?QWidget::paintEvent(). This virtual function is responsible for reacting appropriately, normally by repainting the widget. If you do not perform all the necessary work in your implementation of the virtual function, you may need to call the base class's implementation.
For example, the following code handles left mouse button clicks on a custom checkbox widget while passing all other button clicks to the base?QCheckBox?class:
正常情況下發(fā)送事件的辦法是調(diào)用虛函數(shù)。比如,QPaintEvent通過paintEvent()發(fā)送。這個虛函數(shù)通常用來重繪界面。假如你沒有重寫虛函數(shù),你或許應(yīng)調(diào)用基類的虛函數(shù)。
下面的例子中,MyCheckBox繼承了QCheckBox,重寫了鼠標(biāo)左鍵點擊checkbox的的行為,而其他類型的點擊仍通過基類QCheckBox實現(xiàn):
void MyCheckBox::mousePressEvent(QMouseEvent *event) {if (event->button() == Qt::LeftButton) {// handle left mouse button here} else {// pass on other buttons to base classQCheckBox::mousePressEvent(event);} }If you want to replace the base class's function, you must implement everything yourself. However, if you only want to extend the base class's functionality, then you implement what you want and call the base class to obtain the default behavior for any cases you do not want to handle.
Occasionally, there isn't such an event-specific function, or the event-specific function isn't sufficient. The most common example involves Tab key presses. Normally,?QWidget?intercepts these to move the keyboard focus, but a few widgets need the Tab key for themselves.
These objects can reimplement?QObject::event(), the general event handler, and either do their event handling before or after the usual handling, or they can replace the function completely. A very unusual widget that both interprets Tab and has an application-specific custom event might contain the following?event()?function:
假如你想替代基類的函數(shù),就必須完全重寫。假如你想在基類的基礎(chǔ)上做功能擴(kuò)展,那么你可以寫出你的新功能,并調(diào)用基類的虛函數(shù)來保持對其他功能的默認(rèn)實現(xiàn)。
個別事件是沒有對應(yīng)的響應(yīng)函數(shù)的。最常見的例子是Tab鍵按下這個事件。通常QWidget截獲這個事件,然后執(zhí)行焦點轉(zhuǎn)移。很少有窗體需要響應(yīng)tab事件本身。
響應(yīng)此類事件可以通過重寫QObject::event()完成。重寫可以發(fā)生在正常的事件響應(yīng)之前或者之后,或者完全覆蓋QObject::event()。下面的代碼展示了一種罕見的窗體,既能截獲Tab,同時又處理自定義的事件:
bool MyWidget::event(QEvent *event) {if (event->type() == QEvent::KeyPress) {QKeyEvent *ke = static_cast<QKeyEvent *>(event);if (ke->key() == Qt::Key_Tab) {// special tab handling herereturn true;}} else if (event->type() == MyCustomEventType) {MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);// custom event handling herereturn true;}return QWidget::event(event); }Note that?QWidget::event() is still called for all of the cases not handled, and that the return value indicates whether an event was dealt with; a?true?value prevents the event from being sent on to other objects.
注意,其他沒有被處理的事件交給了QWidget::event()。QWidget::event()的返回值說明了事件是否被處理了。返回值為真,說明已經(jīng)被處理,這樣事件就不會發(fā)送到其他object了。
事件過濾器(Event filters)
Sometimes an object needs to look at, and possibly intercept, the events that are delivered to another object. For example, dialogs commonly want to filter key presses for some widgets; for example, to modify Return-key handling.
The?QObject::installEventFilter() function enables this by setting up an?event filter, causing a nominated filter object to receive the events for a target object in its?QObject::eventFilter() function. An event filter gets to process events before the target object does, allowing it to inspect and discard the events as required. An existing event filter can be removed using the?QObject::removeEventFilter() function.
When the filter object's?eventFilter()?implementation is called, it can accept or reject the event, and allow or deny further processing of the event. If all the event filters allow further processing of an event (by each returning?false), the event is sent to the target object itself. If one of them stops processing (by returning?true), the target and any later event filters do not get to see the event at all.
?有的對象需要檢查或者截獲發(fā)向其他對象的事件。比如說,對話框都想過濾一些widget的按鍵事件;又比如修改回車鍵響應(yīng)。
QObject::installEventFilter()函數(shù)令被點名的過濾器對象在目標(biāo)對象的eventFilter()函數(shù)中接收事件。過濾器在目標(biāo)對象處理事件之前就已經(jīng)處理了事件。過濾器通過removeEventFilter來移除。
重寫eventFilter時,事件既可以被接收,也可以被拒絕;既可以允許其走向下一個對象,也可以終止其繼續(xù)傳遞。如果所有的過濾器都允許事件繼續(xù)傳遞(通過設(shè)置返回值為假),事件就被送到了目標(biāo)對象處。假如中途某個過濾器返回了真,則后面的過濾器和目標(biāo)對象都不會看到這個事件。
bool FilterObject::eventFilter(QObject *object, QEvent *event) {if (object == target && event->type() == QEvent::KeyPress) {QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);if (keyEvent->key() == Qt::Key_Tab) {// Special tab handlingreturn true;} elsereturn false;}return false; }The above code shows another way to intercept Tab key press events sent to a particular target widget. In this case, the filter handles the relevant events and returns?true?to stop them from being processed any further. All other events are ignored, and the filter returns?false?to allow them to be sent on to the target widget, via any other event filters that are installed on it.
It is also possible to filter?all?events for the entire application, by installing an event filter on the?QApplication?or?QCoreApplication?object. Such global event filters are called before the object-specific filters. This is very powerful, but it also slows down event delivery of every single event in the entire application; the other techniques discussed should generally be used instead.
上面的代碼展示了另一種截獲tab按鍵事件的辦法。過濾器處理tab事件,并返回真。真意味著此事件被過濾器處理后不再向后傳遞。其他的事件返回假,所以將繼續(xù)向目標(biāo)Widget傳遞。
你也可以在QApplication或者QCoreApplication上按裝過濾器,整個應(yīng)用程序的事件都會被此過濾器處理。這種全局的過濾器將在具體對象的過濾器之前被調(diào)用。這個方法固然強(qiáng)大,但也會使應(yīng)用程序的事件處理放緩。
發(fā)送事件(Sending Events)
Many applications want to create and send their own events. You can send events in exactly the same ways as Qt's own event loop by constructing suitable event objects and sending them with?QCoreApplication::sendEvent() and?QCoreApplication::postEvent().
sendEvent()?processes the event immediately. When it returns, the event filters and/or the object itself have already processed the event. For many event classes there is a function called?isAccepted()?that tells you whether the event was accepted or rejected by the last handler that was called.
postEvent()?posts the event on a queue for later dispatch. The next time Qt's main event loop runs, it dispatches all posted events, with some optimization. For example, if there are several resize events, they are compressed into one. The same applies to paint events:?QWidget::update() calls?postEvent(), which eliminates flickering and increases speed by avoiding multiple repaints.
postEvent()?is also used during object initialization, since the posted event will typically be dispatched very soon after the initialization of the object is complete. When implementing a widget, it is important to realize that events can be delivered very early in its lifetime so, in its constructor, be sure to initialize member variables early on, before there's any chance that it might receive an event.
To create events of a custom type, you need to define an event number, which must be greater than?QEvent::User, and you may need to subclass?QEvent?in order to pass specific information about your custom event. See the?QEvent?documentation for further details.
許多應(yīng)用需要創(chuàng)建并發(fā)送自己的事件。你可以像qt自己的事件循環(huán)一樣來創(chuàng)建合適的事件對象,并利用QCoreApplication::sendEvent, QCoreApplication::postEvent來發(fā)送它們。
sendEvent()函數(shù)返回時,事件已經(jīng)被處理完成了。很多事件類都有isAccepted()方法,告知事件最終被接受還是拒絕。
postEvent()函數(shù)則將事件放入隊列,留待稍后分發(fā)。當(dāng)qt的事件循環(huán)開始新一輪處理時,將分發(fā)所有的事件。分發(fā)過程可能會被優(yōu)化。比如說,假如隊列里有多個resize事件,則這些事件將壓縮為一個再分發(fā)。QWidget::update()也采用類似方式:這樣可以避免閃爍,并增加處理速度。
postEvent也在對象初始化過程中被調(diào)用。在實例化一個窗體時,要注意窗體創(chuàng)建不久,就會有事件發(fā)送出去。所以要確保窗體的成員今早初始化,以免沒有初始化的成員卷入事件處理中。
自定義事件類型時,你需要給事件一個編號。編號必須大于QEvent::User。而且你或許要繼承QEvent,在派生類里添加一些信息。具體可參閱QEvent文檔。
總結(jié)
以上是生活随笔為你收集整理的Qt事件体系概述(The Event System)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用什么软件测试光纤稳定性,工欲善其事,谈
- 下一篇: 1.1 VSCode中使用Code ru