在使用狀態機器表述的系統中中存在一個屬性,這個屬性取值都是是互斥的,比如電源有開啟和關閉,燈有亮和滅,但是不一定只有兩個取值。我們的理想狀態機器不可能只有一個單一的屬性。
下面我們舉一個例子,在一個狀態機器中存在下面這幾種屬性,每個屬性都有幾個取值。
所以就會有四種狀態,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();}