对话框是 GUI 程序中不可或缺的组成部分。不少不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框一般会是一个顶层窗口,出如今程序最上层,用于实现短时间任务或者简洁的用户交互。尽管 Ribbon 界面的出如今必定程度上减小了对话框的使用概率,可是,咱们依然能够在最新版本的 Office 中发现很多对话框。所以,在可预见的将来,对话框会一直存在于咱们的程序之中。函数
Qt 中使用QDialog
类实现对话框。就像主窗口同样,咱们一般会设计一个类继承QDialog
。QDialog
(及其子类,以及全部Qt::Dialog
类型的类)的对于其 parent 指针都有额外的解释:若是 parent 为 NULL,则该对话框会做为一个顶层窗口,不然则做为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有本身的位置,而非顶层窗口则会共享其父组件的位置。this
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 指针,咱们会看到在任务栏出现了对话框的位置。spa
咱们修改一下open()
函数的内容:.net
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()
将对话框显示出来,所以这就是一个模态对话框。当对话框出现时,咱们不能与主窗口进行任何交互,直到咱们关闭了该对话框。code
下面咱们试着将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()
开始一个新的事件循环)。