【问题标题】:How to use conditional transitions in Qt SCXML statecharts如何在 Qt SCXML 状态图中使用条件转换
【发布时间】:2021-03-02 19:34:37
【问题描述】:

我目前正在尝试了解 Qts scxml 状态图,以及如何将它们正确集成到我的应用程序中。我偶然发现的一个问题是条件转换。为了解释我在这里如何使用条件以及出了什么问题,我做了一个最小可行的例子:

初始状态s_initial 有两个转换到状态s_falses_true。两个转换都由同一事件t_button_clicked 触发。根据变量test_var,任何时候都只能进行一次转换。当另一个t_button_clicked事件发生时,状态机返回s_initial

为了测试状态机,我创建了一个简单的 Qt-Widgets 应用程序,通过一个按钮触发t_button_clicked,以及一个用于更改变量test_var 的复选框:

(mainwindow.cpp)

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , chart(this)
{
    ui->setupUi(this);
    connect(ui->checkBox, &QCheckBox::clicked, [this](bool checked){
        qDebug() << "> checkbox:" << checked;
        chart.dataModel()->setProperty("test_var", checked);
    });
    connect(ui->pushButton, &QPushButton::released, [this](){
        qDebug() << "> button";
        chart.submitEvent("t_button_clicked");
    });
    chart.start();
}

(testchart.scxml)

<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="TestChart" qt:editorversion="4.14.1" datamodel="ecmascript" initial="s_initial">
    <qt:editorinfo initialGeometry="213.11;86.67;-20;-20;40;40"/>
    <state id="s_initial">
        <qt:editorinfo scenegeometry="213.11;233.50;153.11;183.50;120;100" geometry="213.11;233.50;-60;-50;120;100"/>
        <transition type="external" event="t_button_clicked" target="s_false" cond="!test_var">
            <qt:editorinfo endTargetFactors="11.79;50.87"/>
        </transition>
        <transition type="external" event="t_button_clicked" target="s_true" cond="test_var">
            <qt:editorinfo movePoint="37.73;-3.06" endTargetFactors="19.26;54.39"/>
        </transition>
        <onentry>
            <log expr="&quot;s_initial&quot;"/>
        </onentry>
    </state>
    <state id="s_false">
        <qt:editorinfo scenegeometry="529.21;233.50;469.21;183.50;120;100" geometry="529.21;233.50;-60;-50;120;100"/>
        <onentry>
            <log expr="&quot;s_false&quot;"/>
        </onentry>
        <transition type="external" event="t_button_clicked" target="s_initial">
            <qt:editorinfo movePoint="3.06;9.18" endTargetFactors="88.28;64.08" startTargetFactors="13.98;61.45"/>
        </transition>
    </state>
    <state id="s_true">
        <qt:editorinfo scenegeometry="529.21;419.09;469.21;369.09;120;100" geometry="529.21;419.09;-60;-50;120;100"/>
        <onentry>
            <log expr="&quot;s_true&quot;"/>
        </onentry>
        <transition type="external" event="t_button_clicked" target="s_initial">
            <qt:editorinfo movePoint="-37.73;6.12" endTargetFactors="68.74;85.18" startTargetFactors="14.04;72.02"/>
        </transition>
    </state>
    <datamodel>
        <data id="test_var" expr="false"/>
    </datamodel>
</scxml>

从 scxml 文件中可以看出,我将日志输出添加到每个 state-onentry 以查看该状态是否已进入。此外,我向按钮和复选框单击添加了调试输出。当我运行应用程序时,控制台输出不是我所期望的:

scxml.statemachine: "" : "s_initial"
> checkbox: false
> button
scxml.statemachine: "" : "s_false"
> button
scxml.statemachine: "" : "s_initial"
> checkbox: true
> button
scxml.statemachine: "" : "s_false"
> button
scxml.statemachine: "" : "s_initial"
> checkbox: false
> button
scxml.statemachine: "" : "s_false"
> button
scxml.statemachine: "" : "s_initial"

test_var 的值是什么并不重要。状态机总是首先转换到s_false,而不检查我添加的条件保护。据我所知,我在图表中使用了有效的 ecmascript 表达式,并且 scxml 应该能够根据其specification 选择正确的转换。我做错了什么?

【问题讨论】:

    标签: c++ qt state-machine qtwidgets scxml


    【解决方案1】:
    1. 您应该使用 setScxmlProperty 而不是 setProperty

      chart.dataModel()-&gt;setScxmlProperty("test_var", checked, "");

    2. 您可以简单地使用 _event.data 来传递复选框当前值。

      chart.submitEvent("t_button_clicked", ui-&gt;checkBox-&gt;checked() ? 1:0 );

    <scxml datamodel="ecmascript" initial="s_initial" name="TestChart" version="1.0" xmlns="http://www.w3.org/2005/07/scxml">
        <state id="s_initial">
            <onentry>
                <log expr="'s_initial'"/>
            </onentry>
            <transition cond="_event.data==1" event="t_button_clicked" target="s_true"/>
            <transition event="t_button_clicked" target="s_false"/>
        </state>
        <state id="s_false">
            <onentry>
                <log expr="'s_false'"/>
            </onentry>
            <transition cond="_event.data==1" event="t_button_clicked" target="s_true"/>
        </state>
        <state id="s_true">
            <onentry>
                <log expr="'s_true'"/>
            </onentry>
            <transition cond="! (_event.data==1)" event="t_button_clicked" target="s_false"/>
        </state>
    </scxml>
    

    statechart

    附言您可以使用下一个参考资料来更好地理解 SCXML(我正在推广自己的网站)

    【讨论】:

    • 当链接到您自己的网站或内容(或您附属的内容)时,您must disclose your affiliation in the answer 以免被视为垃圾邮件。根据 Stack Exchange 政策,在您的用户名中包含与 URL 相同的文本或在您的个人资料中提及它不被视为充分披露。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多