How to add custom items to QFileDialog?

I am using non-native QFileDialog (for choosing directory path) and I need to add some custom drives. I don't even need to display any content inside of these drives for now, I just need to display these drives on the top level (and preferably with my icon) and output some special string in the result when user selects it.

在这里输入图像描述

What is the easiest way to implement this?

I have read in the documentation that proxy model can be used for that, but I don't understand how to implement such model, all examples show only filtering and sorting of the already available items.


If I understand you correctly, you want to add additional drives to the side bar on the left side of the file dialog?

The function you are looking for is 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();
}

The result of this is shown below:

在这里输入图像描述

If, however, the drives you've added don't exist / aren't accessible, then they will be shown greyed out.


General Information

You are right that you need to set a proxy model. Basically your task is to add a row with a QAbstractProxyModel . This is quite a bit more difficult than removing rows.

If we look at the source code of QFileDialog::setProxyModel , we find this:

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

From this we know that QFileDialog has an internal model which is automatically set as the source of the proxy model. Looking in the private header, we find that the source model's type is QFileSystemModel . We can therefore expect that our proxy model needs to be able to supply the same roles as the source model. The docs has a list of them: FileIconRole , FileNameRole , FilePathRole , FilePermissionRole .

To make matters worse, QFileDialog sometimes calls proxyModel.mapToSource() and proxyModel.mapFromSource() to access the source index. Because we want to add a row, our new indexes have no corresponding source index (in the source model). This means we have to write our own implementation of mapToSource and mapFromSource .

Implementation

I would suggest starting from a QIdentityProxyModel because you can use the available methods for all indexes you just pass to the source model.

I don't know exactly how many methods you need to re-implement and how often you can just use the ones provided by QIdentityProxyModel . Start with the easy stuff:

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

and then re-implement the two mapping methods:

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 {
    ...
}

Once this works, you need to do implement at least QAbstractItemModel::data and QAbstractItemModel::flags in a similar manner.

Conclusion

It should be doable but is quite a bit of work where it's very easy to make mistakes. What Qt really seems to need is a way to combine multiple models into one, but I haven't seen such a class yet and therefore you'll have to do this the hard way.

链接地址: http://www.djcxy.com/p/96516.html

上一篇: 你有没有在Java中使用volatile关键字?

下一篇: 如何将自定义项目添加到QFileDialog?