#(17):文件對話框
在前面的章節中,我們討論了 Qt 標準對話框`QMessageBox`的使用。所謂標準對話框,其實也就是一個普通的對話框。因此,我們同樣可以將`QDialog`所提供的其它特性應用到這種標準對話框上面。今天,我們繼續討論另外一個標準對話框:`QFileDialog`,也就是文件對話框。在本節中,我們將嘗試編寫一個簡單的文本文件編輯器,我們將使用`QFileDialog`來打開一個文本文件,并將修改過的文件保存到硬盤。這或許是我們在本系列中所提供的第一個帶有實際功能的實例。
首先,我們需要創建一個帶有文本編輯功能的窗口。借用我們前面的程序代碼,應該可以很方便地完成:
~~~
openAction = new QAction(QIcon(":/images/file-open"), tr("&Open..."), this);
openAction->setShortcuts(QKeySequence::Open);
openAction->setStatusTip(tr("Open an existing file"));
saveAction = new QAction(QIcon(":/images/file-save"), tr("&Save..."), this);
saveAction->setShortcuts(QKeySequence::Save);
saveAction->setStatusTip(tr("Save a new file"));
QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
file->addAction(saveAction);
QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);
toolBar->addAction(saveAction);
textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
~~~
我們在菜單和工具欄添加了兩個動作:打開和保存。接下來是一個`QTextEdit`類,這個類用于顯示富文本文件。也就是說,它不僅僅用于顯示文本,還可以顯示圖片、表格等等。不過,我們現在只用它顯示純文本文件。`QMainWindow`有一個`setCentralWidget()`函數,可以將一個組件作為窗口的中心組件,放在窗口中央顯示區。顯然,在一個文本編輯器中,文本編輯區就是這個中心組件,因此我們將`QTextEdit`作為這種組件。
我們使用`connect()`函數,為這兩個`QAction`對象添加響應的動作:
~~~
/// !!!Qt5
connect(openAction, &QAction::triggered, this, &MainWindow::openFile);
connect(saveAction, &QAction::triggered, this, &MainWindow::saveFile);
/// !!!Qt4
connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));
connect(saveAction, SIGNAL(triggered()), this, SLOT(saveFile()));
~~~
這些應該都不是問題。我們應該能夠很清楚這些代碼的含義。下面是最主要的`openFile()`和`saveFile()`這兩個函數的代碼:
~~~
void MainWindow::openFile()
{
QString path = QFileDialog::getOpenFileName(this,
tr("Open File"),
".",
tr("Text Files(*.txt)"));
if(!path.isEmpty()) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("Read File"),
tr("Cannot open file:\n%1").arg(path));
return;
}
QTextStream in(&file);
textEdit->setText(in.readAll());
file.close();
} else {
QMessageBox::warning(this, tr("Path"),
tr("You did not select any file."));
}
}
void MainWindow::saveFile()
{
QString path = QFileDialog::getSaveFileName(this,
tr("Open File"),
".",
tr("Text Files(*.txt)"));
if(!path.isEmpty()) {
QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::warning(this, tr("Write File"),
tr("Cannot open file:\n%1").arg(path));
return;
}
QTextStream out(&file);
out << textEdit->toPlainText();
file.close();
} else {
QMessageBox::warning(this, tr("Path"),
tr("You did not select any file."));
}
}
~~~
在`openFile()`函數中,我們使用`QFileDialog::getOpenFileName()`來獲取需要打開的文件的路徑。這個函數具有一個長長的簽名:
~~~
QString getOpenFileName(QWidget * parent = 0,
const QString & caption = QString(),
const QString & dir = QString(),
const QString & filter = QString(),
QString * selectedFilter = 0,
Options options = 0)
~~~
不過注意,它的所有參數都是可選的,因此在一定程度上說,這個函數也是簡單的。這六個參數分別是:
* parent:父窗口。我們前面介紹過,Qt 的標準對話框提供靜態函數,用于返回一個模態對話框(在一定程度上這就是外觀模式的一種體現);
* caption:對話框標題;
* dir:對話框打開時的默認目錄,“.” 代表程序運行目錄,“/” 代表當前盤符的根目錄(特指 Windows 平臺;Linux 平臺當然就是根目錄),這個參數也可以是平臺相關的,比如“C:\\”等;
* filter:過濾器。我們使用文件對話框可以瀏覽很多類型的文件,但是,很多時候我們僅希望打開特定類型的文件。比如,文本編輯器希望打開文本文件,圖片瀏覽器希望打開圖片文件。過濾器就是用于過濾特定的后綴名。如果我們使用“Image Files(*.jpg *.png)”,則只能顯示后綴名是 jpg 或者 png 的文件。如果需要多個過濾器,使用“;;”分割,比如“JPEG Files(*.jpg);;PNG Files(*.png)”;
* selectedFilter:默認選擇的過濾器;
* options:對話框的一些參數設定,比如只顯示文件夾等等,它的取值是`enum QFileDialog::Option`,每個選項可以使用 | 運算組合起來。
`QFileDialog::getOpenFileName()`返回值是選擇的文件路徑。我們將其賦值給 path。通過判斷 path 是否為空,可以確定用戶是否選擇了某一文件。只有當用戶選擇了一個文件時,我們才執行下面的操作。在`saveFile()`中使用的`QFileDialog::getSaveFileName()`也是類似的。使用這種靜態函數,在 Windows、Mac OS 上面都是直接調用本地對話框,但是 Linux 上則是`QFileDialog`自己的模擬。這暗示了,如果你不使用這些靜態函數,而是直接使用`QFileDialog`進行設置,就像我們前面介紹的 QMessageBox 的設置一樣,那么得到的對話框很可能與系統對話框的外觀不一致。這一點是需要注意的。
首先,我們創建一個`QFile`對象,將用戶選擇的文件路徑傳遞給這個對象。然后我們需要打開這個文件,使用的是`QFile::open()`,其參數是指定的打開方式,這里我們使用只讀方式和文本方式打開這個文件(因為我們選擇的是后綴名 txt 的文件,可以認為是文本文件。當然,在實際應用中,可能需要進行進一步的判斷)。`QFile::open()`打開成功則返回 true,由此繼續進行下面的操作:使用`QTextStream::readAll()`讀取文件所有內容,然后將其賦值給`QTextEdit`顯示出來。最后不要忘記關閉文件。另外,`saveFile()`函數也是類似的,只不過最后一步,我們使用`<<`重定向,將`QTextEdit`的內容輸出到一個文件中。關于文件操作,我們會在后面的章節中進一步介紹。
這里需要注意一點:我們的代碼僅僅是用于演示,很多必須的操作并沒有進行。比如,我們沒有檢查這個文件的實際類型是不是一個文本文件。并且,我們使用了`QTextStream::readAll()`直接讀取文件所有內容,如果這個文件有 100M,程序會立刻死掉,這些都是實際程序必須考慮的問題。不過這些內容已經超出我們本章的介紹,也就不再詳細說明。
至此,我們的代碼已經介紹完畢,馬上可以編譯運行一下了:
[](http://files.devbean.net/images/2012/09/qfiledialog-kde.png)
本章的代碼可以在這里下載:
* [filedialog-qt4.zip](http://files.devbean.net/code/filedialog-qt4.zip)
* [filedialog-qt5.zip](http://files.devbean.net/code/filedialog-qt5.zip)
- (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(續)