multithreading - 在moveToThread之后pyqtSlot中未发出PyQt信号

标签 multithreading pyqt

因此,我想学习使用moveToThread并查看从另一个线程(在本例中为主线程)调用onTimeout()类的GenericWorker的效果。奇怪的是,finish_sig中的GenericWorker从未发出(应该在onTimeout()的最后一行发生)。由于它连接到terminate_thread()类中的Sender,因此至少应在控制台中打印出terminate_thread,但什么也没有发生。

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

谢谢大家花时间解决我的问题!

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_())

输出:
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

更新:

引用@tmoreau和@ekhumoro的答案后,此代码存在两个关键问题:
  • exit(1)不是退出的正确方法,我需要删除此行。
  • 我没有退出QApplication的方法,我需要做的是添加t.finish.connect(app.quit)退出应用程序。 (顺便说一句,最后一行sys.exit(app.exec_())似乎没有处理QApplication的退出问题)

  • 总而言之,我基本上需要退出三件事:QThreadQApplicationsys,我错过的是退出QApplication。让我知道我的理解是否正确...

    最佳答案

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

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

    即使线程尚未完成运行,exit()也会结束您的程序,因此出现错误:

    QThread: Destroyed while thread is still running



    如果删除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__结束时,它们将被销毁。

    关于multithreading - 在moveToThread之后pyqtSlot中未发出PyQt信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33923393/

    相关文章:

    multithreading - 信号量小书

    c# - ToggleSwitch.On & Off内容不更新

    multithreading - 从 servlet 内的线程访问请求对象

    c++ - 为什么 ZeroMQ 不使用其内部线程等待(接收/发送)?

    python - Qt QDialog渲染堆叠

    python - 向 QTextEdit 添加多行 PyQt

    java - 通过多线程的并发方法

    python - QDialog - 防止在 Python 和 PyQt 中关闭

    python - 在pyqt5中向QScrollArea添加自定义小部件不会使滚动条出现

    dialog - PyQt:如何创建一个对话框作为另一个对话框的子项