【QT】状态机框架

状态机框架

状态机框架提供了用于创建和执行状态图的类。其概念和符号基于哈瑞尔的《Statecharts: A visual formalism for complex systems》,该文档也是UML状态图的基础。状态机执行的语义基于State Chart XML(SCXML)。

状态图提供了一种图形化建模系统对刺激的反应方式。这是通过定义系统可能处于的 状态,以及系统如何从一个状态转移到另一个状态(状态之间的转换)来实现的。事件驱动系统(如Qt应用程序)的一个关键特征是行为往往不仅取决于最后或当前事件,还取决于先前的事件。使用状态图,可以轻松表达这些信息。

状态机框架提供了一个API和执行模型,可以有效地在Qt应用程序中嵌入状态图的元素和语义。该框架与Qt的元对象系统紧密集成;例如,状态之间的转换可以由信号触发,并且可以配置状态以在QObject上设置属性和调用方法。Qt的事件系统用于驱动状态机。

状态机框架中的状态图是分层的。状态可以嵌套在其他状态内部,并且状态机的当前配置包括当前活动的状态集。状态机的有效配置中的所有状态将具有共同的祖先。

状态机框架中的类

这些类由Qt提供,用于创建基于事件驱动的状态机。

QAbstractState QStateMachine 的状态的基类
QAbstractTransition QAbstractState 对象之间转换的基类
QEventTransition 专门用于 Qt 事件的转换
QFinalState 最终状态
QHistoryState 返回到之前活动的子状态的方式
QKeyEventTransition 键盘事件的转换
QMouseEventTransition 鼠标事件的转换
QSignalTransition 基于 Qt 信号的转换
QState QStateMachine 的通用状态
QStateMachine 分层有限状态机
QStateMachine::SignalEvent 表示 Qt 信号事件
QStateMachine::WrappedEvent 继承自 QEvent 并持有与 QObject 关联的事件的克隆

一个简单的状态机

为了演示状态机 API 的核心功能,让我们看一个小例子:一个拥有三个状态 s1s2s3 的状态机。这个状态机由一个单独的 QPushButton 控制;当按钮被点击时,状态机会转移到另一个状态。最初,状态机处于状态 s1。这个状态机的状态图如下:

在这里插入图片描述

以下代码片段显示了创建这样一个状态机所需的代码。首先,我们创建状态机和状态:

    QStateMachine machine;
    QState *s1 = new QState();
    QState *s2 = new QState();
    QState *s3 = new QState();

然后,我们使用 QState::addTransition() 函数创建转换:

    s1->addTransition(button, &QPushButton::clicked, s2);
    s2->addTransition(button, &QPushButton::clicked, s3);
    s3->addTransition(button, &QPushButton::clicked, s1);

接下来,我们将状态添加到状态机中,并设置状态机的初始状态:

    machine.addState(s1);
    machine.addState(s2);
    machine.addState(s3);
    machine.setInitialState(s1);

最后,我们启动状态机:

    machine.start();

状态机是异步执行的,即它成为应用程序事件循环的一部分。

在状态进入和退出时执行有用的工作

上面的状态机仅仅是从一个状态转移到另一个状态,它并没有执行任何操作。可以使用 QState::assignProperty() 函数在状态进入时设置 QObject 的属性。在下面的代码片段中,为每个状态指定了应该分配给 QLabel 的 text 属性的值:

s1->assignProperty(label, "text", "In state s1");
s2->assignProperty(label, "text", "In state s2");
s3->assignProperty(label, "text", "In state s3");

当任何状态被进入时,标签的文本将相应更改。

当状态被进入时,会发射 QState::entered() 信号,当状态被退出时,会发射 QState::exited() 信号。在下面的代码片段中,当状态 s3 被进入时,将调用按钮的 showMaximized() 槽,并且当 s3 被退出时,将调用按钮的 showMinimized() 槽:

QObject::connect(s3, &QState::entered, button, &QPushButton::showMaximized);
QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);

自定义状态可以重新实现 QAbstractState::onEntry()QAbstractState::onExit()

完成的状态机

在上一节中定义的状态机永远不会完成。为了使状态机能够完成,它需要有一个顶级的 最终 状态(QFinalState 对象)。当状态机进入顶级最终状态时,状态机将发射 finished() 信号并停止。

要在图中引入最终状态,你只需创建一个 QFinalState 对象,并将其用作一个或多个转换的目标即可。

通过分组状态共享转换

假设我们希望用户能够随时通过单击“退出”按钮退出应用程序。为了实现这一点,我们需要创建一个最终状态,并将其作为与“退出”按钮的 clicked() 信号关联的转换的目标。我们可以从 s1s2s3 中的每个状态添加一个转换;然而,这似乎是冗余的,并且还需要记住在未来添加的每个新状态中添加这样的转换。

我们可以通过将状态 s1s2s3 分组来实现相同的行为(即,单击“退出”按钮退出状态机,无论状态机当前处于哪个状态)。这是通过创建一个新的顶级状态,并使三个原始状态成为新状态的子状态来完成的。下面的图表显示了新的状态机。

在这里插入图片描述

原来的三个状态已经被重命名为 s11s12s13,以反映它们现在是新的顶级状态 s1 的子状态。子状态隐式地继承其父状态的转换。这意味着现在只需要从 s1 到最终状态 s2 添加一个转换就足够了。添加到 s1 的新状态也将自动继承这个转换。

要将状态分组,只需在创建状态时指定正确的父状态即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值