今天我們來(lái)看一個(gè)很有用的 model:QDirModel。這個(gè) model 允許我們?cè)?view 中顯示操作系統(tǒng)的目錄結(jié)構(gòu)。這次讓我們先來(lái)看看運(yùn)行結(jié)果:
這個(gè)界面很熟悉吧?不過(guò)這可不是由 QFileDialog 打開(kāi)的哦,這是我們自己實(shí)現(xiàn)的。而提供這種實(shí)現(xiàn)支持的,就是 QDirModel 和 QTreeView。我們來(lái)看一下代碼。
mytreeview.h
#ifndef MYLISTVIEW_H
#define MYLISTVIEW_H
#include <QtGui>
class MyTreeView : public QWidget
{
Q_OBJECT
public:
MyTreeView();
private:
QDirModel *model;
QTreeView *treeView;
private slots:
void mkdir();
void rm();
};
#endif // MYLISTVIEW_H
mytreeview.cpp
#include "mylistview.h"
MyTreeView::MyTreeView()
{
model = new QDirModel;
model->setReadOnly(false);
model->setSorting(QDir::DirsFirst | QDir::IgnoreCase | QDir::Name);
treeView = new QTreeView;
treeView->setModel(model);
treeView->header()->setStretchLastSection(true);
treeView->header()->setSortIndicator(0, Qt::AscendingOrder);
treeView->header()->setSortIndicatorShown(true);
treeView->header()->setClickable(true);
QModelIndex index = model->index(QDir::currentPath());
treeView->expand(index);
treeView->scrollTo(index);
treeView->resizeColumnToContents(0);
QHBoxLayout *btnLayout = new QHBoxLayout;
QPushButton *createBtn = new QPushButton(tr("Create Directory..."));
QPushButton *delBtn = new QPushButton(tr("Remove"));
btnLayout->addWidget(createBtn);
btnLayout->addWidget(delBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(treeView);
mainLayout->addLayout(btnLayout);
this->setLayout(mainLayout);
connect(createBtn, SIGNAL(clicked()), this, SLOT(mkdir()));
connect(delBtn, SIGNAL(clicked()), this, SLOT(rm()));
}
void MyTreeView::mkdir()
{
QModelIndex index = treeView->currentIndex();
if (!index.isValid()) {
return;
}
QString dirName = QInputDialog::getText(this,
tr("Create Directory"),
tr("Directory name"));
if (!dirName.isEmpty()) {
if (!model->mkdir(index, dirName).isValid()) {
QMessageBox::information(this,
tr("Create Directory"),
tr("Failed to create the directory"));
}
}
}
void MyTreeView::rm()
{
QModelIndex index = treeView->currentIndex();
if (!index.isValid()) {
return;
}
bool ok;
if (model->fileInfo(index).isDir()) {
ok = model->rmdir(index);
} else {
ok = model->remove(index);
}
if (!ok) {
QMessageBox::information(this,
tr("Remove"),
tr("Failed to remove %1").arg(model->fileName(index)));
}
}
構(gòu)造函數(shù)中,首先我們創(chuàng)建了 QDirModel 的一個(gè)對(duì)象,并且設(shè)置 ReadOnly 為 false,也就是說(shuō)我們可以對(duì)其進(jìn)行修改。而下一個(gè) setSorting()函數(shù)是對(duì)其進(jìn)行排序,排序的依據(jù)也很清楚:文件夾優(yōu)先(QDir::DirsFirst),忽略大小寫(QDir::IgnoreCase),而且是根據(jù)名字排序(QDir::Name)。更多的規(guī)則組合可以參見(jiàn) API 文檔了。
然后我們創(chuàng)建一個(gè) QTreeView 實(shí)例,并且把 model 設(shè)置為剛剛的 QDirModel 實(shí)例。然后我們開(kāi)始設(shè)置 QTreeView 的相關(guān)屬性。首先把 stretchLastSection 設(shè)置為 true。如果把這個(gè)屬性設(shè)置為true,就是說(shuō),當(dāng) QTreeView 的寬度大于所有列寬之和時(shí),最后一列的寬度自動(dòng)擴(kuò)展以充滿最后的邊界;否則就讓最后一列的寬度保持原始大小。第二個(gè) setSortIndicator()函數(shù)是設(shè)置哪一列進(jìn)行排序。由于我們前面設(shè)置了 model 是按照名字排序,所以我們這個(gè)傳遞的第一個(gè)參數(shù)是0,也就是第1列。setSortIndicatorShown()函數(shù)設(shè)置顯示列頭上面的排序小箭頭。setClickable(true)則允許鼠標(biāo)點(diǎn)擊列頭。這樣,我們的 QTreeView 就設(shè)置完畢了。最后,我們通過(guò) QDir::currentPath()獲取當(dāng)前 exe 文件運(yùn)行時(shí)路徑,并把這個(gè)路徑當(dāng)成程序啟動(dòng)時(shí)顯示的路徑。expand()函數(shù)即展開(kāi)這一路徑;scrollTo()函數(shù)是把視圖的視口滾動(dòng)到這個(gè)路徑的位置;resizeColumnToContents()是要求把列頭適應(yīng)內(nèi)容的寬度,也就是不產(chǎn)生...符號(hào)。這樣,我們就通過(guò)一系列的參數(shù)設(shè)置好了 QTreeView,讓它能夠?yàn)槲覀冋故灸夸浗Y(jié)構(gòu)。
至于后面的兩個(gè) slot,其實(shí)并不能理解。第一個(gè) mkdir()函數(shù)就是創(chuàng)建一個(gè)文件夾。
void MyTreeView::mkdir()
{
QModelIndex index = treeView->currentIndex();
if (!index.isValid()) {
return;
}
QString dirName = QInputDialog::getText(this,
tr("Create Directory"),
tr("Directory name"));
if (!dirName.isEmpty()) {
if (!model->mkdir(index, dirName).isValid()) {
QMessageBox::information(this,
tr("Create Directory"),
tr("Failed to create the directory"));
}
}
}
正如它的代碼所示,首先獲取選擇的目錄。后面這個(gè) isValid()的判斷很重要,因?yàn)槟J(rèn)情況下是沒(méi)有目錄被選擇的,此時(shí)這個(gè)路徑是非法的,為了避免程序出現(xiàn)異常,必須要有這一步判斷。然后會(huì)彈出對(duì)話框詢問(wèn)新的文件夾名字,如果創(chuàng)建失敗會(huì)有提示,否則就是創(chuàng)建成功。這時(shí)候你就可以到硬盤上的實(shí)際位置看看啦!
刪除目錄的代碼也很類似:
void MyTreeView::rm()
{
QModelIndex index = treeView->currentIndex();
if (!index.isValid()) {
return;
}
bool ok;
if (model->fileInfo(index).isDir()) {
ok = model->rmdir(index);
} else {
ok = model->remove(index);
}
if (!ok) {
QMessageBox::information(this,
tr("Remove"),
tr("Failed to remove %1").arg(model->fileName(index)));
}
}
同樣需要實(shí)現(xiàn)檢測(cè)路徑是否合法。另外需要注意的是,目錄和文件的刪除不是一個(gè)函數(shù),需要調(diào)用 isDir()函數(shù)檢測(cè)。這一步在代碼中有很清楚的描述,這里就不再贅述了。
注意,QDirModel 在最新版 Qt 中已經(jīng)不建議使用了。文檔中說(shuō)使用 QFileSystemModel 代替。由于這兩者的函數(shù)幾乎一樣,所以就沒(méi)有對(duì)代碼進(jìn)行修改。與QDirModel 不同的是,QFileSystemModel會(huì)啟動(dòng)自己的線程進(jìn)行文件夾的掃描,因此不會(huì)發(fā)生因掃描文件夾而導(dǎo)致的主線程阻塞的現(xiàn)象。另外,無(wú)論 QDirModel 還是 QFileSystemModel 都會(huì)對(duì) model 結(jié)果進(jìn)行緩存,如果你要立即刷新結(jié)果,前者提供了 refresh()函數(shù),而后者會(huì)通知 QFileSystemWatcher 類。
本文出自 “豆子空間” 博客,請(qǐng)務(wù)必保留此出處 http://devbean.blog.51cto.com/448512/193918
更多建議: