有很多人对Qt的事件系统不是特别清楚,以下是Qt的官方解释文档。本人对其进行的翻译,为保持原汁原味,将其原文也复制过来。
原英文地址如下:
http://doc.qt.io/qt-5/eventsandfilters.html
在Qt中,事件是一种对象,继承自抽象基类QEvent。QEvent代表了,所有来自程序内部或外部发生的事件。事件可以被QObject子类对象接收和处理,此处的子类多为widget 相关。这篇文章讲述了事件在在某一特定应用程序中分发和处理。
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子类对象,通过调用even()函数分发到QObject或其子类对象中去。
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.
event()函数本身并不处理事件,它会依据事件类型,调用相应的事件处理函数,并依据事件是否被接受或忽略作出回应。
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.
有一些事件,比如鼠标事件、键盘事件来自windows系统,而有些,比如QTimerEvent,来自其它的资源,有些 则来自应用本身。
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.
很多事件类型拥有自己专有的类,比如 窗口缩放事件,绘图事件,鼠标事件,键盘事件,关窗口事件。这些QEvent的子类添加了专用的事件函数,比如:窗口缩放事件添加了size()和oldSize()函数,从而是窗口知其尺寸的改变。
Most events types have special classes, notably QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent, and QCloseEvent. Each class subclasses QEvent and adds event-specific functions. For example, QResizeEvent adds size() and oldSize() to enable widgets to discover how their dimensions have been changed.
有些事件类支持不止一种事件类型,比如,鼠标事件,就支持单击,双击,移动和其它相关的操作。
Some classes support more than one actual event type. QMouseEvent supports mouse button presses, double-clicks, moves, and other related operations.
每一个事件,拥有一个相应的类型,定义在QEvent::Type中,这可以方便的作为运行时信息以便快速的确定给定的事件构建于QEvent的哪一种子类型。
Each event has an associated type, defined in QEvent::Type, and this can be used as a convenient source of run-time type information to quickly determine which subclass a given event object was constructed from.
程序的需要多种而复杂的交互方式,所以Qtr事件分发机制也相当灵活。关于 QCoreApplication::notify() 的文单简明的叙述了整件过程。Qt季刊,从另一个视角重述了上个过程。在此,我们解释95%的应用情形。
Since programs need to react in varied and complex ways, Qt’s event delivery mechanisms are flexible. The documentation for QCoreApplication::notify() concisely tells the whole story; the Qt Quarterly article Another Look at Events rehashes it less concisely. Here we will explain enough for 95% of applications.
处理分发的事件的常规方式是调用虚函数,比如,绘图事件分发通过调用 QWidget::paintEvent(),虚函数负责相关的回应,常重过重绘窗口。如果你不在覆写的虚函数中实现需求,你需要调用基类的实现。
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.
比如:在处理checkbox控件时,把左键单击的事件交由覆写函数处理,而其它的点交同其父类的实现。
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:
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// handle left mouse button here
} else {
// pass on other buttons to base class
QCheckBox::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.
无独有偶,正巧有event函数,或许此函功能并不充足。 许多常见案例中,引入了按压Tab key。通常,窗口截获用于移动窗口焦点,但有一些控件,需要Tab key 为其所用。
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.
这些对象,可以通过覆写QObject::event(),在常规事件前后处理自己的事件,或是完全自实现。一个非寻常的窗口控件,实现的了截获Tab和自定义事件,代码如下:
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:
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 here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
}
注意: QWidget::event()依然被调用,用于处理没有被处理的其它情形,通过返回值来表明事件是否被处理,返回true阻止了事件继续被分发。
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.
有时一个对象,需要观测或是拦截被分发到其它对象上的事件。比如,对话框通常会为其它的控件过滤按键,比如,修改Return-key的处理。
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.
通过函数QObject::installEventFilter()建立一个事件过滤器,可以实现上述功能。通过指定过滤对象的QObject::eventFilter()函数去接收目标对象的事件,事件过滤器先于目标对象处理事件,可依据要求对事件进行检测或丢弃。一个事件过滤器可以通过QObject::removeEventFilter()函数移除。
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.
当事件过滤器对象的覆写函数eventFilter()被调用,事件可以被接受或丢弃,允许或是禁止更进一步的事件处理。如果所有的事件过滤器允许处理一个事件(其中一个返回false),事件将会被发送到目标对象。如果他中有任何一个停止处理(返回true),目标对象和后序的事件过滤器不会再看到事件。
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.
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 handling
return true;
} else
return false;
}
return false;
}
上述代码用另外一种方式,截获了发向指定目标控件的Tab 按压事件。在这种情况下,过滤器处理了相关的事件,并且近回true阻止进一步的处理。其它所有的事件忽略,并且过滤器近返回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.
可以通过按装过滤器到QApplication,QCoreApplication的对象,过滤程序的所有事件。这样的全局过滤器在其它对象过滤器之前被调用。这样强大的功能,却以牺牲事件的分发效率为代价,应用其它的技术来取代它。
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.
很多程序希望自己创建并发送事件。你可以通过QCoreApplication::sendEvent() and QCoreApplication::postEvent()两个函数,像Qt事件循环构一样建事件对象并发送事件。
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()函数立即处理事件,当它返回。事件过滤器,或对象本身己经处理完事件,对于许多事件类来说,有一个函数叫isAccepted(),通知你事件是否被上一个处理函数接受或是拒绝。
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()函数把事件投到队列中等待处理。在下一轮的Qt事件主循环中,事件被带有某些优化规则的分发。比如,如果有很多resize事件,他们会被压缩为一个。这同样应用于绘图事件,QWidget::update() 调用 postEvent(),可以消除不稳定,减少重绘以加快速度。
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.
要创建自定义事件类型,你需要定义一个事件号码,该号码要大过QEvent::User,为了传递自定义事件信息,你需为创建一个QEvent 的子类。
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.