本地QEventLoop的工作原理

5
我的问题是关于在QT中使用类的一般性问题。我有两个主要问题。
问题1) 它在QT内部是如何工作的(我的主要关注点是为什么一个QEventLoop对象的执行不会阻塞QT应用程序主循环 - [编辑3]此最后一条评论是不正确的,请参见以下答案)。详见下文。
问题2) 除了阻止之外,它还有其他用途吗?似乎我只能遇到使用QEventLoop等待的示例。它可以用于其他目的吗?例如,我们是否可以想象将特定事件的处理委托给本地QEventLoop而不是主应用程序循环?(不确定这个问题是否有意义)
问题1的发展:
我对QT主事件循环的基本工作原理的理解如下。应用程序主事件循环(QCoreApplication::exec())从队列中抓取一个QEvent“E”,并将其分派给它决定事件应该去哪个QObject“A”(例如,在鼠标左键按下时QWidget的位置和Z值)如果我们假设对象“A”正在使用事件“E”,那么该对象的事件方法(还有其他方便的方法和事件过滤器,但让我们假设事件方法在我们的情况下处理事件)被调用-此处发生与对象“A”相关的一些处理-并返回true。然后,QT主事件循环开始处理队列中的下一个事件,以此类推。
但是,如果在调用对象“A”的事件方法时出现了阻塞,我会期望主应用程序循环被阻塞,因为它等待接收器(对象“A”)的事件方法返回...例如,如果事件的处理最终调用我们创建永远不会退出的本地QEventLoop的对象“A”的一个方法,那么我的期望是整个应用程序将停止,并且不再处理任何事件,直到本地QEventLoop退出,而对象“A”的事件方法返回。否则,这是不正确的,因为我可以看到本地QEventLoop不会阻塞应用程序主事件循环... 有人能给我更多关于本地QEventLoop如何工作的见解吗?[编辑3]请参见以下答案,本地事件循环处理事件

---- EDIT 1 ----

如果我没有表达清楚,我很抱歉,用词确实很困难,因此以下是一个小代码片段,以更好地说明我的问题1。

mainwindow.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

mainwindow.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()
{
}

main.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会阻塞主应用程序循环。 - Dmitry Sazonov
1
@DmitrySazonov:抱歉,我会继续阅读,但是我在网上找不到很多QEventLoop的示例,而且QT文档对于这个类的使用也不是非常明确,所以我才提出了这个问题。也许我漏掉了什么,但是根据我在EDIT 1中的示例,本地QEventLoop似乎无法阻止主应用程序事件循环。 - user3722440
1个回答

5
不,事件不是由主QApplication事件循环处理的。它们是由"loop"处理并分派到它们通常所在的位置。
您提供的代码没有结束"loop"的方法。没有任何阻止您再次点击"button2"并有另一个"loop"运行(第一个"loop"和主QApplication循环)的方法。
如果您将调试器附加到进程中,则会看到每个正在运行的循环的堆栈帧。

谢谢Caleth...我之前很困惑,认为本地的“QEventLoop”与QApplication完全独立。现在我明白了它们并不是这样的。 - user3722440
1
这个概念的更详细解释应该在文档中。所以我的理解是,如果内部循环在外部循环事件执行期间被执行,那么内部循环将“接管”当前线程中执行事件的责任,而外部循环则会在堆栈上“冻结”,直到内部循环退出。对吗?对吧? - Juan Gonzalez Burgos
1
@JuanGonzalezBurgos 一个长时间运行的内部函数意味着堆栈上方的事情需要等待,是的。这并不是QEventLoop所特有的。 - Caleth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接