Qt状态机框架介绍(二)
前言
上一篇博客中已經介紹了Qt狀態機的基礎概念和用法,文章在這里,接下來繼續介紹Qt狀態機的使用。
歷史狀態的保存和恢復
前一個示例中,我們通過一個按鈕中斷狀態機,在此基礎上,如果我們中斷狀態機過后想再次回到之前停下來的地方,這時候就需要使用到歷史狀態。
歷史狀態是一個假想的狀態,它表示了父狀態上次退出時的子狀態。
歷史狀態通常創建為想要保存的那個狀態的子狀態。這樣在程序運行時,當狀態機檢測到這種狀態的存在就會在父狀態退出時自動記錄當前的子狀態。連接到歷史狀態的過渡實際上就是連接到狀態機上次保存的子狀態,狀態機會自動的將過渡前移到正在的子狀態。下面是執行流程:
先看一下效果圖:
代碼如下:
#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; }使用并行狀態來避免過多的狀態組合
當需要同步執行多個狀態時,可以將狀態機設置成并行狀態組,進入到并行狀態后,所有子狀態都會同時開始運行,每個子狀態的過渡都會正常執行。但是,每一個子狀態都有可能退出父狀態,如果這樣,父狀態和它所有的子狀態都會結束。
在Qt狀態機框架的并行機制里有一個交錯語義。所有的并行操作都是在一個事件處理中獨立的、原子的被執行,所以沒有事件能打斷并行操作。但是,事件仍然是被順序的處理的,因為狀態機本身是單線程的。舉個栗子,如果有兩個過渡退出同一個并行狀態組,并且它們的觸發條件同時被滿足。在這種情況下,第二個被處理的退出事件將沒有任何實際的反應,因為第一個事件已經導致了狀態機從并行狀態中結束。
并行狀態示例:
QState *s1 = new QState(QState::ParallelStates);// s11 and s12 will be entered in parallelQState *s11 = new QState(s1);QState *s12 = new QState(s1);檢測組合狀態的結束
子狀態可以是一個final狀態;當進入一個final子狀態時,父狀態會發出finished() 信號。下圖顯示了一個組合狀態s1在做了一系列的處理后進入了一個final狀態:
當s1進入一個final子狀態時,s1會自動發出finished() 信號。我們使用一個 信號過渡 來觸發一個狀態轉換:
s1->addTransition(s1, SIGNAL(finished()), s2);在組合狀態中使用final狀態對應想隱藏組合狀態的內部細節來說是非常有用的。也就是說,對應外部世界來說,只需要進入這個狀態,然后等待這個狀態的完成信號即可。這對于構建復雜的狀態機來說是一種強有力的的封裝和抽象機制。但是,對應并行狀態組來說,finishe()信號只有在所以的子狀態都進入final狀態時才會發出。
無目標狀態的過渡
一個Transition并不是一定要有一個目標狀態,并且沒有目標狀態的過渡也可以像其他過渡一樣被觸發。區別是當一個沒有目標狀態的過渡被觸發時,不會導致任何狀態的改變。這運行你在狀態機進入某個狀態時響應一個信號或事件而不必離開那個狀態。
示例:
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();當每次點擊按鈕時,都會彈出消息框,但是狀態會一直停留在m_pState1,如果顯示的把狀態機的狀態設置為s1,s1狀態會結束,然后重新進入該狀態。
總結
以上是生活随笔為你收集整理的Qt状态机框架介绍(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt状态机框架介绍(一)
- 下一篇: Qt IFW框架简介