Qt事件体系概述(The Event System)

本文译自https://doc.qt.io/qt-5/eventsandfilters.html,是意译。

目录

事件的发送(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的对象。QEvent类代表应用程序内部发生的事情,或者代表程序需要知悉的一些外部活动的结果。事件可以被任何的QObject的派生类接收、处理,但是尤其事件都跟QWidget有关。本文介绍事件被程序发送、处理的典型流程。

事件的发送(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或者其派生类。

event函数并不处理事件,而是根据事件类型调用相应的event handler,然后根据事件被接受(accept)还是忽略(ignore)来发送一个响应。

有的事件来自窗体系统,如鼠标事件、键盘事件。还有的如定时器事件来自其他源头。还有的来自程序自身。

事件类型(Event Types)

大多数事件都有独特的类与之对应,如鼠标事件,重绘事件等。每一个类都继承自QEvent,但是在其基础上添加了函数。比如说,QResizeEvent添加了size() 和 oldSize()函数来描述窗体尺寸的变化。

有的事件可以描述多种不同的事件。如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:

正常情况下发送事件的办法是调用虚函数。比如,QPaintEvent通过paintEvent()发送。这个虚函数通常用来重绘界面。假如你没有重写虚函数,你或许应调用基类的虚函数。

下面的例子中,MyCheckBox继承了QCheckBox,重写了鼠标左键点击checkbox的的行为,而其他类型的点击仍通过基类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截获这个事件,然后执行焦点转移。很少有窗体需要响应tab事件本身。

响应此类事件可以通过重写QObject::event()完成。重写可以发生在正常的事件响应之前或者之后,或者完全覆盖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 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()。QWidget::event()的返回值说明了事件是否被处理了。返回值为真,说明已经被处理,这样事件就不会发送到其他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.

 有的对象需要检查或者截获发向其他对象的事件。比如说,对话框都想过滤一些widget的按键事件;又比如修改回车键响应。

QObject::installEventFilter()函数令被点名的过滤器对象在目标对象的eventFilter()函数中接收事件。过滤器在目标对象处理事件之前就已经处理了事件。过滤器通过removeEventFilter来移除。

重写eventFilter时,事件既可以被接收,也可以被拒绝;既可以允许其走向下一个对象,也可以终止其继续传递。如果所有的过滤器都允许事件继续传递(通过设置返回值为假),事件就被送到了目标对象处。假如中途某个过滤器返回了真,则后面的过滤器和目标对象都不会看到这个事件。

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.

上面的代码展示了另一种截获tab按键事件的办法。过滤器处理tab事件,并返回真。真意味着此事件被过滤器处理后不再向后传递。其他的事件返回假,所以将继续向目标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 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.

许多应用需要创建并发送自己的事件。你可以像qt自己的事件循环一样来创建合适的事件对象,并利用QCoreApplication::sendEvent, QCoreApplication::postEvent来发送它们。

sendEvent()函数返回时,事件已经被处理完成了。很多事件类都有isAccepted()方法,告知事件最终被接受还是拒绝。

postEvent()函数则将事件放入队列,留待稍后分发。当qt的事件循环开始新一轮处理时,将分发所有的事件。分发过程可能会被优化。比如说,假如队列里有多个resize事件,则这些事件将压缩为一个再分发。QWidget::update()也采用类似方式:这样可以避免闪烁,并增加处理速度。

postEvent也在对象初始化过程中被调用。在实例化一个窗体时,要注意窗体创建不久,就会有事件发送出去。所以要确保窗体的成员今早初始化,以免没有初始化的成员卷入事件处理中。

自定义事件类型时,你需要给事件一个编号。编号必须大于QEvent::User。而且你或许要继承QEvent,在派生类里添加一些信息。具体可参阅QEvent文档。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值