Qt文档之事件体系 (The Event System)

本文译自 https://doc.qt.io/archives/qt-4.8/eventsandfilters.html

Qt的文档做的很好,功能都写的都很清楚,我们平时遇到什么问题,都可以去查看相关文档。

以下是Qt文档中对于其事件体系的描述。

目录

How Events are Delivered(事件如何传递)

Event Types(事件类型)

Event Handlers(事件处理器)

Event Filters(事件过滤器)

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类派生的对象,它们表示发生在应用程序内部的事情,或应用程序需要知道的外部活动的结果。 事件可以由QObject子类的任何实例接收和处理,但它们与widget尤其相关。 本文档介绍了在典型应用程序中如何传递和处理事件。

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.

当事件发生时,Qt通过构造一个适当的 QEvent子类的实例,创建一个事件对象来表示它,并通过调用其 event() 函数,将其传递给 QObject(或其子类)的一个特定实例。

此函数不处理事件本身,而是根据所传递事件的类型,调用对应的事件处理程序,并根据该事件被接受还是忽略,发送响应。

某些事件来自窗口系统,例如 QMouseEvent(鼠标事件)和 QKeyEvent(键盘事件); 有些来自其他来源,例如 QTimerEvent(定时器事件); 还有些来自应用程序本身。

Event Types(事件类型)

Most events types have special classes, notably QResizeEventQPaintEventQMouseEventQKeyEvent, 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.

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.

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.

大多数事件类型都有特殊的类,尤其是QResizeEvent,QPaintEvent,QMouseEvent,QKeyEvent和QCloseEvent。 每个类都将QEvent子类化,并添加特定于事件的功能。 例如,QResizeEvent添加 size() 和 oldSize(),以使 widget 知道其自身尺寸的更改。

一些类实际支持多种事件类型。QMouseEvent 支持鼠标按键按下,双击,移动和其他相关操作。

每个事件都有一个关联的类型,在QEvent ::Type中定义,这可以在运行时快速确定事件对象的类型。

Qt 的事件传递机制非常灵活, 程序可以用各种复杂的方式做出反应。这在 QCoreApplication::notify() 的文档中有简明扼要的描述, 而另一篇文章《Another Look at Events》则不够简洁。 在这里,我们将为应用程序的事件传递机制做出足够的解释(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:

传递事件的一般方法是调用虚函数。 例如,通过调用 QWidget::paintEvent() 传递 QPaintEvent。 这个虚函数通常用来重新绘制 widget。 如果你没有在该虚函数中执行完所有必要的工作,则可能需要调用基类的虚函数。

例如下面的代码,MyCheckBox 继承自 QCheckBox, 重写了 mousePressEvent,处理了鼠标左键单击事件,而对于其他鼠标事件,仍通过基类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);
    }
}

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:

如果你想替换基类的函数,则必须自己实现所有功能。 但是如果你只想扩展基类的功能,那么你可以只实现你想要的,并调用基类的虚函数,以获取任何情况下的默认行为。

有些事件是没有这种对应的处理函数的,最常见的例子是Tab键按下事件,通常 QWidget 会拦截以移动焦点,但有一些widget需要自己处理这个事件。

这些对象可以重写通用事件处理器 QObject::event(),可以在常规处理之前或之后进行他们的事件处理,或者可以完全替换该函数。 下面的代码实现了一个不一样的 widget,既可以处理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 here
            return true;
        }
    } else if (event->type() == MyCustomEventType) {
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return 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() 仍会被调用 ,返回值表示事件是否已被处理,若返回值为true,会阻止事件被发送给其他对象。

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.

有时,一个对象需要查看并可能拦截传递给另一个对象的事件。例如,对话框通常要过滤某些widget的按键,比如修改回车键的处理。

QObject::installEventFilter() 函数通过设置事件过滤器来启用此功能,从而使指定的过滤器对象在其 QObject::eventFilter() 函数中接收目标对象的事件。事件过滤器可以在目标对象之前处理事件,从而使它可以根据需要检查和丢弃事件。可以使用QObject::removeEventFilter() 函数移除已有的事件过滤器。

调用过滤器对象的 eventFilter() 时,它可以接受或拒绝事件,并允许或拒绝事件的进一步处理。如果所有事件过滤器都允许进一步处理事件(每个返回false),则事件将发送给目标对象本身。如果其中一个停止了处理(返回true),则目标和任何以后的事件过滤器将根本看不到该事件。

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.

上面的代码显示了另一种拦截发送给特定目标widget的Tab键按下事件的方法。 在这种情况下,过滤器将处理相关事件,并返回true以阻止对其进行进一步处理。 所有其他事件不做处理,返回false,向目标widget传递。

通过在 QApplication 或 QCoreApplication 对象上安装事件过滤器,还可以过滤整个应用程序的所有事件。 此类全局事件过滤器在特定于对象的过滤器之前被调用。 这是非常强大的功能,但是它也会减慢整个应用程序中每个事件的传递速度。 通常应使用所讨论的其他技术来代替。

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 realise 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.

许多应用程序都希望创建并发送自己的事件。您可以通过构造合适的事件对象,并使用QCoreApplication::sendEvent() 和 QCoreApplication::postEvent() 发送事件,以与Qt自己的事件循环完全相同的方式发送事件。

sendEvent() 立即处理事件。返回时,事件过滤器和/或对象本身已经处理了该事件。对于许多事件类,有一个称为 isAccepted() 的函数,表示该事件是被最后一个调用的处理程序接受还是拒绝。

postEvent() 将事件发布到队列中以备后用。下次Qt的主事件循环运行时,它将分派所有已发布的事件,并进行一些优化。例如,如果有多个 reisize 事件,它们将被压缩为一个。这同样适用于 paint 事件:QWidget::update() 调用 postEvent(),通过避免多次重新绘画来消除闪烁,并提高速度。

postEvent() 也用于对象初始化期间,因为通常会在对象初始化完成后很快分派已发布的事件。在实现 widget 时,重要的是要意识到事件可以在其生命周期的很早就交付,因此,在其构造函数中,请务必在有可能接收事件之前尽早初始化成员变量。

要创建自定义类型的事件,您需要定义一个事件编号,该事件编号必须大于QEvent::User,并且可能需要子类 QEvent 才能传递有关自定义事件的特定信息。有关更多详细信息,请参见QEvent文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值