如何将自定义项目添加到 QFileDialog?

Posted

技术标签:

【中文标题】如何将自定义项目添加到 QFileDialog?【英文标题】:How to add custom items to QFileDialog? 【发布时间】:2017-06-15 12:53:39 【问题描述】:

我正在使用非本机 QFileDialog(用于选择目录路径),我需要添加一些自定义驱动器。我现在什至不需要在这些驱动器中显示任何内容,我只需要在顶层显示这些驱动器(最好使用我的图标)并在用户选择它时在结果中输出一些特殊字符串。

最简单的实现方法是什么?

我在documentation 中读到代理模型可以用于此,但我不明白如何实现这样的模型,所有示例仅显示对已可用项目的过滤和排序。

【问题讨论】:

所以你想添加假驱动器?不幸的是,开箱即用的QFileDialog 不提供这种行为。一个选项是构建您自己的对话框并使用自定义 QFileSystemModel 是的。确定它不支持这个?文档说,“如果您想修改底层模型;例如,添加列、过滤数据或添加驱动器”,可以使用代理模型。 我可以看到如何过滤行,但添加行我不确定,抱歉 也许可以尝试编辑底层文件系统模型。 【参考方案1】:

如果我理解正确,您想在文件对话框左侧的侧栏中添加其他驱动器吗?

你要找的函数是QFileDialog::setSidebarUrls

#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QPushButton>
#include <QFileDialog>

int main(int argc, char** argv)

    QApplication app(argc, argv);
    QMainWindow window;

    QWidget widget;
    QHBoxLayout layout(&widget);

    QPushButton open("open");
    layout.addWidget(&open);

    QObject::connect(&open, &QPushButton::clicked, [&]()
        
            QFileDialog dialog;
            dialog.setOption(QFileDialog::DontUseNativeDialog);

            QList<QUrl> drives;
            drives << QUrl::fromLocalFile(QDir("D:").absolutePath());
            drives << QUrl::fromLocalFile(QDir("E:").absolutePath());
            drives << QUrl::fromLocalFile(QDir("foobar").absolutePath());
            dialog.setSidebarUrls(drives);

            dialog.exec();
        );

    window.setCentralWidget(&widget);
    window.show();
    return app.exec();

这样的结果如下所示:

但是,如果您添加的驱动器不存在/不可访问,则它们将显示为灰色。

【讨论】:

哦,我想这也是一个不错的选择,但是我实际上想将它添加到显示所有 Windows 驱动器的目录层次结构的顶层(也称为“我的电脑”)。它是仅限 Windows 的应用程序。为我的问题添加了屏幕截图。【参考方案2】:

一般信息

你是对的,你需要设置一个代理模型。 基本上,您的任务是添加带有QAbstractProxyModel 的行。这比删除行要困难得多。

如果我们查看QFileDialog::setProxyModel的源代码,我们会发现:

proxyModel->setParent(this);
d->proxyModel = proxyModel;
proxyModel->setSourceModel(d->model);

由此我们知道QFileDialog有一个内部模型,它被自动设置为代理模型的来源。查看private header,我们发现源模型的类型是QFileSystemModel。因此,我们可以期望我们的代理模型需要能够提供与源模型相同的角色。 docs 有一个列表:FileIconRoleFileNameRoleFilePathRoleFilePermissionRole

更糟糕的是,QFileDialogsometimes calls proxyModel.mapToSource() and proxyModel.mapFromSource() 访问源索引。因为我们要添加一行,所以我们的新索引没有对应的源索引(在源模型中)。这意味着我们必须编写自己的 mapToSourcemapFromSource 实现。

实施

我建议从QIdentityProxyModel 开始,因为您可以对刚刚传递给源模型的所有索引使用可用的方法。

我不确切知道您需要重新实现多少方法,以及您多久可以使用QIdentityProxyModel 提供的方法。从简单的东西开始:

int MyDriveProxyModel::rowCount(const QModelIndex &parent = QModelIndex()) const 
    if (parent.isValid()) 
        return QIdentityProxyModel::rowCount(parent);
     else 
        return QIdentityProxyModel::rowCount(parent) + 1;
    

然后重新实现这两种映射方式:

QModelIndex MyDriveProxyModel::mapToSource(const QModelIndex &proxyIndex) const 
    if (this_index_belongs_to_the_added_row)  // there are many ways for this
        return this->createIndex(proxyIndex.row(), proxyIndex.column(), /* some_data */);
    
    return QIdentityProxyModel::mapToSource(proxyIndex);


QModelIndex MyDriveProxyModel::mapFromSource(const QModelIndex &proxyIndex) const 
    ...

一旦这工作,您需要以类似的方式至少实现QAbstractItemModel::dataQAbstractItemModel::flags

结论

这应该是可行的,但工作量很大,很容易出错。 Qt 真正需要的似乎是将多个模型组合成一个的方法,但我还没有见过这样的类,因此你必须努力做到这一点。

【讨论】:

以上是关于如何将自定义项目添加到 QFileDialog?的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义密码编码器添加到 Spring Security?

如何将自定义标头从 mvc 项目发送到 Web api 项目?

如何将自定义模型添加到 django celery

如何将自定义 UIButton 添加到 Interface Builder 对象库?

将自定义元素添加到 ngRepeat 列表

如何将自定义字体系列添加到 react-reader npm