《Qt编程的艺术》——8.2.2 实现文件选择对话框里面的功能

在给头文件filedialog.h添加include保护(include guard)后的内容,包含了FileDialog类的声明,我们包含(#include)每一个uic从UI文件生成的类定义。前向类声明使得免于读相应类的头文件(这招貌似只在头文件里有用):
// filedialog/filedialog.h
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
#include "ui_filedialog.h"
class QModelIndex;
class QDirModel;
class QItemSelectionModel;
class FileDialog: public QDialog, private Ui::FileDialog {
    Q_OBJECT
public:
    FileDialog(QWidget *parent = 0);
...
private:
    QItemSelectionModel *selModel;
    QDirModel *dirModel;
};
#endif // FILEDIALOG_H
就如同QDirModel一样,我们现在需要一个选择模型,能够管理、比较views返回给它的选择。相应地,我们在FileDialog的构造函数里面,除了目录模型,再创建一个QItemSelectionModel,代码位于在我们调用setupUi()初始化Designer生成的widget之后。要实现这个功能,我们把目录模型指定给QItemSelectionModel的构造函数。这样,选择模型识别了数据源,即可管理条目。
 
现在我们指定所有的view以相同的模型,通过setModel()并且指定相应的选择模型通过setSelectionModel()。后一个步骤保证了所有三个view打开了相同的选项:如果你在一个view选择了几个文件,这些文件自动在其他两个视图里面也亮了:
// filedialog/filedialog.cpp
#include <QtGui>
#include "filedialog.h"
FileDialog::FileDialog(QWidget *parent)
    : QDialog(parent)
{
    setupUi(this);
    dirModel =new QDirModel;
    selModel =new QItemSelectionModel(dirModel);
    listView->setModel(dirModel);
    treeView->setModel(dirModel);
    iconView->setModel(dirModel);
    listView->setSelectionModel(selModel);
    treeView->setSelectionModel(selModel);
    iconView->setSelectionModel(selModel);
    QModelIndex cwdIndex = dirModel->index(QDir::rootPath());
    listView->setRootIndex(cwdIndex);
    treeView->setRootIndex(cwdIndex);
    iconView->setRootIndex(cwdIndex);
这些Views任然需要模型的入口点,所以,我们使用setRootIndex()设置。这个函数需要一个QModelIndex作为参数,但是我们在这里使用了文件系统的语义(dirModel)。要调和这两个“世界”(model中的条目和filesystem中的条目),我们将QDirModel中的index()方法重载,作为一个过渡者:它接收一个文件路径,寻找model中匹配的索引,并将它返回。
 
然后我们把Designer中生成的组合框用根目录填充起来。因为QDir::rootPath()包含了Windows的c盘,问题就来了,我们如何得到可用的盘符的表?作为答案,我们建议说点简短的题外话,关于通常而言的model函数和QDirModel独特的功能。

 一个model基本上包含两维结构:在QDirModel中,每一行表示一个文件条目,而每一列则包含了一个文件的属性(名称、大小、创建日期)。如果一个文件条目又指向了一个有效地QModelIndex,这表示一个子目录。就像图8.9所示的一样,这就形成了一个第三维度,就像图中表示的一样。尽管list和table views不能显示出这种附加的结构层次,tree views在视觉上从源表示数据项目(也就是文件系统),也就是说他们自己允许QModelIndex对象作为子树。这就解释了file dialog构造函数中的下一部分代码:
// filedialog/filedialog.cpp (continued)
for(intr= 0; r <dirModel->rowCount(QModelIndex());++r) {
    QModelIndex index =dirModel->index(r,0, QModelIndex());
    if (index.isValid())
        comboBox->addItem(dirModel->fileIcon(index),dirModel->filePath(index));
}
 一个无效的(也就是空的)QModelIndex意味着model应当选择文件系统的根级作为开始的index。在我们的案例中,这一级包含了Windows下所有的分区,在Linux里面,只有目录树。我们通过rowCount()决定分区条目的数量,因此有了这些知识,我们就能迭代出来所有条目。为此目的,我们使用第零列,既然QModelIndex向我们提供了这些位置需要的信息。为了保险起见,我们检查index是否真的有效。在这个案例中,我们向组合框添加一条条目以对应分区。
 
要能够让model工作,我们需要一些槽,我们在头文件里声明他们,在构造函数的后面:
// filedialog/filedialog.h(replenished)
...
    protected slots:
    void switchToDir(const QModelIndex& index);
    void syncActive(const QModelIndex& index);
    void switchView();
...
 switchToDir()应当响应鼠标点击并且更新其它的list view,因此他们也能显示选择的目录。syncActive()比较活动的条目,换句话说,所有三个view中颜色高亮显示的那个,并且在treeView的对应分支打开,而switchView()响应鼠标开关,并且在vews间切换,把被stacked的放到最顶上来
 
我们现在需要连接构造函数里每个新槽跟三个view中的activated()信号。switchToDir()和syncActive()需要QmodelIndex作为参数,引用的是新的目录。最终我们指挥Qt,如果点击了Designer中定义的Switch按钮就调用switchView()槽:
// filedialog/filedialog.cpp (continued)
    connect(listView, SIGNAL(activated(const QModelIndex&)),
        SLOT(switchToDir(const QModelIndex&)));
    connect(treeView, SIGNAL(activated(const QModelIndex&)),
        SLOT(switchToDir(const QModelIndex&)));
    connect(iconView, SIGNAL(activated(const QModelIndex&)),
        SLOT(switchToDir(const QModelIndex&)));
    connect(listView, SIGNAL(clicked(const QModelIndex&)),
        SLOT(syncActive(const QModelIndex&)));
    connect(treeView, SIGNAL(clicked(const QModelIndex&)),
        SLOT(syncActive(const QModelIndex&)));
    connect(iconView, SIGNAL(clicked(const QModelIndex&)),
        SLOT(syncActive(const QModelIndex&)));
    connect(switchButton, SIGNAL(clicked()),SLOT(switchView()));
}
 构造函数现在完成了,我们把注意力放在槽函数的实现上:在switchToDir()我们首先检查传递来的index是否真的是一个目录。QDirModel自身就包含了合适的方法。如果是的话,我们设置model中的开始index到新的目录。注意:我们并不需要在treeView中切换,因为这种view应该总是显示所有的分区内容。既然tree view使用了相同的selection model和其他的views,它自动地显示选择的条目:
// filedialog/filedialog.cpp (continued)
void FileDialog::switchToDir(const QModelIndex& index)
{
    if (dirModel->isDir(index)) {
        listView->setRootIndex(index);
        iconView->setRootIndex(index);
    }
}
 syncActive()在所有的3个viewsonic中比较活动的条目。对应的QAbstractItemView中的API是CurrentIndex():
// filedialog/filedialog.cpp (continued)
void FileDialog::syncActive(const QModelIndex& index)
{
    listView->setCurrentIndex(index);
    treeView->setCurrentIndex(index);
    iconView->setCurrentIndex(index);
}
 在views间切换的槽函数只有一行长度:它需要当前Widget的index,然后加1,要保证index不超出上限,我们添加一个取摸操作:
// filedialog/filedialog.cpp (continued)
void FileDialog::switchView()
{
    stackedWidget->setCurrentIndex(
        (stackedWidget->currentIndex()+1)%stackedWidget->count());
}
 最终,我们使得选中的文件对用户有效。要完成这个,我们定义一个叫做selectedFiles()的方法。在dialog结束——当用户按下打开建(Designer自动跟accept()槽联系起来了)——你可以读出选择的文件名,以QStringList格式,使用FileDialog方法:
// filedialog/filedialog.cpp (continued)
QStringList FileDialog::selectedFiles()
{
    QStringList fileNames;
    QModelIndexList indexes = selModel->selectedIndexes();
    foreach( QModelIndex index,indexes)
    fileNames.append( dirModel->filePath(index) );
    returnfileNames;
}
 返回的条目反映了selection model:selectedIndexes()返回从QDirModel实例中选择的QmodelIndex条目。有了模型提供的FilePath()方法,我们可以得到文件路径。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值