Qt 状态机框架:The State Machine Framework (二)

传送门:
Qt 状态机框架:The State Machine Framework (一)
Qt 状态机框架:The State Machine Framework (二)

1、利用并行态避免态的组合爆炸

假设您想在单个状态机中对汽车的一组互斥属性进行建模。假设我们感兴趣的属性是干净与肮脏,以及移动与不移动。需要四个相互排斥的状态和八个转换才能表示所有可能的组合并在它们之间自由移动。
在这里插入图片描述
如果我们增加第三个属性(比如红色与蓝色),状态的总数将翻一番,达到8个;如果我们增加第四个属性(比如,封闭与可转换),状态的总数将再次翻倍,达到16。

使用并行状态,状态和转换的总数随着我们添加更多属性而线性增长,而不是指数增长。此外,状态可以添加到并行状态或从并行状态中删除,而不会影响其任何兄弟状态。

在这里插入图片描述
【codes】:


#include <QApplication>
#include <QWidget>

#include <QState>
#include <QStateMachine>
#include <QPushButton>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;

    QPushButton* controlButton_0 = new QPushButton(QStringLiteral("雨刷"),&w);
    QPushButton* controlButton_1 = new QPushButton(QStringLiteral("刹车"),&w);
    QLabel* label_0 = new QLabel(&w);
    QLabel* label_1 = new QLabel(&w);
    controlButton_0->setGeometry(0,0,200,60);
    controlButton_1->setGeometry(0,70,200,60);
    label_0->setGeometry(200,0,200,60);
    label_1->setGeometry(200,70,200,60);

    QStateMachine machine;

    QState* s1  = new QState(QState::ParallelStates);
    {
        QState* s11 = new QState(s1);
        {
            QState* s11_clean = new QState(s11);
            QState* s11_dirty = new QState(s11);

            s11_clean->assignProperty(label_0,"text","clean");
            s11_dirty->assignProperty(label_0,"text","dirty");

            s11_clean->addTransition(controlButton_0,SIGNAL(clicked(bool)),s11_dirty);
            s11_dirty->addTransition(controlButton_0,SIGNAL(clicked(bool)),s11_clean);

            s11->setInitialState(s11_clean);
        }


        QState* s12 = new QState(s1);
        {
            QState* s12_notMoving = new QState(s12);
            QState* s12_moving = new QState(s12);

            s12_notMoving->assignProperty(label_1,"text","not moving");
            s12_moving->assignProperty(label_1,"text","moving");

            s12_notMoving->addTransition(controlButton_1,SIGNAL(clicked(bool)),s12_moving);
            s12_moving->addTransition(controlButton_1,SIGNAL(clicked(bool)),s12_notMoving);

            s12->setInitialState(s12_notMoving);
        }
    }

    machine.addState(s1);
    machine.setInitialState(s1);
    machine.start();

    w.show();
    return a.exec();
}

【运行效果】:
假设汽车已经启动。开/关雨刷和是否踩住刹车是两个并行的,互不干扰的状态。
在这里插入图片描述

2、状态结束状态检测

在示例1的基础上,我们对汽车的 【启动->运行->结束】这一完整的过程进行简单的状态模拟。
在实际生活中,我们踩住刹车同时按住【一键启动】按钮,汽车点火,进入s1启动状态。当汽车熄火,进入s2状态。当汽车驻车和锁闭车门后,进入s3状态。生活中的car 启停和运行过程中的操作状态切换模型,远比我们下图中所绘制的要复杂的多。本例对模型进行适当的简化。

在启动状态下,我们可以对汽车进行一些操作,有些操作状态时串行的,也有一些操作状态是并行的。 并行的状态譬如我们在示例1或者如下状态表所示的 开关雨刷与控制汽车的行驶和停止;串行的状态,譬如我们汽车点火和熄火两个状态,他不能同时存在,类似这样的状态我们认为是串行的。不过,串行的状态还可以分为循环状态和 可中止状态。 譬如我们打开车门,在主驾驶座位上可以循环重复【打火启动】->【停车熄火】这两个状态的切换;但是如果我们已经下车,在【熄火】状态下,我们可以将车门重【未锁车】状态切换到【锁车】终止状态。

当进入到终止状态(QFinalState)后,会发送 finished信号
在这里插入图片描述

【codes】:


#include <QApplication>
#include <QWidget>

#include <QState>
#include <QStateMachine>
#include <QFinalState>

#include <QPushButton>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;


    QPushButton* fire_button = new QPushButton(QStringLiteral("点火/熄火"),&w);
    QPushButton* controlButton_0 = new QPushButton(QStringLiteral("雨刷"),&w);
    QPushButton* controlButton_1 = new QPushButton(QStringLiteral("刹车"),&w);
    QPushButton* lock_button = new QPushButton(QStringLiteral("锁车"),&w);

    QLabel* label_0 = new QLabel(&w);
    QLabel* label_1 = new QLabel(&w);


    fire_button->setGeometry(0,300,200,60);
    lock_button->setGeometry(0,400,200,60);
    controlButton_0->setGeometry(0,0,200,60);
    controlButton_1->setGeometry(0,70,200,60);
    label_0->setGeometry(200,0,200,60);
    label_1->setGeometry(200,70,200,60);

    QStateMachine machine;

    QState* car = new QState();

    QState* s1  = new QState(QState::ParallelStates,car);
    {
        QState* s11 = new QState(s1);
        {
            QState* s11_clean = new QState(s11);
            QState* s11_dirty = new QState(s11);

            s11_clean->assignProperty(label_0,"text","clean");
            s11_dirty->assignProperty(label_0,"text","dirty");

            s11_clean->addTransition(controlButton_0,SIGNAL(clicked(bool)),s11_dirty);
            s11_dirty->addTransition(controlButton_0,SIGNAL(clicked(bool)),s11_clean);

            s11->setInitialState(s11_clean);
        }


        QState* s12 = new QState(s1);
        {
            QState* s12_notMoving = new QState(s12);
            QState* s12_moving = new QState(s12);

            s12_notMoving->assignProperty(label_1,"text","not moving");
            s12_moving->assignProperty(label_1,"text","moving");

            s12_notMoving->addTransition(controlButton_1,SIGNAL(clicked(bool)),s12_moving);
            s12_moving->addTransition(controlButton_1,SIGNAL(clicked(bool)),s12_notMoving);

            s12->setInitialState(s12_notMoving);
        }
    }


    QState* s2 = new QState(car);
    {
        QState* s21 = new QState(s2);
        QFinalState* finalState = new QFinalState(s2);

        s21->addTransition(lock_button,SIGNAL(clicked(bool)),finalState);

        s2->setInitialState(s21);

    }

    s1->addTransition(fire_button,SIGNAL(clicked(bool)),s2);
    s2->assignProperty(label_0,"text",QStringLiteral("已熄火"));
    s2->assignProperty(label_1,"text",u8"已熄火");

    QState* s3 = new QState(car);
    s2->addTransition(s2, SIGNAL(finished()), s3);
    s3->assignProperty(label_0,"text",u8"已熄火锁车");
    s3->assignProperty(label_1,"text",u8"已熄火锁车");

    car->setInitialState(s1);

    machine.addState(car);
    machine.setInitialState(car);
    machine.start();


    w.show();
    return a.exec();
}

【运行效果】:
在这里插入图片描述

解释一下代码演示的流程:
程序启动,状态机启动,进入到默认的car.s1状态(点火状态),s1是一个并行的子状态机,包括雨刷和刹车这两个对象的状态控制,所以如图演示的,我们点击雨刷和刹车按钮,可以自由切换两个对象的状态,互补干扰;
当我们点击【点火/熄火】按钮,car的状态切换到car.s2(熄火状态),s2是一个串行的状态机,默认状态是[熄火/未锁车],当我们点击【锁车】按钮时,car的状态切换到car.s2.finalState s2状态结束,发送一个finished的信号,car切换到s3状态。

至此,一个相对复杂一点儿的状态机模型我们演示完成。当然实际的应用场景往往比这个要复杂的更多。但是我们只要保证一下几个步骤/原则,无论多复杂的状态转换模型,都可以轻而易举的完成。

* 1、状态模块划分(先父后子)
* 2、区分并行or串行
* 3、状态是否要中断
* 4、是否有结束状态
* 5、父状态需添加默认子状态
* 6、状态机需设置默认顶层状态


[参考文档]:
1、https://blog.csdn.net/qq_35629971/article/details/125988152
2、https://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf
3、Qt Assistant 5.14.2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/330186.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【教3妹学编程-算法题】检查按位或是否存在尾随零

3妹&#xff1a;呜呜&#xff0c;烦死了&#xff0c; 脸上长了一个痘 2哥 : 不要在意这些细节嘛&#xff0c;不用管它&#xff0c;过两天自然不就好了。 3妹&#xff1a;切&#xff0c;你不懂&#xff0c;影响这两天的心情哇。 2哥 : 我看你是不急着找工作了啊&#xff0c; 工作…

Golang通过Gorm操作Mysql时遇到的datetime时区问题

情景描述 golang使用Gorm操作MySQL&#xff0c;MySQL中数据类型是datetime&#xff0c;Golang中用的是time.now。 但是会导致存储的时间与北京时间有8h误差&#xff0c; 显然是没有初始化时区导致。 问题修复 初始化设置时区 参考我自己之前写过的一篇总结——Mysql中多种日…

QT上位机开发(不同场景下界面的设计模板)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 qt由于其优秀的跨平台属性&#xff0c;几乎成了嵌入式开发界面开发的标配。同时呢&#xff0c;由于它在windows平台开发出来的效果也是非常的好&am…

Python开发环境安装:梦的起点

Python解释器安装 前言 解释器&#xff08;Interpreter&#xff09;&#xff0c;又译为直译器&#xff0c;是一种电脑程序能够把高级编程语言一行一行直接转译运行。解释器不会一次把整个程序转译出来&#xff0c;只像一位“中间人”&#xff0c;每次运行程序时都要先转成另一…

dubbo入门案例!!!

入门案例之前我们先介绍一下&#xff1a;zookeeper。 Zookeeper是Apacahe Hadoop的子项目&#xff0c;可以为分布式应用程序协调服务&#xff0c;适合作为Dubbo服务的注册中心&#xff0c;负责服务地址的注册与查找&#xff0c;相当于目录服务&#xff0c;服务提供者和消费者只…

rust跟我学七:获取外网IP地址

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎么获取到本机的外网IP地址。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址:[…

测试驱动开发:基于Jenkins+GoTest+HTML的持续化集成

目录 前言 一、项目框架 1.项目迭代 2.项目时序图 3.项目测试执行 二、项目具体实现 1.创建流水线 2.拉取代码 3.执行测试代码 4.生成测试报告 5.报告内容解读 6.数据统计 7.邮件通知 8.企业微信通知 三、项目遇到的问题 1.go test -args 2.go test生…

MyBatisPlus学习笔记四-扩展功能

1、代码生成器 1.1、官方的1 1.3、官方的2-idea插件 1.3、非官方的-idea插件 2、静态工具 先查询&#xff0c;再分组 3、逻辑删除 4、枚举处理器 5、JSON处理器

二、ArcGIS Pro SDK 开发环境配置踩坑

上篇写了如何配置开发环境&#xff0c;也确实是配置好了&#xff0c;激动的就睡觉去了&#xff0c;万万没想到&#xff0c;今天当要创建工程的时候&#xff0c;结果发现创建不了&#xff0c;弹出了如下错误&#xff1a; 很郁闷&#xff0c;于是有查找了资料发现&#xff1a; 是…

2024年华数杯国际赛B题超详细解题思路

ICM B题&#xff1a;光伏发电 该题目出题的难度与方向都与美赛ICM的题型高度相似&#xff0c;将本次竞赛当做美赛的练手赛&#xff0c;个人认为是非常合适的一种选择。同时28号就可以出成绩&#xff0c;也可以在美赛前实现查漏补缺&#xff0c;提前预祝大家比赛顺利&#xff0…

如何用WhatsApp做外贸?

WhatsApp 可帮助企业和客户快速建立个性化的联系&#xff0c;进行产品和服务类营销推广&#xff0c;并在购物过程中及时回应和解决客户的问题。 WhatsApp Business还可以帮助大中型企业提供客户服务支持&#xff0c;并向客户发出消息通知。 如果是中小企业&#xff0c;可以使用…

Centos 8 安装 Elasticsearch

简介&#xff1a;CentOS 8是一个基于Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码构建的开源操作系统。它是一款稳定、可靠、安全的服务器操作系统&#xff0c;适合用于企业级应用和服务的部署。CentOS 8采用了最新的Linux内核和软件包管理系统&#xff0c;提供…

SAP 销售订单审批状态(查询/修改)

销售订单审批状态启用后&#xff0c;前端显示界面如下图 销售订单审批状态读取&#xff1a;STATUS_READ 销售订单审批状态修改&#xff1a;I_CHANGE_STATUS 销售订单审批状态读取 代码样例如下&#xff1a; DATA: lv_objnr TYPE vbak-objnr,lv_objnr_t TYPE jsto-objnr,l…

深度学习记录--正则化(regularization)

什么是正则化&#xff1f; 正则化(regularization)是一种实用的减少方差(variance)的方法&#xff0c;也即避免过度拟合 几种正则化的方法 L2正则化 又被称为权重衰减(weight dacay) 在成本函数中加上正则项&#xff1a; 其中 由于在w的更新过程中会递减&#xff0c;即权…

【备战蓝桥杯】吃奶酪问题 / 超硬核,文附template拓展知识!

蓝桥杯备赛 | 洛谷做题打卡day9 文章目录 蓝桥杯备赛 | 洛谷做题打卡day9再来了解一下状压dp**简介(Introduction)****描述(Description)** - 吃奶酪题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示数据规模与约定提示 * template拓展知识我的一些话 【引入】今天…

广州市工信局、天河区商务金融局及广州专精特新促进会走访思迈特

2024年1月11日下午&#xff0c;广州市工信局、天河区商务金融局及广州专精特新促进会相关负责人莅临广州思迈特软件总部调研指导&#xff0c;思迈特软件总裁兼COO姚诗成代表公司热情接待&#xff0c;并陪同调研。 调研组实地参观了思迈特软件&#xff0c;深入了解了思迈特发展历…

通过OpenIddict设计一个授权服务器03-客户凭证流程

在本部分中&#xff0c;我们将把 OpenIddict 添加到项目中&#xff0c;并实施第一个授权流程&#xff1a;客户端凭证流。 添加 OpenIddict 软件包 首先&#xff0c;我们需要安装 OpenIddict NuGet 软件包 dotnet add package OpenIddict dotnet add package OpenIddict.AspN…

springboot mybatis-plus swing实现报警监听

通过声音控制报警器&#xff0c;实现声光报警&#xff0c;使用beautyeye_lnf.jar美化界面如下 EnableTransactionManagement(proxyTargetClass true) SpringBootApplication EnableScheduling public class AlarmWarnApplication {public static void main(String[] args) …

大数据毕业设计:基于python美食推荐系统+爬虫+Echarts可视化+协同过滤推荐算法+Django框架(源码)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

Net Core Ocelot+Consul实现网关、服务注册、服务发现

什么是Ocelot? Ocelot是一个开源的ASP.NET Core微服务网关&#xff0c;它提供了API网关所需的所有功能&#xff0c;如路由、认证、限流、监控等。 Ocelot是一个简单、灵活且功能强大的API网关&#xff0c;它可以与现有的服务集成&#xff0c;并帮助您保护、监控和扩展您的微…