Python PyQT: ¿Cómo llamar a una función GUI desde un subproceso de trabajo?

Tengo un gui pyqt y estoy llamando a un proceso largo (ffmpeg) que puse en un hilo separado para no bloquear el gui. Luego deseo actualizar una barra de progreso cuando finalice un comando de una lista más larga de comandos. El problema es que no puedo llamar a una función en el subproceso de la interfaz gráfica de usuario fuera del subproceso de trabajo. Así que dejo correr un ticker en el subproceso de trabajo, pero cuando actualizo la barra de progreso con un ciclo de tiempo y leo el valor del ticker, la gui se bloquea de nuevo. Como puedo resolver esto. Utilicé hilos de Python y no Qthread. Gracias por cualquier ayuda!

import threading, pexpect self.cmd_list = ['ffmpeg -i file outfile','and so on'] self.stop_proc = False self.executeCMD() def spawn_ffmpeg_cmd(self): for cmd in self.cmd_list: if self.stop_proc == False: thread = pexpect.spawn(cmd) print "\nstarted: %s" % cmd cpl = thread.compile_pattern_list([pexpect.EOF,"frame= *\d+ fps=*\d+",'(.+)']) while True: i = thread.expect_list(cpl, timeout=None) if i == 0: # EOF print "the sub process exited" self.pgticker += 1 break elif i == 1: frame_number_fps = thread.match.group(0) print frame_number_fps thread.close elif i == 2: pass self.startButton.setEnabled(True) def executeCMD(self): self.startButton.setEnabled(False) self.pgticker = 0 threading.Thread(target=self.spawn_ffmpeg_cmd, name="_proc").start() def stopprocess(self): self.stop_proc = True self.cmd_list = [] os.system('pkill ffmpeg') self.pgticker = len(self.cmd_list) self.startButton.setEnabled(True) def updateProgress(self): pgfactor = 100 / len(self.cmd_list) progress = 0.0 progress = pgfactor*int(self.pgticker) self.progressBar.setProperty("value", progress) 

En resumen: QThread a QThread y use las señales y ranuras de Qt, son la forma preferida de comunicarse entre hilos.

Esta respuesta proporciona algunos ejemplos de cómo podría verse esto: https://stackoverflow.com/a/6789205/2319400

En su caso, el uso de la versión “SomeObject” de la anterior podría verse así:

 class Worker(QtCore.QObject): madeProgress = QtCore.pyqtSignal([int]) finished = QtCore.pyqtSignal() def __init__(self, cmdlist): self.cmdlist = cmdlist def run(self): for icmd, cmd in enumerate(self.cmdlist): # execute your work # processCommand(cmd) # signal that we've made progress self.madeProgress.emit(icmd) # emit the finished signal - we're done self.finished.emit() 

Luego mueva este trabajador a una instancia de QThread que cree. Siguiendo el patrón de la respuesta vinculada, puede conectar la señal madeProgress a la ranura setValue de una setValue de progreso:

 workerThread = QThread() workerObject = Worker(cmdlist) workerObject.moveToThread(workerThread) workerThread.started.connect(workerObject.run) workerObject.finished.connect(workerThread.quit) # create a progressbar with min/max according to # the length of your cmdlist progressBar = QProgressBar() progressBar.setRange(0, len(cmdlist)) # connect the worker's progress signal with the progressbar workerObject.madeProgress.connect(progressBar.setValue) # start the thread (starting your worker at the same time) workerThread.start()