在线程中创建QPixmaps

时间:2017-10-26 04:43:36

标签: python pyqt multiprocessing pyqt5 python-multiprocessing

我有一个问题,我正在尝试加载大量的png图像,然后使用PyQt显示主题。我当前的工作流程是使用多处理器池映射一个函数,该函数使用'rb'值打开每个文件,然后将每个文件的字节读入统一列表。最后,父进程通过调用QPixmap对象的fromImageData方法显示图像。这种方法似乎工作正常,但每次在图像之间切换(8K分辨率)时重绘新像素图的速度都很慢。

我希望可以更快地为每个图像创建一个像素图并循环通过像素图,而不是在每个步骤中使用新图像重新创建相同的像素图。为此,我尝试在多进程函数中创建一个pixmap,但是这是不允许的,因为线程中没有父QApp。

我的问题是,是否有正确的方法来做到这一点?我有想法用celery / reddis做这个,但我看不出有任何不同的结果。为每个图像创建一个新的像素图并使用setPixmap切换它们甚至是一个可行的选择,还是有更合适的方法来实现这个目标?

1 个答案:

答案 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}}