Qt-事件循环 & 模态窗口

本文最后更新于:2022年10月21日 晚上

前情提要

質問:

为什么模态对话框会阻塞其他窗口的交互?

众所周知,调用QDialog::exec会开启一个模态对话框,阻塞后续代码,并阻止其他窗口的交互

But Why?

1
2
3
4
5
6
7
8
9
10
11
12
int QDialog::exec()
{
Q_D(QDialog);
...
setAttribute(Qt::WA_ShowModal, true);
...
show();
...
QEventLoop eventLoop;
(void) eventLoop.exec(QEventLoop::DialogExec);
...
}

观察源码可知,exec方法将窗口设置为了模态(Modal),并开启了局部事件循环

那么,究竟是局部事件循环阻止了与其他窗口的交互,还是Modal属性的仕業(しわざ)呢

EventLoop

首先来看看什么是事件循环

我们在Qt桌面程序的main函数里就能看到QApplicaion::exec()

其内部就开了一个事件循环,也称为Qt程序的主循环

1
2
3
4
5
6
7
int QCoreApplication::exec()
{
...
QEventLoop eventLoop;
eventLoop.exec();
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int QEventLoop::exec(ProcessEventsFlags flags)
{
...
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
...
}

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}

实际上,事件循环的本质就是一个死循环,不断接收内部外部的各种消息,并派发到对应的函数进行处理

所以exec之后的(同一函数内的)代码在循环结束前是不会得到执行机会的(阻塞)

事件循环的本质就是以队列的方式再次分配线程时间片

嵌套循环

事件循环还有一个重要性质,ta是可以被嵌套的

也就是在QApplicaion::exec()主循环内还可以开启局部事件循环

1
2
QEventLoop eventLoop;
eventLoop.exec();

就像while循环一样,局部事件循环运行时,父循环就处于停滞状态

局部事件循环跳出后,父循环继续运行

不过,问题不大,事件循环本身就只是派发事件,与谁父谁子无关,局部循环同样能承担一样的职能

Qt会把事件分发到生效的事件循环中去

所以,虽然eventLoop.exec();后的代码被阻塞,但是UI交互、Timer等等各类事件都是正常执行的

也就是一个函数被阻塞,但是千千万万个函数在阻塞前的那行代码内运行

时间停止了?不,世界仍在运行,只有你停滞不前 —— 蔡姬

正因为如此,局部事件循环的妙用就是在不阻塞UI的前提下,实现函数的同步执行

例如:等待服务器响应,计算超时等等

综上

事件循环并不阻塞UI,不可能导致对话框阻塞其他窗口的交互

那究竟是谁,干了这好事(恼)

只有模态属性了吧

1
2
3
QDialog dia;
dia.setModal(true);
dia.show();

确实如此,只要设置模态属性,就能实现交互阻塞

而且还有两种模式:

模式 效果
Qt::ApplicationModal 阻塞应用程序的所有窗口
Qt::WindowModal 阻塞父窗口、祖先窗口及它们的子窗口

始作俑者

但是,为什么

难道说

我真傻,真的

我一直以为阻塞是不得已而为之,却忘了人心险恶

这极有可能是Qt在内部手动屏蔽了其他窗口的交互,只为了强制用户专心于模态窗口

www 我真傻 真的

补充

对了,还有一点

QEventLoop::ProcessEventsFlags的枚举值在Qt文档中被刻意隐藏了一些

比如Qt源码中的QEventLoop::DialogExec

原因未知

Peace

Ref

Qt事件循环详解(一)_一只向前的程序猿的博客-CSDN博客_qt循环

Qt 之 模态、非模态、半模态窗口的介绍及 实现QDialog的exec()方法 - maxiongying - 博客园 (cnblogs.com)

Qt 之 模态、非模态、半模态窗口的介绍及 实现QDialog的exec()方法_前行中的小猪的博客-CSDN博客_qdialog模态显示

QDialog 模态对话框与事件循环(exec其实就是调用了show和eventLoop.exec) - findumars - 博客园 (cnblogs.com)

QT中使用QEventLoop来实现事件循环_「已注销」的博客-CSDN博客_qeventloop

Qt | 模态对话框和非模态对话框 QDialog - 知乎 (zhihu.com)

Qt 模态窗口 - 索智源 - 博客园 (cnblogs.com)

Qt Object模型及其线程和事件处理_szonebit的博客-CSDN博客

event handling - Undocumented ProcessEventsFlag enums in QT - Stack Overflow


Qt-事件循环 & 模态窗口
https://mrbeancpp.github.io/2022/10/21/Qt-事件循环-模态窗口/
作者
MrBeanC
发布于
2022年10月21日
许可协议