## 8.8 多文檔
在每一個主窗口中只提供一個文檔的應用程序被稱為單文檔界面( SDI)應用程序。基 于 SDI 的應用程序只提供了一個單一主窗口,并且在同一時間只能處理一個文檔。如果想 讓它在同一時間具有處理多個文檔的能力,就需要同時啟動多個應用程序實例。但是這對于 用戶來講是很不方便的。針對這種情況,我們可以使用基于多文檔界面( MDI)的應用程 序,它也只有一個主窗口,但可以產生和管理多個文檔窗口。基于多文檔的應用程序也是很 常見的,比如 FireFox 瀏覽器就是典型的例子,它允許用戶在同一時間內打開多個瀏覽器 窗口。
Qt 可以在它所支持的所有平臺上創建 SDI 和 MDI 應用程序。 根據筆者的經驗,在通常的工程項目中,使用 SDI 的應用程序占到了大多數的比例,但我們仍然需要對 MDI 有所了解。
### 8.8.1 創建多文檔
在 Qt4 中創建 MDI 應用程序主要有以下方法:
1\. 多實例實現主窗口的多文檔
在一個應用程序中實例化多個主窗口,即當打開或新建文檔的時候,文本編輯器應用 程序新建一個主窗口,這個主窗口單獨加載和編輯文檔。這種情況下,多個主窗口屬于同一 個應用程序,當關閉所有的主窗口的時候,文本編輯器應用程序也就結束了運行。這種方法 稱為多實例實現主窗口的多文檔。
下面將修改應用程序,以使它可以處理多個文檔。首先,需要對 File 菜單做一些簡單 改動:
+ 利用 File→New 創建一個空文檔窗口,而不是再次使用已經存在的主窗口。
+ 利用 File→Close 關閉當前主窗口。
+ 利用 File→Exit 關閉所有窗口。
在 File 菜單的最初版本中,并沒有 Close 選項,這只是因為當時它還和 Exit 一樣具 有相同的功能。新的 File 菜單如圖 8-27 所示。
新的 main()函數為:
```
int main(int argc,char *argv[])
{
QApplication app(argc,argv); MainWindow *mainWin = new MainWindow; mainWin->show();
return app.exec();
}
```
具有多窗口功能后,現在就需要使用菜單中的 new 來創建 MainWindow。考慮到節省內 存,可以再工作完成之后使用 delete 操作來刪除主窗口。
這是新的 MainWindow::newFile()槽:
```
void MainWindow::newFile()
{
MainWindow *mainWin = new MainWindow; mainWin->show();
}
```
我們只創建了一個新的 MainWindow 實例。這看起來有些奇怪,因為沒有保留指向這個 新窗口的任何指針,但實際上這并不是什么問題,因為 Qt 會對所有的窗口進行跟蹤。
以下是用于 Close 和 Exit 的動作:
```
void MainWindow::createActions()
{
...
closeAct = new QAction(tr("Cl&ose"), this); closeAct->setShortcut(tr("Ctrl+F4"));
closeAct->setStatusTip(tr("Close the active window"));
connect(closeAct, SIGNAL(triggered()),mdiArea, SLOT(closeActiveSubWindow()));
exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q"));
exitAct->setStatusTip(tr("Exit the application"));
connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
}
```
到此,我們就實現了從 SDI 到 MDI 的“轉變”,組合好你的工程文件,編譯、鏈接、 運行程序即可。
小貼士:在 Mac OS X 系統中,通常采用這種方法實現 MDI 應用程序。
2\.使用 QWorkSpace
使用 QWorkSpace 作為主窗口的中心部件,在 QWorkspace 中打開多個子窗口,每一個 子窗口可以單獨對文檔進行加載和編輯。
QWorkspace 類繼承自 QWidget 類,利用它可以很方便的實現多文檔的應用。使用它的 方法有如下步驟:
第 1 步,在主窗口的頭文件(如 mainwindow.h)中加入 QWorkspace 類的頭文件,代碼 如下:
```
#include <QWorkspace>
```
也可以采用類的前置聲明,代碼如下:
```
class QWorkspace;
```
第 2 步,在主窗口頭文件中聲明一個 QWorkspace 類對象,代碼如下:
```
QWorkspace *workspace;
```
第 3 步,在構造函數中實例化該對象,并把該對象設置為主窗口的中心窗口部件,代 碼如下:
```
MainWindow::MainWindow()
{
workspace = new QWorkspace; setCentralWidget(workspace);
...
}
```
第 4 步,創建多個子窗口,并創建它們各自的中心窗口部件 。
```
QMainWindow *window1 = new QMainWindow;
window1->setWindowTitle(tr("window 1"));
QTextEdit *textEdit1 = new QTextEdit;
textEdit1->setText(tr("Window 1"));
window1->setCentralWidget(textEdit1);
QMainWindow *window2 = new QMainWindow;
window2->setWindowTitle(tr("window 2"));
QTextEdit * textEdit2 = new QTextEdit;
textEdit2->setText(tr("Window 2"));
window2->setCentralWidget(textEdit2);
QMainWindow *window3 = new QMainWindow;
window3->setWindowTitle(tr("window 3"));
QTextEdit * textEdit3 = new QTextEdit;
textEdit3->setText(tr("Window 3"));
window3->setCentralWidget(textEdit3);
```
第 5 步,在 workSpace 對象中加入這幾個子窗口,對它們進行管理,代碼如下:
```
workSpace->addWindow(window1);
workSpace->addWindow(window2);
workSpace->addWindow(window3);
```
第 6 步,組織好你的工程文件,編譯、鏈接、運行程序即可。 完整的示例程序在本章目錄下的 workspace 文件夾下面。
小貼士:在 Qt4.5 版推出以后,不推薦采用上述方法來創建 MDI 應用程序。來自 Qt 的 官方說法是,QWorkspace 是被廢棄的類,它的存在就是為了使采用以前版本的 Qt 開發的程 序能夠正常運行。所以,如果你使用的是 Qt4.5 及以后的版本,我們強烈建議你使用 QMdiArea 來創建 MDI 應用程序。
3\.使用 QMdiArea
這種方法的核心主要是掌握兩個類的用法: QMdiArea 和 QMdiSubWindow,前者主要用 于創建程序主窗口的中心窗口部件,后者用于創建主窗口的各個子窗口。具體的做法是把 QMdiArea 類的實例作為主窗口的中心部件,把 QMdiSubWindow 類的實例作為子窗口,并由 QMdiArea 實現對多個子窗口的管理。
QMdiArea 類繼承自 QAbstractScrollArea,它是 Qt 4.3 以后新增加的類。在創建 MDI 應用程序時,QMdiArea 類的實例通常被用作主窗口的中心窗口部件,但也可以被放置于一 個布局中。實際上,QMdiArea 是 MDI 應用程序的窗口管理器。它建立、繪制、管理在它之 上的子窗口,并可采用層疊或者平鋪的方式排列它們。
QMdiSubWindow 繼承自 QWidget,它的作用是為 QMdiArea 創建子窗口。它代表了在 QMdiArea 中創建的頂層窗口。它主要包含一個標題欄、一個內部窗口( Internal Widget)、一個窗口框架和一個大小控制手柄。 QMdiSubWindow 有自己的布局(Layout), 在其中包含窗口標題欄以及內部窗口的中心窗口區域。一個典型的 QMdiSubWindow 實例如 圖 8-27 所示。

圖 8-27 QMdiSubWindow 子窗口
聽了上面的介紹,大家是不是有點“暈”呢?別著急,請看圖 8-28,它示意了采用這 種方法創建 MDI 應用程序的窗口布局以及創建的方法。圖中的主窗口是子類化 QMainWindow 類創建的;主窗口的中心窗口部件使用 QMdiArea 的實例創建;子窗口是子類化 QWidget 實 現的,而子窗口的內部窗口部件是使用 QMdiSubWindow 的實例創建的。
它們之間的關系總結如下:QMdiArea 是所有子窗口的容器和管理器,QMdiArea 中的子 窗口都是 QMdiSubWindow 類的實例。我們通過 addSubWindow()方法把它們加入到 MDI 應用 程序中。使用時,通常先建立一個 QWidget 或其子類的實例,然后把它作為參數調用 addSubWindow()函數,addSubWindow()函數將把它作為子窗口的內部窗口,并填充中心窗口 區域。由于 QMdiSubWindow 是 QWidget 的子類,所以你可以像使用以前我們介紹過的常見 頂層窗口那樣使用它,如可以調用基類 QWidget 的 show(), hide(), showMaximized(), 以及 setWindowTitle()等方法對窗口實例進行設置。
看著這張圖再對照筆者上面的講解,應該就很清楚了吧。

圖 8-28 使用 QMdiArea 類創建 MDI 時的窗口框架
小貼士:為 QMdiSubWindow 創建內部窗口有兩種方法,一種是調用 addSubWindow(widget),其中 widget 參數將作為內部窗口部件;另一種是先創建一個繼承 自 QWidget 的窗口實例,然后調用 setWidget ( QWidget * widget )方法,把 widget 作為子 窗口的內部窗口部件即可,這個內部窗口部件將被顯示在子窗口的中心區域。注意, QMdiArea 會對其內部的子窗口進行管理,你不必使用代碼顯式的管理它們。
QMdiSubWindow 還有許多專門對應 MDI 應用類型而設置的方法和屬性,大家可以在 Qt Assistant 中獲得詳盡的介紹。
好了,我們詳細解說一下如何采用 QMdiArea 和 QMdiSubWindow 類來創建 MDI 應用程 序,這個例子在本章目錄 mdi 文件夾下面。
第 1 步,包含用到的類
在主窗口的頭文件(如 mainwindow.h)中包含程序中使用到的類,有兩種方法,一是可 以加入頭文件;二是當情況比較簡單時,也可以采用類的前置聲明。
加入頭文件:
```
#include <QMdiArea>
#include <QMdiSubWindow>
...
```
或者采用類前置聲明:
```
class QMdiArea;
class QMdiSubWindow;
...
```
第 2 步,聲明一個 QMdiArea 類對象
在主窗口的頭文件中聲明一個 QMdiArea 類對象,在后面還需要聲明一個你的子窗口類 的對象,代碼如下:
```
QMdiArea *mdiArea;
...
MdiChild *child;//聲明子窗口類的對象
...
```
第 3 步,設置中心窗口部件
在主窗口類的實現文件中(如 mainwindow.cpp,通常在其構造函數中)實例化該對 象,并把它設置為主窗口的中心窗口部件,代碼如下:
```
MainWindow::MainWindow()
{
mdiArea = new QMdiArea; setCentralWidget( mdiArea );
...
}
```
第 4 步,創建子窗口
新建一個子窗口類,它可派生自 QWidget 或其子類,比如 QTextEdit。這個類的實例將 作為子窗口的內部窗口部件。這個子窗口類的創建與我們前面講到的子類化對話框和子類化 QWidget 的方法相同,只是它沒有菜單欄、工具欄和狀態欄。
另外記得在主窗口的頭文件中加入該子窗口類的聲明。
第 5 步,實例化子窗口類,并使用 QMdiArea 對它進行管理,代碼如下:
```
child = new MdiChild;
QMdiSubWindow *subWindow = mdiArea->addSubWindow(child); subWindow->show();
...
```
小貼士:QMdiArea::addSubWindow()函數創建一個新的 QMdiSubWindow,把作為參數傳遞的 該窗口部件放進子窗口中,并且返回該子窗口。最后一行代碼調用 show()方法,使該子窗口可見。
第 6 步,創建并顯示子窗口
這通常是在用戶點擊 File->NewFile 時完成的,代碼如下:
```
void MainWindow::newFile()
{
child->newFile(); child->show();
}
```
整個程序的實現過程可以用圖描述。

圖 8-29 程序的實現過程
小貼士:在 MDI 應用程序中,主窗口類并不需要對文檔進行具體處理,這些工作是在子窗 口類中完成的,相當于在 SDI 應用程序中實現的文檔處理功能。
第 7 步,創建 main.cpp 文件 這步沒有什么好說的,代碼如下:
```
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(mdi);//使用 Qt 資源系統
QApplication app(argc, argv); MainWindow mainWin; mainWin.show();
return app.exec();
}
```
做完這些后,組織好你的工程內的文件(頭文件、實現文件、資源文件、資源集文件 等),編譯、鏈接、運行程序即可。一個典型的 MDI 應用程序界面如圖 8-30 所示。

圖 8-30 使用 QMdiArea 建立的 MDI 應用程序界面
到這里,關于如何使用 Qt4 創建 MDI 應用程序就講解完了。采用 QMdiArea 的方法是筆 者重點講解的,一是因為它是 Qt4.5 版以后官方所推薦采用的方法,用于替代 QWorkspace;二是因為它也是實現 MDI 的方法中使用起來最為復雜和令人困惑的一個。即 使是讓 Qt“老鳥”來解釋清楚什么是“主窗口的中心窗口”、“子窗口的內部窗口”、 “子窗口的中心區域”等等這些名詞以及用法,也不是一件容易的事。
相對而言,使用 QWorkspace 創建 MDI 是比較容易的,但 Qt 以后將不再對這個類繼續更新和支持,而“多實例實現多文檔”的方法在 Mac OS X 上應用很廣泛,它實質上就是使 用了多個頂層窗口,也是比較容易掌握的。
熟練使用 Qt4 建立 MDI 應用程序所涉及的內容遠遠不止本書所介紹的這些,比如子窗 口如何響應鍵盤與鼠標事件,如何同步所有主窗口的 “最近打開文件列表”等等問題都需要 費些力氣解決。對于初學者而言,這是一個比較復雜的話題。大家在閱讀到此處時,建議仍 然采用“知道、會用即可”的原則,不必深究它們背后的機理。隨著學習進程的逐步深入, 一些開始接觸時覺得困難的內容,就會在你心中逐漸明晰了。
- 第 1 章 走近 Qt
- 1.1 Qt 簡介
- 1.2 Qt 紀事概覽
- 1.3 Qt 套件的組成(以 Qt4.5 為準)
- 1.4 Qt 的授權
- 1.5 Qt 的產品
- 1.6 Qt 的服務與支持
- 1.7 Qt 的最新進展
- 1.8為什么選擇 Qt
- 1.9 問題與解答
- 1.10 總結與提高
- 第 2 章 Qt 的安裝與配置
- 2.1 獲取 Qt
- 2.2 協議說明
- 2.3 安裝 Qt
- 2.4 配置 Qt4 環境
- 2.5 問題與解答
- 2.6 總結與提高
- 第 3 章 Qt 編程基礎
- 3.1 標準 C++精講
- 3.2 Windows 編程基礎
- 3.3 Linux 編程基礎
- 3.4 Mac 編程基礎
- 3.5 問題與解答
- 3.6 總結與提高
- 第 4 章 Qt 4 集成開發環境
- 4.1 常見的 Qt IDE
- 4.2 Qt Creator
- 4.3 Eclipse
- 4.5 問題與解答
- 4.6 總結與提高
- 第 5 章 使用 Qt 基本 GUI 工具
- 5.1 使用 Qt Designer 進行 GUI 設計
- 5.2 使用 Qt Assistant 獲取在線文檔與幫助
- 5.3 使用 Qt Demo 學習 Qt 應用程序開發
- 5.4 問題與解答
- 5.5 總結與提高
- 第 6 章 Qt 4 程序開發方法和流程
- 6.1 開發方法
- 6.2 Hello Qt
- 6.3 幾個重要的知識點
- 6.4 問題與解答
- 6.5 總結與提高
- 第 7 章 對話框
- 7.1 QDialog 類
- 7.2 子類化 QDialog
- 7.3 快速設計對話框
- 7.4 常見內建(built in)對話框的使用
- 7.5 模態對話框與非模態對話框
- 7.6 問題與解答
- 7.7 總結與提高
- 第 8 章 主窗口
- 8.1 主窗口框架
- 8.2 創建主窗口的方法和流程
- 8.3 代碼創建主窗口
- 8.4 使用 Qt Designer 創建主窗口
- 8.5 中心窗口部件專題
- 8.6 Qt4 資源系統專題
- 8.7 錨接窗口
- 8.8 多文檔
- 8.9 問題與解答
- 8.10 總結與提高
- 第 9 章 Qt 樣式表與應用程序觀感
- 9.1 應用程序的觀感
- 9.2 QStyle 類的使用
- 9.3 樣式表概述
- 9.4 使用樣式表
- 9.5 問題與解答
- 9.6 總結與提高
- 第 10 章 在程序中使用.ui 文件
- 10.1 uic 的使用
- 10.2 Ui_YourFormName.h 文件的組成
- 10.3 編譯時加入處理.ui 文件的方法
- 10.4 運行時加入處理.ui 文件的方法
- 10.5 信號與槽的自動連接
- 10.6 問題與解答
- 10.7 總結與提高 本章主要講解了以下內容:
- 第 11 章 布局管理
- 11.1 基本概念和方法
- 11.2在 Qt Designer 中使用布局
- 11.3 基本布局實踐
- 11.4 堆棧布局
- 11.5 分裂器布局
- 11.6 自定義布局管理器
- 11.7 布局管理經驗總結
- 11.8 問題與解答
- 11.9 總結與提高
- 第 12 章 使用 Qt Creator
- 12.1 Qt Creator 概覽
- 12.2 Qt Creator 的組成
- 12.3 快捷鍵和常用技巧
- 12.4 Qt Creator 構建系統的設置
- 12.5 處理項目間依賴關系( Dependencies )
- 12.6 Qt 多版本共存時的管理
- 12.7 使用定位器在代碼間快速導航
- 12.8 如何創建一個項目
- 12.9 實例講解
- 12.10 使用 Qt Creator 調試程序
- 12.11 問題與解答
- 12.12 總結與提高
- 第 13 章 Qt 核心機制與原理
- 13.1 Qt 對標準 C++的擴展
- 13.2 信號與槽
- 13.3 元對象系統
- 13.4 Qt 的架構
- 13.5 Qt 的事件模型
- 13.6 構建 Qt 應用程序
- 13.7 總結與提高
- 附錄 A qmake 使用指南
- A.1 qmake 簡介
- A.2 使用 qmake
- 附錄 B make 命令
- B.1 命令解釋
- B.2 使用 make 自動構建
- 附錄 C Qt 資源
- C.1Qt 官方資源
- C.2 Qt 開發社區