【问题标题】:Background worker populate QListView thumbnails后台工作人员填充 QListView 缩略图
【发布时间】:2021-10-16 13:35:03
【问题描述】:

我有一个显示项目名称列表的简单 QListview。我想在下载后显示每个项目的缩略图。我如何执行以下操作,因为我不熟悉使用后台工作人员之类的东西并且我不确定如何实现这一点。

这解释了我认为最好的方法......

  • 使用覆盖 initStyleOption() 函数的自定义 QStyledItemDelegate。

  • 检测缺少图标并发出异步请求以加载它。

  • 同时,显示默认的空图标,以便用户看到占位符

  • 当下载图标的异步请求完成时,它会通知我的小部件更新项目图标。

  • 当我创建所有 QStandardModelItems 时,我给他们一个自定义数据(自定义角色),其中包含每个项目的缩略图路径

    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_())
    

【问题讨论】:

    标签: python pyside2 qlistview


    【解决方案1】:

    您可以使用 QNetworkAccessManager 进行异步下载,而不是使用后台工作程序。

    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_())
    

    【讨论】:

    • 感谢 OP 代码块修复。无论我做什么,我都不会修复。
    • 感谢您展示使用 qnetwork 的新选项。如果可能的话,您能否也显示背景选项?仅适用于那些可能也希望看到该选项的人,包括我自己。
    • @JokerMartini 我认为这是不必要的,因为该选项意味着使用我更愿意避免的线程。这个选项更加优雅和简单。
    • @JokerMartini 但是你可以查看这篇文章以获得灵感stackoverflow.com/a/59537535/6622587
    • 非常感谢您的帮助。我确实收到如下所示的错误qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed PySide2.QtNetwork.QNetworkReply.NetworkError.UnknownNetworkError TLS initialization failed
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多