我有一个问题,我正在尝试加载大量的png图像,然后使用PyQt显示主题。我当前的工作流程是使用多处理器池映射一个函数,该函数使用'rb'值打开每个文件,然后将每个文件的字节读入统一列表。最后,父进程通过调用QPixmap对象的fromImageData
方法显示图像。这种方法似乎工作正常,但每次在图像之间切换(8K分辨率)时重绘新像素图的速度都很慢。
我希望可以更快地为每个图像创建一个像素图并循环通过像素图,而不是在每个步骤中使用新图像重新创建相同的像素图。为此,我尝试在多进程函数中创建一个pixmap,但是这是不允许的,因为线程中没有父QApp。
我的问题是,是否有正确的方法来做到这一点?我有想法用celery / reddis做这个,但我看不出有任何不同的结果。为每个图像创建一个新的像素图并使用setPixmap
切换它们甚至是一个可行的选择,还是有更合适的方法来实现这个目标?
答案 0 :(得分:2)
您应该能够使用QThreadPool
和一些QRunnable
来执行此操作,这些from PyQt5 import QtCore, QtGui
class PixmapLoader(QtCore.QRunnable):
def __init__(self, filename):
super().__init__()
self.filename = filename
def run(self):
# Load pixmap at filename
# ...
# then emit in a signal
loaded.emit(pixmap)
loaded = QtCore.pyqtSignal(QtGui.QPixmap)
包含加载像素图的代码。类似的东西:
pool = QtCore.QThreadPool()
loaders = [PixmapLoader(filename) for filename in filenames]
for loader in loaders:
loader.loaded.connect(handle_new_pixmap)
pool.start(loader)
def handle_new_pixmap(QtGui.QPixmap):
# do stuff with pixmap
然后在主应用程序的某处,创建一个线程池,运行加载对象,并处理它们的信号。
QRunnable
我没试过这个,但是因为Qt正在处理线程,所以应该能够利用多个线程就好了。
修改强>
正如评论中所解释的那样,这是行不通的。我忘记了QObject
没有继承QPixmaps
,并且QImage
无法在主线程之外创建。但是,使用图像加载器对象,将其移动到一个或多个后台线程,在其中加载#!/usr/bin/env python3
import os
from PyQt5.QtCore import pyqtSignal, QObject, QThread
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import QApplication
class ImageLoader(QObject):
loaded = pyqtSignal(str, QImage)
def __init__(self, filename):
super().__init__()
self.filename = filename
def on_load_signal(self):
img = QImage(self.filename)
self.loaded.emit(self.filename, img)
class LoaderManager(QObject):
request_img_load = pyqtSignal()
def __init__(self):
super().__init__()
self.loaders = list(map(ImageLoader,
filter(lambda f: f.endswith('.png'), os.listdir())))
self.bg_thread = QThread()
for loader in self.loaders:
self.request_img_load.connect(loader.on_load_signal)
loader.loaded.connect(self.handle_img_loaded)
loader.moveToThread(self.bg_thread)
self.bg_thread.start()
def __del__(self):
self.bg_thread.quit()
def load_all(self):
self.request_img_load.emit()
def handle_img_loaded(self, name, img):
print('File {} of size {} loaded'.format(name, img.byteCount()))
if __name__ == '__main__':
app = QApplication([])
manager = LoaderManager()
manager.load_all()
,然后将其发送到主线程以供以后使用,这非常简单。这是经过测试的代码,可以完成这些操作,在当前目录中加载所有PNG文件。
{{1}}