Ich habe ein Programm nach dem MVC-Modell entwickelt. Meine Ansicht hat eine open
Schaltfläche, wo ich Dateien öffnen kann. Die Dateien werden analysiert und es wird viel mit den Dateiinhalten kalkuliert.QProgressDialog wird nur angezeigt, nachdem der Code für die lange Ausführung fertig ist
Jetzt möchte ich einen Loader anzeigen, um anzuzeigen, dass der Benutzer warten sollte. Ich bin völlig neu in Python und Qt. Ich verwende PyQt5 (Qt 5.6.2) mit Python 3.6.2.
ich hinzugefügt, um die showLoader()
Verfahren zum openFiles()
Methode meiner Ansicht:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, controller, parent = None):
# initializing the window
def showOpenDialog(self):
files, filters = QtWidgets.QFileDialog.getOpenFileNames(self, 'Open file(s)', '',
"Raw files (*.rw.dat);;Data files (*.dat)" +
";;Text files (*.txt);;All files (*)")
self.showLoader("Loading file(s)")
self.doSomeStuffWithTheFiles(files)
self.hideLoader()
def showLoader(self, text):
self._progress = QtWidgets.QProgressDialog(text, "Abort", 0, 0, self);
self._progress.setWindowModality(QtCore.Qt.WindowModal);
Dadurch wird der Loader angezeigt werden, aber es wird nach die Datei geladen wird, erscheinen. Nicht einmal direkt nach dem Laden der Datei, aber es dauert noch 1-2 Sekunden, nachdem alles fertig ist (einschließlich einiger Neuanstriche des Fensters)
Ich lese viel über Threads, also nehme ich an, dass das Parsen der Datei blockiert Fortschrittslader, der Sinn macht. Ich lese, dass ich die QProgressDialog
zu einem Steckplatz hinzufügen sollte (ich weiß nicht wirklich, was das ist), aber das hilft mir nicht, weil ich die QProgressDialog
angezeigt werden soll nach die QFileDialog
.
Ich lese auch etwas über das Hinzufügen QtWidgets.QApplication.processEvents()
, um das Fenster neu zu streichen, aber das hat nicht für mich funktioniert (oder ich habe es falsch verwendet).
Also meine Fragen sind:
- Wie zeige ich die
QProgressDialog
, wenn ich dieshowLoader()
Methode aufrufen? - Muss ich meine Berechnungen und Dateianalyse in einem anderen Thread ausführen und wenn ja, wie mache ich das?
- Wenn ich mehr Informationen in der
QProgressDialog
wie Aktualisierung des Textes und des Fortschritts anzeigen wollte, wie mache ich das?
Weitere Frage
Die Lösung wies darauf hin, durch @ekhumoro funktioniert gut. Ich sehe den Loader und die Dateien werden korrekt analysiert. Mein Problem ist jetzt, dass das Aktualisieren meiner vorhandenen MainWindow
nicht funktioniert.
Nach dem Ausführen des Codes sehe ich ein kleines Fenster auftauchen, aber es verschwindet sofort. (Ich hatte ein Problem wie dieses und es ging um den C++ - Garbage Collector im Hintergrund von Qt. Aber in meinem Verständnis sollte das Layout einen Verweis auf die ParsedDataWidget
behalten, so dass dies für mich keinen Sinn ergibt.) Auch der ParsedDataWidget
ist ein Widget, das zu der layout
"Inline" hinzugefügt werden sollte und nicht als "Fenster" erscheint.
# a class that handles the data parsing of each file and creates an
# object that contains all the data with some methods...
class DataParser
def __init__(self, data):
# handle the data
# displaying the parsed data in a fancy way
class ParsedDataWidget(QtWidgets.QWidget)
def __init__(self, data):
# create some UI
# the worker class just like @ekhumoro wrote it (stripped down to
# relevant code)
class Worker(QtCore.QObject):
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
# parse the data in the DataParser and create an object
# of the files data
data = DataParser(file)
# works fine, the data is parsed correctly
print(data)
# does not work
window.addParsedData(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
# the window class like mentioned before (or like @ekhumoro wrote it)
class Window(QtWidgets.QWidget):
def __init__(self):
self._data_container = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
self._data_container.setLayout(layout)
def addParsedData(data):
data_widget = ParsedDataWidget(data)
layout = self._data_container.layout()
layout.addWidget(data_widget)
Also, was habe ich zu tun, um die addParsedData
Methode zur Arbeit zu kommen?
bearbeiten
ich einige Auswirkungen von Veränderungen der Code versuchte.Wenn ich die ParsedDataWidget
mit einem QLabel
ersetzen erhalte ich das folgende Ergebnis:
Wenn ich das Fenster Python Abstürze schließen.
Lösung
Mit einigen weiteren Recherchen habe ich mein Problem gefunden: Sie sollten nicht Threads mit PyQt verwenden, sollten Sie SIGNALS
stattdessen (written here)
Also änderte ich den Code des Arbeitnehmers , Fügte ich ein weiteres SIGNAL
genannt finishedParsing
hinzu, das ausgegeben wird, wenn das Laden abgeschlossen ist. Diese SIGNAL
enthält die DataParser
. Die könnte wie folgt aussehen:
class Worker(QtCore.QObject):
finishedParsing = QtCore.pyqtSignal(DataParser)
def run(self):
self._stop = False
for count, file in enumerate(self._files, 1):
# parse the data in the DataParser and create an object
# of the files data
data = DataParser(file)
# emit a signal to let the window know that this data is
# ready to use
self.finishedParsing.emit(data)
self.loaded.emit(count, file)
if self._stop:
break
self.finished.emit()
class Window(QtWidgets.QWidget):
def showOpenDialog(self):
if files and not self.thread.isRunning():
# do the opening stuff like written before
self.worker = Worker(files)
#...
self.worker.finishedParsing.connect(self.addParsedData)
Dies funktioniert jetzt!
Ja, Sie dürfen niemals versuchen, GUI-Operationen außerhalb des Hauptthreads auszuführen. Cross-Thread-Signale sind garantiert threadsicher, definieren Sie also benutzerdefinierte Signale für die Kommunikation mit dem Haupt-Thread und aktualisieren Sie nur die GUI in den mit ihnen verbundenen Slots. – ekhumoro