在PySide中异步加载图像
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在PySide中异步加载图像相关的知识,希望对你有一定的参考价值。
我正在尝试使用QAbstractListModel将数千张或更多张图像加载到QListView中。我想尽可能地利用它们异步加载。
为了简单起见,现在我的模型中有一个称为LoadImages的方法,可以将其更改为任何图像目录进行测试。
- QStandardItem(DisplayRole):图像名称
- QStandardItem(UserRole):图像的完整文件路径
我的目标是,首先为所有列表项加载一个占位符图像,然后在单独的线程中加载磁盘中该特定项的缩略图。加载后,应将GUI更新为加载的图像。原始代码使用QStyledItemDelegate处理所有需要加载的图像的缓存。如果该图像不在高速缓存中,则它将绘制占位符图像并将信号发送到另一个线程,该线程将加载该图像并将其放入高速缓存。
我的代码很大程度上基于此帖子:How to correctly load images asynchronously in PyQt5?,该代码未提供完整的代码。
它似乎正在正确加载占位符图像,但是不会从磁盘加载图像。我不断收到这些错误:
QPixmap::scaled: Pixmap is a null pixmap
QPixmap: It is not safe to use pixmaps outside the GUI thread
Stackoverflow上的原始线程说,有一种解决方案,我认为我已正确实现。我有点迷茫,为什么它不起作用。
import os
import sys
from PySide.QtCore import *
from PySide.QtGui import *
PLACEHOLDER_IMAGE_PATH = "C:/Users/jmartini/Desktop/Trash/imageIcon.svg"
IMAGES_PATH = "C:/Users/jmartini/Desktop/Trash/imagesList"
class MyDelegate(QStyledItemDelegate):
t1 = Signal(str, str, dict)
def __init__(self, image_cache, loader_thread, parent=None):
super(MyDelegate, self).__init__(parent)
self.placeholder_image = QIcon(PLACEHOLDER_IMAGE_PATH).pixmap(QSize(128,128))
self.image_cache = image_cache
self.loader_thread = loader_thread
self.t1.connect(self.loader_thread.insert_into_queue)
def paint(self, QPainter, QStyleOptionViewItem, QModelIndex):
rect = QStyleOptionViewItem.rect
asset_name = QModelIndex.data(Qt.DisplayRole)
asset_thumb = QModelIndex.data(Qt.UserRole)
pic_rect = QRect(rect.left(), rect.top(), 128, 128)
text_rect = QRect(rect.left(), rect.top() + 128, 128, 22)
try:
cached_thumb = self.image_cache[asset_name]
print("Got image: from cache".format(asset_name))
except KeyError as e:
self.t1.emit(asset_name, asset_thumb, self.image_cache)
cached_thumb = self.placeholder_image
print("Drawing placeholder image for ".format(asset_name))
QPainter.drawPixmap(pic_rect, cached_thumb)
QPainter.drawText(text_rect, Qt.AlignCenter, asset_name)
if QStyleOptionViewItem.state & QStyle.State_Selected:
highlight_color = QStyleOptionViewItem.palette.highlight().color()
highlight_color.setAlpha(50)
highlight_brush = QBrush(highlight_color)
QPainter.fillRect(rect, highlight_brush)
def sizeHint(self, QStyleOptionViewItem, QModelIndex):
return QSize(128, 150)
class MyModel(QAbstractListModel):
def __init__(self, *args, **kwargs):
QAbstractListModel.__init__(self, *args, **kwargs)
self._items = []
def rowCount(self, index=QModelIndex()):
return len(self._items)
def itemByIndex(self, index):
if (index < 0 or index >= len(self._items)):
return None
return self._items[index]
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
item = self.itemByIndex(index.row())
if not item:
return None
elif role == Qt.DisplayRole:
return item.data(role=Qt.DisplayRole)
elif role == Qt.DecorationRole:
return item.data(role=Qt.DecorationRole)
elif role == Qt.UserRole:
return item.data(role=Qt.UserRole)
return None
# Extra Methods
def loadImages(self):
self.image_dir = IMAGES_PATH
for img in os.listdir(self.image_dir):
filepath = os.path.join(self.image_dir, img)
item = QStandardItem(filepath)
item.setData(os.path.basename(filepath), role=Qt.DisplayRole)
item.setData(filepath, role=Qt.UserRole)
self.appendItem(item)
def appendItem(self, item):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._items.append(item)
self.endInsertRows()
class LoaderThread(QObject):
def __init__(self):
super(LoaderThread, self).__init__()
@Slot(str, str, dict)
def insert_into_queue(self, name, thumb_path, image_cache):
print("Got signal, loading image for from disk".format(name))
image = QImage(thumb_path)
pixmap = QPixmap.fromImage(image).scaled(128, 128)
image_cache[name] = pixmap
print("Image for inserted to cache".format(name))
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.resize(960, 800)
self.setWindowTitle('Image Viewer')
image_cache =
lt = LoaderThread()
self.thread = QThread()
lt.moveToThread(self.thread)
self.thread.start()
self.delegate = MyDelegate(image_cache, lt)
self.model = MyModel()
self.proxyModel = QSortFilterProxyModel()
self.proxyModel.setSortCaseSensitivity(Qt.CaseInsensitive)
self.proxyModel.setSourceModel(self.model)
self.list = QListView()
self.list.setViewMode(QListView.IconMode)
self.list.setResizeMode(QListView.Adjust)
self.list.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.list.setModel(self.proxyModel)
self.list.setItemDelegate(self.delegate)
self.model.loadImages()
# Layout
layout = QVBoxLayout()
layout.addWidget(self.list)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def main():
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
答案
从您的示例中,您只需要将呼叫方式切换为setData
,这是另一种方式:
item.setData(Qt.DisplayRole, os.path.basename(filepath)
item.setData(Qt.UserRole, filepath)
我还将删除您的data()
重新实现。
以上是关于在PySide中异步加载图像的主要内容,如果未能解决你的问题,请参考以下文章