在 Qt 中事件是对象,派生自抽象 QEvent 类,表示应用程序中发生的事情,或作为应用程序需要知道的有关外部活动的结果。事件可以被接收并处理通过任何实例化的 QObject 子类,但它们尤其与 Widget 相关。本文档描述在典型应用程序中,如何交付和处理事件。
当事件出现时,Qt 创建事件对象来表示它,通过构造实例化的适当 QEvent 子类,并把其交付给特定实例化的 QObject (或它的某个子类) 通过调用其 event() 函数。
此函数不处理事件本身;根据交付的事件类型,它调用用于特定事件类型的事件处理程序,并根据事件是被接受还是忽略发送响应。
某些事件,譬如 QMouseEvent and QKeyEvent ,来自窗口系统;某些,譬如 QTimerEvent ,来自其它源;某些来自应用程序本身。
大多数事件类型都拥有特殊类,尤其是 QResizeEvent , QPaintEvent , QMouseEvent , QKeyEvent ,和 QCloseEvent 。各类子类化 QEvent 并添加特定事件函数。例如, QResizeEvent 添加 size() and oldSize() 以使 Widget 能够探索它们的维度是如何改变的。
有些类支持,多种实际事件类型。 QMouseEvent 支持鼠标按键按下、双击、移动、及其它相关操作。
各事件都拥有关联类型,定义在 QEvent::Type ,且这可以用作运行时类型信息的方便源,以快速确定给定事件对象是从哪个子类构造的。
由于程序需要以各种且复杂的方式作出反应,Qt 的事件交付机制是很灵活的。文档编制对于 QCoreApplication::notify () 将简洁叙述整个故事; Qt 季刊 文章 从另一角度看事件 不那么简洁地重讲了它。在这里,我们将对 95% 的应用程序进行足够阐述。
用于交付事件的正常方式,是通过调用虚函数。例如, QPaintEvent 的交付是通过调用 QWidget::paintEvent ()。此虚函数负责适当应对,通常是通过重新描绘 Widget。若在虚函数实现中不履行所有必要工作,可能需要调用基类实现。
例如,以下代码处理自定义复选框 Widget 中的鼠标左键点击,同时把所有其它按钮点击传递给基 QCheckBox 类:
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); } }
若想要替换基类函数,自己必须实现一切。不管怎样,若只想扩展基类功能,则实现想要的,并调用基类以获得不想处理的任何情况的默认行为。
有时,没有这种特定事件函数,或特定事件函数不够。最常见范例涉及 Tab 键按下。通常, QWidget 拦截这些以移动键盘聚焦,但一些 Widget 本身需要 Tab 键。
这些对象可以重实现 QObject::event ()、一般事件处理程序、及在之前履行它们的事件处理 (或通常在之后处理,或它们可以完全替换函数)。非常不寻常的 Widget,既解释 Tab 又拥有特定应用程序的自定义事件,可能包含以下 event() 函数:
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
值阻止把事件,发送给其它对象。
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 事件过滤器 , 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 () 函数。
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; }
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.
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 () 和 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 () 调用 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 文档编制进一步了解细节。