2017-10-26 4 views
0

Ich habe ein Problem, wo ich versuche, eine ganze Menge von PNG-Bildern zu laden und anschließend Thema mit PyQt anzuzeigen. Mein aktueller Arbeitsablauf besteht darin, einen Multiprozessor-Pool zu verwenden, um eine Funktion zuzuordnen, die jede Datei mit 'rb' Werten öffnet und dann die Bytes jeder Datei in eine vereinheitlichte Liste liest. Schließlich zeigt der Elternprozess das Bild an, indem er die Methode fromImageData eines QPixmap-Objekts aufruft. Diese Methode scheint gut zu funktionieren, aber es ist ziemlich langsam, jedes Mal, wenn ich zwischen den Bildern umschalte (8K-Auflösung), eine neue Pixmap neu zu zeichnen.Erstellen von QPixmaps in einem Thread

Ich hatte gehofft, dass es vielleicht schneller ist, stattdessen eine Pixmap für jedes Bild zu erstellen und durch die Pixmap zu fahren, anstatt dieselbe Pixmap bei jedem Schritt mit einem neuen Bild neu zu erstellen. Um dies zu tun, habe ich versucht, eine Pixmap in der Multiprozessfunktion zu erstellen, dies ist jedoch nicht erlaubt, da es keine übergeordnete QApp im Thread gibt.

Meine Frage ist, ob es einen richtigen Weg gibt, dies zu tun? Ich habe immer daran gedacht, es mit Sellerie/Reddis zu tun, aber ich kann nicht sehen, dass es andere Unterschiede gibt. Erstellen Sie für jedes Bild ein neues Pixmap und schalten Sie sie mit setPixmap um? Ist dies sogar eine praktikable Option oder gibt es dafür geeignete Möglichkeiten?

Antwort

2

sollten Sie in der Lage sein, dies ein QThreadPool und einige QRunnable s mit zu tun, der Code, wickeln, die die pixmaps lädt. Etwas wie:

Dann irgendwo in der Hauptanwendung, erstellen Sie einen Thread-Pool, führen Sie die Ladeobjekte, und behandeln Sie ihre Signale.

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 

Ich habe diese nicht versucht, aber da Qt das Einfädeln ist der Umgang, sollte dies der Lage sein, die Vorteile von mehreren Threads zu nehmen, nur in Ordnung.

EDIT

Wie in den Kommentaren erklärt, wird dies nicht funktionieren. Ich habe vergessen, dass QRunnableQObject nicht erbt, und dass QPixmaps nicht außerhalb des Hauptthreads erstellt werden kann. Aber es ist ziemlich einfach, stattdessen ein Image Loader-Objekt zu verwenden, es zu einem oder mehreren Hintergrundthreads zu verschieben, in denen es eine QImage lädt, und diese dann an den Hauptthread zur späteren Verwendung sendet. Hier ist ein getesteter Code, der die blanken Bones ausführt und alle PNG-Dateien im aktuellen Verzeichnis lädt.

#!/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() 
+0

Großartig, danke @bnaecker! Ich werde es versuchen so schnell wie möglich –

+1

Dieser Code wird nicht funktionieren. Qt unterstützt das Erstellen von Pixmaps außerhalb des Hauptthreads nicht, daher müssen Sie 'QImage' verwenden. Außerdem erbt "QRunnable" 'QObject' nicht, so dass es keine Signale aussenden kann. Sie sollten 'QThread' verwenden. – ekhumoro

+0

@ekhumoro Danke, dass du darauf hingewiesen hast, ich habe meine Antwort aktualisiert. – bnaecker

Verwandte Themen