【问题标题】:How to create custom icons for QFileSystemModel in a background thread如何在后台线程中为 QFileSystemModel 创建自定义图标
【发布时间】:2017-01-01 20:21:02
【问题描述】:

我正在 qt 中为一些自定义设计文件制作文件浏览器。我想将他们的预览加载为他们的缩略图,因此我使用QIconProvider 将图标返回到我的QFileSystemModel

问题是创建QIcon 的算法需要一些资源,因此我的应用程序在加载完所有缩略图之前没有响应。

我想知道是否有任何方法可以将我的 QIconProvider 放在后台线程中,以便我的应用程序响应。

【问题讨论】:

  • 如果您将预览生成代码打包为一个函数,请考虑将其传递给 QtConcurrent::run 以进行后台执行,然后使用排队信号进行通知。

标签: qt thumbnails background-thread qfilesystemmodel qicon


【解决方案1】:

不幸的是,QFileIconProvider API 和模型 api 之间存在阻抗不匹配:QFileSystemModel 在事情发生变化时向视图提供异步通知,但图标提供者无法在图标发生变化或变为时异步通知模型已知。

您可以在文件系统模型和视图之间安装身份代理。然后,该代理的 data 方法将异步查询图标。模型的同步图标提供程序将不再使用且不必要。

// https://github.com/KubaO/stackoverflown/tree/master/questions/icon-proxy-39144638
#include <QtWidgets>
#include <QtConcurrent>

/// A thread-safe function that returns an icon for an item with a given path.
/// If the icon is not known, a null icon is returned.
QIcon getIcon(const QString & path);

class IconProxy : public QIdentityProxyModel {
    Q_OBJECT
    QMap<QString, QIcon> m_icons;
    Q_SIGNAL void hasIcon(const QString&, const QIcon&, const QPersistentModelIndex& index) const;
    void onIcon(const QString& path, const QIcon& icon, const QPersistentModelIndex& index) {
        m_icons.insert(path, icon);
        emit dataChanged(index, index, QVector<int>{QFileSystemModel::FileIconRole});
    }
public:
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override {
        if (role == QFileSystemModel::FileIconRole) {
            auto path = index.data(QFileSystemModel::FilePathRole).toString();
            auto it = m_icons.find(path);
            if (it != m_icons.end()) {
                if (! it->isNull()) return *it;
                return QIdentityProxyModel::data(index, role);
            }
            QPersistentModelIndex pIndex{index};
            QtConcurrent::run([this,path,pIndex]{
                emit hasIcon(path, getIcon(path), pIndex);
            });
            return QVariant{};
        }
        return QIdentityProxyModel::data(index, role);
    }
    IconProxy(QObject * parent = nullptr) : QIdentityProxyModel{parent} {
        connect(this, &IconProxy::hasIcon, this, &IconProxy::onIcon);
    }
};

【讨论】:

  • 感谢您的回复。我尝试了您的解决方案,它似乎有效。但是现在我在访问我的 QFileSystemModel 时遇到了问题。 1. 我通过调用 setSourceModel 将我的 QFileSytemModel 设置为我的 QIdentityProxyModel 2. 我通过调用 setModel 将我的 QFileIdentityModel 设置为我的 QListView 并且我在尝试访问我的 QFileSystemModel 的选定 QModelIndex 的文件路径时迷恋运行时。关于我做错了什么有什么想法吗?
  • 我正在回答我自己的问题:这是我的错误,我试图使用 QListView 中的 QModelIndexes 访问 QFileSystemModel 的项目,该 QListView 已设置为我的 QIdentityProxyModel。正确的方法是使用 QListView 返回的 QModelIndex 直接从 QIdentityProxyModel 访问项目(因为 QIdentityProxyModel 设置为 QList View)。
  • 你是对的。索引属于特定模型。当您使用代理时,您几乎应该忘记源模型的存在。代理就是你使用的 - 有一个源模型是一个实现细节:)
  • @KubaOrder:我有什么方法可以返回 QFileSystemModel 的默认图标(例如在文件夹的情况下)?我试图让 getIcon 返回 'QIdentityProxyModel::data(index, QFileSystemModel::FileIconRole).value();'在文件夹的情况下,但它不起作用。
  • 我已经更新了答案。 getIcon 对任何模型一无所知。如果它不知道如何获取图标,它可以返回一个默认构造的,然后代理将请求转发给源模型以获得默认图标。
【解决方案2】:

接受的答案很棒 - 向我介绍了一些更高级的 Qt 概念。

对于将来尝试此操作的任何人,我必须进行一些更改才能使其顺利运行:

  • 限制线程:QThreadPool 传递给QConcurrent::run,将最大线程数设置为1 或2。使用默认值会终止应用程序,因为所有线程都会在构建图像预览时被烧毁。瓶颈将是磁盘,所以不会 感觉这个任务有超过 1 或 2 个线程。
  • 避免重入:需要处理多次查询同一路径的图标才完成图标生成的情况。当前代码将产生多个线程,生成相同的图标。简单的解决方案是在 QConcurrent::run 调用之前向 m_icons 映射添加一个占位符条目。我刚刚调用了默认的QIdentityProxyModel::data(index, QFileSystemModel::FileIconRole),所以图标在加载完成之前得到了一个不错的默认值
  • 任务取消:如果您销毁模型(或想要切换视图文件夹等),您将需要一种取消活动任务的方法。不幸的是,没有内置方法可以取消挂起的QConcurrent::run 任务。我使用std::atomic_bool 发出取消信号,任务在执行前对其进行检查。还有一个 std::condition_variable 等待,直到所有任务都被取消/完成。

提示:我的用例是从磁盘上的图像加载缩略图预览(可能是常见用例)。经过一些实验,我发现生成预览最快的方法是使用QImageReader,将您的缩略图大小传递给setScaledSize。请注意,如果您有非方形图像,则需要传递具有适当纵横比的尺寸,如下所示:

    const QSize originalSize = reader.size(); // Note: Doesn't load the file contents
    QSize scaledSize = originalSize;
    scaledSize.scale(MaximumIconSize, Qt::KeepAspectRatio);
    reader.setScaledSize(scaledSize);

【讨论】:

  • 您知道如何将 options.rect 从 ItemDelegate::paint() 传递给数据模型,以便模型可以处理缩略图生成吗?我需要缩放+裁剪的缩略图适合 ItemDelegate 内的矩形。
猜你喜欢
  • 2015-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 2012-01-08
  • 1970-01-01
相关资源
最近更新 更多