在线程中使用 PySide6

Use PySide6 in thread

Qt 有一个很有前途的 SCXML 模块。由于 PySCXML 已过时,因此没有其他本机 python scxml 库,它让我 运行 一个 scxml 状态机。这就是我尝试 PySide6 的原因。

尽管有 scxml 库,但由于我不需要任何 Qt,所以我考虑 运行在单独的线程中设置 QCoreApplication,以便在那里设置事件循环。 根据文档,QScxmlStateMachine 需要一个。

不幸的是,我的 start_statemachine() 方法没有 return,但状态机开始工作。

欢迎就如何在线程中启动 QScxmlStateMachine 提出任何建议。

from PySide6.QtCore import QCoreApplication, QObject
from PySide6.QtScxml import QScxmlStateMachine
from PySide6.QtCore import QTimer
import threading


def start_statemachine(filename):
    app = QCoreApplication()
    mysm = MyStateMachine(filename)
    mysm.start_sm()
    app.exec()


class MyStateMachine(QObject):

    def __init__(self, filename):
        super(MyStateMachine, self).__init__()
        self.sm = QScxmlStateMachine.fromFile(filename)
        self.counter = 0
        self.timer = QTimer()
        self.timer.setInterval(2000)
        self.timer.timeout.connect(self.recurring_timer)
        self.timer.start()

    def start_sm(self):
        print('starting statemachine')
        self.sm.setRunning(True)

    def recurring_timer(self):
        print(self.sm.activeStateNames())
        self.counter += 1
        print("Counter: %d" % self.counter)
        print('statemachine running status: ' + str(self.sm.isRunning()))


if __name__ == '__main__':
    x = threading.Thread(target=start_statemachine('statemachine.scxml'))
    x.start() #won't be reached
    while True:
        pass #do something else
    x.join()

线程目标需要是将在外部线程中调用的函数的引用,但您不是运行start_statemachine()在另一个线程中:你实际上是在原地执行它:

x = threading.Thread(target=start_statemachine('statemachine.scxml'))
                                              ^^^^^^^^^^^^^^^^^^^^^^

你的程序卡在那里,甚至没有创建线程,因为构造函数仍在“等待”start_statemachine() 到 return,并且由于 exec() 正在阻塞,所以没有其他事情发生.

一个基本的解决方案可能是使用 lambda:

x = threading.Thread(target=lambda: start_statemachine('statemachine.scxml'))

但是您需要访问该应用程序才能退出它:x.join()不会什么都不做,因为 QCoreApplication 事件循环将继续进行,所以可能是创建一个基本 class,提供对应用程序的引用:

class StateMachineWrapper:
    app = None
    def __init__(self, filename):
        self.filename = filename

    def start(self):
        self.app = QCoreApplication([])
        mysm = MyStateMachine(self.filename)
        mysm.start_sm()
        self.app.exec()

# ...

if __name__ == '__main__':
    statemachine = StateMachineWrapper('statemachine.scxml')
    x = threading.Thread(target=statemachine.start)
    x.start()
    while True:
        pass #do something else

    if statemachine.app:
        statemachine.app.quit()

    x.join()