本页内容

事件系统

在 Qt 中,事件是继承自抽象类 `QEvent ` 的对象,它们代表了应用程序内部发生的事情,或是应用程序需要了解的由外部活动引发的事件。 任何QObject 子类的实例都可以接收和处理事件,但事件与小部件的关系尤为密切。本文档描述了事件在典型应用程序中的传递和处理方式。

事件的传递方式

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

该函数本身并不处理事件;它会根据传递事件的类型,调用该特定事件类型的事件处理程序,并根据事件是被接受还是被忽略,发送相应的响应。

有些事件,例如QMouseEvent 和QKeyEvent ,来自窗口系统;有些,例如QTimerEvent ,来自其他来源;还有些则来自应用程序本身。

事件类型

大多数事件类型都有专门的类,尤其是QResizeEvent 、QPaintEvent 、QMouseEvent 、QKeyEvent 和QCloseEvent 。每个类都是QEvent 的子类,并添加了特定于该事件的函数。例如,QResizeEvent 添加了size() 和oldSize(),以便小部件能够检测其尺寸发生了哪些变化。

某些类支持多种实际事件类型。QMouseEvent 支持鼠标按钮按下、双击、移动以及其他相关操作。

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

由于程序需要以多样且复杂的方式做出响应,Qt 的事件传递机制非常灵活。QCoreApplication::notify() 的文档简明扼要地阐述了全部内容;而《Qt Quarterly》杂志上的文章《再探事件》则对此进行了较为冗长的阐述。本文将提供足以满足 95% 应用程序需求的解释。

事件处理程序

事件传递的常规方式是调用虚拟函数。例如,QPaintEvent 事件通过调用QWidget::paintEvent() 进行传递。该虚拟函数负责做出适当的响应,通常是重绘小部件。如果你在虚拟函数的实现中未执行所有必要的工作,可能需要调用基类的实现。

例如,以下代码处理自定义复选框控件上的鼠标左键点击,同时将所有其他按钮点击事件传递给基类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` 键又具有应用程序特定自定义事件的非常特殊的控件,可能包含以下 `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 以允许这些事件通过目标控件上已安装的任何其他事件过滤器继续发送至目标控件。

此外,还可以通过在QApplication 或QCoreApplication 对象上安装事件过滤器,来过滤整个应用程序的所有事件。此类全局事件过滤器会在特定于对象的过滤器之前被调用。虽然这种方法非常强大,但也会减慢整个应用程序中每个事件的传递速度;因此,通常应优先采用前文讨论的其他技术。

发送事件

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

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

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

postEvent() 还用于对象初始化过程中,因为发布事件通常会在对象初始化完成后很快被分发。 在实现小部件时,必须意识到事件可能会在其生命周期的非常早期就被传递,因此,在构造函数中,请务必尽早初始化成员变量,以免在可能收到事件之前出现任何问题。

要创建自定义类型的事件,需要定义一个事件编号,该编号必须大于QEvent::User ,并且可能需要继承QEvent 类,以便传递有关自定义事件的具体信息。更多详细信息请参阅QEvent 文档。

© 2026 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.