multiprocesamiento y actualización de la GUI – ¿Qprocess o multiprocesamiento?

Después de leer la documentación sobre QProcesses y el módulo de multiprocesamiento para python, todavía estoy teniendo problemas para crear una GUI que funcione y responda a lo largo de tener grandes procesos en curso en segundo plano. Hasta ahora, he encontrado esta versión simplificada de mi aplicación, que todavía muestra problemas similares a los que muchos han descrito.

from PyQt4 import QtCore, QtGui import multiprocessing as mp import numpy as np import sys class Spectra: def __init__(self, spectra_name, X, Y): self.spectra_name = spectra_name self.X = X self.Y = Y self.iteration = 0 def complex_processing_on_spectra(self, pipe_conn): self.iteration += 1 pipe_conn.send(self.iteration) class Spectra_Tab(QtGui.QTabWidget): def __init__(self, parent, spectra): self.parent = parent self.spectra = spectra QtGui.QTabWidget.__init__(self, parent) self.treeWidget = QtGui.QTreeWidget(self) self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"]) self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"]) self.consumer, self.producer = mp.Pipe() # Make process associated with tab self.process = mp.Process(target=self.spectra.complex_processing_on_spectra, args=(self.producer,)) def update_GUI(self, iteration): self.step.setText(1, str(iteration)) def start_computation(self): self.process.start() while(True): message = self.consumer.recv() if message == 'done': break self.update_GUI(message) self.process.join() return class MainWindow(QtGui.QMainWindow): def __init__(self, parent = None): QtGui.QMainWindow.__init__(self) self.setTabShape(QtGui.QTabWidget.Rounded) self.centralwidget = QtGui.QWidget(self) self.top_level_layout = QtGui.QGridLayout(self.centralwidget) self.tabWidget = QtGui.QTabWidget(self.centralwidget) self.top_level_layout.addWidget(self.tabWidget, 1, 0, 25, 25) process_button = QtGui.QPushButton("Process") self.top_level_layout.addWidget(process_button, 0, 1) QtCore.QObject.connect(process_button, QtCore.SIGNAL("clicked()"), self.process) self.setCentralWidget(self.centralwidget) self.centralwidget.setLayout(self.top_level_layout) # Open several files in loop from button - simplifed to one here X = np.arange(0.1200,.2) Y = np.arange(0.1200,.2) self.spectra = Spectra('name', X, Y) self.spectra_tab = Spectra_Tab(self.tabWidget, self.spectra) self.tabWidget.addTab(self.spectra_tab, 'name') def process(self): self.spectra_tab.start_computation() return if __name__ == "__main__": app = QtGui.QApplication([]) win = MainWindow() win.show() sys.exit(app.exec_()) 

Esto debería ser completamente capaz de ejecutarse si tiene las dependencias. En este momento tengo una versión QThreaded de mi progtwig que funciona con señales y ranuras; Sin embargo, creo que es importante tener la capacidad de usar todos los procesadores de una computadora, ya que la mayoría de los usuarios tienen ~ 8 núcleos disponibles para ellos. Por lo tanto, me gustaría ampliar este enfoque de señal / ranura a la versión multiprocessing utilizando multiprocessing o QProcess es.
¿Alguien tiene sugerencias sobre si usar o no QProcess o multiprocessing ? Si bien ambos son complicados para mí, parece que QProcess tiene menos foros de personas que usan pyQt, así que opté por el multiprocesamiento. ¿Sería más fácil ir con QProcess ya que ya tengo señales / ranuras que trabajan con subprocesos?

EDITAR: ¿Debo agregar una clase como esta como se sugiere?

 class My_Process(QtCore.QProcess): def __init__(self, spectra): QtCore.QProcess.__init__(self) self.spectra = spectra def worker(self): QtConcurrent.run(self.spectra, self.spectra.complex_processing_on_spectra) def run(self): QtCore.QObject.connect(self, QtCore.SIGNAL(QTimer.timeout()), self.worker) 

QProcess es para leer y escribir en tuberías (shell / cmd). Usa uno de estos.

  1. Cree una clase con función de trabajo y ejecútela como un hilo por QtConcurrent.run(object, method, args)
  2. conecte QTimer.timeout() con su función de trabajador.

A pesar de que la pregunta es antigua y ha sido respondida, me gustaría agregar una aclaración:

  • AFAIK QtConcurrent no está disponible en PyQt.
  • Los hilos en Qt con C ++ son diferentes de los hilos PyQt o python. Estos últimos necesitan adquirir GIL de Python (locking de intérprete global) cuando se ejecutan, lo que efectivamente significa que no hay una concurrencia real entre los hilos de python / pyqt.

Los subprocesos de Python están bien para mantener la interfaz de usuario receptiva, pero no verás mejoras en el rendimiento de las tareas vinculadas a la CPU. Recomiendo usar el multiprocessing junto con un QThread en su proceso principal que maneja la comunicación con el proceso hijo. Puede usar señales y ranuras entre su hilo principal (gui) y su hilo de comunicación.

introduzca la descripción de la imagen aquí

Edición: acabo de tener el mismo problema e hice algo así:

 from multiprocessing import Process, Queue from PyQt4 import QtCore from MyJob import job_function # Runner lives on the runner thread class Runner(QtCore.QObject): """ Runs a job in a separate process and forwards messages from the job to the main thread through a pyqtSignal. """ msg_from_job = QtCore.pyqtSignal(object) def __init__(self, start_signal): """ :param start_signal: the pyqtSignal that starts the job """ super(Runner, self).__init__() self.job_input = None start_signal.connect(self._run) def _run(self): queue = Queue() p = Process(target=job_function, args=(queue, self.job_input)) p.start() while True: msg = queue.get() self.msg_from_job.emit(msg) if msg == 'done': break # Things below live on the main thread def run_job(input): """ Call this to start a new job """ runner.job_input = input runner_thread.start() def handle_msg(msg): print(msg) if msg == 'done': runner_thread.quit() runner_thread.wait() # Setup the OQ listener thread and move the OQ runner object to it runner_thread = QtCore.QThread() runner = Runner(start_signal=runner_thread.started) runner.msg_from_job.connect(handle_msg) runner.moveToThread(runner_thread)