如何使这个Qt状态机工作?

Ton*_*vel 13 c++ qt state-machine qstatemachine

我有两个可以检查的小部件,以及一个应该包含大于零的值的数字输入字段.每当检查两个小部件,并且数字输入字段包含大于零的值时,应启用一个按钮.我正在努力为这种情况定义一个合适的状态机.到目前为止,我有以下内容:

QStateMachine *machine = new QStateMachine(this);

QState *buttonDisabled = new QState(QState::ParallelStates);
buttonDisabled->assignProperty(ui_->button, "enabled", false);

QState *a = new QState(buttonDisabled);
QState *aUnchecked = new QState(a);
QFinalState *aChecked = new QFinalState(a);
aUnchecked->addTransition(wa, SIGNAL(checked()), aChecked);
a->setInitialState(aUnchecked);

QState *b = new QState(buttonDisabled);
QState *bUnchecked = new QState(b);
QFinalState *bChecked = new QFinalState(b);
employeeUnchecked->addTransition(wb, SIGNAL(checked()), bChecked);
b->setInitialState(bUnchecked);

QState *weight = new QState(buttonDisabled);
QState *weightZero = new QState(weight);
QFinalState *weightGreaterThanZero = new QFinalState(weight);
weightZero->addTransition(this, SIGNAL(validWeight()), weightGreaterThanZero);
weight->setInitialState(weightZero);

QState *buttonEnabled = new QState();
buttonEnabled->assignProperty(ui_->registerButton, "enabled", true);

buttonDisabled->addTransition(buttonDisabled, SIGNAL(finished()), buttonEnabled);
buttonEnabled->addTransition(this, SIGNAL(invalidWeight()), weightZero);

machine->addState(registerButtonDisabled);
machine->addState(registerButtonEnabled);
machine->setInitialState(registerButtonDisabled);
machine->start();
Run Code Online (Sandbox Code Playgroud)

这里的问题是以下过渡:

buttonEnabled->addTransition(this, SIGNAL(invalidWeight()), weightZero);
Run Code Online (Sandbox Code Playgroud)

导致状态中的所有子状态registerButtonDisabled恢复到其初始状态.这是不需要的行为,因为我希望ab状态保持相同的状态.

我如何确保ab保持相同的状态?有没有其他/更好的方法可以使用状态机解决这个问题?


注意.有无数(可以说是更好的)方法来解决这个问题.但是,我只对使用状态机的解决方案感兴趣.我认为使用简单的状态机可以解决这么简单的用例,对吧?

Har*_*ich 10

在阅读了您的要求以及这里的答案和评论后,我认为merula的解决方案或类似的东西是唯一纯粹的Statemachine解决方案.

正如已经注意到的那样,使并行状态finished()发出信号所有禁用的状态都必须是最终状态,但这并不是他们应该是什么,因为有人可以取消选中其中一个复选框然后你将不得不离开最终状态州.你不能这样做,因为FinalState不接受任何转换.使用FinalState退出并行状态也会导致并行状态在重新进入时重新启动.

一种解决方案可以是编码仅在所有三个状态都处于"良好"状态时触发的转换,以及当其中任何状态不是时触发的第二个转换.然后将禁用和启用状态添加到已有的并行状态,并将其与上述转换连接.这将使按钮的启用状态与UI片段的所有状态保持同步.它还可以让您离开并行状态并返回一组一致的属性设置.

class AndGateTransition : public QAbstractTransition
{
    Q_OBJECT

public:

    AndGateTransition(QAbstractState* sourceState) : QAbstractTransition(sourceState)
        m_isSet(false), m_triggerOnSet(true), m_triggerOnUnset(false)

    void setTriggerSet(bool val)
    {
        m_triggerSet = val;
    }

    void setTriggerOnUnset(bool val)
    {
        m_triggerOnUnset = val;
    }

    addState(QState* state)
    {
        m_states[state] = false;
        connect(m_state, SIGNAL(entered()), this, SLOT(stateActivated());
        connect(m_state, SIGNAL(exited()), this, SLOT(stateDeactivated());
    }

public slots:
    void stateActivated()
    {
        QObject sender = sender();
        if (sender == 0) return;
        m_states[sender] = true;
        checkTrigger();
    }

    void stateDeactivated()
    {
        QObject sender = sender();
        if (sender == 0) return;
        m_states[sender] = false;
        checkTrigger();
    }

    void checkTrigger()
    {
        bool set = true;
        QHashIterator<QObject*, bool> it(m_states)
        while (it.hasNext())
        {
            it.next();
            set = set&&it.value();
            if (! set) break;
        }

        if (m_triggerOnSet && set && !m_isSet)
        {
            m_isSet = set;
            emit (triggered());

        }
        elseif (m_triggerOnUnset && !set && m_isSet)
        {
            m_isSet = set;
            emit (triggered());
        }
    }

pivate:
    QHash<QObject*, bool> m_states;
    bool m_triggerOnSet;
    bool m_triggerOnUnset;
    bool m_isSet;

}
Run Code Online (Sandbox Code Playgroud)

没有编译或甚至测试它,但它应该证明原理