#(13):對話框簡介
對話框是 GUI 程序中不可或缺的組成部分。很多不能或者不適合放入主窗口的功能組件都必須放在對話框中設置。對話框通常會是一個頂層窗口,出現在程序最上層,用于實現短期任務或者簡潔的用戶交互。盡管 Ribbon 界面的出現在一定程度上減少了對話框的使用幾率,但是,我們依然可以在最新版本的 Office 中發現不少對話框。因此,在可預見的未來,對話框會一直存在于我們的程序之中。
Qt 中使用`QDialog`類實現對話框。就像主窗口一樣,我們通常會設計一個類繼承`QDialog`。`QDialog`(及其子類,以及所有`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 指針,我們會看到在任務欄出現了對話框的位置:
[](http://files.devbean.net/images/2012/09/dialog-without-parent.png)
我們修改一下`open()`函數的內容:
~~~
void MainWindow::open()
{
QDialog dialog(this);
dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.exec();
}
~~~
重新運行一下,對比一下就會看到 parent 指針的有無對`QDialog`實例的影響。
對話框分為模態對話框和非模態對話框。所謂模態對話框,就是會阻塞同一應用程序中其它窗口的輸入。模態對話框很常見,比如“打開文件”功能。你可以嘗試一下記事本的打開文件,當打開文件對話框出現時,我們是不能對除此對話框之外的窗口部分進行操作的。與此相反的是非模態對話框,例如查找對話框,我們可以在顯示著查找對話框的同時,繼續對記事本的內容進行編輯。
Qt 支持模態對話框和非模態對話框。其中,Qt 有兩種級別的模態對話框:應用程序級別的模態和窗口級別的模態,默認是應用程序級別的模態。應用程序級別的模態是指,當該種模態的對話框出現時,用戶必須首先對對話框進行交互,直到關閉對話框,然后才能訪問程序中其他的窗口。窗口級別的模態是指,該模態僅僅阻塞與對話框關聯的窗口,但是依然允許用戶與程序中其它窗口交互。窗口級別的模態尤其適用于多窗口模式,更詳細的討論可以看[以前發表過的文章](http://www.devbean.net/2011/03/qdialog_window_modal/)。
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();
}
~~~
對比一下這個非模態對話框和之前的模態對話框。我們在對話框出現的時候可以與主窗口交互,因此我們可以建立多個相同的對話框:
[](http://files.devbean.net/images/2012/09/non-modal-dialog.png)
如果你足夠細心,應該發現上面的代碼是有問題的: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()`開始一個新的事件循環)。關于事件循環,我們會在后面的文章中詳細說明。
- (1)序
- (2)Qt 簡介
- (3)Hello, world!
- (4)信號槽
- (5)自定義信號槽
- (6)Qt 模塊簡介
- (7)MainWindow 簡介
- (8)添加動作
- (9)資源文件
- (10)對象模型
- (11)布局管理器
- (12)菜單欄、工具欄和狀態欄
- (13)對話框簡介
- (14)對話框數據傳遞
- (15)標準對話框 QMessageBox
- (16)深入 Qt5 信號槽新語法
- (17)文件對話框
- (18)事件
- (19)事件的接受與忽略
- (21)事件過濾器
- (22)事件總結
- (23)自定義事件
- (24)Qt 繪制系統簡介
- (25)畫刷和畫筆
- (26)反走樣
- (27)漸變
- (28)坐標系統
- (29)繪制設備
- (30)Graphics View Framework
- (31)貪吃蛇游戲(1)
- (32)貪吃蛇游戲(2)
- (33)貪吃蛇游戲(3)
- (34)貪吃蛇游戲(4)
- (35)文件
- (36)二進制文件讀寫
- (37)文本文件讀寫
- (38)存儲容器
- (39)遍歷容器
- (40)隱式數據共享
- (41)model/view 架構
- (42)QListWidget、QTreeWidget 和 QTableWidget
- (43)QStringListModel
- (44)QFileSystemModel
- (45)模型
- (46)視圖和委托
- (47)視圖選擇
- (48)QSortFilterProxyModel
- (49)自定義只讀模型
- (50)自定義可編輯模型
- (51)布爾表達式樹模型
- (52)使用拖放
- (53)自定義拖放數據
- (54)剪貼板
- (55)數據庫操作
- (56)使用模型操作數據庫
- (57)可視化顯示數據庫數據
- (58)編輯數據庫外鍵
- (59)使用流處理 XML
- (60)使用 DOM 處理 XML
- (61)使用 SAX 處理 XML
- (62)保存 XML
- (63)使用 QJson 處理 JSON
- (64)使用 QJsonDocument 處理 JSON
- (65)訪問網絡(1)
- (66)訪問網絡(2)
- (67)訪問網絡(3)
- (68)訪問網絡(4)
- (69)進程
- (70)進程間通信
- (71)線程簡介
- (72)線程和事件循環
- (73)Qt 線程相關類
- (74)線程和 QObject
- (75)線程總結
- (76)QML 和 QtQuick 2
- (77)QML 語法
- (78)QML 基本元素
- (79)QML 組件
- (80)定位器
- (81)元素布局
- (82)輸入元素
- (83)Qt Quick Controls
- (84)Repeater
- (85)動態視圖
- (86)視圖代理
- (87)模型-視圖高級技術
- (88)Canvas
- (89)Canvas(續)