2017-12-19 20 views
0

Ich habe dieses einfache Programm, wo ich ein modales, nicht blockierendes Fortschrittsfenster haben möchte (mit einem QProgressDialog), das remote aktualisiert wird. SIZE steuert einfach den Maximalwert von QProgressDialog. wenn ich es jedoch auf einen Wert von 4 oder weniger haben, sieht das Fenster wie dies während der gesamten Dauer der Maßnahme:PyQt QProgressDialog wird als leeres, weißes Fenster angezeigt

enter image description here

Mit anderen Worten, ist das Fenster komplett weiß und zeigt keinen Text noch Fortschrittsbalken. Wenn ich den Wert von SIZE-5 oder mehr eingestellt, arbeitet das Display anzeigt, aber erst nach den ersten 2-3 Wiederholungen:

enter image description here

und später

enter image description here

import sys, time 
from PyQt5.QtWidgets import * 
from PyQt5.QtCore import * 
from PyQt5.QtGui import * 

SIZE = 5 

def doGenerate(setValue): 
    for x2 in range(SIZE): 
     time.sleep(1) 
     setValue(x2 + 1) 
    print('Done') 


class MainMenu(QMainWindow): 
    def __init__(self): 
     super().__init__() 

     self.genAudioButton = QPushButton('Generate', self) 
     self.genAudioButton.clicked.connect(self.generate) 

     self.setCentralWidget(self.genAudioButton) 
     self.show() 

    def generate(self): 
     try: 
      progress = QProgressDialog('Work in progress', '', 0, SIZE, self) 
      progress.setWindowTitle("Generating files...") 
      progress.setWindowModality(Qt.WindowModal) 
      progress.show() 
      progress.setValue(0) 
      doGenerate(progress.setValue) 
     except Exception as e: 
      errBox = QMessageBox() 
      errBox.setWindowTitle('Error') 
      errBox.setText('Error: ' + str(e)) 
      errBox.addButton(QMessageBox.Ok) 
      errBox.exec() 
      return 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    ex = MainMenu() 
    ret = app.exec_() 
    sys.exit(ret) 

Was verursacht das und wie kann ich es beheben?

Zusätzlich, ist es eine Möglichkeit, vollständig auf die Schaltfläche Abbrechen zu entfernen, anstatt eine leere Schaltfläche zu haben, die noch die Aktion abgebrochen? Die PyQt4-Dokumente (ich verwende PyQt5) zeigen an, dass ein leerer String dieses Ergebnis erreichen soll, und die C++ - Dokumente für Qt5 zeigen dasselbe an, aber das funktioniert hier eindeutig nicht. Ich habe keine eigenständige Dokumentation für PyQt5 gefunden.

Antwort

1

Die GUI implementiert eine Hauptschleife durch app.exec_(), diese Schleife wird verwendet, um Aufgaben wie das Überprüfen von Ereignissen, Signalen, Aufrufen einiger Funktionen usw. durchzuführen. Wenn wir die Schleife unterbrechen, können wir unerwartetes Verhalten wie das beobachten. in Ihrem Fall sleep() ist eine Sperrfunktion, die nicht verwendet werden sollten, bietet Qt Alternativen zu ihm, und einer von ihnen ist ein QEventLoop mit einem QTimer verwenden:

def doGenerate(setValue): 
    for x2 in range(SIZE): 
     loop = QEventLoop() 
     QTimer.singleShot(1000, loop.quit) 
     loop.exec_() 
     setValue(x2 + 1) 
    print('Done') 

Wenn Sie die Taste zeigen nicht abbrechen möchten, Sie Keine passieren muss:

progress = QProgressDialog('Work in progress', None, 0, SIZE, self) 

Wenn Sie gTTS verwenden möchten Sie es durch Threads tun müssen, bietet Qt mehrere Möglichkeiten, es zu implementieren, in diesem Fall ich QThreadPool mit QRunnable verwenden. Wir werden die QMetaObject.invokeMethod verwenden, um die Werte der GUI zu aktualisieren, da Qt die Aktualisierung der GUI von einem anderen Thread verhindert, der nicht vom Hauptthread ist.

import sys, time 
from PyQt5.QtWidgets import * 
from PyQt5.QtCore import * 
from PyQt5.QtGui import * 
from gtts import gTTS 


class GTTSRunnable(QRunnable): 
    def __init__(self, data, progress): 
     QRunnable.__init__(self) 
     self.data = data 
     self.w = progress 

    def run(self): 
     for i, val in enumerate(self.data): 
      text, filename = val 
      tts = gTTS(text=text, lang='en') 
      tts.save(filename) 
      QMetaObject.invokeMethod(self.w, "setValue", 
       Qt.QueuedConnection, Q_ARG(int, i+1)) 
      QThread.msleep(10) 

class MainMenu(QMainWindow): 
    def __init__(self): 
     super().__init__() 
     self.genAudioButton = QPushButton('Generate', self) 
     self.genAudioButton.clicked.connect(self.generate) 
     self.setCentralWidget(self.genAudioButton) 
     self.show() 

    def generate(self): 
     try: 
      info = [("hello", "1.mp4"), ("how are you?", "2.mp4"), ("StackOverFlow", "3.mp4")] 
      self.progress = QProgressDialog('Work in progress', '', 0, len(info), self) 
      self.progress.setWindowTitle("Generating files...") 
      self.progress.setWindowModality(Qt.WindowModal) 
      self.progress.show() 
      self.progress.setValue(0) 
      self.doGenerate(info) 
     except Exception as e: 
      errBox = QMessageBox() 
      errBox.setWindowTitle('Error') 
      errBox.setText('Error: ' + str(e)) 
      errBox.addButton(QMessageBox.Ok) 
      errBox.exec() 
      return 

    def doGenerate(self, data): 
     self.runnable = GTTSRunnable(data, self.progress) 
     QThreadPool.globalInstance().start(self.runnable) 

if __name__ == '__main__': 
    app = QApplication(sys.argv) 
    ex = MainMenu() 
    ret = app.exec_() 
    sys.exit(ret) 
+0

Vielen Dank für Ihre Antwort. 'sleep' ist das, was ich verwendet habe, um ein minimales Beispiel zu schreiben, das das Problem reproduziert, aber mein ursprüngliches Programm schrieb tatsächlich Dateien in' doGenerate', anstatt zu schlafen. Das wurde nicht durch das Schreiben von nativen Python-Dateien gemacht, sondern durch die Verwendung von Funktionen aus einer Bibliothek, auf die ich wenig bis gar keine Kontrolle habe ('save' oder' write_to_fp' aus [gTTS] (https://github.com/pndurette/gTTS/)) . Wie kann ich Ihre Lösung darauf anpassen? – pie3636

+1

@ pie3636 Wie erhalten Sie den Prozentsatz durch gTTs? Habe ich diese Bibliothek benutzt und keine Funktion gefunden, die mir den prozentualen Fortschritt anzeigt? – eyllanesc

+0

Ich muss eine große Anzahl von Dateien erzeugen, also mache ich einfach, dass ich nach jeder Datei den Prozentsatz als 'files_done/total_files' berechne. Aber die Aktualisierung der GUI nach jeder Datei verursacht das Problem, wo das Fenster leer ist, weil das Schreiben in eine Datei blockiert ist, nehme ich an? – pie3636