QWidget 模态
模态窗口会阻止其他窗口的输入型事件(如获取焦点),但是模态窗口的子窗口不会被限制。
(设置 setAttribute(Qt::WA_showModal, true); 的方式Qt4.5已废弃,略过)
目前,Qt中设置窗口模态是通过 QWidget 的 setWindowModality() 函数:
参数有三种枚举:
- Qt::NonModal :(=0)表示该窗口不是模态窗口,不会阻止其他窗口的输入。
- Qt::WindowModal :(=1)表示该窗口是单个窗口层次结构的模态,会阻止输入到其父窗口、所有祖父窗口以及其父窗口和祖父窗口的所有同级窗口。(说人话就是这个模态窗口对象树上的输入型事件都被阻止了,除了自己,对象树的意思就是这个模态框得设置 parent,不然是阻塞不了别的窗口的)
- Qt::ApplicationModal :(=2)表示该窗口是应用程序的模态窗口,会阻止本应用所有窗口的输入。(WindowModal 多个不在同一对象树的窗口还可以切换,ApplicationModal 就没有对象树的限制了,除了自己谁也不听使唤)
(注意,Qt::WindowModal 的窗口你得设置 parent, 不然你也阻塞不了谁啊,毕竟他只会坑爹,遇到不认识的就唯唯诺诺)
QDialog 模态
QDialog 是 QWidget 的派生类,不同于 QWidget 的默认无模态,QDialog 有三种情况:
- 调用 show() 显示:此时的模态属性是根据你 setWindowModality 设置的模式来决定的。或者你可以设置 QDialog::setModal(bool model) ,为 false(默认 false)则表示 Qt::NonModal ,为 true 则表示 Qt::ApplicationModal 。
- 调用 exec() 显示:此时会忽略你设置的模态属性,默认以 Qt::ApplicationModal 显示,阻塞整个应用的窗口交互,并且会同步等待返回值。
- 调用 open() 显示:此时会忽略你设置的模态属性,默认以 Qt::WindowModal 显示,但是是异步处理,立即返回的。
相关源码赏析:
int QDialog::exec()
{
Q_D(QDialog);
if (Q_UNLIKELY(d->eventLoop)) {
qWarning("QDialog::exec: Recursive call detected");
return -1;
}
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_DeleteOnClose, false);
d->resetModalitySetByOpen();
bool wasShowModal = testAttribute(Qt::WA_ShowModal);
setAttribute(Qt::WA_ShowModal, true);
setResult(0);
show();
QPointer<QDialog> guard = this;
if (d->nativeDialogInUse) {
d->platformHelper()->exec();
} else {
QEventLoop eventLoop;
d->eventLoop = &eventLoop;
(void) eventLoop.exec(QEventLoop::DialogExec);
}
if (guard.isNull())
return QDialog::Rejected;
d->eventLoop = nullptr;
setAttribute(Qt::WA_ShowModal, wasShowModal);
int res = result();
if (d->nativeDialogInUse)
d->helperDone(static_cast<QDialog::DialogCode>(res), d->platformHelper());
if (deleteOnClose)
delete this;
return res;
}
void QDialog::open()
{
Q_D(QDialog);
Qt::WindowModality modality = windowModality();
if (modality != Qt::WindowModal) {
d->resetModalityTo = modality;
d->wasModalitySet = testAttribute(Qt::WA_SetWindowModality);
setWindowModality(Qt::WindowModal);
setAttribute(Qt::WA_SetWindowModality, false);
#ifdef Q_OS_MAC
setParent(parentWidget(), Qt::Sheet);
#endif
}
setResult(0);
show();
}