Qt 学习之路 2(13):对话框简介

对话框是 GUI 程序中不可或缺的组成部分。很多不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。尽管 Ribbon 界面的出现在一定程度上减少了对话框的使用几率,但是,我们依然可以在最新版本的 Office 中发现不少对话框。因此,在可预见的未来,对话框会一直存在于我们的程序之中。

Qt 中使用QDialog类实现对话框。就像主窗口一样,我们通常会设计一个类继承QDialogQDialog(及其子类,以及所有Qt::Dialog类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle(tr("Main Window"));

    openAction = new QAction(QIcon(":/images/doc-open"), tr("&Open..."), this);
    openAction->setShortcuts(QKeySequence::Open);
    openAction->setStatusTip(tr("Open an existing file"));
    connect(openAction, &QAction::triggered, this, &MainWindow::open);

    QMenu *file = menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    QToolBar *toolBar = addToolBar(tr("&File"));
    toolBar->addAction(openAction);
}

MainWindow::~MainWindow()
{
}

void MainWindow::open()
{
    QDialog dialog;
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.exec();
}

上面我们使用了前面的示例代码。注意看的是open()函数里面的内容。我们使用QDialog创建了一个对话框,设置其标题为“Hello, dialog!”,然后调用exec()将其显示出来。注意看的是任务栏的图标,由于我们没有设置对话框的 parent 指针,我们会看到在任务栏出现了对话框的位置:

不带 parent 的对话框

我们修改一下open()函数的内容:

void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.exec();
}

重新运行一下,对比一下就会看到 parent 指针的有无对QDialog实例的影响。

对话框分为模态对话框和非模态对话框。所谓模态对话框,就是会阻塞同一应用程序中其它窗口的输入。模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的。与此相反的是非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。

Qt 支持模态对话框和非模态对话框。其中,Qt 有两种级别的模态对话框:应用程序级别的模态和窗口级别的模态,默认是应用程序级别的模态。应用程序级别的模态是指,当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。窗口级别的模态是指,该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式,更详细的讨论可以看以前发表过的文章

Qt 使用QDialog::exec()实现应用程序级别的模态对话框,使用QDialog::open()实现窗口级别的模态对话框,使用QDialog::show()实现非模态对话框。回顾一下我们的代码,在上面的示例中,我们调用了exec()将对话框显示出来,因此这就是一个模态对话框。当对话框出现时,我们不能与主窗口进行任何交互,直到我们关闭了该对话框。

下面我们试着将exec()修改为show(),看看非模态对话框:

void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.show();
}

是不是事与愿违?对话框竟然一闪而过!这是因为,show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。注意,dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了。知道了原因就好改了,我们将 dialog 改成堆上建立,当然就没有这个问题了:

void MainWindow::open()
{
    QDialog *dialog = new QDialog;
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();
}

对比一下这个非模态对话框和之前的模态对话框。我们在对话框出现的时候可以与主窗口交互,因此我们可以建立多个相同的对话框:

非模态对话框

如果你足够细心,应该发现上面的代码是有问题的:dialog 存在内存泄露!dialog 使用 new 在堆上分配空间,却一直没有 delete。解决方案也很简单:将 MainWindow 的指针赋给 dialog 即可。还记得我们前面说过的 Qt 的对象系统吗?

不过,这样做有一个问题:如果我们的对话框不是在一个界面类中出现呢?由于QWidget的 parent 必须是QWidget指针,那就限制了我们不能将一个普通的 C++ 类指针传给 Qt 对话框。另外,如果对内存占用有严格限制的话,当我们将主窗口作为 parent 时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存。在这种情景下,我们可以设置 dialog 的WindowAttribute

void MainWindow::open()
{
    QDialog *dialog = new QDialog;
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();
}

setAttribute()函数设置对话框关闭时,自动销毁对话框。另外,QObject还有一个deleteLater()函数,该函数会在当前事件循环结束时销毁该对话框(具体到这里,需要使用exec()开始一个新的事件循环)。关于事件循环,我们会在后面的文章中详细说明。

57 Comments

  1. 斯啦丝拉 2013年1月9日
    • titi璇 2013年7月13日
      • 豆子 2013年7月17日
  2. 特地来支持豆子 2013年6月7日
  3. 恒古炎 2013年9月8日
    • 豆子 2013年9月9日
      • 恒古炎 2013年9月9日
  4. otto 2013年10月11日
    • 豆子 2013年10月11日
  5. 2013年12月9日
    • 豆子 2013年12月9日
  6. lauthor 2013年12月14日
  7. 折一 2014年1月11日
    • 豆子 2014年1月13日
  8. 大灰狼嘎嘎 2014年1月26日
    • 豆子 2014年1月26日
  9. Const_Lin 2014年3月6日
    • Const_Lin 2014年3月6日
    • 豆子 2014年3月10日
      • Pegasus 2014年8月18日
        • 豆子 2014年8月20日
          • 大风 2015年5月4日
  10. gzy0011 2014年6月13日
    • 豆子 2014年6月13日
  11. xuan120520 2014年6月28日
    • 豆子 2014年6月28日
      • xuan120520 2014年6月28日
  12. cg 2014年9月1日
    • 豆子 2014年9月2日
    • 瞬间 2016年8月31日
  13. 叔叔级人物 2014年9月1日
    • 豆子 2014年9月2日
    • 怀想 2015年8月9日
  14. qt 初学者 2015年6月14日
    • 豆子 2015年6月15日
  15. davidzwb 2015年6月27日
    • 豆子 2015年6月29日
      • davidzwb 2015年6月29日
        • xululu 2015年10月14日
          • 豆子 2015年10月15日
  16. 动感超人 2015年11月7日
    • 豆子 2015年11月9日
  17. 2015年12月8日
    • 豆子 2015年12月8日
  18. 动感超人 2016年3月7日
    • 豆子 2016年3月8日
      • 动感超人 2016年3月8日
  19. 王月 2016年4月1日
  20. le 2016年4月26日
    • 豆子 2016年4月26日
  21. ccppaa 2016年8月22日
  22. 小怪 2016年12月19日
    • 豆子 2016年12月25日
  23. 2018年1月10日
    • 豆子 2018年1月13日
      • 2018年1月15日
      • 2018年1月15日

Leave a Reply