2016-05-12 4 views
0

Ich habe ein Laden Widget, das aus zwei Etiketten besteht, eines ist die Statusbezeichnung und das andere ist die Bezeichnung, die das animierte GIF angezeigt wird. Wenn ich Call show() Methode, bevor schwere Sachen verarbeitet wird, aktualisiert sich das gif am Lade-Widget überhaupt nicht. Es ist nichts falsch mit dem gif btw (Looping Probleme etc.). Der Hauptcode (Anrufer) sieht wie folgt aus:Gifs innerhalb eines Etiketts angezeigt aktualisiert sich nicht regelmäßig in Pyqt5

self.loadingwidget = LoadingWidgetForm() 
self.setCentralWidget(self.loadingwidget) 
self.loadingwidget.show() 
... 
... 
heavy stuff 
... 
... 
self.loadingwidget.hide() 

Das Widget-Klasse:

class LoadingWidgetForm(QWidget, LoadingWidget): 
    def __init__(self, parent=None): 
     super().__init__(parent=parent) 
     self.setupUi(self) 
     self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) 
     self.setAttribute(Qt.WA_TranslucentBackground) 
     pince_directory = SysUtils.get_current_script_directory() # returns current working directory 
     self.movie = QMovie(pince_directory + "/media/loading_widget_gondola.gif", QByteArray()) 
     self.label_Animated.setMovie(self.movie) 
     self.movie.setScaledSize(QSize(50, 50)) 
     self.movie.setCacheMode(QMovie.CacheAll) 
     self.movie.setSpeed(100) 
     self.movie.start() 
     self.not_finished=True 
     self.update_thread = Thread(target=self.update_widget) 
     self.update_thread.daemon = True 

    def showEvent(self, QShowEvent): 
     QApplication.processEvents() 
     self.update_thread.start() 

    def hideEvent(self, QHideEvent): 
     self.not_finished = False 

    def update_widget(self): 
     while self.not_finished: 
      QApplication.processEvents() 

Wie Sie sehen, ich versucht, einen separaten Thread zu erstellen Arbeitsbelastung zu vermeiden, aber es machte keinen Unterschied machen. Dann versuchte ich mein Glück mit der QThread-Klasse, indem ich die run()-Methode außer Kraft setzte, aber es hat auch nicht funktioniert. Aber Ausführung QApplication.processEvents() Methode innerhalb der schweren Sachen funktioniert gut. Ich denke auch, dass ich keine separaten Threads verwenden sollte. Ich denke, es sollte einen eleganteren Weg geben, dies zu tun. Das Widget sieht wie folgt aus btw:

gondola Processing ...
Vollversion der gif: gondola

Vielen Dank im Voraus! Haben Sie einen guten Tag.

Edit: Ich kann die schweren Sachen nicht auf einen anderen Thread aufgrund von Bugs in Pexpect verschieben. Die spawn() - Methode von Pexpect erfordert, dass das erzeugte Objekt und alle mit dem erzeugten Objekt verbundenen Operationen im selben Thread sind. Ich möchte den Arbeitsablauf des gesamten Programms nicht ändern

Antwort

0

Um GUI-Animationen zu aktualisieren, muss die Haupt-Qt-Schleife (im Haupt-GUI-Thread) ausgeführt werden und Ereignisse verarbeiten. Die Qt-Ereignisschleife kann jedoch nur ein einzelnes Ereignis gleichzeitig verarbeiten, da die Behandlung dieser Ereignisse in der Regel sehr kurz dauert und die Steuerung schnell in die Schleife zurückkehrt. Dadurch können die GUI-Aktualisierungen (Repaints, einschließlich Animationen usw.) flüssig erscheinen.

Ein übliches Beispiel ist eine Schaltfläche, um das Laden einer Datei zu initiieren. Der Knopfdruck erzeugt ein Ereignis, das behandelt und an Ihren Code weitergeleitet wird (entweder über Ereignisse direkt oder über Signale). Jetzt befindet sich der Hauptthread in Ihrem lang laufenden Code, und die Ereignisschleife wird angehalten - und bleibt blockiert, bis der Job mit langer Laufzeit (z. B. Laden der Datei) abgeschlossen ist.

Sie haben Recht, dass Sie dies mit Threads lösen können, aber Sie sind rückwärts gegangen. Sie möchten Ihren lang laufenden Code in einen Thread einfügen (nicht den Aufruf von processEvents). In der Tat, Aufruf (oder Interaktion mit) der GUI von einem anderen Thread ist ein Rezept für einen Absturz.

Die einfachste Möglichkeit, mit Threads zu arbeiten, besteht in der Verwendung von QRunner und QThreadPool. Dies ermöglicht mehrere Ausführungs-Threads. Die folgende Codewand gibt Ihnen eine benutzerdefinierte Worker Klasse, die es einfach macht, damit umzugehen. Ich dies in der Regel in einer Datei speichern, threads.py zu halten, aus dem Weg:

import sys 
from PyQt5.QtCore import QObject, QRunnable 


class WorkerSignals(QObject): 
    ''' 
    Defines the signals available from a running worker thread. 
    error 
     `tuple` (exctype, value, traceback.format_exc()) 

    result 
     `dict` data returned from processing 
    ''' 
    finished = pyqtSignal() 
    error = pyqtSignal(tuple) 
    result = pyqtSignal(dict) 


class Worker(QRunnable): 
    ''' 
    Worker thread 

    Inherits from QRunnable to handler worker thread setup, signals and wrap-up. 

    :param callback: The function callback to run on this worker thread. Supplied args and 
        kwargs will be passed through to the runner. 
    :type callback: function 
    :param args: Arguments to pass to the callback function 
    :param kwargs: Keywords to pass to the callback function 

    ''' 

    def __init__(self, fn, *args, **kwargs): 
     super(Worker, self).__init__() 
     # Store constructor arguments (re-used for processing) 
     self.fn = fn 
     self.args = args 
     self.kwargs = kwargs 
     self.signals = WorkerSignals() 

    @pyqtSlot() 
    def run(self): 
     ''' 
     Initialise the runner function with passed args, kwargs. 
     ''' 

     # Retrieve args/kwargs here; and fire processing using them 
     try: 
      result = self.fn(*self.args, **self.kwargs) 
     except: 
      traceback.print_exc() 
      exctype, value = sys.exc_info()[:2] 
      self.signals.error.emit((exctype, value, traceback.format_exc())) 
     else: 
      self.signals.result.emit(result) # Return the result of the processing 
     finally: 
      self.signals.finished.emit() # Done 

die oben nutzen zu können, benötigen Sie ein QThreadPool die Fäden zu handhaben. Sie müssen dies nur einmal erstellen, zum Beispiel während der Anwendungsinitialisierung.

threadpool = QThreadPool() 

Nun erstellen, einen Arbeiter, indem sie in der Python-Funktion übergibt auszuführen:

from .threads import Worker # our custom worker Class 
worker = Worker(fn=<Python function>) # create a Worker object 

Nun Signale befestigen das Ergebnis zurück zu bekommen, oder einen Fehler benachrichtigt:

worker.signals.error.connect(<Python function error handler>) 
worker.signals.result.connect(<Python function result handler>) 

Um diese Worker auszuführen, können Sie sie einfach an die QThreadPool übergeben.

threadpool.start(worker) 

Alles wird für sich selbst sorgen, mit dem Ergebnis der Arbeit an das angeschlossene Signal zurück ... und die Haupt wird GUI-Schleife ist es, was zu tun frei sein!

+0

Vielen Dank, dass Sie sich die Zeit genommen haben und mir eine gründliche Antwort gegeben haben. Aber wie ich verstanden habe, erstellen Sie einen Thread, der vom Hauptthread getrennt ist. Ich habe bereits gesagt, dass ich die schwere Arbeit wegen Bugs in pexpect (am Ende meines Posts) nicht in einen anderen Thread verschieben kann. – Desertricker

Verwandte Themen