在将 Worker 对象移动到 pyqt 中的 QThread 之前设置信号和槽

Setup signal and slot before moving Worker object to QThread in pyqt

在 Qt/PyQt 中,我曾经使用 Worker class 和 QThread 创建线程。

self.worker = Worker()
self.thread = QThread()

worker.moveToThread(thread)
setup_signal_slot_with_main_object()

// start 
thread.start()

我必须在 moveToThread() 之后放置 setup_signal_slot_with_main_object()。但是我有一个复杂的工人。在Worker.__ init__()中,它创建了许多QObjects并连接内部信号和槽。我不想创建一个在 worker->moveToThread(&thread) 之后建立所有连接和调用 worker.setup_signal_slot() 的方法因为 Worker 包含许多子 QObject,并且每个 QObject 都可以在其构造函数中创建 signal/slot。

在 Qt/C++ 中,我可以在 worker 的构造函数中建立 signal/slot 连接。但是在 PyQt 中,插槽不会 运行 在新线程中。

这是一个 Worker 包含 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_())

在Worker中,定时器超时会在主线程中调用main_process。我可以将 self.timer.timeout.connect(self.main_process) 移动到方法 worker.start() 中。但是正如我上面所说,我仍然想在它的构造函数中放置 internal signal/slot 。 谁能建议我一个解决方案?谢谢!

如果您希望在接收方使用 pyqtSlot() 装饰器的同一线程中调用这些方法,如果您不这样做,那么它将在发送方的线程中调用。

import sys
import signal
import threading
import datetime

from PyQt5.QtCore import QObject, pyqtSignal, QTimer, QCoreApplication, QThread, pyqtSlot


class Worker(QObject):
    timeChanged = pyqtSignal(object)

    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        self.timer = QTimer(self)
        self.timer.setInterval(1000)

        self.timer.timeout.connect(self.main_process)

    @pyqtSlot()
    def start(self):
        self.timer.start()
        print("Worker thread {}: Start timer".format(threading.get_ident()))

    @pyqtSlot()
    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)

    @pyqtSlot()
    def start(self):
        self.thread.start()

    def stop(self):
        if self.thread.isRunning():
            self.thread.quit()
            self.thread.wait()
            print("Exit thread")

    @pyqtSlot(object)
    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_())

输出:

Main    thread 140175719339648
Worker thread 140175659480832: Start timer
Worker thread 140175659480832: 26-07-2019 04-39-42
Main   thread 140175719339648: 26-07-2019 04-39-42
Worker thread 140175659480832: 26-07-2019 04-39-43
Main   thread 140175719339648: 26-07-2019 04-39-43
Worker thread 140175659480832: 26-07-2019 04-39-44
Main   thread 140175719339648: 26-07-2019 04-39-44
Worker thread 140175659480832: 26-07-2019 04-39-45
Main   thread 140175719339648: 26-07-2019 04-39-45