Qt事件系统

一、事件系统概述

Qt 的事件系统是一个事件驱动架构,它让程序能够响应用户操作(鼠标、键盘等)、系统消息(窗口调整、定时器)、以及自定义事件。所有事件都被封装为 QEvent 对象,通过事件循环分发给对应的 QObject处理。

二、核心概念

类 / 概念 作用
QEvent 所有事件的基类,封装事件类型和状态信息
QObject::event() 事件的统一入口函数,根据事件类型调用具体事件处理函数
installEventFilter() / eventFilter() 实现事件过滤器机制,拦截监听目标对象的事件

三、事件与信号的区别

对比维度 事件(Event) 信号(Signal)
定义 QEvent 类的实例,表示底层操作或系统消息 QObject 派生类中声明的特殊成员函数
触发方式 由系统或 Qt 事件循环触发(常用),也可通过 postEvent()/sendEvent() 手动发送 由开发者显式调用 emit,或由 Qt 内部自动触发
处理机制 重写 event() 或特定事件处理函数(如 mousePressEvent() 通过槽函数连接,信号触发时槽函数自动调用
传播方式 可沿对象父子层级向上传播(子->父) 直接调用连接的槽函数,无层级传播
典型用途 处理底层交互(输入、绘图、定时器)或系统通知 实现对象间的松耦合通信

四、事件传递流程

复制代码
QCoreApplication::notify() 派发事件
        ↓
【第1层】目标对象的 eventFilter()(返回值为bool,返回 true则事件被拦截)
        ↓ (若未拦截)
【第2层】目标对象的 event() 函数 (事件处理的统一入口,根据事件类型分发到具体的处理函数)
        ↓
【第3层】具体事件处理函数(如 mousePressEvent()、keyPressEvent()、paintEvent()等,通常重写来让事件“生效”)
        ↓ (若 ignore()或者调用父处理函数)
【第4层】父组件的 event() → 具体事件处理函数
        ↓ (若仍 ignore())
【第5层】继续向上冒泡,直到被处理或到达顶层窗口

五、事件过滤器详解

5.1工作原理

事件过滤器允许一个对象(监视对象)拦截并处理另一个对象(目标对象/被监视对象)的事件,无需子类化目标对象

5.2核心步骤

  1. 在目标对象上调用 installEventFilter() 安装过滤器

  2. 在监视对象中重写 eventFilter(QObject *watched, QEvent *event) 函数

  3. eventFilter() 返回 true,事件被拦截;返回 false 则事件继续传递

5.3代码示例

复制代码
//监视对象
class MyFilter : public QObject {
    Q_OBJECT
protected:
    bool eventFilter(QObject *watched, QEvent *event) override {
        // 检查目标对象
        if (watched == targetWidget) {
            // 检查事件类型
            if (event->type() == QEvent::KeyPress) {
                QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                if (keyEvent->key() == Qt::Key_Space) {
                    // 拦截空格键,不让目标控件处理
                    qDebug() << "Space key blocked";
                    return true;
                }
            }
        }
        // 其他事件继续传递
        return QObject::eventFilter(watched, event);
    }
};

//被监视对象
MyFilter *filter = new MyFilter(this);
button->installEventFilter(filter);

六、Event函数

作用就在于事件的分发 。如果想在事件的分发之前就进行一些操作,可以重写event函数,特定事件返回true,就阻断了该时间的传播,并且最后最好要写return QWidget::event(e);(其余事件正常传播)。如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象。

复制代码
bool myWidget::event(QEvent *e)
{
    if (e->type() == QEvent::KeyPress) 
    {
        //将QEvent对象转换为真正的QKeyEvent对象
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
        if (keyEvent->key() == Qt::Key_Tab) 
        {
            qDebug() << "You press tab.";
            return true;//按下tab键,这个事件已经被处理完
        }
    }
    //按照原来的流程来进行事件的分发
    return QWidget::event(e);
}

七、事件处理函数

重写paintEventmousePressEvent等事件处理函数。这是最普通、最简单的形式,当发生该事件,就会按照你重写的处理函数执行逻辑。

**问题一:**重写事件处理函数时,什么时候调用基类处理?

解答:

核心在于:你是否需要基类的处理

基类函数 内部做了什么 不调用的后果
showEvent 1. 更新窗口系统的可见状态。 2. 触发 QEvent::ShowQEvent::ShowToParen-t 的传播。 3. 激活布局和更新区域计算。 窗口可能无法正常显示,或者父窗口不知道子窗口已显示(导致焦点/激活状态异常)。
hideEvent 1. 更新隐藏状态。 2. 触发 QEvent::HideQEvent::HideToParen-t 传播。 窗口隐藏后,底层窗口资源未释放,可能造成鼠标穿透或焦点残留。
paintEvent 默认什么都不做(空函数),仅仅是为了防止未重载时调用纯虚函数。 无影响。
resizeEvent 1. 更新内部几何缓存。 2. 触发布局重新计算(如果 sizeConstraint 启用)。 布局不会自动调整,子控件位置错乱。
keyPressEvent 实现基础的焦点切换(Tab/Backtab)、快捷键(Alt+字母)和默认按钮行为。 如果完全自定义键盘处理(如游戏操作),可以不调,但会失去标准的 Tab 切换焦点功能。
closeEvent 接受或忽略关闭事件 。默认调用 event->accept() 允许窗口关闭。 如果重写了且不调用基类,除非手动 accept(),否则窗口无法关闭(点击 X 没反应)。

结论:

生命周期/尺寸变化事件showEventhideEventmoveEventresizeEvent这类事件必须处理,且在函数前列加上父类处理;

关闭事件closeEvent : 手动 accept()窗口关不掉,ignore(),不关闭

绘制事件 paintEvent,可以不调用。

输入事件 (键盘、鼠标、滚轮、拖拽)

默认不调用基类 ,除非你明确需要保留 Qt 的标准交互(例如你需要 mousePressEvent 同时触发 clicked 信号,或者你需要 Tab 键正常切换焦点),如果调用基类,一般基类处理殿后。

代码示例:

复制代码
void ArrowWidget::showEvent(QShowEvent* event) {
    QWidget::showEvent(event);   // 先让基类完成状态设置
    m_pTimer->start(1);          // 之后再执行自定义延迟计算
    emit showed();
}
相关推荐
AI科技星2 小时前
全维度相对论推导、光速螺旋时空与北斗 GEO 钟差的统一理论
开发语言·线性代数·算法·机器学习·数学建模
赵优秀一一2 小时前
Python 工程化基础1:环境(conda)、pip、requirements.txt
linux·开发语言·python
li1670902702 小时前
第十章:list
c语言·开发语言·数据结构·c++·算法·list·visual studio
游乐码2 小时前
C#List
开发语言·c#·list
xyq20242 小时前
jQuery Tooltip:深入解析与最佳实践
开发语言
夜猫子ing2 小时前
如何编写一个CMakelists文件
开发语言·c++
Ivanqhz2 小时前
LLVM IR 转 SMT公式
java·开发语言
小红的布丁2 小时前
Reactor 模型详解:单 Reactor、主从 Reactor 与 Netty 思想
android·java·开发语言
被摘下的星星3 小时前
Java的类加载
java·开发语言