Quali sono i modi migliori per creare un sistema di codifica GUI multiprocessing?schemi di GUI multiprocessing per combattere il blocco "Non risponde"
Vorrei creare un posto per la comunità di internet e trovare esempi su come utilizzare il modulo multiprocessing
in python.
Ho visto diversi piccoli esempi di processi multiprocessing
su internet di semplici funzioni globali che sono chiamate in un modulo principale, ma ho trovato che questo si traduce raramente facilmente in qualsiasi cosa che chiunque faccia effettivamente riguardo alle GUI. Penserei che molti programmi avrebbero le funzioni che vogliono usare in un processo separato come metodi di oggetti (che possono essere aggregati di altri oggetti, ecc.) E forse un singolo elemento della GUI avrebbe un oggetto associato che deve chiamare questo processo, ecc
per esempio, io ho un programma relativamente complessa e sto avendo problemi ad ottenere una GUI reattivo per esso, che ho creduto di essere a causa della mia mancanza di comprensione in multiprocessing
e filettatura con QThread
. Tuttavia, so che l'esempio riportato di seguito almeno trasferirà le informazioni tra i processi nel modo in cui desidero (poiché è in grado di eseguire le istruzioni print
) ma la mia GUI sta ancora bloccando. Qualcuno sa che cosa potrebbe causare questo, e se è ancora un problema con la mia mancanza di comprensione in architetture mutlithreaded/multiprocessing?
Ecco un piccolo esempio di codice pseudo di quello che sto facendo:
class Worker:
...
def processing(self, queue):
# put stuff into queue in a loop
# This thread gets data from Worker
class Worker_thread(QThread):
def __init__(self):
...
# make process with Worker inside
def start_processing(self):
# continuously get data from Worker
# send data to Tab object with signals/slots
class Tab(QTabWidget):
# spawn a thread separate from main GUI thread
# update GUI using slot
def update_GUI()
e questo codice è esempio completamente compilabile che incarna lo sturcture sovrastante del mio programma:
from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
import time
# This object can hold several properties which will be used for the processing
# and will be run in the background, while it updates a thread with all of it's progress
class Worker:
def __init__(self, some_var):
self.some_var = some_var
self.iteration = 0
def some_complex_processing(self, queue):
for i in range(0,5000):
self.iteration += 1
queue.put(self.iteration)
queue.put('done with processing')
# This Woker_thread is a thread which will spawn a separate process (Worker).
# This separate is needed in order to separate the data retrieval
# from the main GUI thread, which should only quickly update when needed
class Worker_thread(QtCore.QThread):
# signals and slots are used to communicate back to the main GUI thread
update_signal = QtCore.pyqtSignal(int)
done_signal = QtCore.pyqtSignal()
def __init__(self, parent, worker):
QtCore.QThread.__init__(self, parent)
self.queue = mp.Queue()
self.worker = worker
self.parent = parent
self.process = mp.Process(target=self.worker.some_complex_processing, args=(self.queue,))
# When the process button is pressed, this function will start getting data from Worker
# this data is then retrieved by the queue and pushed through a signal
# to Tab.update_GUI
@QtCore.pyqtSlot()
def start_computation(self):
self.process.start()
while(True):
try:
message = self.queue.get()
self.update_signal.emit(message)
except EOFError:
pass
if message == 'done with processing':
self.done_signal.emit()
break
#self.parent.update_GUI(message)
self.process.join()
return
# Each tab will start it's own thread, which will spawn a process
class Tab(QtGui.QTabWidget):
start_comp = QtCore.pyqtSignal()
def __init__(self, parent, this_worker):
self.parent = parent
self.this_worker = this_worker
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.thread = Worker_thread(parent=self, worker=self.this_worker)
self.thread.update_signal.connect(self.update_GUI)
self.thread.done_signal.connect(self.thread.quit)
self.start_comp.connect(self.thread.start_computation)
self.thread.start()
###############################
# Here is what should update the GUI at every iteration of Worker.some_complex_processing()
# The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
@QtCore.pyqtSlot(int)
def update_GUI(self, iteration):
self.step.setText(0, str(iteration))
#time.sleep(0.1)
print iteration
def start_signal_emit(self):
self.start_comp.emit()
# GUI stuff
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self)
self.tab_list = []
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)
# Make Tabs in loop from button
for i in range(0,10):
name = 'tab' + str(i)
self.tab_list.append(Tab(self.tabWidget, Worker(name)))
self.tabWidget.addTab(self.tab_list[-1], name)
# Do the processing
def process(self):
for tab in self.tab_list:
tab.start_signal_emit()
return
if __name__ == "__main__":
app = QtGui.QApplication([])
win = MainWindow()
win.show()
sys.exit(app.exec_())
Altro Informazioni: Sto scrivendo un programma che mi piacerebbe generare diversi processi e farli mostrare continuamente i loro progressi attraverso la loro elaborazione. Vorrei che il programma fosse multiprocesso per ottenere la massima velocità possibile dal programma.
Al momento, sto cercando di utilizzare un thread per generare un processo e utilizzare segnali e slot per aggiornare la GUI mentre i dati vengono continuamente recuperati da una coda. Sembra che il queues
, signals
e slots
funzionino quando si utilizzano le istruzioni print
, ma non è possibile aggiornare la GUI. Se qualcuno ha altri suggerimenti su come dovrei strutturare questo per mantenere il programma più gestibile, mi piacerebbe imparare.
EDIT: ho fatto le regolazioni messe avanti da Lin Min, con l'aggiunta di fare Worker
un QObject
in modo che moveToThread()
avrebbe funzionato.
Ecco il nuovo codice che ho in questo momento:
from PyQt4 import QtCore, QtGui
import multiprocessing as mp
import numpy as np
import sys
import time
class Worker(QtCore.QObject):
update_signal = QtCore.pyqtSignal(int)
done_signal = QtCore.pyqtSignal()
def __init__(self, some_var):
QtCore.QObject.__init__(self, parent=None)
self.some_var = some_var
self.iteration = 0
self.queue = mp.Queue()
self.process = mp.Process(target=self.some_complex_processing, args=(self.queue,))
def some_complex_processing(self, queue):
for i in range(0,5000):
self.iteration += 1
queue.put(self.iteration)
queue.put('done with processing')
@QtCore.pyqtSlot()
def start_computation(self):
self.process.start()
while(True):
try:
message = self.queue.get()
self.update_signal.emit(message)
except EOFError:
pass
if message == 'done with processing':
self.done_signal.emit()
break
self.process.join()
return
class Tab(QtGui.QTabWidget):
start_comp = QtCore.pyqtSignal()
def __init__(self, parent, this_worker):
self.parent = parent
self.this_worker = this_worker
QtGui.QTabWidget.__init__(self, parent)
self.treeWidget = QtGui.QTreeWidget(self)
self.properties = QtGui.QTreeWidgetItem(self.treeWidget, ["Properties"])
self.step = QtGui.QTreeWidgetItem(self.properties, ["Iteration #"])
# Use QThread is enough
self.thread = QtCore.QThread();
# Change the thread affinity of worker to self.thread.
self.this_worker.moveToThread(self.thread);
self.this_worker.update_signal.connect(self.update_GUI)
self.this_worker.done_signal.connect(self.thread.quit)
self.start_comp.connect(self.this_worker.start_computation)
self.thread.start()
###############################
# Here is what should update the GUI at every iteration of Worker.some_complex_processing()
# The message appears to be getting sent, due to seeing the print statement in the console, but the GUI is not updated.
@QtCore.pyqtSlot(int)
def update_GUI(self, iteration):
self.step.setText(0, str(iteration))
#time.sleep(0.1)
print iteration
def start_signal_emit(self):
self.start_comp.emit()
# GUI stuff
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent = None):
QtGui.QMainWindow.__init__(self)
self.tab_list = []
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)
# Make Tabs in loop from button
for i in range(0,10):
name = 'tab' + str(i)
self.tab_list.append(Tab(self.tabWidget, Worker(name)))
self.tabWidget.addTab(self.tab_list[-1], name)
# Do the processing
def process(self):
for tab in self.tab_list:
tab.start_signal_emit()
return
if __name__ == "__main__":
app = QtGui.QApplication([])
win = MainWindow()
win.show()
sys.exit(app.exec_())
Grazie per tutte le risposte, apprezzo il livello di dettaglio che ognuno è andato in nel descrivere l'idea che credono di essere una soluzione, ma sfortunatamente non sono ancora stato in grado di eseguire questi tipi di processi che operano sull'oggetto a cui appartengono mentre si visualizza l'attributo dell'oggetto su una GUI.
Tuttavia, ho imparato una quantità decente da questo post, che mi ha permesso di rendermi conto che la versione con thread che ho al momento è appesa alla GUI poiché la funzione di aggiornamento della GUI è troppo grande e richiede troppa elaborazione.
Quindi, ho preso l'approccio QTimer()
alla mia versione multi-thread e sta andando molto meglio! Consiglierei a tutti coloro che affrontano problemi simili di tentare almeno qualcosa di simile a questo.
Non ero a conoscenza di questo approccio per risolvere i problemi di aggiornamento della GUI ed è ora una soluzione pseudo o temporanea al problema che sto affrontando.
Il mio consiglio è di provare e semplificare ancora di più il vostro esempio. Prova a realizzare ciò che stai facendo con un singolo pulsante/elenco/thread. Poi, una volta che hai funzionato, costruisci su di esso. Un po 'come questo articolo http://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/ – jross