QDialog 模态对话框与事件循环

原文地址: https://blog.csdn.net/dbzhang800/article/details/6300416

qtcn中文论坛中有网友问到:

设程序正常运行时,只有一个简单的窗体A,此时只有一个GUI主线程,在这个主线程中有一个事件循环处理窗体上的事件。当此程序运行到某阶段时,弹出一个模态窗体B(书上说模态窗体是有其自己的事件循环的),此时模态窗体B是否会有一个对应的子线程处理其事件循环?
这儿其实有两个问题:

  • 模态对话框 和 事件循环 没有必然联系
  • 事件循环 和 子线程 没有必然联系
  • 如果进一步呢?其实我们还可以说:

  • 模态对话框 和 QDialog 没必要联系
  • QDialog 对话框

    两种常规用法:

    QDialog * dlg = new QDialog()
    dlg->show();
    

    当然,这儿用指针(即分配到heap中)不是必须的。 (有疑问?或者有时发现窗口一闪而过?那么你需要了解C、C++中变量的作用域和生存周期)

    QDialog dlg;
    dlg.exec();
    

    这种情况下,我们一般都是将对象分配上 stack 上,而不是heap上。

    当然,你喜欢用 heap,也没问题:

    Dialog * dlg = new QDialog();
    dlg->exec();
    delete dlg;
    

    模态对话框

    前面的 show() 与 exec() 并不是模态与非模态的区别。

    setAttribute(Qt::WA_ShowModal, true);
    

    注意:这是QWidget的成员函数 ,也就是说,QWidget可以显示为模态或非模态!

    setWindowModality

    除了直接调用setAttribute外,QWidget 提供了一个易用的函数,来设置窗体的模态。其源码如下:

    void QWidget::setWindowModality(Qt::WindowModality windowModality)
        data->window_modality = windowModality;
        // setModal_sys() will be called by setAttribute()
        setAttribute(Qt::WA_ShowModal, (data->window_modality != Qt::NonModal));
        setAttribute(Qt::WA_SetWindowModality, true);
    

    注意:该函数的参数取值:NonModal、WindowModal、ApplicationModal 分别对应默认情况下的

  • QDialog::show()
  • QDialog::open()
  • QDialog::exec()
    如果你没有使用QDialog::open()的需求,你可能也不需要该函数。
  • setModal

    除了QWidget提供的成员,QDialog 提供了 setModal 的成员函数,我们看看其代码:

    void QDialog::setModal(bool modal)
        setAttribute(Qt::WA_ShowModal, modal);
    

    不用解释了吧?我们要显示模态对话框,只需要类似下面的代码:

    QDialog * dlg = new QDialog();
    dlg->setAttribute(Qt::WA_ShowModal, true);
    dlg->show();
    

    exec()

    有问题是不?为啥exec() 直接可以显示模态对话框呢?看QDialog源代码吧

    int QDialog::exec()
        Q_D(QDialog);
        setAttribute(Qt::WA_ShowModal, true);
        show();
        QEventLoop eventLoop;
        (void) eventLoop.exec(QEventLoop::DialogExec);
    

    看到答案没:exec() 先设置modal属性,而后调用 show() 显示对话框,最后启用事件循环

    Qt 程序时事件驱动的,每个程序,我们需要调用 QApplication::exec() 来启用事件循环。

    int QCoreApplication::exec()
        QEventLoop eventLoop;
        int returnCode = eventLoop.exec();
        return returnCode;
    

    用前面的 QDialog::exec() 一样,都是调用的 QEventLoop::exec()

    int QEventLoop::exec(ProcessEventsFlags flags)
        Q_D(QEventLoop);
        while (!d->exit)
            processEvents(flags | WaitForMoreEvents | EventLoopExec);
        return d->returnCode;
    
    bool QEventLoop::processEvents(ProcessEventsFlags flags)
        Q_D(QEventLoop);
        if (!d->threadData->eventDispatcher)
            return false;
        if (flags & DeferredDeletion)
            QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
        return d->threadData->eventDispatcher->processEvents(flags);
    

    进一步:这将调用平台相关的函数,比如在windows下

    bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
        if (!QEventDispatcherWin32::processEvents(flags))
            return false;
        if (configRequests)                        // any pending configs?
            qWinProcessConfigRequests();