如何让这个Qt状态机工作? [英] How to get this Qt state machine to work?

查看:151
本文介绍了如何让这个Qt状态机工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  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();

这里的问题是以下过渡:

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

会导致 registerButtonDisabled 状态恢复到其初始状态。这是不想要的行为,因为我想要 a b 状态保持在相同的状态。

如何确保 a b 保持在相同的状态?有没有另一个/更好的方式这个问题可以使用状态机解决?






注意。有无数的(可能更好的)解决这个问题的方法。然而,我只对使用状态机的解决方案感兴趣。我认为这样一个简单的用例应该可以使用简单的状态机解决,对吧?

解决方案

并在这里评论我认为merula的解决方案或类似的东西是唯一的纯粹的Statemachine解决方案。



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



一个解决方案是编写一个只在所有三个状态处于良好状态,并且第二个状态在任何那些不是时触发。然后,将禁用和启用状态添加到您已有的并行状态,并将其与上述转换连接。这将保持按钮的启用状态与您的UI部分的所有状态同步。它还将允许您离开并行状态并返回一组一致的属性设置。

 类AndGateTransition:public QAbstractTransition 
{
Q_OBJECT

public:

AndGateTransition(QAbstractState * sourceState):QAbstractTransition(sourceState)
m_isSet(false),m_triggerOnSet ),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 ;
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&&&&&!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;

}

没有编译,甚至测试,证明原则


I have two widgets that can be checked, and a numeric entry field that should contain a value greater than zero. Whenever both widgets have been checked, and the numeric entry field contains a value greater than zero, a button should be enabled. I am struggling with defining a proper state machine for this situation. So far I have the following:

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();

The problem here is that the following transition:

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

causes all the child states in the registerButtonDisabled state to be reverted to their initial state. This is unwanted behaviour, as I want the a and b states to remain in the same state.

How do I ensure that a and b remain in the same state? Is there another / better way this problem can be solved using state machines?


Note. There are a countless (arguably better) ways to solve this problem. However, I am only interested in a solution that uses a state machine. I think such a simple use case should be solvable using a simple state machine, right?

解决方案

After reading your requirements and the answers and comments here I think merula's solution or something similar is the only pure Statemachine solution.

As has been noted to make the Parallel State fire the finished() signal all the disabled states have to be final states, but this is not really what they should be as someone could uncheck one of the checkboxes and then you would have to move away from the final state. You can't do that as FinalState does not accept any transitions. The using the FinalState to exit the parallel state also causes the parallel state to restart when it is reentered.

One solution could be to code up a transition that only triggers when all three states are in the "good" state, and a second one that triggers when any of those is not. Then you add the disabled and enabled states to the parallel state you already have and connect it with the aforementioned transitions. This will keep the enabled state of the button in sync with all the states of your UI pieces. It will also let you leave the parallel state and come back to a consistent set of property settings.

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;

}

Did not compile this or even test it, but it should demonstrate the principle

这篇关于如何让这个Qt状态机工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆