Reputation: 1
I am building an application in PyQt5. There is a point in my code with high computational demand that takes a long time, so to prevent the GUI from freezing I am using QThrads.
I have a process
thread to do the calculations, but I use a different thread to give the user feedback (with help of a progress bar), that the program is actually working. I need to do this because the computation happens i a block, so I can not update my progress bar based on the state of the calculation. I create 2 threads and 2 workers based on the same method, yet when I emit the finish signal of the second worker (signaling that the work is done), the program crashes and throws an error without explanation. The same happens sometimes in debug mood too.
An example where the same problem occurs:
main
import sys
from Workers import LoadingWorker, ProcessWorker
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QProgressBar, QListWidget
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.clicksCount = 0
self.setupUi()
def setupUi(self):
# Create widgets
self.setWindowTitle("MRE")
self.resize(300, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.progressBar = QProgressBar()
self.progressBar.setValue(0)
self.progressBar.setTextVisible(False)
self.list = QListWidget()
self.Btn = QPushButton("Do Stuff", self)
# Set the layout
layout = QVBoxLayout()
layout.addWidget(self.progressBar)
layout.addWidget(self.list)
layout.addWidget(self.Btn)
self.centralWidget.setLayout(layout)
self.data = None
self.runtime = 0
# Add callbacks
self.Btn.clicked.connect(self.btn_licked)
def btn_licked(self):
# Call functions to start threads
self.loading()
self.process()
def process(self):
# Set up process worker and get it running
self.ThreadProcess = QThread()
self.WorkerProceess = ProcessWorker()
self.WorkerProceess.moveToThread(self.ThreadProcess)
self.ThreadProcess.started.connect(self.WorkerProceess.run)
self.WorkerProceess.finished.connect(self.ThreadProcess.quit)
self.WorkerProceess.finished.connect(self.WorkerProceess.deleteLater)
self.ThreadProcess.finished.connect(self.ThreadProcess.deleteLater)
self.WorkerProceess.progress.connect(self.process_progress)
self.ThreadProcess.start()
self.ThreadProcess.finished.connect(self.process_end)
def process_end(self):
self.WorkerLoading.done = True
def process_progress(self, data, runtime):
self.data = data
self.runtime = runtime
# Some operations on data
def loading(self):
# Set up loading worker and get it running
self.ThreadLoading = QThread()
self.WorkerLoading = LoadingWorker()
self.WorkerLoading.moveToThread(self.ThreadLoading)
self.ThreadLoading.started.connect(self.WorkerLoading.run)
self.WorkerLoading.finished.connect(self.ThreadLoading.quit)
self.WorkerLoading.finished.connect(self.WorkerLoading.deleteLater)
self.WorkerLoading.finished.connect(self.ThreadLoading.deleteLater)
self.WorkerLoading.progress.connect(self.loading_progress)
self.ThreadLoading.start()
self.ThreadLoading.finished.connect(self.loading_end)
def loading_progress(self, val):
self.progressBar.setValue(val)
def loading_end(self,):
print("Stepped into loading ended")
self.list.addItem("Runtime (s): " + str(self.runtime))
self.progressBar.setValue(0)
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())
Worker Classes
from time import sleep
from PyQt5.QtCore import QObject, pyqtSignal
import numpy as np
from time import time
class ProcessWorker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(np.ndarray, float)
def run(self):
start_time = time()
data = np.random.random(size=(50, 500000))
data = np.fft.fft(np.linalg.pinv(data))
runtime = time() - start_time
self.progress.emit(data, runtime)
self.finished.emit()
class LoadingWorker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(int)
done = False
def run(self):
cnt=0
while (not self.done):
cnt+=1
if cnt==101:
cnt=0
sleep(0.1)
sleep(0.005)
self.progress.emit(cnt)
self.finished.emit()
print("loading finish sign supposedly emitted")
The error does not happen all the time:
Process finished with exit code -1073740791 (0xC0000409).
I tried to debug it, but I just can't figure out the cause of the problem.
Upvotes: 0
Views: 343
Reputation: 48509
You are deleting the thread when the worker has finished, so you get some sort of race condition: when the finished
signal of the worker is emitted, the thread is still running, and you're requesting it to quit and delete it; since, at that point, the thread is still running, this creates a problem.
You should delete the thread when the thread has finished.
Change this:
self.WorkerLoading.finished.connect(self.ThreadLoading.deleteLater)
to this:
self.ThreadLoading.finished.connect(self.ThreadLoading.deleteLater)
Note that for simple cases like this, you can just subclass QThread, implement its run()
and start()
it.
This makes things easier, as you only have a single object to reference to.
Upvotes: 2