11.文件對話框
在前面的章節中,我們討論了 Qt 標準對話框QMessageBox
的使用。所謂標準對話框,其實也就是一個普通的對話框。因此,我們同樣可以將QDialog
所提供的其它特性應用到這種標準對話框上面。今天,我們繼續討論另外一個標準對話框:QFileDialog
,也就是文件對話框。在本節中,我們將嘗試編寫一個簡單的文本文件編輯器,我們將使用QFileDialog
來打開一個文本文件,並將修改過的文件保存到硬盤。這或許是我們在本系列中所提供的第一個帶有實際功能的實例。
首先,我們需要創建一個帶有文本編輯功能的窗口。借用我們前面的程序代碼,應該可以很方便地完成:
123456789101112131415161718 | 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-> |
我們在菜單和工具欄添加了兩個動作:打開和保存。接下來是一個QTextEdit
類,這個類用於顯示富文本文件。也就是說,它不僅僅用於顯示文本,還可以顯示圖片、表格等等。不過,我們現在只用它顯示純文本文件。QMainWindow
有一個setCentralWidget()
函數,可以將一個組件作為窗口的中心組件,放在窗口中央顯示區。顯然,在一個文本編輯器中,文本編輯區就是這個中心組件,因此我們將QTextEdit
作為這種組件。
我們使用connect()
函數,為這兩個QAction
對象添加響應的動作:
1234567 | /// !!!Qt5connect(openAction, &QAction::triggered, this, &MainWindow::openFile);connect(saveAction, &QAction::triggered, this, &MainWindow::saveFile); /// !!!Qt4connect(openAction, SIGNAL(triggered()), this, SLOT(openFile()));connect(saveAction, SIGNAL(triggered()), this, SLOT(saveFile())); |
這些應該都不是問題。我們應該能夠很清楚這些代碼的含義。下面是最主要的openFile()
和saveFile()
這兩個函數的代碼:
12345678910111213141516171819202122232425262728293031323334353637383940414243 | 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()
來獲取需要打開的文件的路徑。這個函數具有一個長長的簽名:
123456 | 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,程序會立刻死掉,這些都是實際程序必須考慮的問題。不過這些內容已經超出我們本章的介紹,也就不再詳細說明。
至此,我們的代碼已經介紹完畢,馬上可以編譯運行一下了:
11.文件對話框