Qt状态机框架介绍(二)
前言
上一篇博客中已經(jīng)介紹了Qt狀態(tài)機(jī)的基礎(chǔ)概念和用法,文章在這里,接下來(lái)繼續(xù)介紹Qt狀態(tài)機(jī)的使用。
歷史狀態(tài)的保存和恢復(fù)
前一個(gè)示例中,我們通過(guò)一個(gè)按鈕中斷狀態(tài)機(jī),在此基礎(chǔ)上,如果我們中斷狀態(tài)機(jī)過(guò)后想再次回到之前停下來(lái)的地方,這時(shí)候就需要使用到歷史狀態(tài)。
歷史狀態(tài)是一個(gè)假想的狀態(tài),它表示了父狀態(tài)上次退出時(shí)的子狀態(tài)。
歷史狀態(tài)通常創(chuàng)建為想要保存的那個(gè)狀態(tài)的子狀態(tài)。這樣在程序運(yùn)行時(shí),當(dāng)狀態(tài)機(jī)檢測(cè)到這種狀態(tài)的存在就會(huì)在父狀態(tài)退出時(shí)自動(dòng)記錄當(dāng)前的子狀態(tài)。連接到歷史狀態(tài)的過(guò)渡實(shí)際上就是連接到狀態(tài)機(jī)上次保存的子狀態(tài),狀態(tài)機(jī)會(huì)自動(dòng)的將過(guò)渡前移到正在的子狀態(tài)。下面是執(zhí)行流程:
先看一下效果圖:
代碼如下:
#include <QWidget> #include <QState> #include <QStateMachine> #include <QFinalState> #include <QHistoryState>namespace Ui { class Widget; }class Widget : public QWidget {Q_OBJECTpublic:explicit Widget(QWidget *parent = nullptr);~Widget();private slots:void onOutputMessage();private:Ui::Widget *ui;QStateMachine * m_pStateMachine = nullptr;QState * m_pState1 = nullptr;QState * m_pState2 = nullptr;QState * m_pState3 = nullptr;QState * m_pStateParent = nullptr;QFinalState * m_pFinalState = nullptr;QHistoryState * m_pHistoryState = nullptr; }; Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);m_pStateMachine = new QStateMachine(this);m_pStateParent = new QState();m_pState1 = new QState(m_pStateParent);m_pState2 = new QState(m_pStateParent);m_pState3 = new QState(m_pStateParent);m_pStateParent->assignProperty(ui->label, "text", "In s1");m_pState1->assignProperty(ui->btn2,"pos",QPoint(20,40));m_pState2->assignProperty(ui->btn2,"pos",QPoint(80,40));m_pState3->assignProperty(ui->btn2,"pos",QPoint(120,40));m_pState1->addTransition(ui->btn1,SIGNAL(clicked()),m_pState2);m_pState2->addTransition(ui->btn1,SIGNAL(clicked()),m_pState3);m_pState3->addTransition(ui->btn1,SIGNAL(clicked()),m_pState1);m_pStateParent->setInitialState(m_pState1);m_pFinalState = new QFinalState();m_pStateParent->addTransition(ui->btn3,SIGNAL(clicked()),m_pFinalState);m_pStateMachine->addState(m_pStateParent);m_pStateMachine->addState(m_pFinalState);m_pStateMachine->setInitialState(m_pStateParent);m_pHistoryState = new QHistoryState(m_pStateParent);QState *s3 = new QState();s3->assignProperty(ui->label, "text", "In s3");QMessageBox *mbox = new QMessageBox(this);mbox->addButton(QMessageBox::Ok);mbox->setText("Interrupted!");mbox->setIcon(QMessageBox::Information);QObject::connect(s3, SIGNAL(entered()), mbox, SLOT(exec()));s3->addTransition(m_pHistoryState);m_pStateMachine->addState(s3);m_pStateParent->addTransition(ui->btn2, SIGNAL(clicked()), s3);m_pStateMachine->start();}Widget::~Widget() {delete ui; }使用并行狀態(tài)來(lái)避免過(guò)多的狀態(tài)組合
當(dāng)需要同步執(zhí)行多個(gè)狀態(tài)時(shí),可以將狀態(tài)機(jī)設(shè)置成并行狀態(tài)組,進(jìn)入到并行狀態(tài)后,所有子狀態(tài)都會(huì)同時(shí)開(kāi)始運(yùn)行,每個(gè)子狀態(tài)的過(guò)渡都會(huì)正常執(zhí)行。但是,每一個(gè)子狀態(tài)都有可能退出父狀態(tài),如果這樣,父狀態(tài)和它所有的子狀態(tài)都會(huì)結(jié)束。
在Qt狀態(tài)機(jī)框架的并行機(jī)制里有一個(gè)交錯(cuò)語(yǔ)義。所有的并行操作都是在一個(gè)事件處理中獨(dú)立的、原子的被執(zhí)行,所以沒(méi)有事件能打斷并行操作。但是,事件仍然是被順序的處理的,因?yàn)闋顟B(tài)機(jī)本身是單線程的。舉個(gè)栗子,如果有兩個(gè)過(guò)渡退出同一個(gè)并行狀態(tài)組,并且它們的觸發(fā)條件同時(shí)被滿足。在這種情況下,第二個(gè)被處理的退出事件將沒(méi)有任何實(shí)際的反應(yīng),因?yàn)榈谝粋€(gè)事件已經(jīng)導(dǎo)致了狀態(tài)機(jī)從并行狀態(tài)中結(jié)束。
并行狀態(tài)示例:
QState *s1 = new QState(QState::ParallelStates);// s11 and s12 will be entered in parallelQState *s11 = new QState(s1);QState *s12 = new QState(s1);檢測(cè)組合狀態(tài)的結(jié)束
子狀態(tài)可以是一個(gè)final狀態(tài);當(dāng)進(jìn)入一個(gè)final子狀態(tài)時(shí),父狀態(tài)會(huì)發(fā)出finished() 信號(hào)。下圖顯示了一個(gè)組合狀態(tài)s1在做了一系列的處理后進(jìn)入了一個(gè)final狀態(tài):
當(dāng)s1進(jìn)入一個(gè)final子狀態(tài)時(shí),s1會(huì)自動(dòng)發(fā)出finished() 信號(hào)。我們使用一個(gè) 信號(hào)過(guò)渡 來(lái)觸發(fā)一個(gè)狀態(tài)轉(zhuǎn)換:
s1->addTransition(s1, SIGNAL(finished()), s2);在組合狀態(tài)中使用final狀態(tài)對(duì)應(yīng)想隱藏組合狀態(tài)的內(nèi)部細(xì)節(jié)來(lái)說(shuō)是非常有用的。也就是說(shuō),對(duì)應(yīng)外部世界來(lái)說(shuō),只需要進(jìn)入這個(gè)狀態(tài),然后等待這個(gè)狀態(tài)的完成信號(hào)即可。這對(duì)于構(gòu)建復(fù)雜的狀態(tài)機(jī)來(lái)說(shuō)是一種強(qiáng)有力的的封裝和抽象機(jī)制。但是,對(duì)應(yīng)并行狀態(tài)組來(lái)說(shuō),finishe()信號(hào)只有在所以的子狀態(tài)都進(jìn)入final狀態(tài)時(shí)才會(huì)發(fā)出。
無(wú)目標(biāo)狀態(tài)的過(guò)渡
一個(gè)Transition并不是一定要有一個(gè)目標(biāo)狀態(tài),并且沒(méi)有目標(biāo)狀態(tài)的過(guò)渡也可以像其他過(guò)渡一樣被觸發(fā)。區(qū)別是當(dāng)一個(gè)沒(méi)有目標(biāo)狀態(tài)的過(guò)渡被觸發(fā)時(shí),不會(huì)導(dǎo)致任何狀態(tài)的改變。這運(yùn)行你在狀態(tài)機(jī)進(jìn)入某個(gè)狀態(tài)時(shí)響應(yīng)一個(gè)信號(hào)或事件而不必離開(kāi)那個(gè)狀態(tài)。
示例:
m_pStateMachine = new QStateMachine(this);m_pState1 = new QState();QSignalTransition *trans = new QSignalTransition(ui->btn1, SIGNAL(clicked()));m_pState1->addTransition(trans);m_pStateMachine->addState(m_pState1);m_pStateMachine->setInitialState(m_pState1);QMessageBox *mbox = new QMessageBox(this);mbox->addButton(QMessageBox::Ok);mbox->setText("Interrupted!");mbox->setIcon(QMessageBox::Information);QObject::connect(trans, SIGNAL(triggered()), mbox, SLOT(exec()));m_pStateMachine->start();當(dāng)每次點(diǎn)擊按鈕時(shí),都會(huì)彈出消息框,但是狀態(tài)會(huì)一直停留在m_pState1,如果顯示的把狀態(tài)機(jī)的狀態(tài)設(shè)置為s1,s1狀態(tài)會(huì)結(jié)束,然后重新進(jìn)入該狀態(tài)。
總結(jié)
以上是生活随笔為你收集整理的Qt状态机框架介绍(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Qt状态机框架介绍(一)
- 下一篇: Python 为啥不建议使用 threa