Qt事件循环队列剖析!!!

目录

[一、事件(Event)vs 信号(Signal)](#一、事件(Event)vs 信号(Signal))

[1. 事件(QEvent)](#1. 事件(QEvent))

[2. 信号(Signal)](#2. 信号(Signal))

[二、Qt 事件循环是干嘛的?](#二、Qt 事件循环是干嘛的?)

三、一次"鼠标点击"大概流程

[四、怎么"拦截 / 过滤"事件?](#四、怎么“拦截 / 过滤”事件?)

方法一:重写控件自己的事件函数(简单直接)

[方法二:事件过滤器(Event Filter)(更灵活)](#方法二:事件过滤器(Event Filter)(更灵活))

五、把面试要点压缩成几句话


✅ 槽函数本身不是事件,鼠标点击这种才是事件(QEvent)。

✅ 事件由事件循环从队列里取出,发给控件处理;控件在事件处理函数里可能 emit 信号。

✅ 信号触发槽函数时:

  • 同线程 + AutoConnection:直接调用槽函数

  • 跨线程 + Auto/QueuedConnection:把"调用槽函数"的请求丢到接收者线程的事件队列里,由那个线程执行槽。

一、事件(Event)vs 信号(Signal)

先记一句话:

事件是"系统给对象发消息",信号是"对象给别人发通知"。

1. 事件(QEvent)

  • 来源:操作系统 / Qt 内部

    比如:鼠标移动、按键、窗口大小改变......

  • 走向:发给某个具体对象(一个控件、一个窗口)。

  • 处理方式:覆盖虚函数:

    复制代码
    bool MyWidget::event(QEvent *e) override;        // 总入口
    void MyWidget::mousePressEvent(QMouseEvent *e);  // 专门处理鼠标按下

2. 信号(Signal)

  • 来源:对象自己发

    比如:按钮检测到点击后,发 clicked()

  • 走向:谁连我我就通知谁(广播)

  • 处理方式:connect 到槽函数:

    复制代码
    connect(button, &QPushButton::clicked,
            this,   &MyWindow::onButtonClicked);

可以这样类比:

  • 事件:快递员把快递送到你家门口(发给"你"这个对象)。

  • 信号:你在小区群里发一条消息"我到家啦"(谁关心就监听)。


二、Qt 事件循环是干嘛的?

Qt 程序本质上就是一个大循环:不停地拿事件、分发事件。

伪代码大概是:

复制代码
int main() {
    QApplication app(argc, argv);

    MyWindow w;
    w.show();

    return app.exec();  // 事件循环在这里转圈
}

app.exec() 做的事可以理解为:

复制代码
while (程序没退出) {
    // 1. 从操作系统拿消息(鼠标、键盘等)
    // 2. 转成对应的 QEvent(QMouseEvent、QKeyEvent......)
    // 3. 找到应该接收的那个控件
    // 4. 调用 QApplication::notify() 把事件发给它
}

三、一次"鼠标点击"大概流程

以你点一下按钮为例,简化成几步:

  1. 操作系统:窗口系统发现你在某个坐标点按下鼠标 → 产生原始消息。

  2. Qt 平台插件 :把系统消息转成一个 QMouseEvent

  3. 事件循环app.exec()):从队列里取出这个 QMouseEvent

  4. QApplication::notify()

    找到那个被点中的控件(比如 QPushButton),准备把事件发给它。

  5. 事件过滤器(可选)

    • 如果别的对象给这个按钮装了 eventFilter,会先问一嘴:

      "这个事件你要拦下来吗?"

    • eventFilter() 返回 true → 事件就到此为止,不再往下走。

    • 返回 false → 继续发给按钮本身。

  6. 控件接收事件

    • button->event(e) 被调用;

    • event() 里发现这是鼠标按下,就继续调用 mousePressEvent(e)

    • QPushButton::mousePressEvent() 里判断:按下 + 抬起 → 触发 clicked() 信号。

  7. 信号发出

    • button 发出 clicked()

    • 所有 connect 过的槽函数被依次调用,比如 onButtonClicked()


四、怎么"拦截 / 过滤"事件?

你想在事件"正常处理前"动手脚,有两个常见方式:

方法一:重写控件自己的事件函数(简单直接)

复制代码
class MyButton : public QPushButton {
protected:
    void mousePressEvent(QMouseEvent *e) override {
        // 这里先做你自己的事
        // ...

        // 继续让父类处理(保持原有按钮行为)
        QPushButton::mousePressEvent(e);
    }
};

适合你能改这个控件的类的时候。


方法二:事件过滤器(Event Filter)(更灵活)

适合"我不想改控件的类,但想听它的所有事件"。

  1. 写一个过滤器类,重写 eventFilter

    复制代码
    class MyFilter : public QObject {
    protected:
        bool eventFilter(QObject *watched, QEvent *event) override {
            if (watched == targetButton && event->type() == QEvent::KeyPress) {
                // 拦截:不让这个按钮响应键盘
                return true;   // true = 我已经处理,不要再往下发了
            }
            return false;      // false = 放行,照常处理
        }
    };
  2. 在某处安装到目标对象上:

    复制代码
    MyFilter *filter = new MyFilter(this);
    targetButton->installEventFilter(filter);

记住一句话:

eventFilter 返回 true = 拦截;返回 false = 放行。


五、把面试要点压缩成几句话

你可以最后这样记:

  1. 事件 :系统 → 对象,event()/xxxEvent() 处理;
    信号 :对象 → 其他对象,slot 处理。

  2. 程序核心是 app.exec() 事件循环:拿系统消息 → 转 QEvent → notify() 分发。

  3. 一次鼠标点击:系统消息 → QMouseEventeventFilter(可选) → event()mousePressEvent() →(可能)发 clicked() 信号。

  4. 想拦截事件:

    • 能改控件:重写 event()mousePressEvent() 等;

    • 改不了控件:写 eventFilter + installEventFilter()true 拦截,false 放行。

如果你愿意,我下一步可以帮你把这些内容浓缩成一页"小抄",专门给你背八股用 😄

你这句话大体是对的,只是可以再精细一点点 👍

先回答你的问题:

事件循环是不是就是事件队列循环,队列就是先进先出?

可以这么理解:

  • Qt 里确实有一个事件队列

  • app.exec() 就是在不停地:

    • 从队列里取出一个事件

    • 分发给对应对象处理

  • 队列大致上可以按 FIFO 理解,虽然内部还有优先级之类的细节,但入门可以不用管。


再看你说的流程:

点击按钮,触发这一流程

系统 → 事件 → 通知 → 按钮事件 → 发送信号 → 处理槽函数

把 Qt 版"官话"稍微补全一下就是:

  1. 系统:鼠标点击消息 →

  2. Qt 平台层 :转成 QMouseEvent,丢进 Qt 的事件队列 →

  3. 事件循环app.exec() 从队列里取出这个 QMouseEvent

  4. notifyQApplication::notify() 把事件发给对应的按钮对象

    • (期间可能先经过 eventFilter 过滤器)
  5. 按钮事件处理 :按钮的 event() / mousePressEvent() 里处理这个点击

  6. 按钮内部判断为"真正点了一下"emit clicked() 发出信号

  7. 槽函数 :所有 connectclicked() 的槽函数被调用


再补一个小但重要的点

  • 默认情况下(在同一线程,Qt::AutoConnection),
    信号发出后,槽函数是立刻在当前调用栈里执行的

    而不是再丢回事件队列里排队(除非你用的是 Qt::QueuedConnection)。

所以可以用一句"口语版总结":

Qt:事件循环负责从系统拿事儿 → 找到控件 → 调控件的事件函数

控件:在事件函数里觉得该通知别人了,就发信号 → 调槽函数

相关推荐
v***5651 小时前
Spring Cloud Gateway 整合Spring Security
java·后端·spring
python零基础入门小白2 小时前
【万字长文】大模型应用开发:意图路由与查询重写设计模式(从入门到精通)
java·开发语言·设计模式·语言模型·架构·大模型应用开发·大模型学习
高山上有一只小老虎2 小时前
构造A+B
java·算法
学困昇2 小时前
C++中的异常
android·java·c++
MC丶科2 小时前
Java设计模式漫画英雄宇宙-工厂模式 —Factory博士的“超级英雄制造机”!
java·设计模式·漫画
虎子_layor2 小时前
告别Redis瓶颈:Caffeine本地缓存优化实战指南
java·后端
q***98522 小时前
什么是Spring Boot 应用开发?
java·spring boot·后端
带刺的坐椅2 小时前
Solon AI 开发学习4 - chat - 模型实例的构建和简单调用
java·ai·chatgpt·solon
hadage2332 小时前
--- JavaScript 的一些常用语法总结 ---
java·前端·javascript