使用QThread和pyqtSignal,为什么位于不同线程中的进程会冻结GUI?

2024-04-24 05:47:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个应用程序,其中一个线程正在执行一项冗长的任务。我使用一个发出信号的QPushButton来触发这个冗长的任务。但是,单击按钮会使GUI对进程的长度不响应,即使该进程位于另一个线程中。下面是一个复制此行为的小程序:

import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import pyqtSignal, QThread, Qt


class WriteThread(QThread):
    write_signal = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.write_signal.connect(self.worker, Qt.QueuedConnection)

    def worker(self):
        print("Before sleep")
        time.sleep(2)
        print("After sleep")
        return True


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.CustomEvent = None
        self.write_thread = None
        self.init_ui()

    def init_ui(self):
        self.write_thread = WriteThread()

        redb = QPushButton('Red', self)
        redb.move(10, 10)

        blueb = QPushButton('Blue', self)
        blueb.move(10, 50)
        blueb.clicked.connect(self.print_method)

        redb.clicked.connect(self.send_event)

        self.write_thread.start()

        self.setGeometry(300, 300, 300, 250)
        self.setWindowTitle('Toggle button')
        self.show()

    def send_event(self):
        print("\tSending signal")
        self.write_thread.write_signal.emit()
        print("\tFinished sending signal")

    def print_method(self):
        print("Not frozen")


def main():
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

time.sleep(2)模拟冗长的过程。 我的问题是:为什么不同线程中的进程会冻结GUI


Tags: importselfsignaltime进程initdefsys
1条回答
网友
1楼 · 发布于 2024-04-24 05:47:16

说明:

您必须了解,QThread不是一个Qt-thread,而是本机OS线程的处理程序,类似于threading.thread

正如the docs所指出的:

Unlike queued slots or invoked methods, methods called directly on the QThread object will execute in the thread that calls the method. When subclassing QThread, keep in mind that the constructor executes in the old thread while run() executes in the new thread. If a member variable is accessed from both functions, then the variable is accessed from two different threads. Check that it is safe to do so.

(强调矿山)

也就是说,只有run()方法在新线程上执行,因此“worker”在属于QThread的线程上执行,在本例中,QThread是主线程

解决方案:

如果希望执行“work”方法,则类对象必须位于辅助线程中,这样WriteThread就足够成为QObject(不需要是QThread),并将其移动到另一个线程:

import sys
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QThread, Qt, QObject


class WriteObject(QObject):
    write_signal = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.write_signal.connect(self.worker, Qt.QueuedConnection)

    @pyqtSlot()
    def worker(self):
        print("Before sleep")
        time.sleep(2)
        print("After sleep")
        return True


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.CustomEvent = None
        self.write_thread = None
        self.init_ui()

    def init_ui(self):
        self.write_object = WriteObject()
        self.write_thread = QThread(self)
        self.write_thread.start()
        self.write_object.moveToThread(self.write_thread)

        redb = QPushButton("Red", self)
        redb.move(10, 10)

        blueb = QPushButton("Blue", self)
        blueb.move(10, 50)
        blueb.clicked.connect(self.print_method)

        redb.clicked.connect(self.send_event)

        self.setGeometry(300, 300, 300, 250)
        self.setWindowTitle("Toggle button")
        self.show()

    def send_event(self):
        print("\tSending signal")
        self.write_object.write_signal.emit()
        print("\tFinished sending signal")

    def print_method(self):
        print("Not frozen")


def main():
    app = QApplication(sys.argv)
    ex = Example()
    ret = app.exec_()
    ex.write_thread.quit()
    ex.write_thread.wait()
    sys.exit(ret)


if __name__ == "__main__":
    main()

相关问题 更多 >