Reputation: 147
Qt has a promising SCXML module. Since PySCXML is obsolete, there is no other native python scxml library, which lets me run a scxml statemachine. That's why I try PySide6.
Since I don't need any Qt despite of the scxml library, I thought about running the QCoreApplication in a seperate thread, in order to have the event-loop right there. According to the documentation QScxmlStateMachine needs one.
Unfortunately my start_statemachine()
method doesn't return, but the statemachine starts working.
Any advice on how to start a QScxmlStateMachine in a thread is welcomed.
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()
Upvotes: 0
Views: 769
Reputation: 48241
The thread target needs to be a reference to a function that will be called in the external thread, but you're not running start_statemachine()
in another thread: you're actually executing it in place:
x = threading.Thread(target=start_statemachine('statemachine.scxml'))
^^^^^^^^^^^^^^^^^^^^^^
Your program is stuck there, no thread is even created because the constructor is still "waiting" for start_statemachine()
to return, and since exec()
is blocking, nothing else happens.
A basic solution could be to use a lambda:
x = threading.Thread(target=lambda: start_statemachine('statemachine.scxml'))
But you'll need access to the application in order to be able to quit it: x.join()
won't do nothing, because the QCoreApplication event loop will keep going, so a possibility is to create a basic class that provides a reference to the application:
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()
Upvotes: 1