在moveToThread

时间:2015-11-25 17:58:03

标签: multithreading pyqt

所以我想学习使用moveToThread,看看从另一个线程(本例中是主线程)调用类onTimeout()的{​​{1}}的效果。奇怪的是,GenericWorker中的finish_sig永远不会被释放(应该发生在GenericWorker的最后一行)。由于它连接到onTimeout()类中的terminate_thread(),它至少应该在控制台中打印出Sender,但根本不会发生任何事情。

我使用它的最初目的是在terminate_thread完成后发出一个信号退出线程。但是现在我只能从onTimeout()执行t.quit()来退出线程。

感谢大家花时间照顾我的问题!

main

输出:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import threading
from time import sleep
import sys

class GenericWorker(QObject):
    finish_sig = pyqtSignal() # this one never gets emitted!

    @pyqtSlot(str, str)
    def onTimeout(self, cmd1, cmd2):
        print 'onTimeout get called from thread ID: '
        print QThread.currentThreadId()
        print 'received cmd 1: ' + cmd1
        print 'received cmd 2: ' + cmd2
        self.finish_sig.emit()    # supposed to emit here!  

class Sender(QObject):
    send_sig = pyqtSignal(str, str)
    terminate_sig = pyqtSignal()
    def emit_sig(self, cmd):
        print 'emit_sig thread ID: '
        print QThread.currentThreadId()
        sleep(1)
        self.send_sig.emit(cmd, '2nd_cmd')

    def terminate_thread(self):
        print 'terminate_thread'
        self.terminate_sig.emit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    print 'Main thread ID: '
    print QThread.currentThreadId()

    t = QThread()
    my_worker = GenericWorker()
    my_worker.moveToThread(t)
    t.start()

    my_sender = Sender()
    my_sender.send_sig.connect(my_worker.onTimeout)
    my_sender.terminate_sig.connect(t.quit)

    my_worker.finish_sig.connect(my_sender.terminate_thread)
    # my_worker.finish_sig.connect(t.quit)

    my_sender.emit_sig('hello')
    sleep(1)
    # my_sender.terminate_thread()
    # t.quit() # this one works
    # t.wait()
    exit(1)

    sys.exit(app.exec_())

更新

在提到@tmoreau和@ ekhumoro的答案之后,这段代码有两个关键问题:

  1. Main thread ID: 46965006517856 emit_sig thread ID: 46965006517856 onTimeout get called from thread ID: 1111861568 received cmd 1: hello received cmd 2: 2nd_cmd QThread: Destroyed while thread is still running 不是退出的正确方法,我需要删除此行。
  2. 我无法退出exit(1),我需要做的是添加QApplication以退出应用程序。 (顺便说一句,最后一行t.finish.connect(app.quit)似乎没有处理退出sys.exit(app.exec_())
  3. 总而言之,基本上我需要退出三件事:QApplicationQThreadQApplication,我错过的是退出sys。如果我的理解是对的,请告诉我......

1 个答案:

答案 0 :(得分:1)

您的问题是您在程序完成之前退出程序。

my_sender.emit_sig('hello')
sleep(1) 
exit(1)
sys.exit(app.exec_())

exit()结束你的程序,即使线程尚未运行,因此错误:

  

QThread:在线程仍在运行时被销毁

如果您删除sleep(1),您甚至可以更早地看到程序停止:

Main thread ID:
46965006517856
emit_sig thread ID:
46965006517856
QThread: Destroyed while thread is still running

这里或多或少地发生了并行的事情:

# main thread                      #worker thread
my_sender.emit_sig('hello')        #slot onTimeout is called
sleep(1)                           #print "onTimeout get called..."
exit(1)                            #emit finish_sig
sys.exit(app.exec_())
# slot terminate_thread is called  #thread ends (t.quit)

如果删除exit(1),您的程序将会正常运行,因为您使用app.exec_()创建了一个事件循环。事件循环意味着您的程序始终在等待捕获信号,即使没有任何事情可做,也不会停止。所以线程有足够的时间来结束:)

在Qt中,您通常会通过关闭主窗口来停止事件循环。因此,实现您的线程的更简洁方法是:

class window(QWidget):
    def __init__(self,parent=None):
        super(window,self).__init__(parent)

        t=QThread(self)
        self.my_worker = GenericWorker()
        self.my_worker.moveToThread(t)
        t.start()

        self.my_sender = Sender()
        self.my_sender.send_sig.connect(self.my_worker.onTimeout)
        self.my_sender.terminate_sig.connect(t.quit)

        self.my_worker.finish_sig.connect(self.my_sender.terminate_thread)

        self.my_sender.emit_sig('hello')

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

您需要self来保持对线程和类的引用。否则,__init__结束时会销毁它们。