Details
Description
Just writing something like
class MyClass(QObject): def __init__(self, parent): self.machine = QStateMachine(self) ... @Slot() def some_handler(self): self.machine.postEvent(QEvent(QEvent.Type.User))
will cause a hard crash shortly after the state machine finishes processing the event.
If you keep the Python reference to the QEvent alive at least long enough for the machine to process it then the crash can be avoided, e.g. writing something like
@Slot() def some_handler(self): self.last_event = QEvent(QEvent.Type.User) self.machine.postEvent(self.last_event)
is sufficient to prevent the crash though I imagine it might not work if multiple calls to some_handler occurred during the same tick of the event loop.
The root cause seems to be QStateMachine.postEvent() not taking ownership of the event object despite that appearing to be the correct behaviour per the documentation.
I can demonstrate this by contrasting it with QCoreApplication.postEvent which has similar phrasing around ownership in the docs, e.g.
e1 = QEvent(QEvent.Type.User) print(f"Created event 1, owned by python? {Shiboken.ownedByPython(e1)}") QCoreApplication.postEvent(self.machine, e1) print(f"Posted event 1, owned by python? {Shiboken.ownedByPython(e1)}") e2 = QEvent(QEvent.Type.User) print(f"Created event 2, owned by python? {Shiboken.ownedByPython(e2)}") self.machine.postEvent(e2) print(f"Posted event 2, owned by python? {Shiboken.ownedByPython(e2)}")
Produces the output:
Created event 1, owned by python? True Posted event 1, owned by python? False Created event 2, owned by python? True Posted event 2, owned by python? True
I think the fix might be a change to the shiboken configuration files for QStateMachine but I wasn't able to get a local build of PySide6 working on my machine to test that theory.