In Qt/PyQt, I used to make threading using a Worker class and a QThread.
self.worker = Worker()
self.thread = QThread()
worker.moveToThread(thread)
setup_signal_slot_with_main_object()
// start
thread.start()
I must place setup_signal_slot_with_main_object() after moveToThread(). But I have a complex worker. In Worker.__ init__(), it creates many QObjects and connects internal signals and slots. I don't want to create a method which makes all connections and calls worker.setup_signal_slot() after worker->moveToThread(&thread) because Worker contains many child QObjects and each QObject can make signal/slot in its constructor.
In Qt/C++, I can make signal/slot connection in worker's constructor. But in PyQt, slot will not run in new thread.
This is an example with a Worker contains a QTimer
import sys
import signal
import threading
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, QCoreApplication, QThread
import datetime
class Worker(QObject):
timeChanged = pyqtSignal(object)
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.timer = QTimer(self)
self.timer.setInterval(1000)
# I want to make connection at here
self.timer.timeout.connect(self.main_process)
def start(self):
# self.timer.timeout.connect(self.main_process)
self.timer.start()
print('Worker thread {}: Start timer'.format(threading.get_ident()))
# this method still run in main thread
def main_process(self):
timestamp = datetime.datetime.now()
print('Worker thread {}: {}'.format(threading.get_ident(), timestamp.strftime('%d-%m-%Y %H-%M-%S')))
self.timeChanged.emit(timestamp)
class WorkerThread(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.emitter = Worker()
self.thread = QThread(self)
self.emitter.moveToThread(self.thread)
self.thread.started.connect(self.emitter.start)
self.thread.finished.connect(self.emitter.deleteLater)
self.emitter.timeChanged.connect(self.show_time)
def start(self):
self.thread.start()
def stop(self):
if self.thread.isRunning():
self.thread.quit()
self.thread.wait()
print('Exit thread')
def show_time(self, timestamp):
print('Main thread {}: {}'.format(threading.get_ident(), timestamp.strftime('%d-%m-%Y %H-%M-%S')))
def signal_handler(sig, frame):
print('Quit')
app.quit()
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
app = QCoreApplication(sys.argv)
timer = QTimer()
timer.timeout.connect(lambda: None)
timer.start(500)
print('Main thread {}'.format(threading.get_ident()))
emitter = WorkerThread()
emitter.start()
sys.exit(app.exec_())
In Worker, timer timeout will call main_process in main thread. I can move self.timer.timeout.connect(self.main_process) into method worker.start(). But as I have said above, I still want to place internal signal/slot in its constructor. Could anyone suggest me a solution ? Thanks !