发出`Signal`来直接将`QStateMachine`转换到另一个`QState`

问题描述 投票:0回答:1

我有以下状态机:

我已经通过标准状态模式在 Python 中实现了这一点。该状态机在与 PySide6 应用程序线程不同的线程中运行,而 PySide6 应用程序线程在程序的主线程上运行。我们称这个状态机类为

Controller

在 GUI 方面,我使用 Qt for Python 状态机框架。 (注意:链接的文档适用于 PySide2/PySide5,但我使用的是 Pyside6。出于某种原因,PySide6 不存在相同的文档。)这使得定义进入状态时 GUI 中发生的情况非常好.

我正在寻找的是不是让GUI启动转换,而是让我的核心状态机

Controller
在另一个线程中运行来控制转换。通常,人们使用
<QState>.addTransition(...)
在 GUI 级别添加转换,但我希望 GUI 简单地将命令发送到
Controller
,然后当
Controller
转换其状态时,我希望它发出
Signal
触发 PySide6
QStateMachine
进入给定状态,从而设置适合该状态的所有属性。换句话说,我希望 GUI 将命令分派到
Controller
状态机,并让 GUI “监听”
Controller
的状态转换。


所以问题是,给定一个

QState
,我如何向它发送一个信号来强制它所属的
QStateMachine
转换到该状态?或者我是否需要向
QStateMachine
发送信号并为其提供
QState
来转换到?

示例程序:

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import override
from PySide6.QtStateMachine import QState


class Controller:
    def __init__(self, initial_state: IState, states: list[QState]) -> None:
        """Initialize the controller to the given initial state and call the `
        on_entry` method for the state.
        """
        self.__state = initial_state
        self.__state.controller = self
        self.__state.on_entry()
        self.__state.states = states

    def _transition_to(self, new_state: IState) -> None:
        """Transition from the current state to the given new state. This calls
        the `on_exit` method on the current state and the `on_entry` of the
        new state. This method should not be called by any object other than
        concrete implementations of `IState`.
        """
        self.__state.on_exit()
        self.__state = new_state
        self.__state.controller = self
        self.__state.on_entry()

    @property
    def state(self):
        """Get the current state"""
        return self.__state

    def start_camera_exposure(self) -> None:
        self.__state.start_camera_exposure()

    def stop_camera_exposure(self) -> None:
        self.__state.stop_camera_exposure()

    def abort_camera_exposure(self) -> None:
        self.__state.abort_camera_exposure()


class IState(ABC):
    """Serve as an interface between the controller and the explicit, individual states."""

    @property
    def controller(self) -> Controller:
        return self.__controller

    @controller.setter
    def controller(self, controller: Controller) -> None:
        self.__controller = controller

    @property
    def states(self) -> list[QState]:
        return self.__states

    @states.setter
    def states(self, states: list[QState]):
        self.__states = states

    def on_entry(self) -> None:
        """Can be overridden by a state to perform an action when the state is
        being entered, i.e., transitions into. It is not required to be overridden.
        """
        pass

    def on_exit(self) -> None:
        """Can be overridden by a state to perform an action when the state is
        being exited, i.e., transitioned from. It is not required to be overridden.
        """
        pass

    # If a concrete implementation does not handle the called method, i.e., it is an invalid action
    # in the specific state, it is enough to simply call `pass`.

    @abstractmethod
    def start_camera_exposure(self) -> None: ...

    @abstractmethod
    def stop_camera_exposure(self) -> None: ...

    @abstractmethod
    def abort_camera_exposure(self) -> None: ...


class Idle(IState):
    @override
    def on_entry(self):
        # I want to emit a signal here to force a `QStateMachine` to go to state: `self.__states[0]`
        print("Idling ...")

    def start_camera_exposure(self) -> None:
        self.controller._transition_to(CameraExposing())

    def stop_camera_exposure(self) -> None:
        pass

    def abort_camera_exposure(self) -> None:
        pass


class CameraExposing(IState):
    @override
    def on_entry(self) -> None:
        # I want to emit a signal here to force a `QStateMachine` to go to state: `self.__states[1]`
        print("Starting camera exposure ...")

    @override
    def on_exit(self) -> None:
        print("Stopping camera exposure ...")

    def start_camera_exposure(self) -> None:
        pass

    def stop_camera_exposure(self) -> None:
        self.controller._transition_to(SavingCameraImages())

    def abort_camera_exposure(self) -> None:
        self.controller._transition_to(AbortingCameraExposure())


class SavingCameraImages(IState):
    @override
    def on_entry(self) -> None:
        # I want to emit a signal here to force a `QStateMachine` to go to state: `self.__states[2]`
        print("Saving camera images ...")
        self.controller._transition_to(Idle())

    def start_camera_exposure(self) -> None:
        pass

    def stop_camera_exposure(self) -> None:
        pass

    def abort_camera_exposure(self) -> None:
        pass


class AbortingCameraExposure(IState):
    @override
    def on_entry(self) -> None:
        # I want to emit a signal here to force a `QStateMachine` to go to state: `self.__states[3]`
        print("Aborting camera exposure ...")
        self.controller._transition_to(Idle())

    def start_camera_exposure(self) -> None:
        pass

    def stop_camera_exposure(self) -> None:
        pass

    def abort_camera_exposure(self) -> None:
        pass

在 GUI 方面,我有类似的东西:

from PySide6.QtStateMachine import QState, QStateMachine


class MainWindow(QWidget):
    def __init__(self) -> None:
        super().__init__()

        self.machine = QStateMachine(parent=self)
        self.state_idle = QState(self.machine)
        self.state_camera_exposing = QState(self.machine)
        self.state_saving_camera_images = QState(self.machine)
        self.state_aborting_camera_exposure = QState(self.machine)
        self.machine.setInitialState(self.state_idle)
        self.states = [
            self.state_idle,
            self.state_camera_exposing,
            self.state_saving_camera_images,
            self.state_aborting_camera_exposure,
        ]

        self.initialize()
python pyqt pyside pyside6 qstatemachine
1个回答
0
投票

到目前为止,我想出的唯一方法是通过简单地为每个状态的每个转换信号创建添加所有可能的转换。

class MainWindow(QWidget)
内部,定义类变量来表示各种转换信号:

transition_to_idle = Signal()
transition_to_camera_exposing = Signal()
transition_to_saving_camera_images = Signal()
transition_to_aborting_camera_exposure = Signal()

然后为每个状态添加每个信号的转换,转换到由信号确定的状态:

machine = self.machine
state_idle = self.state_idle
state_camera_exposing = self.state_camera_exposing
state_saving_camera_images = self.state_saving_camera_images
state_aborting_camera_exposure = self.state_aborting_camera_exposure

state_idle.addTransition(self.transition_to_idle, state_idle)
state_idle.addTransition(self.transition_to_camera_exposing, state_camera_exposing)
state_idle.addTransition(self.transition_to_saving_camera_images, state_saving_camera_images)
state_idle.addTransition(self.transition_to_aborting_camera_exposure, state_aborting_camera_exposure)

state_camera_exposing.addTransition(self.transition_to_idle, state_idle)
state_camera_exposing.addTransition(self.transition_to_camera_exposing, state_camera_exposing)
state_camera_exposing.addTransition(self.transition_to_saving_camera_images, state_saving_camera_images)
state_camera_exposing.addTransition(self.transition_to_aborting_camera_exposure, state_aborting_camera_exposure)

state_saving_camera_images.addTransition(self.transition_to_idle, state_idle)
state_saving_camera_images.addTransition(self.transition_to_camera_exposing, state_camera_exposing)
state_saving_camera_images.addTransition(self.transition_to_saving_camera_images, state_saving_camera_images)
state_saving_camera_images.addTransition(self.transition_to_aborting_camera_exposure, state_aborting_camera_exposure)

state_aborting_camera_exposure.addTransition(self.transition_to_idle, state_idle)
state_aborting_camera_exposure.addTransition(self.transition_to_camera_exposing, state_camera_exposing)
state_aborting_camera_exposure.addTransition(self.transition_to_saving_camera_images, state_saving_camera_images)
state_aborting_camera_exposure.addTransition(self.transition_to_aborting_camera_exposure, state_aborting_camera_exposure)

然后在另一个线程中,用

emit
:

发出信号
transition_to_idle.emit()
transition_to_camera_exposing.emit()
transition_to_saving_camera_images.emit()
transition_to_aborting_camera_exposure.emit()
© www.soinside.com 2019 - 2024. All rights reserved.