简体   繁体   中英

Background worker populate QListView thumbnails

I have a simple QListview that displays a list of items Names. I would like to display the thumbnail of each item once it has been downloaded. How can i do the following as I'm new to using something like a background worker and I'm not sure how to achieve this.

This explains what i think would be the best approach...

  • Use a custom QStyledItemDelegate that overrides the initStyleOption() function.

  • Detects the lack of an icon and issues an asynchronous request to load it.

  • In the meantime, display default empty icon so user sees placeholder

  • When the asynchronous request to download the icon is done, it signals my widget which updates the items icon.

  • When i create all of my QStandardModelItems, I give them a custom piece of data (a custom role) that holds the path of the thumbnail for each item

    import os import sys from PySide2 import QtCore, QtGui, QtWidgets try: # python 2 from urllib import urlretrieve from urllib2 import urlopen except Exception as e: # python 3 from urllib.request import urlretrieve, urlopen import time from urllib.parse import urlparse def getThumbnail(url, output): if os.path.exists(output): return output # # download 1 # # urlretrieve(url, output) # # return os.path.abspath(output) # download 2 response = urlopen(url, timeout=5000) f = open(output, "wb") try: f.write(response.read()) finally: f.close() return output class ExampleDialog(QtWidgets.QDialog): def __init__(self): super(ExampleDialog, self).__init__() self.itemModel = QtGui.QStandardItemModel() self.uiListView = QtWidgets.QListView() # self.uiListView.setViewMode(QtWidgets.QListView.IconMode) self.uiListView.setIconSize(QtCore.QSize(80, 60)) #set icon size self.uiListView.setGridSize(QtCore.QSize(90, 70)) #set icon grid display self.uiListView.setModel(self.itemModel) self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.addWidget(self.uiListView) self.populateImages() def populateImages(self): root = os.path.join(os.getenv('APPDATA'), 'MyApp\\\\cache') if not os.path.exists(root): os.makedirs(root) print('IMAGES:', root) for x in range(20): url = 'https://picsum.photos/id/{}/80/60.jpg'.format(x) p = urlparse(url).path ext = os.path.splitext(p)[-1] output = os.path.join(root, '{}{}'.format(x, ext)) # get thumbnail getThumbnail(url, output) # Item item = QtGui.QStandardItem('{}'.format(x)) item.setData(QtGui.QPixmap(output), QtCore.Qt.DecorationRole) item.setData(output, QtCore.Qt.UserRole) self.itemModel.appendRow(item) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) window = ExampleDialog() window.show() window.raise_() sys.exit(app.exec_())

Instead of using a background worker you can use QNetworkAccessManager for asynchronous downloading.

from dataclasses import dataclass
from functools import cached_property
import sys

from PySide2 import QtCore, QtGui, QtWidgets, QtNetwork


@dataclass
class IconDownloader(QtCore.QObject):
    url: QtCore.QUrl
    index: QtCore.QPersistentModelIndex
    _parent: QtCore.QObject = None

    def __post_init__(self):
        super().__init__()
        self.setParent(self._parent)

    @cached_property
    def network_manager(self):
        manager = QtNetwork.QNetworkAccessManager()
        manager.finished.connect(self._handle_finished)
        return manager

    def start(self):
        if self.index.isValid():
            request = QtNetwork.QNetworkRequest(self.url)
            request.setAttribute(
                QtNetwork.QNetworkRequest.FollowRedirectsAttribute, True
            )
            self.network_manager.get(request)

    def _handle_finished(self, reply):
        if reply.error() == QtNetwork.QNetworkReply.NoError:
            pixmap = QtGui.QPixmap()
            ok = pixmap.loadFromData(reply.readAll())
            if ok and self.index.isValid():
                model = self.index.model()
                model.setData(
                    QtCore.QModelIndex(self.index), pixmap, QtCore.Qt.DecorationRole
                )
        else:
            print(reply.error(), reply.errorString())
        reply.deleteLater()
        self.deleteLater()


class ExampleDialog(QtWidgets.QDialog):
    def __init__(self):
        super(ExampleDialog, self).__init__()

        self.itemModel = QtGui.QStandardItemModel()

        self.uiListView = QtWidgets.QListView()
        # self.uiListView.setViewMode(QtWidgets.QListView.IconMode)
        self.uiListView.setIconSize(QtCore.QSize(80, 60))  # set icon size
        self.uiListView.setGridSize(QtCore.QSize(90, 70))  # set icon grid display
        self.uiListView.setModel(self.itemModel)

        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.addWidget(self.uiListView)

        self.populateImages()

    def populateImages(self):
        for x in range(20):
            url = f"https://picsum.photos/id/{x}/80/60.jpg"
            item = QtGui.QStandardItem(f"x")
            self.itemModel.appendRow(item)
            downloader = IconDownloader(
                QtCore.QUrl(url),
                QtCore.QPersistentModelIndex(self.itemModel.indexFromItem(item)),
                self,
            )
            downloader.start()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = ExampleDialog()
    window.show()
    window.raise_()
    sys.exit(app.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM