3 Stimmen

PyQt4, QThread und das Öffnen großer Dateien ohne Einfrieren der GUI

Ich möchte fragen, wie man eine große Datei von der Festplatte zu lesen und die PyQt4 UI reaktionsfähig (nicht blockiert) zu halten. Ich hatte das Laden der Datei in eine QThread-Unterklasse verschoben, aber mein GUI-Thread wird eingefroren. Irgendwelche Vorschläge? Ich denke, es muss etwas mit der GIL sein, aber ich weiß nicht, wie ich es sortieren kann?

EDIT: Ich verwende vtkGDCMImageReader aus dem GDCM-Projekt, um ein Multiframe-DICOM-Bild zu lesen und es mit vtk und pyqt4 anzuzeigen. Ich tue dies in einem anderen Thread (QThread) laden, aber meine app einfrieren, bis das Bild geladen wird. hier ist ein Beispiel-Code:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

3voto

cregox Punkte 16639

Ich nehme an, Sie rufen direkt die run um das Thema zu beginnen. Das würde dazu führen, dass die GUI einfriert, weil Sie den Thread nicht aktivieren.

Sie verpassen also die start dort, die indirekt und korrekt die run :

thread = ReadThread()
thread.begin()

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
        self.reader.vtkgdcm.vtkGDCMImageReader()

    def run(self): 
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

    def begin(self): 
        self.start()

2voto

Ian Punkte 1841

Etwas spät, aber ich glaube, ich kann das Problem, das Sie haben, genau erkennen.

Das Bild ist groß, und das Entpacken ist wahrscheinlich eine rechenintensive Aufgabe. Das bedeutet, dass Ihr GUI-Thread in den Ruhezustand geht und der Lade-Thread an die CPU gebunden ist. Zu diesem Zeitpunkt hat der Lade-Thread die GIL, und die grafische Benutzeroberfläche kann nicht starten.

Selbst wenn Sie sich in den Lade-Thread einklinken und einen sleep(0) einführen können, damit die GUI weiterläuft, wird dies auf einem Mehrkern- oder Multiprozessor-Rechner nicht helfen. Was passiert, ist, dass das Betriebssystem zwei Threads hat und denkt, dass es beide laufen lassen kann. Der Lade-Thread ist auf (sagen wir) Kern 1 eingerichtet und die GUI kann auf (sagen wir) Kern 2 geladen und ausgeführt werden. Nachdem der GUI-Thread auf Kern 2 geladen und gestartet wurde, setzt das Betriebssystem den Lade-Thread auf Kern 1 fort, der sofort die GIL abruft. Wenige Augenblicke später ist der GUI-Thread startbereit und versucht, die GIL zu erfassen, was jedoch fehlschlägt. Alles, was er ohne die GIL tun kann, ist zurück in den Schlaf zu gehen!

Eine Lösung besteht darin, in strategischen Abständen einen kurzen (größer als Null) Ruhezustand in den Hintergrund-Thread einzufügen, damit die grafische Benutzeroberfläche laufen kann. Dies ist nicht immer möglich.

1voto

Martin Beckett Punkte 92477

0voto

Gary van der Merwe Punkte 8557

Vielleicht erstellen Sie Ihr Leserobjekt in der Lauffläche:

class ReadThread(QThread): 
    def __init__(self, file_name): 
        super(ReadThread, self).__init__(self) 
        self.file_name = file_name 
-       self.reader = vtkgdcm.vtkGDCMImageReader()

    def run(self): 
+       self.reader = vtkgdcm.vtkGDCMImageReader()
        self.reader.SetFileName(self.file_name) 
        self.reader.Update() 
        self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X