【问题标题】:PyQt: How to deal with QThread?PyQt:如何处理 QThread?
【发布时间】:2017-01-30 23:27:23
【问题描述】:

在下面的代码中,我尝试处理QThread。在这个可执行示例中,有三个按钮:第一个用于启动,第二个用于停止,第三个用于关闭。好吧,当我开始任务时,它的运行就像一个魅力。 但是当我想要 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_()

【问题讨论】:

    标签: multithreading python-2.7 pyqt4 signals-slots qthread


    【解决方案1】:

    stop_loop 信号被转换为事件并发送到信号接收器的线程。但是您的工作对象正在运行一个阻塞的while循环,这会阻止线程处理其事件队列中的任何未决事件。所以连接到stop_loop信号的槽永远不会被调用。

    要解决这个问题,您可以在 while 循环中调用 processEvents 以允许线程处理其挂起的事件:

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

    或者,您可以直接调用 worker 的 stop() 方法,而不是发出信号。严格来说,这不是线程安全的,但只有多个线程可以同时调用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() 是线程安全的。

    【讨论】:

    • 似乎带有 QMutex()-class 的 idia 不起作用。我已经更新了我的代码。
    • @Sophus。它确实有效,但正如我在回答中所说,您必须直接致电stop()。所以在stop_looping 中用self.task_thread.work.stop() 替换发射。那么stop_loop 信号就完全不需要了。
    • @ekhumoro:你是对的。它确实有效。谢谢你。直接调用方法,数据封装不会取消吗?
    • @Sophus。这就是互斥锁的用途——它保护_end_loop 值(因此调用stop() 是线程安全的)。除此之外,没有任何区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 2017-07-01
    • 2015-09-08
    • 2014-03-25
    • 2014-09-05
    相关资源
    最近更新 更多