本地 QEventLoop 是如何工作的

Posted

技术标签:

【中文标题】本地 QEventLoop 是如何工作的【英文标题】:How local QEventLoop works 【发布时间】:2018-10-31 05:34:19 【问题描述】:

我的问题很笼统,与 QT 中 QEventLoop 类的使用有关。我有两个主要问题。

问题 1) 它是如何在 QT 内部工作的(我主要关心的是为什么执行 QEventLoop 对象不会阻塞 QT 应用程序主循环 - [编辑 3] 最后一条评论不正确,请参阅下面的答案)。 详情见下文。

问题 2) 除了阻塞还有其他目的吗?看来我只能遇到 QEventLoop 用于等待目的的示例。 可以用于其他用途吗?就像我们可以想象将特定事件的处理从主应用程序循环委托给本地 QEventLoop 一样吗? (不确定这个问题是否有意义)

问题1)的发展:

我对 QT 主事件循环的基本工作原理的理解如下。应用程序主事件循环 (QCoreApplication::exec()) 从队列中抓取一个 QEvent "E",并将其分派给 QObject "A" 它决定事件应该去往(例如,在鼠标左键按下的情况下,QWidget 的位置和 Z 值)。如果我们假设对象“A”正在使用事件“E”,则调用该对象的事件方法(还有其他便利方法和事件过滤器,但我们假设事件方法在我们的例子中处理事件) - 一些处理与对象“A”相关的发生在这里 - 并返回 true。然后,QT 主事件循环开始处理队列中的下一个事件,以此类推。

然而,IF在对象“A”的事件方法的调用中有一些阻塞,我希望主应用程序循环被阻塞,因为它等待的事件方法接收者(对象“A”)返回... 例如,如果一个事件的处理最终调用了对象“A”的方法,在该方法中我们创建了一个永远不会退出的本地 QEventLoop,我的期望是整个应用程序都停止并且没有处理更多事件,直到退出本地QEventLoop,返回对象“A”的事件方法。 否则,这是不正确的,因为我可以看到本地 QEventLoop 没有阻塞应用程序主事件循环... 有人可以让我更深入地了解本地 QEventLoop 的工作原理吗? [编辑 3] 见下面的答案,本地事件循环处理事件

---- 编辑 1 ----

对不起,如果我不清楚,这实际上很难用语言解释,所以下面是一个小代码sn-p,以更好地说明我的问题1

ma​​inwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui 
class MainWindow;


class MainWindow : public QMainWindow

    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
;

#endif // MAINWINDOW_H

ma​​inwindow.cpp

#include "mainwindow.h"
#include <QPushButton>
#include <QEventLoop>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)

    QPushButton* button1 = new QPushButton(this);
    button1->setText("button 1");
    button1->setCheckable(true);

    QPushButton* button2 = new QPushButton(this);
    button2->setText("button 2");
    button2->move(0,50);

    connect(button1,&QPushButton::toggled,button1,[button1](bool toggled)
        button1->setText(toggled ? "CHECKED" : "NOT CHECKED");
    );

    connect(button2,&QPushButton::clicked,button2,[]()
        QEventLoop loop;
        qInfo() << "Before loop exec";
        loop.exec();
        qInfo() << "After loop exec";
    );


MainWindow::~MainWindow()


ma​​in.cpp

#include "mainwindow.h"
#include <QApplication>

    int main(int argc, char *argv[])
    
        QApplication a(argc, argv);
        MainWindow w;
        w.show();

        return a.exec();
    

在上面的示例中,我有一个简单的窗口,其中包含 2 个按钮,“按钮 1”(显示其状态:选中或未选中)和“按钮 2”(单击时触发本地 QEventLoop)。 当我单击按钮 2 时,它会在“loop.exec()”处阻塞,即永远不会输出第二条调试消息。然后,我希望主应用程序循环也被阻止,但是,我不认为它是因为我仍然可以单击显示其状态(选中或未选中)的“按钮 1”,这意味着鼠标事件是仍由主应用程序事件循环处理。

【问题讨论】:

阅读源码有什么问题?顺便说一句,你错了 - QEventLoop 阻止主应用程序循环。 @DmitrySazonov :对不起,我会进一步阅读,但我在网上看不到很多 QEventLoop 的例子,加上 QT 文档对这个类的用法不是很明确,所以我问了这个问题。也许我遗漏了一些东西,但是对于我的 EDIT 1 中的示例,本地 QEventLoop 似乎并没有阻塞主应用程序事件循环。 【参考方案1】:

不,事件由主 QApplication 事件循环处理。他们由loop处理,并被派送到他们通常的地方。

您提供的代码无法以loop 结尾。没有什么可以阻止您再次单击button2,并在当前运行的两个(第一个loop 和主QApplication 循环)中拥有另一个loop

如果您将调试器附加到您的进程,您将看到每个正在运行的循环的堆栈帧

【讨论】:

感谢 Caleth... 我很困惑,并认为本地“QEventLoop”完全独立于 QApplication。我知道不是。 对这个概念的更详细的解释应该在文档中。所以我的理解是,如果在执行外循环的事件时执行内循环,则内循环“接管”当前线程中执行事件的责任,而外循环被“冻结”向上堆栈直到内部循环退出。正确的?对吗? @JuanGonzalezBurgos 一个长时间运行的内部函数确实意味着堆栈上的东西等待,是的。这不是 QEventLoop 独有的

以上是关于本地 QEventLoop 是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章

何时以及如何使用 PySide QEventLoop

QEventLoop 的使用两例

QEventLoop 处理所有事件

Qt Dialog窗口关闭时如何终止QEventLoop

如何修复“在 Qt 中将两个定时器变为一个函数,使用 qmutex 将 qeventloop 进行睡眠”

关于Qt的事件循环以及QEventLoop的简单使用(QEventLoop::quit()能够终止事件循环,事件循环是可以嵌套的)