Icons in QFileSystemModel



  • I want to create an application which will behave like the one in this gif in terms of handling the icons. It comes from SideFX's Houdini software. The main thing is to make it work with .exr and .hdr formats which are natively not supported on Windows without extensions like SageThumbs.

    I have little idea of how exactly QFileSystemModel works with the OS to pull these icons. I installed SageThumbs to have the icons appear in both Windows explorer and my application, but ideally I don't want the user to install any additional 3rd party extensions. Houdini loaded the icons despite them not appearing in Windows explorer.

    Does anyone have any idea of how this can be done?


  • Qt Champions 2018

    You have to derive from QFileSystemModel and overwrite data() for Qt::DecorationRole. There you can return the icon you want.


  • Lifetime Qt Champion

    Hi,

    I think what you are looking for is to subclass QFileIconProvider.



  • @Christian-Ehrlicher I tried overriding the data function, but I would like to keep most of the default implementation since my changes for now are going to be subtle (e.g concatenating displayName(index) with some metadata).

    //HM_Model.h
    #pragma once
    
    #include <QtWidgets/qfilesystemmodel.h>
    #include <QtCore/qabstractitemmodel.h>
    #include <QtCore/qdatetime.h>
    #include <QtGui/qicon.h>
    #include <qstring.h>
    
    class Model : public QFileSystemModel
    {
    	Q_OBJECT
    
    public:
    
    	Model(QObject *parent = nullptr);
    	~Model();
    	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
    
    private:
    	Q_DECLARE_PRIVATE(QFileSystemModel)
    
    	QFileSystemModel* myModel;
    
    };
    
    
    //HM_Model.cpp
    
    #include "HM_Model.h"
    #include <qfilesystemmodel_p.h>
    
    Model::Model(QObject *parent)
    	: QFileSystemModel(parent)
    {
    }
    Model::~Model() {
    	delete myModel;
    }
    
    QVariant Model::data(const QModelIndex &index, int role) const
    {
    	Q_D(const QFileSystemModel);
    
    		if (!index.isValid() || index.model() != this)
    			return QVariant();
    
    		switch (role) {
    		case Qt::EditRole:
    		case Qt::DisplayRole:
    			switch (index.column()) {
    			case 0: return d->displayName(index);
    			case 1: return d->size(index);
    			case 2: return d->time(index);
    			default:
    				qWarning("data: invalid display value column %d", index.column());
    				break;
    			}
    			break;
    		case FilePathRole:
    			return filePath(index);
    		case FileNameRole:
    			return d->name(index);
    		case Qt::DecorationRole:
    			if (index.column() == 0) {
    				QIcon icon = d->icon(index);
    #if QT_CONFIG(filesystemwatcher)
    				if (icon.isNull()) {
    					if (d->node(index)->isDir())
    						icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::Folder);
    					else
    						icon = d->fileInfoGatherer.iconProvider()->icon(QFileIconProvider::File);
    				}
    #endif // filesystemwatcher
    				return icon;
    			}
    			break;
    		case Qt::TextAlignmentRole:
    			if (index.column() == 1)
    				return QVariant(Qt::AlignRight | Qt::AlignVCenter);
    			else
    				return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    			break;
    		case FilePermissions:
    			int p = permissions(index);
    			return p;
    		}
    
    		return QVariant();
    }
    

    I'm getting following errors:

    error LNK2019: unresolved external symbol "public: class QFileIconProvider * __cdecl QFileInfoGatherer::iconProvider(void)const "
    error LNK2019: unresolved external symbol "public: class QFileSystemModelPrivate::QFileSystemNode * __cdecl QFileSystemModelPrivate::node(class QModelIndex const &)const "
    error LNK2019: unresolved external symbol "public: class QIcon __cdecl QFileSystemModelPrivate::icon(class QModelIndex const &)const "
    error LNK2019: unresolved external symbol "public: class QString __cdecl QFileSystemModelPrivate::name(class QModelIndex const &)const "
    error LNK2019: unresolved external symbol "public: class QString __cdecl QFileSystemModelPrivate::displayName(class QModelIndex const &)const "
    error LNK2019: unresolved external symbol "public: class QString __cdecl QFileSystemModelPrivate::size(class QModelIndex const &)const "
    error LNK2019: unresolved external symbol "public: class QString __cdecl QFileSystemModelPrivate::time(class QModelIndex const &)const "
    


  • @krzysieklfc
    I don't claim to understand the ins & outs of Q_DECLARE_PRIVATE, but are all of these unresolveds virtual functions? Because that's what https://stackoverflow.com/questions/27189472/virtual-functions-causing-link-errors reported. It didn't seem to get answered though --- other than make sure you do a fully clean rebuild? You might want to read https://stackoverflow.com/questions/25250171/how-to-use-the-qts-pimpl-idiom to see if it helps you --- makes by brain ache!

    Just a couple of thoughts while you await an expert!


  • Qt Champions 2018

    @krzysieklfc said in Icons in QFileSystemModel:

    Q_DECLARE_PRIVATE(QFileSystemModel)

    I don't see any reason for this at all...



  • @Christian-Ehrlicher
    Nor did I, but isn't OP using that as his way of accessing private stuff out of QFileSystemModel... I thought that was what he was saying... ?


  • Qt Champions 2018

    @JonB: But it's not needed - he wants to change the icon which can be done in data()...



  • I followed the solution described here, but I'm having a weird problem. QTableView doesn't consistentely update the selection when an item is selected, but it does when the mouse cursor leaves the widget area. I haven't had this problem when I used just QFileSystemInfo with no proxy.
    Initially I thought it's a threading issue, but I get it on a single threaded version too.

    0_1557782374412_problem.gif

    //HM.Model.h
    class ModelProxy : public QIdentityProxyModel {
    
    public:
    	QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    	ModelProxy(QObject *parent = nullptr);
    	virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
    	virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
    	virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    	 QModelIndex setRootPath(const QString& path);
    
    
    private:
    	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);
    	QFileSystemModel* fs() const { return qobject_cast<QFileSystemModel*>(sourceModel()); }
    
    signals:
    	void rootPathChanged(const QString &path);
    
    };
    
    //HM.Model.cpp
    void ModelProxy::onIcon(const QString& path, const QIcon& icon, const QPersistentModelIndex& index)
    {
    	m_icons.insert(path, icon);
    	emit dataChanged(index, index, QVector<int>{QFileSystemModel::FileIconRole});
    }
    
    ModelProxy::ModelProxy(QObject* parent) : QIdentityProxyModel{ parent } {
    
    	setSourceModel(new QFileSystemModel(this));
    	fs()->setRootPath(QDir::rootPath());
    	fs()->setReadOnly(true);
    	fs()->setFilter(QDir::NoDotAndDotDot | QDir::Files);
    	QStringList filters;
    	filters << "*.exr" << "*.hdr";
    	fs()->setNameFilters(filters);
    	fs()->setNameFilterDisables(false);
    
    	connect(this, &ModelProxy::hasIcon, this, &ModelProxy::onIcon);
    	connect(fs(), &QFileSystemModel::rootPathChanged,
    		this, &ModelProxy::rootPathChanged);
    
    }
    
    QVariant ModelProxy::data(const QModelIndex & index, int role) const {
    	switch (role) {
    		case QFileSystemModel::FileIconRole:
    		{
    			if (index.column() == 0) {
    				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 };
    				emit hasIcon(path, getIcon(path), pIndex);
    
    				return QIdentityProxyModel::data(index, role);
    			}
    		}
    		case Qt::DisplayRole:
    		{
    			if (index.column() == 0) {
    				auto path = index.data(QFileSystemModel::FilePathRole).toString();
    				//QFile file(path);
    				//return index.data(QFileSystemModel::FileNameRole).toString() +
    				//	"\n4608x2304\t16 bit float RGB";
    				return QIdentityProxyModel::data(index, role);
    			}
    			return QIdentityProxyModel::data(index, role);
    		}
    		case Qt::FontRole:
    		{
    			return QFont("Roboto", 8);
    		}
    
    		case Qt::TextAlignmentRole:
    		{
    			return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    		}
    	}
    	return QIdentityProxyModel::data(index, role);
    }
    
    QModelIndex ModelProxy::index(int row, int column, const QModelIndex&) const {
    	QModelIndex proxyRootIndex = mapFromSource(fs()->QFileSystemModel::index(fs()->rootPath()));
    	return QIdentityProxyModel::index(row, column, proxyRootIndex);
    }
    
    int ModelProxy::columnCount(const QModelIndex&) const {
    	QModelIndex proxyRootIndex = mapFromSource(fs()->QFileSystemModel::index(fs()->rootPath()));
    	return QIdentityProxyModel::columnCount(proxyRootIndex);
    }
    
    int ModelProxy::rowCount(const QModelIndex&) const {
    	QModelIndex proxyRootIndex = mapFromSource(fs()->QFileSystemModel::index(fs()->rootPath()));
    	return QIdentityProxyModel::rowCount(proxyRootIndex);
    }
    
    QModelIndex ModelProxy::setRootPath(const QString& path)
    {
    	return fs()->setRootPath(path);
    }
    
    

    EDIT:
    The problem was that I set QTableView to a wrong index. The following works:

    QModelIndex getRootIndex() { return mapFromSource(fs()->QFileSystemModel::index(fs()->rootPath())); };
    
    ui.TableView->setRootIndex(model->getRootIndex());
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.