PyQt:如何处理QThread?

PyQt: How to deal with QThread?

在下面的代码中我尝试处理QThread。在此可执行示例中,有三个按钮:第一个用于启动,第二个用于停止,第三个用于关闭。好吧,当我开始任务时,它运行起来就像一个魅力。 BUT 当我希望 while 循环停止时,我单击停止按钮。现在,有一个问题:while 循环不会停止。

你看,停止按钮发出一个信号来调用 TestTask() 上的 stop() 方法。

怎么了?

from sys import argv

from PyQt4.QtCore import QObject, pyqtSignal, QThread, Qt, QMutex

from PyQt4.QtGui import QDialog, QApplication, QPushButton, \
     QLineEdit, QFormLayout, QTextEdit

class TestTask(QObject):

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

        self._mutex = QMutex()

        self._end_loop = True

    def init_object(self):
        while self._end_loop:
            print "Sratus", self._end_loop

    def stop(self):
        self._mutex.lock()
        self._end_loop = False
        self._mutex.unlock()

class Form(QDialog):
    stop_loop = pyqtSignal()

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

        self.init_ui()

    def init_ui(self):


        self.pushButton_start_loop = QPushButton()
        self.pushButton_start_loop.setText("Start Loop") 

        self.pushButton_stop_loop = QPushButton()
        self.pushButton_stop_loop.setText("Stop Loop")       

        self.pushButton_close = QPushButton()
        self.pushButton_close.setText("Close")

        layout = QFormLayout()

        layout.addWidget(self.pushButton_start_loop)
        layout.addWidget(self.pushButton_stop_loop)
        layout.addWidget(self.pushButton_close)

        self.setLayout(layout)
        self.setWindowTitle("Tes Window")

        self.init_signal_slot_pushButton()

    def start_task(self):

         self.task_thread = QThread(self)
         self.task_thread.work = TestTask()
         self.task_thread.work.moveToThread(self.task_thread)
         self.task_thread.started.connect(self.task_thread.work.init_object)

         self.stop_loop.connect(self.task_thread.work.stop)

         self.task_thread.start()

    def stop_looping(self):
        self.stop_loop.emit()

    def init_signal_slot_pushButton(self):

        self.pushButton_start_loop.clicked.connect(self.start_task)

        self.pushButton_stop_loop.clicked.connect(self.stop_looping)

        self.pushButton_close.clicked.connect(self.close)



app = QApplication(argv)
form = Form()
form.show()
app.exec_()

stop_loop信号转换为事件发送给信号接收者的线程。但是您的工作对象是 运行 阻塞 while-loop,这会阻止线程处理其 event-queue 中的任何未决事件。所以连接到 stop_loop 信号的插槽将永远不会被调用。

要解决此问题,您可以在 while-loop 中调用 processEvents 以允许线程处理其未决事件:

def init_object(self):
    while self._end_loop:
        QThread.sleep(1)
        QApplication.processEvents()
        print "Status", self._end_loop

或者,您可以直接调用 worker 的 stop() 方法而不是发出信号。严格来说,这不是thread-safe,但只有当多个线程可以同时调用stop()时才会出现问题。因此,更正确的做法是使用互斥锁来保护被更改的值:

class TestTask(QObject):   
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self._mutex = QtCore.QMutex()
        self._end_loop = True

    def stop(self):
        self._mutex.lock()
        self._end_loop = False
        self._mutex.unlock()

现在直接从主线程调用stop()是thread-safe。