I am trying to execute some functions in the background of an PyQt5 application. The print statement in run() does not print when thread.start() is called. I think I am misunderstanding how threads work.
from PyQt5.QtCore import pyqtSignal, QObject, QThread, QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys, time
class GenericWorker(QObject):
started = pyqtSignal()
finished = pyqtSignal()
error = pyqtSignal(Exception)
def __init__(self, func, *args, **kwargs):
super().__init__()
self.func = func
self.args = args
self.kwargs = kwargs
def run(self):
print("Run function executing.")
try:
self.func(*self.args, **self.kwargs)
except Exception as e:
self.error.emit(e)
finally:
self.finished.emit()
class ParentClass:
def __init__(self, main_app):
pass
def parent_function(self):
print("Parent function.")
def start_thread(self, func, *args, **kwargs):
thread = QThread()
worker = GenericWorker(func, *args, **kwargs)
worker.moveToThread(thread)
# create signals
thread.started.connect(worker.run)
worker.finished.connect(thread.quit)
worker.finished.connect(worker.deleteLater)
thread.finished.connect(thread.deleteLater)
worker.error.connect(self.handle_worker_error)
# store thread references, prevent premature garbage collection
if not hasattr(self, '_threads'):
self._threads = []
self._threads.append(thread)
# cleanup reference upon thread completion to avoid memory leak
thread.finished.connect(lambda: self._threads.remove(thread))
# begin
thread.start()
def handle_worker_error(self, e):
print(f"Error occurred: {e}")
class ChildClass(ParentClass):
def __init__(self, main_app):
super().__init__(main_app)
def functionality(self):
# launch threads for each function
print("Starting threads.\n")
self.start_thread(self.parent_function)
self.start_thread(self.child_function)
def child_function(self):
print("Child function.")
class MainApplication(QMainWindow):
def __init__(self):
super().__init__()
test = ChildClass(self)
test.functionality()
# SEQUENCE OF EVENTS
if __name__ == "__main__":
app = QApplication(sys.argv)
app.main = MainApplication()
app.main.show()
sys.exit(app.exec_())
If lambda functions are used instead, i.e.:
thread.started.connect(lambda: worker.run())
worker.finished.connect(lambda: thread.quit())
worker.finished.connect(lambda: worker.deleteLater())
thread.finished.connect(lambda: thread.deleteLater())
worker.error.connect(lambda: self.handle_worker_error())
Then all statements are printed. If time.sleep(0.1) is placed after thread.start(), all statements will again be printed. Why is this?