繁体   English   中英

PyQt线程通信有帮助吗? QThread和QObject

[英]PyQt thread communication help? QThread and QObject

在读取和搜索之后,我尝试使用生成QObject,然后使用movetoThread方法运行独立进程并允许QMainWindow继续响应。 当我尝试在QThread.run()方法中实现该操作时,这没有用。 以下代码是我尝试做一个简单的例子。 虽然代码在独立于MainWindow的运行线程中工作,但它不会中止。 我可以让线程停止的唯一方法是设置worker.end = True。 我认为不应该这样做。

"""
This is a program to test Threading with Objects in PyQt4.
"""

from time import sleep
import sys

from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal, QThread
from PyQt4.QtGui import QMainWindow, QApplication, QProgressBar
from PyQt4.QtGui import QPushButton, QVBoxLayout, QWidget

class workerObject(QObject):
    bar_signal = pyqtSignal(int)
    res_signal = pyqtSignal(str)
    term_signal = pyqtSignal()

    def __init__(self, maxIters):
        super(workerObject, self).__init__()
        self.maxIters = maxIters

    def run(self):
        self.bar_signal.emit(self.maxIters)        
        sleep(1)
        self.end = False

        for step in range(self.maxIters):
            if self.end:
                self.maxIters = step
                break
            self.bar_signal.emit(step)
            sleep(2)

        self.res_signal.emit("Got to {}".format(self.maxIters)) 
        self.term_signal.emit()

    @pyqtSlot()
    def mystop(self):
        print "stop signalled?"
        self.end = True

class MCwindow(QMainWindow):
    abort_signal = pyqtSignal(name='abort_signal')

    def __init__(self):
        super(MCwindow,self).__init__()        
        self.maxIters = 50

        widget = QWidget()
        layout = QVBoxLayout(widget)
        self.go_btn = QPushButton()
        self.go_btn.setText('Go')
        layout.addWidget(self.go_btn)
        self.abort_btn = QPushButton()
        self.abort_btn.setText('Stop')
        layout.addWidget(self.abort_btn)
        self.simulation_bar = QProgressBar()
        self.simulation_bar.setRange(0, self.maxIters)
        self.simulation_bar.setFormat("%v")
        layout.addWidget(self.simulation_bar)
        self.setCentralWidget(widget)

        self.go_btn.clicked.connect(self.run_mc)
        # The button calls the windows method to stop --- it could 
        # be that is 'clicked' calls the worker.mystop
#        self.abort_btn.clicked.connect(self.stop_mc)
        # This allows for the abort button to do somethign in the MainWindow
        # before the abort_signal is sent, this works
        self.abort_btn.clicked.connect(self.stop_mc)

    def run_mc(self):        
        self.thread = QThread()                
        self.worker = workerObject(self.maxIters)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        # This is the simple stop method, but does not work
#        self.abort_btn.clicked.connect(self.worker.mystop)
        # This uses the signal in the MCwindow - this connection does NOT works
        self.abort_signal.connect(self.worker.mystop)
        # This does NOT stop the thread
        # and would not allow for any clean up in the worker.
#        self.abort_signal.connect(self.thread.terminate)
        # This is a 'bad' way to stop the woker ... It does, however, work
#        self.abort_signal.connect(self.stopper)
        self.worker.bar_signal.connect(self.setBar)
        self.worker.res_signal.connect(self.setData)
        self.worker.term_signal.connect(self.thread.terminate)
        self.thread.start()

    def stop_mc(self):
        print "Stopping?!"
        # This signal is NEVER seen by the Worker.
        self.abort_signal.emit()

    def stopper(self):
        print "I should stop?!"
        # Should use signals to tell the worker to stop - and not setting a attribute
        self.worker.end=True

    @pyqtSlot(int)
    def setBar(self, val):
        self.simulation_bar.setValue(val)

    @pyqtSlot(str)    
    def setData(self, txt):
        print "Got done Sig!", txt

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MCwindow()
    window.show()
    sys.exit(app.exec_())

连接到abort_signal的插槽似乎没有被调用的原因是因为跨线程信号默认排队 这意味着信号将作为事件包装并发布到接收器所处的任何线程的事件队列中。

在您的特定示例中,接收器是一个已移动到工作线程的工作对象。 在工作线程上调用start()将启动其事件循环,这就是abort_signal将排队的位置。 但是,worker对象的run()方法启动一个for loop ,这将阻止线程的事件处理,就像它在主gui线程中执行时一样!

如果您对示例进行一些调整,您可以更清楚地了解发生了什么:

class MCwindow(QMainWindow):
    abort_signal = pyqtSignal(name='abort_signal')

    def __init__(self):
        super(MCwindow,self).__init__()
        # use a sane default
        self.maxIters = 5
        ...
        # DO NOT use QThread.terminate
        self.worker.term_signal.connect(self.thread.quit)

现在运行该示例,然后单击Go按钮,单击Stop按钮,等待worker正常完成。 这应该产生这样的输出:

Stopping?!
Got done Sig! Got to 5
stop signalled?

请注意,“stop signaled” 最后输出 - 即 run()退出并且控制返回到线程的事件循环之后。 为了在工作程序运行时处理进入的信号,您需要强制立即处理线程的挂起事件。 这可以这样做:

     for step in range(self.maxIters):
        QApplication.processEvents()
        ...

有了这个,你应该看到这样的输出:

Stopping?!
stop signalled?
Got done Sig! Got to 2

这可能是你想要的。

通常,线程在退出run方法时将关闭。 获取常规python线程的另一种方法是调用它的join方法。

对于PyQt,join方法应该是quit或terminate方法。 您可能仍应将结束变量设置为True。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM