2016-08-25 21 views
4

일부 사용자 지정 디자인 파일에 대해 qt 파일 브라우저를 만들고 있습니다. 미리보기 이미지를 미리보기 이미지로로드하고 그 이유로 QIconProvider을 사용하여 아이콘을 QFileSystemModel으로 되 돌리려합니다.QFileSystemModel의 사용자 지정 아이콘을 백그라운드 스레드에 만드는 방법

QIcon을 생성하는 알고리즘은 일부 리소스가 필요하므로 결과적으로 모든 축소판로드가 완료 될 때까지 내 응용 프로그램이 응답하지 않습니다.

QIconProvider을 배경 스레드에 넣을 수있는 방법이 있는지 궁금해서 내 응용 프로그램을 응답하도록 만듭니다.

+1

미리보기 생성 코드를 함수로 패키지화 한 경우 [ 'QtConcurrent :: run'] (http://doc.qt.io/qt-5/qtconcurrentrun.html)를 실행 한 다음 알림을 위해 대기중인 신호를 사용합니다. 대답을 위해 –

답변

5

불행하게도, 거기 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); 
    } 
}; 
+0

보다. 나는 당신의 솔루션을 시도하고 그것은 작동하는 것 같습니다. 하지만 지금은 내 QFileSystemModel에 액세스하는 데 문제가 있습니다. 내가 선택된 QModelIndex의 파일 경로를 액세스하려고하는 동안이 setSourceModel 호출하여 내 QIdentityProxyModel 내 QFileSytemModel을 설정하고 1. 사용해 setModel 를 호출하여 내 QListView 내 QFileIdentityModel 설정 2. 스피 나는 런타임에 호감을 가지고 내 QFileSystemModel. 내가 뭘 잘못하고 있는지에 대한 아이디어가 있습니까? – michalis

+0

내 질문에 대답하고있다 : 내 실수였다. 내 QIdentityProxyModel로 설정된 QListView에서 QModelIndexes를 사용하여 QFileSystemModel의 항목에 액세스하려고 시도했다. 정확한 방법은 QListView가 반환하는 QModelIndex를 사용하여 QIdentityProxyModel에서 항목에 직접 액세스하는 것입니다 (QIdentityProxyModel이 QList 뷰로 설정되기 때문에). – michalis

+0

네 말이 맞아. 인덱스는 특정 모델에 속합니다. 프록시를 사용할 때 원본 모델이 존재한다는 사실을 잊어 버려야합니다. 프록시는 당신이 사용하는 것입니다 - 소스 모델은 구현 세부 사항입니다. –

3

대답이 환상적입니다. 몇 가지 고급 Qt 개념을 소개해 드렸습니다. 최대 스레드가 1 또는 2로 설정하여이하는 QThreadPoolQConcurrent::run에 전달 :

  • 제한 스레드 : 앞으로이 시도 사람들을위한

    , 여기에 내가해야했던 일부 변경이 원활하게 작동이를 얻을 수 있어요 기본값을 사용하면 모든 스레드가 화상 미리보기를 구울 때 응용 프로그램이 종료됩니다. 병목 현상은 디스크이므로이 작업에 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);