0

I have a file browser system based on QFileSystemModel + QListView. I use a custom QFileIconProvider to use the specified picture as the icon.
enter image description here

class ListIconProvider(QtWidgets.QFileIconProvider):
    def icon(self, fileInfo):
        try:
            fn = fileInfo.filePath()
            pix_map = QtGui.QPixmap(QtCore.QSize(128,128))
            f = os.path.join(fn,"preview.png")
            pix_map.load(f)
            return QtGui.QIcon(pix_map)
        except:
            return super(ListIconProvider, self).icon(QtWidgets.QFileIconProvider.Folder)

When I update the picture of "pTorus3", the icon in the listView is not refreshed. I tried this code but it didn't work.

# ...
model = index.model()
# ...
model.setData(index, QtGui.QIcon("New_preview.png"), QtCore.Qt.DecorationRole)

How to refresh a QListView item's icon?

UPDATE1: more test.

listView.update(index) # not work.

UPDATE2: a minimal, reproducible example
This script can list all subfolders under "ROOT". If there is a "preview.png" file in this subfolder, it will be displayed in QListView as the icon.

Now I copied a new "preview.png" file to overwrite the old one, the QListView still displays the previous "preview.png". How do I refresh it manually?

import sys
import os

from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from PySide2.QtUiTools import QUiLoader 

UI_FILE = "F:/tmp/untitled.ui"
ROOT = "F:/tmp"

class ListIconProvider(QFileIconProvider):
    def icon(self, fileInfo):
        try:
            fn = fileInfo.filePath()
            pix_map = QPixmap(QSize(128,128))
            f = os.path.join(fn, "preview.png")
            pix_map.load(f)
            return QIcon(pix_map)
        except:
            return super(ListIconProvider, self).icon(QFileIconProvider.Folder)
            
class AMWidget(QWidget):
    def __init__(self, parent=None):
        super(AMWidget, self).__init__(parent)
        
        """
            load UI file
        """
        ui_file = QFile(UI_FILE)
        ui_file.open(QFile.ReadOnly)
        loader = QUiLoader()
        self.ui = loader.load(ui_file, parentWidget=self)
        ui_file.close()

        """
            model 
        """
        model = QFileSystemModel()
        model.setFilter(QDir.NoDotAndDotDot | QDir.Dirs)
        model.setRootPath(ROOT)
        model.setIconProvider(ListIconProvider())
        model.setReadOnly(False)
        """
            view 
        """
        self.view = QListView()
        self.view.setModel(model)
        self.view.setRootIndex(model.index(ROOT))
        self.view.setViewMode(QListView.IconMode)
        """ 
            test button
        """
        btn = QPushButton("send dataChanged() singal.")
        btn.clicked.connect(self.update_view)
        """
            layout 
        """
        layout = QVBoxLayout(self)
        layout.addWidget(self.view)
        layout.addWidget(btn)

    def update_view(self):
        print("update_view ...")
        index = self.view.currentIndex()
        model = index.model()
        model.dataChanged.emit(index, index, Qt.DecorationRole)

## run =============================== ##

if __name__ == '__main__':

    if not QApplication.instance():
        app = QApplication(sys.argv)
    else:
        app = QApplication.instance()

    widget = AMWidget()
    widget.show()

    sys.exit(app.exec_())

untitled.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>352</width>
    <height>218</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout"/>
 </widget>
 <resources/>
 <connections/>
</ui>
g2m.agent
  • 170
  • 1
  • 11
  • You don't usually need to subclass QFileIconProvider for such a task, just override `data()` of the model and provide your value for the `DecorationRole`. In any case, if you're setting data internally, you should emit the `dataChanged` signal. See [PyQt - Automatically refresh a custom view when the model is updated?](https://stackoverflow.com/questions/13999970/pyqt-automatically-refresh-a-custom-view-when-the-model-is-updated). Otherwise, as *we almost always request you*, you should provide a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – musicamante Feb 16 '21 at 09:09
  • `model.dataChanged(index,index,QtCore.Qt.DecorationRole)` # Error: TypeError: file line 1: native Qt signal is not callable # – g2m.agent Feb 16 '21 at 13:12
  • `model.dataChanged.emit(index, index, [QtCore.Qt.DecorationRole])` – musicamante Feb 16 '21 at 13:14
  • return `True`, but seems nothing happened... – g2m.agent Feb 16 '21 at 13:16
  • The model returns True if the data is correctly applied. And, again, you should probably override `data()`. Finally, as already said, provide a MRE. – musicamante Feb 16 '21 at 13:29
  • see UPDATE2, it took me 1 hour to finish it. – g2m.agent Feb 16 '21 at 13:34
  • If you want my test directory tree, I can upload it to you, but it doesn't seem to be allowed to write URLs here. – g2m.agent Feb 16 '21 at 13:38
  • After I removed the `QFileIconProvider` and subclass the `QFileSystemModel` and make my `data()`, the problem seemed to be solved. and no need to refresh manually! a bit excited, I need more testing. – g2m.agent Feb 16 '21 at 13:53
  • If you solved it, then provide your own answer. – musicamante Feb 16 '21 at 14:15

0 Answers0