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

26

我在Python中使用QThreads遇到了问题。我想要改变标签的背景颜色。 但是我的应用程序在启动时崩溃了。 "QThread:线程仍在运行时被销毁"

   class MainWindow(QMainWindow):
      def __init__(self):
          QMainWindow.__init__(self)
          self.ui = Ui_MainWindow()
          self.ui.setupUi(self)

          statusTh = statusThread(self)
          self.connect(statusTh, SIGNAL('setStatus'), self.st, Qt.QueuedConnection)
          statusTh.start()

      def st(self):
          if self.status == 'ON':
              self.ui.label.setStyleSheet('background-color:green')
          else:
              self.ui.label.setStyleSheet('background-color:red')

  class statusThread(QThread):
      def __init__(self, mw):
          super(statusThread, self).__init__()

      def run(self):
          while True:
              time.sleep(1)
              self.emit(SIGNAL('setStatus'))

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

2
一种Qt风格的解决方案,将主窗口指定为statusTh的父级,即super(statusThread, self).__init__(mw) - nymk
仅供参考,这基本上是相同的事情:在这种情况下,父对象将保留对线程实例的引用,因此它不会被垃圾回收。 - rainer
有人可以解释一下,在上面的问题中,Qt.QueuedConnection 是什么意思吗? - Natesh bhat
1
所以基本上这只是个打字错误? - greendino
1
上面示例中的 super().__init__() 调用显然是一个打字错误,因为它没有对 mw 做任何事情。 - Mike C
3个回答

43

创建线程后,您没有保存对该线程的引用,这意味着在程序离开MainWindow__init__后,它将被垃圾回收(即被销毁)。您需要至少在线程运行期间保存它,例如使用self.statusTh

self.statusTh = statusThread(self)
self.connect(self.statusTh, SIGNAL('setStatus'), self.st, Qt.QueuedConnection)
self.statusTh.start()

1
抱歉,我是Python的新手。我不理解。我需要如何存储引用? - Sergey Kostin
9
就像我在答案中写的一样。通过将statusThread(self)的实例分配给一个本地变量(即没有在前面加上self.),当本地变量超出作用域时(即MainWindow__init__结束时),它将被垃圾回收。如果将引用存储在MainWindow类的成员变量中(即在前面加上self.),当__init__完成时,它将不会超出作用域,导致线程对象无法被垃圾回收。 - rainer
我会给你10个赞!很好的答案 - 为我节省了很多时间和烦恼!我认为你应该将评论文本也移到答案中。 - Eugene Sajine
不好意思,如果你不想存储引用,有没有办法创建一个新的对象(线程)的引用?我有很多需要同时工作的方法。 - Victor Polevoy

0

我知道这是很久以前的帖子,但这可能会有用。在脚本的主要部分中,需要将第一级自定义小部件存储在变量中,而不仅仅是创建它。 例如,我有一个名为MainWindow的自定义小部件类,它创建了一个QThread。我的主要代码如下:

from myPackage import MainWindow
if __name__ == "__main__":
    app = QApplication([])    
    widget=MainWindow()    
    sys.exit(app.exec())

如果我避免widged =的定义,只调用MainWindow(),我的脚本将崩溃并显示QThread: Destroyed while thread is still running


OP已经这样做了。此外,从原则上讲,避免这样做是相当错误的,因为对象将被垃圾回收,就像当前接受的答案已经解释的那样。如果你只是做MainWindow()而不是widget = MainWindow(),那个窗口可能根本不会显示。如果它确实显示了,那么你在代码中做出了错误的假设,这可能被认为是不良实践,或者至少在这个上下文中没有适当的理由。此外,你的代码从未调用widget.show(),这将使应用程序在后台运行,消耗资源。 - undefined

-1
statusTh.start()后面添加statusTh.wait():
...
statusTh.start()
statusTh.wait()
...

不行。QThread.wait() 是阻塞调用线程的,类似于Python的join()。它会在程序启动时完全冻结整个程序(因为它在主窗口的__init__中被调用),因为它完全阻塞了事件循环。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接