SCXML和QScxml使用總結


    最近接觸了SCXML這個狀態描述文本,簡單來講就是描述了整個狀態的變遷過程的一種XML格式的表格。Qt labs中有一個項目就是QScxml,它基於QStateMachine上層制作,可以直接讀取SCXML格式的文件生成內部狀態對象和成員,可以直接在Qt中進行狀態變遷,十分方便。

    先來簡單介紹一下SCXML的格式,以

<scxml initial="FirstState" version="0.9" xmlns="http://www.w3.org/2005/07/scxml">

作為整個SCXML的開頭,scxml標簽旁的initial表示狀態機啟動之后進入的第一個初始化狀態,在這里我寫了FirstState,表示狀態幾一啟動,就進入了FirstState.

<state id="FirstState" initial="FirstChildNode">

以state標簽開頭表示了狀態的基本概念,其中的id是作為該狀態的索引號給你之后寫target進行索引,這個時候同學會看到又出現了一個initial,這時的FirstChildNode表示此時的FirstState並不是一個原子狀態,而是一個組合狀態的父狀態。而FirstChildNode恰恰就是它的子狀態。也就是說進入了FirstState之后,就會立即進入FirstChildNode,期間如果你調用了<onentry>和<onexit>標簽,你會發現調用了多次,不必奇怪,其實你的狀態是進入了一層一層中的最里層,每進一層就會調用<onentry>和<onexit>。

<transition event="Key.A" target="SecondState"></transition>
<transition event="Key.B" target="ThirdState"></transition>

又來新東西了,這個<transition>標簽表示真正的事務處理過程,之后的event屬性表示你傳遞給QScxml中的postNamedEvent(const QString)函數中的QString,所以我之前提過那個id的作用,就是全局的索引號,同時請注意:SCXML中默認的event是前綴查找,也就是說對於event="GameTest"來說,你輸入"GameTest","Game","Game.","Game.*"效果是完全一樣的,不過我試了下在QScxml中只有第一種和第三種有效(官方說明)。之后的<target>自然很好理解,就是你在這個狀態下,經過了event事件,達到了target狀態(target不能接收函數,必須是字符串狀態變量而cond可以接收函數或者字符串)。例子中就表示無論你在FirstState中的哪個孩子中,只要你收到了Key.A事件,你都會跳出子狀態乃至父狀態,直接跳到對應的SecondState中去。注意:寫在父狀態中的translation是給它以及它的孩子全局共享的,如果你覺得你可能在孩子節點中對於某一個事件你不滿意,你想要重寫,那你完全可以在FirstChildNode中寫下

<transition event="Key.A" target="FourhState"></transition>
<transition event="Key.B" target="FifthState"></transition>

這個時候狀態機會優先處理最子層的事務處理,如果狀態機發現在最子層並沒有完成該事件(包括沒有找到該translation和找到translation可是cond為false)都會將事件向上傳遞給父狀態進行處理。

    在來說說比較有用的標簽<cond>,這個標簽可以放在<translation>中也可以放在<if>中,當放在<translation中時>

<transition event="Key.A" target="FourhState" cond="isTrue"></transition>

表明當isTrue為true的時候,target才真正進行轉移(在這里isTrue即可以是簡單變量也可以是script函數來返回bool值),比較常用的用法有

<translation event="Key.A"> 
<if cond="isTrue()">
  <script>FuncA()</script>
<elseif cond="isFalse()"/>
  <script>FuncB()</script>
<else/>
  <script>FuncC()</script>
</if>
</translation>

 

表明事件Key.A來的時候進行cond判斷來調用相應的script。

另外我們也可以用到狀態機在上而下處理事件的機制,來進行靈活的target動態轉換工作.

<transition event="Key.A" target="A" cond="isTrue()" />
<transition event="Key.A" target="B" >

細心的你一定會發現,怎么兩個translation的event一樣。其實這種用法在W3C的examples中也提到過,因為狀態機在上而下的處理機制,你可以在斷言為false的時候有一個默認的target,而在true的時候進入你事先設定的target,可以非常靈活的使用這種機制進行判斷.

    另外介紹一下兩個也比較重要的標簽,在上文也提到過<onentry>和<onexit>,

<onentry>
  <script>
    enterState("A");
  </script>
</onentry>
<onexit>
  <script>
    exitState("B");
  </script>

 

 

表明在進入和退出該狀態的時候自動觸發的事件,這里默認調用的script,你可以很靈活的控制狀態切換時應該需要的工作.

    QScxml中有一個功能非常強大的函數

void QScxml::registerObject (QObject* o, const QString & name, bool recursive)

用它進行注冊之后,你可以在SCXML的文件中寫各種script function,比如你注冊的時候m_scxml->registerObject(this, "Widget", true),這個時候你就可以在SCXML文件中寫下

<script>
  function show()
    {
Widget.show();
 }
</script>

表明無論你在translation還是onentry還是onexit中的<script>標簽寫show()這個函數,你最終都會通過QScxml這個強大的類讓你可以和它進行交互。如果你嫌寫script function麻煩,

你也可以直接在<script>標簽中寫上

Widget.show();

一樣可以直接運行。script function的強大不僅僅在於可以通過QScxml讓你和你的對象進行交互,同時它也可以用來做斷言cond判斷,比如

function isTrue()
{
    return Widget.isGood();   
}

你可以非常靈活的實現你自己類的斷言函數,配合之前的translation中的cond做到動態切換target,非常方便。

    今天就簡單介紹到這里,在開始接觸SCXML的時候發現國內的資料很少,寫這篇博文也當貢獻自己的一份力了,更多的內容需要你自己去挖掘,希望你會喜歡這篇文章,留下你的腳印,給我支持,謝謝:)

 

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM