我在qt中爲某些自定義設計文件製作文件瀏覽器。我想將其預覽作爲縮略圖加載,因此我使用QIconProvider
將圖標返回到我的QFileSystemModel
。如何在後臺線程中爲QFileSystemModel創建自定義圖標
問題是,創建QIcon
的算法需要一些資源,因此我的應用程序在加載完所有縮略圖之前無法響應。
我想知道是否有任何方法可以將我的QIconProvider
放在後臺線程中,以便我的應用程序響應。
我在qt中爲某些自定義設計文件製作文件瀏覽器。我想將其預覽作爲縮略圖加載,因此我使用QIconProvider
將圖標返回到我的QFileSystemModel
。如何在後臺線程中爲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);
}
};
比你的迴應。我試過你的解決方案,它似乎工作。但是現在我在訪問我的QFileSystemModel時遇到了問題。 1.我通過調用setSourceModel設置我的QFileSytemModel我QIdentityProxyModel 2.蔭通過調用則setModel 設置我的QFileIdentityModel我而QListView和我有運行時暗戀,而我試圖訪問的選擇QModelIndex的文件路徑我的QFileSystemModel。 關於我在做什麼錯的任何想法? – michalis
我回答了我自己的問題: 這是我的錯誤,我試圖通過使用QModelIndexes從我的QListView,它已被設置爲我的QIdentityProxyModel訪問QFileSystemModel的項目。 正確的方法是使用QListView返回的QModelIndex直接從QIdentityProxyModel訪問項目(因爲QIdentityProxyModel設置爲您的QList視圖)。 – michalis
你說得對。索引屬於特定的模型。當你使用代理時,你應該幾乎忘記了源模型的存在。代理是你使用的 - 有一個源模型是一個實現細節:) –
接受的答案是太棒了 - 向我介紹了一些更高級的Qt的概念。
對於任何人在將來嘗試這一點,這裏的一些變化,我不得不讓得到這個工作順利進行:
QThreadPool
到QConcurrent::run
,與最大線程數設置爲1或2 。使用默認值殺死應用程序,因爲所有線程都會燒燬建築物圖像預覽。瓶頸將是磁盤,因此不會使 意識到此任務上有超過1或2個線程。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);
如果您預覽生成的代碼打包到那麼功能看看它傳遞給[ 'QtConcurrent :: run'](http://doc.qt.io/qt-5/qtconcurrentrun。html)進行後臺執行,然後使用排隊的信號進行通知。 –