對話框是 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()
開始一個新的事件循環)。