在使用状态机表述的系统中中存在一个属性,这个属性取值都是是互相排斥的,比如电源有打开和关闭,灯有亮和灭,但是不一定只有两个取值。我们的理想状态机不可能只有一个单一的属性。
下面我们举一个例子,在一个状态机中存在下面这几种属性,每个属性都有几个取值。
所以就会有四种状态,2*2 = 4;如果每个状态都是可以互相转换的饿,那么就是4*2 = 8中过渡。
如果又添加了一种属性
那么就是2*2*3=12中状态,12*2 = 24中过渡,是指数级的增长,而且在添加和移除属性是,会影响状态。
在此Qt的QState类中有一个枚举类型
第二个平行的状态集就是为了解决这个问题。
#include <QApplication>
#include <QState>
#include <QStateMachine>
#include <QPushButton>
#include <QVariant>
#include <QFinalState>
#include <QLayout>
#include <QHistoryState>
#include <QLabel>
#include <QMessageBox>
#include <QtDebug>
#include <QAbstractTransition>
//Qt的状态机是层次的,是事件驱动的,使用到了事件循环,那么就是异步的
int main(int argc, char* argv[])
{
QApplication app(argc,argv);
QStateMachine sMachine;//一个状态机对象
QState s;//和fState在同一个层次
QState s0;//开始状态,空白状态
QState s1;//3个状态对象
s1.setChildMode(QState::ParallelStates);
s1.setParent(&s);
QState s2;
s2.setParent(&s);
QState s3;
s3.setParent(&s);
s.setInitialState(&s1);//一组状态中要指定一组状态中的初始状态
QState s11;
s11.setChildMode(QState::ExclusiveStates);
s11.setParent(&s1);
QState s111;//a1
s111.setParent(&s11);
QState s112;//a2
s112.setParent(&s11);
s11.setInitialState(&s111);
QState s12;
s12.setChildMode(QState::ExclusiveStates);
s12.setParent(&s1);
QState s121;//b1
s121.setParent(&s12);
QState s122;//b2
s122.setParent(&s12);
s12.setInitialState(&s121);
QHistoryState sh;//记录s组状态被打断的状态
sh.setParent(&s);
QFinalState fState;
QState is;//中断状态
QWidget w;
QHBoxLayout layout;
QPushButton button(QObject::tr("状态改变"));
QPushButton qButton;
QPushButton startButton("start");
QPushButton stopButton("stop");
qButton.setText(QObject::tr("退出"));
QPushButton iButton;
iButton.setText(QObject::tr("打断"));
QPushButton button1("a1,b1");
QPushButton button2("a1,b2");
QPushButton button3("a2,b1");
QPushButton button4("a2,b2");
QLabel showLabel;
QLabel showLabel2;
QLabel showLabel3;
layout.addWidget(&button);
layout.addWidget(&qButton);
layout.addWidget(&iButton);
layout.addWidget(&showLabel);
layout.addWidget(&showLabel2);
layout.addWidget(&showLabel3);
layout.addWidget(&startButton);
layout.addWidget(&stopButton);
layout.addWidget(&button1);
layout.addWidget(&button2);
layout.addWidget(&button3);
layout.addWidget(&button4);
w.setLayout(&layout);
QMessageBox box(&w);
box.addButton(QMessageBox::Ok);
box.setText(QObject::tr("打断了,现在是is"));
box.setIcon(QMessageBox::Information);
s0.addTransition(&button,SIGNAL(clicked()),&s);
s1.addTransition(&button,SIGNAL(clicked()),&s2);//s1为这个过渡的始状态,s2为末状态
//八个过渡,可是应为选择的是2个属性,所以是2+2和2*2一样,但是这样是线性的
//比如按钮3被点击,s111和s122都是activity的同时响应,平行的
s111.addTransition(&button3,SIGNAL(clicked()),&s112);//a1->a2
s111.addTransition(&button4,SIGNAL(clicked()),&s112);
s112.addTransition(&button1,SIGNAL(clicked()),&s111);//a2->a1
s112.addTransition(&button2,SIGNAL(clicked()),&s111);
s121.addTransition(&button2,SIGNAL(clicked()),&s122);//b1->b2
s121.addTransition(&button4,SIGNAL(clicked()),&s122);
s122.addTransition(&button1,SIGNAL(clicked()),&s121);//b2->b1
s122.addTransition(&button3,SIGNAL(clicked()),&s121);
s2.addTransition(&button,SIGNAL(clicked()),&s3);
s3.addTransition(&button,SIGNAL(clicked()),&s1);
//每个状态进入时,设置指定对象指定项指定的值
s1.assignProperty(&showLabel,"text","当前:s1");
s2.assignProperty(&showLabel,"text","当前:s2");
s3.assignProperty(&showLabel,"text","当前:s3");
//同时进入,而且这个是原子操作,不会被事件打断,但是是队列的,因为状态机是单线程的
s11.assignProperty(&showLabel,"text","s11");
s12.assignProperty(&showLabel2,"text","s12");
s111.assignProperty(&showLabel,"text","a1");
s112.assignProperty(&showLabel,"text","a2");
s121.assignProperty(&showLabel2,"text","b1");
s122.assignProperty(&showLabel2,"text","b2");
//给每个状态添加过渡
s.addTransition(&qButton,SIGNAL(clicked()),&fState);//s -- > finalState,但是在这组内的状态对于这个过渡可以覆盖
//s2.addTransition(&qButton,SIGNAL(clicked()),&s3);//如果添加这一个句,那么在s2点击qButton按钮
//就不会退出,只是转向了s3
s.addTransition(&iButton,SIGNAL(clicked()),&is);
is.addTransition(&sh);
QObject::connect(&is,SIGNAL(entered()),&box,SLOT(exec()));
//也可能重写 QAbstractState::onEntry()和QAbstractState::onExit()函数
//在UML的状态图中,每个状态在进入状态和离开状态的时候都会进行相关的操作
//这个可以通过这两个信号来解决,也可通过继承来重写上述的两个函数
QObject::connect(&s3,SIGNAL(entered()),&w,SLOT(showMinimized()));
QObject::connect(&s3,SIGNAL(exited()),&w,SLOT(showMaximized()));
QObject::connect(&sMachine,SIGNAL(finished()),&app,SLOT(quit()));
QObject::connect(&startButton,SIGNAL(clicked()),&sMachine,SLOT(start()));
QObject::connect(&stopButton,SIGNAL(clicked()),&sMachine,SLOT(stop()));
sMachine.addState(&s0);
sMachine.addState(&s);//对于状态机只是添加顶层的状态
// sMachine.addState(&s1);
// sMachine.addState(&s2);
// sMachine.addState(&s3);
sMachine.addState(&fState);
sMachine.addState(&is);
//设置状态机的初始状态
//sMachine.setInitialState(&s1);
sMachine.setInitialState(&s0);//对于状态机的初始化,只是使用顶层的状态初始化,所以每个顶层如果是
//一组状态,那么就要指定这组状态的初始化状态
w.show();
//状态机开启
// sMachine.start();
//可以通过给状态分组来实现状态过渡的共享,比如我们希望在任何状态下我们都能够退出,
//那么这个退出状态就是比其他的状态具有高的状态层次,那么我们就要将其他的状态封装在
//合适的与退出状态同层次的一个高阶的状态层次中
return app.exec();
}