后台工作者填充 QListView 缩略图
Background worker populate QListView thumbnails
我有一个简单的 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_())
您可以使用 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_())