PyQt线程间信号未被发射

3

我卡住了。使用Qt的C++ API,这应该很容易,而且我已经做过很多次,但是出现了问题,当我在PyQt中尝试时,我的几个信号/槽没有起作用(最近我开始使用PyQt中的Worker QObject的概念)。我相信这与我正在发射信号到/从分离线程有关。

from PyQt4.QtCore import QThread, QObject, pyqtSignal, pyqtSlot, QTimer
from PyQt4.QtGui import QApplication, QWidget, QVBoxLayout, QPushButton, QLabel

class Slave(QObject):

  countSignal = pyqtSignal(int)

  def __init__(self, parent = None):
    super(Slave, self).__init__()

    self.toggleFlag = False
    self.counter = 0

  @pyqtSlot()
  def work(self):
    if not self.toggleFlag: return

    if self.counter > 10: self.counter = 0
    self.counter += self.counter
    self.countSignal.emit(self.counter)

  @pyqtSlot()
  def toggle(self):
    self.toggleFlag = not self.toggleFlag


class Master(QWidget):

  toggleSignal = pyqtSignal()

  def __init__(self, parent = None):
    super(Master, self).__init__()

    self.initUi()
    self.setupConn()

  def __del__(self):
    self.thread.quit()
    while not self.thread.isFinished(): pass

  def initUi(self):
    layout = QVBoxLayout()
    self.buttonToggleSlave = QPushButton('Start')
    self.labelCounterSlave = QLabel('0')
    layout.addWidget(self.buttonToggleSlave)
    layout.addWidget(self.labelCounterSlave)
    self.setLayout(layout)
    self.show()

  def setupConn(self):
    self.thread = QThread()
    slave = Slave()
    timer = QTimer()
    timer.setInterval(100)

    # Make sure that both objects are removed properly once the thread is terminated
    self.thread.finished.connect(timer.deleteLater)
    self.thread.finished.connect(slave.deleteLater)

    # Connect the button to the toggle slot of this widget
    self.buttonToggleSlave.clicked.connect(self.toggle)
    # Connect widget's toggle signal (emitted from inside widget's toggle slot) to slave's toggle slot
    self.toggleSignal.connect(slave.toggle)

    # Connect timer's timeout signal to slave's work slot
    timer.timeout.connect(slave.work)
    timer.timeout.connect(self.timeout)
    # Connect slave's countSignal signal to widget's viewCounter slot
    slave.countSignal.connect(self.viewCounter)

    # Start timer
    timer.start()
    # Move timer and slave to thread
    timer.moveToThread(self.thread)
    slave.moveToThread(self.thread)

    # Start thread
    self.thread.start()    

  @pyqtSlot(int)
  def viewCounter(self, value):
    print(value)
    self.labelCounterSlave.setText(str(value))

  @pyqtSlot()
  def toggle(self):
    print("Toggle called")
    self.buttonToggleSlave.setText("Halt" if (self.buttonToggleSlave.text() == "Start") else "Start")
    self.toggleSignal.emit()

  @pyqtSlot()
  def timeout(self):
    print("Tick")


if __name__ == "__main__":
    app = QApplication([])
    w = Master()
    w.setStyleSheet('cleanlooks')
    app.exec_()

以下内容未被触发/发出:
- 我的小部件的timeout()插槽 - 我添加了这个插槽以查看为什么定时器不能触发我的worker插槽,但我发现它在这里也不起作用...... - Slave worker类内部的work()toggle()插槽。 - countSignal - 它从未被发出,因为我的小部件的viewCounter()插槽从未被触发。
我不知道我做错了什么。我已经连接了信号和插槽,启动了我的定时器,将其与worker一起移动到我的单独线程并启动了线程。
这里我是否漏掉了什么?
1个回答

3

这段代码有几个问题,导致它无法正常工作。

  1. 根据文档提示,你必须在其所在的线程中启动(和停止)定时器。你不能从另一个线程启动它。如果你想让定时器驻留在当前线程,则应将实例化代码移到 Slave 对象中,在一个与线程的 started 信号连接的槽函数中调用 timer.start()。但需要注意的是,Slave.__init__ 方法仍将在主线程中运行。或者,你可以将定时器保留在主线程中。

  2. setupConn() 完成后,slavetimer 将被垃圾回收。将它们存储为 self.slaveself.timer。 (或者,你可以为它们指定父级,但这似乎会导致程序退出时崩溃,因此最好将它们存储为实例属性)。

  3. 我猜测这一行 self.counter += self.counter 应该真正地变为 self.counter += 1 ? 否则,计数器将永远不会递增 :)


谢谢!我忘了定时器,这是主要问题。至于增量...(扇自己一巴掌)...对啊,它从未增加:D 顺便说一下针对self.slaveself:timer,我也认为它们超出了范围,但是我太习惯C++版本了,在那里,你要将指针传递给这两个线程,所以我没有想到在此更改。不过,我有一个问题——在C++中,如果您打算将这些东西移动到另一个线程中,则将其创建为类成员是不正确的。我猜Python不是这种情况吗?这不会使QObject.deleteLater()变得毫无意义吗? - rbaleksandar
@rbaleksandar 这可能不是一个好的实践(虽然我不确定有什么替代方案),但在Python中这样做并没有根本性的问题。它只是对C++对象的引用。deleteLater()应该仍然有效,如果您尝试在剩余引用之后访问已删除的Qt对象,则会在终端上打印有关底层C++对象已被删除的警告。 - three_pineapples
在C++中,如果你使用一个类成员变量,它将会(我现在无法正确地记住以下哪种情况会发生)失败或信号不会被正确处理,最终会在错误的线程中进行发射/捕获。这就是为什么我没有使用self,但在Python中传递参数的方式与我们在C++中控制它是通过引用还是值有些不同。如果从属和计时器是部件类的一部分(在我的情况下),那么一旦小部件被销毁,它也应该销毁包括这两个在内的所有成员。嗯... - rbaleksandar
1
@rbaleksandar 对于Python而言,当Python小部件类被垃圾回收时,指向c++对象的Python指针将被删除,但只有在没有其他东西持有对它们的引用且它们在Qt方面没有父项时,c++对象本身才会被垃圾回收。我认为只要您将Qt c++对象和Python引用视为松散耦合,您就能够看到什么有效,什么无效。关于与线程中的对象进行信号连接,这可能会很有帮助:https://dev59.com/oWIi5IYBdhLWcg3w_QiG - three_pineapples
这很有道理。谢谢你提供的链接页面。我现在正在阅读它。 - rbaleksandar

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