I am struggling (a lot) with a problem: I am launching a process that requires some computational resources and sometimes takes a lot of time. Since I cannot kill a running QRunnable with a button, I would like to use a QRunnable that launches no more than 6 other processes in parallel. Here is an example of what I thought:
from PyQt5 import QtWidgets, QtCore
from time import sleep
from sys import argv, exc_info, exit
import traceback
threadpool = QtCore.QThreadPool()
max_paralell_processes = 6
class MainWindow(QtWidgets.QMainWindow):
stopsignal = QtCore.pyqtSignal()
def __init__(self):
super(MainWindow, self).__init__()
self.trainers = {}
self.ntrainers = 10
self.startButton = QtWidgets .QPushButton('Start', self)
self.startButton.resize(self.startButton.sizeHint())
self.startButton.move(50, 50)
self.stopButton = QtWidgets.QPushButton('Stop', self)
self.stopButton.resize(self.stopButton.sizeHint())
self.stopButton.move(150, 50)
self.setGeometry(300, 300, 300, 200)
self.startButton.clicked.connect(self.create_trainers)
self.stopButton.clicked.connect(self.stop)
self.progressBar = QtWidgets.QProgressBar(self)
self.progressBar.setGeometry(50, 20, 250, 20)
self.count = 0
self.progressBar.setValue(self.count)
def create_trainers(self):
global threadpool
for m in range(self.ntrainers):
trainer = Trainer(self.the_long_process, m)
trainer.progress.connect( self.progress_bar)
trainer.finished.connect( self.finished_process)
self.trainers[m] = trainer
launcher = LaunchWorkers(self.trainers, range(self.ntrainers))
launcher.finished.connect(self.__all_processes_finished)
self.stopsignal.connect(launcher.stop)
threadpool.start(launcher)
def the_long_process(self, m):
print("In process {}".format(m))
sleep(2)
def progess_bar(self):
self.count += 1
self.progressBar.setValue(self.count/len(self.trainers)*100)
def finished_process(self):
pass
def stop(self):
self.stopsignal.emit()
class Trainer(QtCore.QRunnable):
finished = QtCore.pyqtSignal(str)
error = QtCore.pyqtSignal(tuple)
progress = QtCore.pyqtSignal()
def __init__(self, the_fn, m):
super(Trainer, self).__init__()
self.the_fn = the_fn
self.m = m
self.terminated = False
@QtCore.pyqtSlot()
def run(self):
try:
self.the_fn ( self.m )
except:
traceback.print_exc()
exctype, value = exc_info()[:2]
self.error.emit((exctype, value, traceback.format_exc()))
self.terminated = True
else:
self.progress.emit()
finally:
self.terminated = True
self.finished.emit( self.m)
class LaunchWorkers(QtCore.QRunnable):
finished = QtCore.pyqtSignal()
error = QtCore.pyqtSignal(tuple)
def __init__(self, trainers, some_list ):
super(LaunchWorkers, self).__init__()
self.trainers = trainers
self.some_list = some_list
self.stop = False
def stop(self):
self.stop = True
@QtCore.pyqtSlot()
def run(self):
global threadpool
finished_threads = 0
try:
for m in self.some_list :
if self.stop:
break
while finished_threads <= max_paralell_processes :
sleep(10)
finished_threads = 0
for t in self.trainers:
if self.trainers[t].terminated:
finished_threads += 1
threadpool.start( self.trainers[m] )
except:
traceback.print_exc()
exctype, value = exc_info()[:2]
self.error.emit((exctype, value, traceback.format_exc()))
finally:
self.finished.emit( )
def main():
app = QtWidgets.QApplication(argv)
ex = MainWindow()
ex.show()
exit(app.exec_())
if __name__ == '__main__':
main()
Doing so, I get a TypeError ModelTrainer cannot be converted to PyQt5.QtCore.QObject in this context.