事件系统

在 Active Qt 中,事件是从抽象的QEvent 类派生出来的对象,代表应用程序内部发生的事情,或者是应用程序需要了解的外部活动的结果。QObject 子类的任何实例都可以接收和处理事件,但它们与 widget 尤为相关。本文档将介绍在典型应用程序中如何传递和处理事件。

如何传递事件

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

该函数本身并不处理事件,而是根据交付的事件类型,调用该特定类型事件的事件处理程序,并根据事件是否被接受或忽略发送响应。

有些事件(如QMouseEventQKeyEvent )来自窗口系统;有些事件(如QTimerEvent )来自其他来源;还有些事件来自应用程序本身。

事件类型

大多数事件类型都有特殊的类,特别是QResizeEvent,QPaintEvent,QMouseEvent,QKeyEventQCloseEvent 。每个类都是QEvent 的子类,并添加了特定于事件的功能。例如,QResizeEvent 增加了size() 和oldSize() 功能,使部件能够发现其尺寸是如何改变的。

QMouseEvent 支持鼠标按键、双击、移动和其他相关操作。

每个事件都有一个相关的类型,定义在QEvent::Type 中,它可以作为运行时类型信息的方便来源,用于快速确定给定事件对象是由哪个子类构造的。

由于程序需要以各种复杂的方式做出反应,因此 Qt 的事件交付机制非常灵活。QCoreApplication::notify() 的文档简明扼要地讲述了整个故事;Qt XML 季刊文章《事件的另一种看法》(Another Look at Events)则不那么简明扼要地重述了整个故事。在此,我们将为 95% 的应用程序解释足够多的内容。

事件处理程序

传递事件的通常方式是调用一个虚拟函数。例如,通过调用QWidget::paintEvent() 发送QPaintEvent 。该虚拟函数负责做出适当的反应,通常是重新绘制部件。如果在实现虚拟函数时没有完成所有必要的工作,则可能需要调用基类的实现。

例如,以下代码在处理自定义复选框部件上的鼠标左键点击时,会将所有其他按钮点击传递给基类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 会拦截这些按键以移动键盘焦点,但也有一些部件需要 Tab 键。

这些对象可以重新实现QObject::event() 这个通用事件处理程序,并在通常处理程序之前或之后进行事件处理,或者完全替换该函数。一个既能解释 Tab 又有特定应用程序自定义事件的非常特殊的 widget 可能包含以下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 ,则事件不会被发送到其他对象。

事件过滤器

有时,一个对象需要查看并截获传递给另一个对象的事件。例如,对话框通常需要过滤某些部件的按键操作;例如,修改返回键处理。

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;
}

上面的代码展示了截获发送到特定目标部件的 Tab 键按下事件的另一种方法。在这种情况下,过滤器会处理相关事件并返回true ,以阻止这些事件被进一步处理。所有其他事件都会被忽略,过滤器会返回false ,以便通过安装在目标部件上的任何其他事件过滤器将这些事件发送到目标部件。

通过在QApplicationQCoreApplication 对象上安装事件过滤器,还可以过滤整个应用程序的所有事件。这种全局事件过滤器会在特定对象过滤器之前调用。这种方法功能强大,但也会减慢整个应用程序中每个事件的发送速度;通常应改用讨论过的其他技术。

发送事件

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

sendEvent() 会立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了事件。对于许多事件类,都有一个名为isAccepted() 的函数,它能告诉你事件是否被最后调用的处理程序接受或拒绝。

postEvent() 会将事件发布到队列中,以便稍后分派。下一次运行 Qt 的主事件循环时,它会分派所有已发布的事件,但会进行一些优化。例如,如果有多个调整大小事件,它们会被压缩为一个。绘制事件也是如此:QWidget::update() 调用postEvent(),这样可以避免多次重绘,从而消除闪烁并提高速度。

postEvent() 也可在对象初始化过程中使用,因为发布的事件通常会在对象初始化完成后很快派发。在实现部件时,重要的是要意识到事件可能会在其生命周期的很早阶段发布,因此在构造函数中,一定要在部件可能收到事件之前尽早初始化成员变量。

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

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.