我正在 qt 中为一些自定义设计文件制作文件浏览器。我想将他们的预览加载为他们的缩略图,因此我QIconProvider
用来将图标返回到我的QFileSystemModel
.
问题是创建这些资源的算法QIcon
需要一些资源,因此我的应用程序在完成加载所有缩略图之前没有响应。
我想知道是否有任何方法可以将我QIconProvider
放在后台线程中,以便我的应用程序响应。
我正在 qt 中为一些自定义设计文件制作文件浏览器。我想将他们的预览加载为他们的缩略图,因此我QIconProvider
用来将图标返回到我的QFileSystemModel
.
问题是创建这些资源的算法QIcon
需要一些资源,因此我的应用程序在完成加载所有缩略图之前没有响应。
我想知道是否有任何方法可以将我QIconProvider
放在后台线程中,以便我的应用程序响应。
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);
}
};
公认的答案太棒了——向我介绍了一些更高级的 Qt 概念。
对于将来尝试此操作的任何人,我必须进行一些更改才能使其顺利运行:
QThreadPool
给QConcurrent::run
,将最大线程数设置为 1 或 2。使用默认值会终止应用程序,因为所有线程都会烧毁构建图像预览。瓶颈将是磁盘,因此在此任务上拥有超过 1 或 2 个线程是没有意义的。QIdentityProxyModel::data(index, QFileSystemModel::FileIconRole)
,所以图标在加载完成之前得到了一个不错的默认值QConcurrent::run
任务。我用 astd::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);