Qt中狀態(tài)機框架QState的實現(xiàn)
QState是Qt狀態(tài)機框架(Qt State Machine Framework)的核心類,用于建模離散狀態(tài)以及狀態(tài)間的轉(zhuǎn)換邏輯,廣泛應用于UI交互流程、設(shè)備狀態(tài)管理、工作流控制等場景。它基于UML狀態(tài)圖規(guī)范設(shè)計,支持層次化狀態(tài)、并行狀態(tài)、歷史狀態(tài)等高級特性,能夠簡化復雜狀態(tài)邏輯的實現(xiàn)。
一、核心定位與繼承關(guān)系
QState繼承自QAbstractState(抽象狀態(tài)基類),而QAbstractState又繼承自QObject,因此QState具備Qt對象模型的所有特性(如信號槽、元對象系統(tǒng)、父子關(guān)系管理)。其核心作用是:
- 封裝一個離散狀態(tài)的l行為(進入/退出時的動作);
- 管理狀態(tài)間的轉(zhuǎn)換規(guī)則(
QAbstractTransition); - 支持子狀態(tài)嵌套,構(gòu)建層次化狀態(tài)機;
- 與
QStateMachine配合,實現(xiàn)狀態(tài)的自動切換。
狀態(tài)機的基本構(gòu)成包括:
- 狀態(tài)(State):如
QState、QParallelState(并行狀態(tài))、QHistoryState(歷史狀態(tài)); - 轉(zhuǎn)換(Transition):如
QSignalTransition(信號觸發(fā))、QEventTransition(事件觸發(fā)); - 事件(Event):觸發(fā)狀態(tài)轉(zhuǎn)換的信號、Qt事件或自定義事件;
- 狀態(tài)機(State Machine):
QStateMachine,負責調(diào)度狀態(tài)切換與事件處理。
二、基礎(chǔ)用法:狀態(tài)創(chuàng)建與轉(zhuǎn)換
1. 狀態(tài)的創(chuàng)建與添加
通過QStateMachine管理狀態(tài),需先創(chuàng)建狀態(tài)實例并添加到狀態(tài)機中:
#include <QStateMachine> #include <QState> #include <QPushButton> QStateMachine *machine = new QStateMachine; // 創(chuàng)建狀態(tài) QState *s1 = new QState(machine); // 直接指定父對象為狀態(tài)機 QState *s2 = new QState; machine->addState(s2); // 或通過addState()添加 // 設(shè)置初始狀態(tài)(狀態(tài)機啟動時進入的第一個狀態(tài)) machine->setInitialState(s1);
2. 狀態(tài)轉(zhuǎn)換的定義
狀態(tài)轉(zhuǎn)換(QAbstractTransition)是狀態(tài)切換的規(guī)則,需指定“觸發(fā)條件”和“目標狀態(tài)”。QState通過addTransition()方法添加轉(zhuǎn)換,常用轉(zhuǎn)換類型包括:
(1)信號觸發(fā)轉(zhuǎn)換(QSignalTransition)
最常用的轉(zhuǎn)換類型,當特定信號發(fā)射時觸發(fā)狀態(tài)切換:
QPushButton *btn = new QPushButton("Next");
// 當btn發(fā)射clicked()信號時,從s1轉(zhuǎn)換到s2
s1->addTransition(btn, &QPushButton::clicked, s2);
// 進階:轉(zhuǎn)換可攜帶動作(通過onTransition()信號)
QSignalTransition *trans = s1->addTransition(btn, &QPushButton::clicked, s2);
connect(trans, &QSignalTransition::onTransition, [](){
qDebug() << "從s1切換到s2"; // 轉(zhuǎn)換過程中執(zhí)行的動作
});
(2)事件觸發(fā)轉(zhuǎn)換(QEventTransition)
基于Qt事件(如QKeyEvent、QMouseEvent)觸發(fā)轉(zhuǎn)換:
#include <QEventTransition> // 當s1收到QEvent::KeyPress事件時,轉(zhuǎn)換到s2 QEventTransition *keyTrans = new QEventTransition(btn, QEvent::KeyPress); keyTrans->setTargetState(s2); s1->addTransition(keyTrans);
(3)守衛(wèi)條件(Guard)
轉(zhuǎn)換可設(shè)置守衛(wèi)條件(布爾函數(shù)),僅當條件為true時才執(zhí)行轉(zhuǎn)換:
// 定義守衛(wèi)函數(shù)(返回bool)
bool canTransition() {
return someCondition; // 例如:檢查輸入是否合法
}
// 為轉(zhuǎn)換設(shè)置守衛(wèi)
trans->setGuard(canTransition); // 僅當canTransition()為true時,轉(zhuǎn)換才生效
三、狀態(tài)行為:進入與退出動作
QState在進入(entered)和退出(exited)時會發(fā)射對應信號,可通過信號槽機制綁定狀態(tài)切換時的動作。此外,還可通過onEntry()和onExit()方法直接設(shè)置動作函數(shù)。
1. 信號綁定方式
// 進入s1時執(zhí)行動作
connect(s1, &QState::entered, [](){
qDebug() << "進入狀態(tài)s1";
// 例如:更新UI顯示、啟動定時器
});
// 退出s1時執(zhí)行動作
connect(s1, &QState::exited, [](){
qDebug() << "退出狀態(tài)s1";
// 例如:停止定時器、保存臨時數(shù)據(jù)
});
2. 動作函數(shù)方式
通過assignProperty()可在進入狀態(tài)時自動為對象設(shè)置屬性,簡化UI狀態(tài)管理:
QPushButton *btn = new QPushButton("Click me");
// 進入s1時,將btn的text屬性設(shè)為"狀態(tài)1",enabled設(shè)為true
s1->assignProperty(btn, "text", "狀態(tài)1");
s1->assignProperty(btn, "enabled", true);
// 進入s2時,更新btn屬性
s2->assignProperty(btn, "text", "狀態(tài)2");
s2->assignProperty(btn, "enabled", false);
當狀態(tài)激活時,assignProperty()設(shè)置的屬性會自動應用到目標對象,退出狀態(tài)時不會自動恢復(需手動在exited信號中處理)。
四、層次化狀態(tài):父狀態(tài)與子狀態(tài)
QState支持嵌套子狀態(tài),形成層次化結(jié)構(gòu)(父狀態(tài)包含子狀態(tài)),這是實現(xiàn)復雜狀態(tài)邏輯的核心特性。
1. 子狀態(tài)的添加與初始子狀態(tài)
// 創(chuàng)建父狀態(tài) QState *parentState = new QState; // 創(chuàng)建子狀態(tài)(指定父狀態(tài)) QState *child1 = new QState(parentState); QState *child2 = new QState(parentState); // 設(shè)置父狀態(tài)的初始子狀態(tài)(進入父狀態(tài)時自動進入該子狀態(tài)) parentState->setInitialState(child1);
2. 層次化狀態(tài)的行為規(guī)則
- 進入父狀態(tài):先執(zhí)行父狀態(tài)的
entered動作,再進入其初始子狀態(tài)(執(zhí)行子狀態(tài)的entered動作); - 退出父狀態(tài):先退出當前活躍的子狀態(tài)(執(zhí)行子狀態(tài)的
exited動作),再執(zhí)行父狀態(tài)的exited動作; - 子狀態(tài)轉(zhuǎn)換限制:子狀態(tài)的轉(zhuǎn)換默認只能在同一父狀態(tài)的子狀態(tài)間進行,若需轉(zhuǎn)換到外部狀態(tài),需顯式指定目標。
示例:播放器的“播放中”狀態(tài)(父狀態(tài))包含“正常播放”和“快進”子狀態(tài):
QState *playing = new QState; // 父狀態(tài):播放中 QState *normalPlay = new QState(playing); // 子狀態(tài):正常播放 QState *fastForward = new QState(playing); // 子狀態(tài):快進 playing->setInitialState(normalPlay); // 子狀態(tài)間轉(zhuǎn)換:正常播放 → 快進(按快進鍵) normalPlay->addTransition(fastForwardBtn, &QPushButton::clicked, fastForward); // 子狀態(tài)轉(zhuǎn)換到外部狀態(tài):任何子狀態(tài)下按停止鍵 → 停止狀態(tài) playing->addTransition(stopBtn, &QPushButton::clicked, stopped);
五、特殊狀態(tài)類型
1. 并行狀態(tài)(QParallelState)
用于建模同時活躍的多個狀態(tài)(如設(shè)備同時處于“聯(lián)網(wǎng)”和“充電”狀態(tài))。QParallelState是QState的子類,其所有子狀態(tài)會同時進入和退出。
#include <QParallelState> QParallelState *parallel = new QParallelState; // 兩個并行子狀態(tài) QState *networkState = new QState(parallel); // 網(wǎng)絡狀態(tài) QState *powerState = new QState(parallel); // 電源狀態(tài) // 進入parallel時,networkState和powerState同時激活 machine->setInitialState(parallel);
并行狀態(tài)的退出規(guī)則:所有子狀態(tài)退出后,并行狀態(tài)才會退出。
2. 歷史狀態(tài)(QHistoryState)
用于保存父狀態(tài)中最后活躍的子狀態(tài),當父狀態(tài)再次進入時,自動恢復到該子狀態(tài)(避免重復初始化)。分為兩種類型:
- 淺歷史(默認):僅恢復直接子狀態(tài)的歷史;
- 深歷史:遞歸恢復所有嵌套子狀態(tài)的歷史(通過
setDeepHistory(true)啟用)。
#include <QHistoryState> QState *parent = new QState; QState *child1 = new QState(parent); QState *child2 = new QState(parent); parent->setInitialState(child1); // 創(chuàng)建歷史狀態(tài)(作為parent的子狀態(tài)) QHistoryState *history = new QHistoryState(parent); // 啟用深歷史(可選) history->setDeepHistory(true); // 從外部狀態(tài)轉(zhuǎn)換到history時,恢復parent的最后活躍子狀態(tài) externalState->addTransition(backBtn, &QPushButton::clicked, history);
六、狀態(tài)機的運行與生命周期
1. 狀態(tài)機的啟動與停止
// 啟動狀態(tài)機(開始處理事件并進入初始狀態(tài)) machine->start(); // 停止狀態(tài)機(退出當前狀態(tài),暫停事件處理) machine->stop();
狀態(tài)機啟動后,會觸發(fā)初始狀態(tài)的entered信號,并開始監(jiān)聽事件以驅(qū)動轉(zhuǎn)換。
2. 狀態(tài)機的完成與終止
當狀態(tài)機進入“終止狀態(tài)”(QFinalState)時,會發(fā)射finished()信號并停止運行:
#include <QFinalState> QFinalState *final = new QFinalState(machine); // 從s2轉(zhuǎn)換到終止狀態(tài) s2->addTransition(quitBtn, &QPushButton::clicked, final); // 狀態(tài)機完成時退出程序 connect(machine, &QStateMachine::finished, qApp, &QApplication::quit);
七、高級特性與底層機制
1. 事件優(yōu)先級與處理順序
狀態(tài)機的事件處理遵循以下規(guī)則:
- 子狀態(tài)的事件處理器優(yōu)先于父狀態(tài);
- 同一狀態(tài)的多個轉(zhuǎn)換按添加順序檢查(守衛(wèi)條件先滿足者觸發(fā));
- 并行狀態(tài)的子狀態(tài)獨立處理事件,互不干擾。
2. 自定義轉(zhuǎn)換與事件
通過繼承QAbstractTransition可實現(xiàn)自定義轉(zhuǎn)換邏輯,通過QEvent子類可定義自定義事件:
// 自定義事件
class MyEvent : public QEvent {
public:
static const QEvent::Type Type = static_cast<QEvent::Type>(QEvent::User + 1);
MyEvent() : QEvent(Type) {}
};
// 自定義轉(zhuǎn)換(監(jiān)聽MyEvent)
class MyTransition : public QAbstractTransition {
protected:
bool eventTest(QEvent *e) override {
return e->type() == MyEvent::Type; // 僅響應MyEvent
}
void onTransition(QEvent *) override {
// 轉(zhuǎn)換動作
}
};
// 使用自定義轉(zhuǎn)換
MyTransition *trans = new MyTransition;
trans->setTargetState(s2);
s1->addTransition(trans);
3. 調(diào)試與狀態(tài)監(jiān)控
Qt提供QStateMachine::setDebuggingEnabled(true)開啟調(diào)試日志,輸出狀態(tài)轉(zhuǎn)換過程:
machine->setDebuggingEnabled(true); // 控制臺會打印狀態(tài)切換日志
也可通過QState::active()方法實時檢查狀態(tài)是否活躍:
if (s1->active()) {
qDebug() << "當前處于s1狀態(tài)";
}
八、常見問題
- 狀態(tài)轉(zhuǎn)換循環(huán):避免無守衛(wèi)條件的循環(huán)轉(zhuǎn)換(如
s1→s2→s1),可能導致狀態(tài)機無限切換; - 子狀態(tài)與父狀態(tài)的信號沖突:子狀態(tài)的
entered信號會在父狀態(tài)之后觸發(fā),需注意動作執(zhí)行順序; - 并行狀態(tài)的同步:并行子狀態(tài)的轉(zhuǎn)換獨立,若需同步退出,可統(tǒng)一轉(zhuǎn)換到同一個終止狀態(tài);
- 歷史狀態(tài)的濫用:僅在需要恢復狀態(tài)時使用,過度使用會增加狀態(tài)機復雜度;
- 性能考量:復雜狀態(tài)機(>100個狀態(tài))需避免頻繁轉(zhuǎn)換,可通過合并狀態(tài)減少開銷。
QState作為Qt狀態(tài)機框架的核心,通過封裝狀態(tài)行為、轉(zhuǎn)換規(guī)則和層次化結(jié)構(gòu),大幅簡化了復雜狀態(tài)邏輯的實現(xiàn)。其特性包括:支持信號/事件觸發(fā)的轉(zhuǎn)換、狀態(tài)進入/退出動作、屬性自動賦值、層次化與并行狀態(tài)、歷史狀態(tài)恢復等。
到此這篇關(guān)于Qt中狀態(tài)機框架QState的實現(xiàn)的文章就介紹到這了,更多相關(guān)Qt 狀態(tài)機框架QState內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用VScode搭建ROS開發(fā)環(huán)境的教程詳解
這篇文章主要介紹了使用VScode搭建ROS開發(fā)環(huán)境,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
matlab遺傳算法求解車間調(diào)度問題分析及實現(xiàn)源碼
這篇文章主要為大家介紹了matlab遺傳算法求解車間調(diào)度問題解析,文中附含詳細實現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-02-02
C語言中g(shù)etchar()與putchar()函數(shù)詳解
本文主要介紹了C語言中g(shù)etchar()與putchar()函數(shù)詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01
C++實現(xiàn)旋轉(zhuǎn)數(shù)組的二分查找
這篇文章主要介紹了C++實現(xiàn)旋轉(zhuǎn)數(shù)組的二分查找方法,涉及數(shù)組的操作,有值得借鑒的技巧,需要的朋友可以參考下2014-09-09

