QState::assignProperty 不起作用 [英] QState::assignProperty doesn't work

查看:40
本文介绍了QState::assignProperty 不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试向 qstatemachine 添加一个带有字符串和枚举的属性,但 machine.property() 的结果为空.

I'm trying to add to qstatemachine a property with string and an enum, but the result from the machine.property() is empty.

QStatemachine m_machine;

idle->assignProperty(&m_machine, "state", Z_IDLE);
cool->assignProperty(&m_machine, "state", Z_COOL);
start_z->assignProperty(&m_machine, "state", Z_START);

QVariant st = m_machine->property("state");
QString s = st.toString();

我试图展示我的方法,看看它是否可行.

I tried to show my approach to see if that can work or no.

更新:

 idle = new QState();
 start_z = new QState();
 lock = new QState();
 connect(this, SIGNAL(machine_exec()), this, SLOT(idle_exec()));
 connect(this, SIGNAL(machine_exec()), this, SLOT(start_z_exec()));
 connect(this, SIGNAL(machine_exec()), this, SLOT(sample_exec()));

  m_machine->addState(idle);
  m_machine->addState(start_z);
  m_machine->addState(lock);

 idle->assignProperty(m_machine, "state", Z_IDLE);
 cool->assignProperty(m_machine, "state", Z_COOL);
 start_z->assignProperty(m_machine, "state", Z_START);

  idle->addTransition(this, SIGNAL(machineToStart()), start_z);
  cool->addTransition(this, SIGNAL(machineToMotDn()), motDn);
  motDn->addTransition(this, SIGNAL(machineToFini()), fini);

    void MeasController::idle_exec()
    {
        qDebug()<<"idle_exec";
         emit machineToStart();

     }

    void MeasController::start_z_exec()
    {
        qDebug()<<"start_z_exec";
        QVariant s = m_machine->property("state");
        qDebug()<<"property value"<<s.toString();
        if (m_machine->property("state") == Z_START) {
            emit machineStartToSample();
            }
    }

推荐答案

属性仅在状态转换时分配,因此如果您在机器启动之前检查属性的值并且事件循环有机会运行,你不会得到一个有效的值.

The properties are assigned on state transitions only, so if you check the value of the property before the machine was started and the event loop had a chance to run, you won't get a valid value.

此外,通过多次使用属性名称,您很可能会犯错.您应该为属性名称使用命名常量而不是字符串文字 - 它们应该有足够的不同,以便捕获随机拼写错误.IE.kStateAkStateB 之类的属性名称可能太相似了 - 拼写错误会无声.

Additionally, by using the name of the property multiple times, you're liable to commit a typo. Instead of string literals, you should be using named constants for property names - and they should be sufficiently different that random typos will get caught. I.e. property names like kStateA and kStateB are probably too similar - a typo would be silent.

所以,我将代码更改为:

So, I'd change the code to be:

static const char kState[] = "state";
QStatemachine m_machine;

idle->assignProperty(&m_machine, kState, Z_IDLE);
cool->assignProperty(&m_machine, kState, Z_COOL);
start_z->assignProperty(&m_machine, kState, Z_START);

qDebug() << m_machine->property(kState).toString();

另请注意,这种单一当前状态的概念仅适用于您的机器是非分层的.由QStateMachine 实现的通用HSM 始终处于一组 状态,可从configuration() 获得.大多数现实的机器应该是分层的,所以你的方法是行不通的.

Also note that this notion of a single current state only applies if your machine is non-hierarchical. A general HSM, as implemented by QStateMachine, is always in a set of states, available from configuration(). Most realistic machines should be hierarchical, so your approach won't work.

目前还不清楚 _exec 方法以及用于在状态之间转换的显式信号是否有任何用途.

It is also unclear that there's any use for the _exec methods, as well as the explicit signals used to transition between states.

以下是您可能会发现有用的方法的完整示例.

Below is a complete example of approaches that you might find useful.

首先,让我们从如何简洁地指定您的 MeasController 开始:

First, let's start with how one might concisely specify your MeasController:

class MeasController : public QObject {
   Q_OBJECT
   QStateMachine m_machine{this};
   NamedState
      m_idle    {"Idle", &m_machine},
      m_start_z {"Start Z", &m_machine},
      m_active_z{"Active Z", &m_machine},
      m_downMove{"DownMove", &m_machine};
   Transition
      m_toStartZ{&m_idle, &m_start_z},
      m_toDownMove{&m_active_z, &m_downMove};
   Delay
      m_d1{&m_start_z, &m_active_z, 1000},
      m_d2{&m_downMove, &m_active_z, 2000};
   QList<QState*> states() const { return m_machine.findChildren<QState*>(); }
public:
   MeasController(QObject * parent = 0) : QObject(parent) {
      for (auto state : states())
         connect(state, &QState::entered, [state]{ qDebug() << state->objectName(); });
      m_machine.setInitialState(&m_idle);
      m_machine.start();
   }
   Q_SLOT void startZ() { m_toStartZ.trigger(); }
   Q_SLOT void moveDown() { m_toDownMove.trigger(); }
};

这是状态机的完整规范.状态及其转换和其他行为作为 MeasController 的成员给出,因此很容易在一个地方找到.为了便于调试,在进入每个状态时都会提供调试输出.提供了插槽来触发类外部的行为,而不必暴露内部.

This is a complete specification of the state machine. The states and their transitions and other behaviors are given as members of MeasController, and are thus easy to find in one place. For ease of debugging, debug output is provided upon each state being entered. Slots are provided to trigger behaviors from outside of the class without having to expose the internals.

现在让我们看看由 NamedStateTransitionDelay 类定义的习语.

Let's now look at the idioms defined by the NamedState, Transition and Delay classes.

命名状态习惯用法,类似于 Qt 3 的 QObject 在构造函数中取名,至少对调试很有用.如果你想分配状态的任何其他属性,你也可以在这个类中这样做.由于状态具有唯一的变量名称并且是父类的成员,因此不需要任何整数标识符:

A named state idiom, similar to Qt 3's QObject taking name in the constructor, is useful for debugging at the very least. If you want to assign any other properties of the state, you can do so in this class as well. Since the states have unique variable names and are members of the parent class any integer identifiers aren't necessary:

// https://github.com/KubaO/stackoverflown/tree/master/questions/state-properties-36745219
#include <QtWidgets>

class NamedState : public QState { Q_OBJECT
public:
   NamedState(const char * name, QStateMachine * parent) : QState(parent) {
      setObjectName(QString::fromUtf8(name));
   }
};

转换类有助于以简洁的方式将状态机的结构指定为 Transition 实例的简单成员定义:

A transition class is useful to give a concise way of specifying the structure of your state machine as simple member definitions of Transition instances:

struct Transition : public QObject { Q_OBJECT
public:
   Transition(QState * source, QState * destination) : QObject(source->machine()) {
      source->addTransition(this, &Transition::trigger, destination);
   }
   Q_SIGNAL void trigger();
};

我确信您的状态机具有更复杂的行为,但通常有助于将特定行为分解为它自己的类.例如.假设你想要一个在延迟之后发生的状态转换:

I'm sure that your state machine has more complex behaviors, but generally it helps to factor out a certain behavior into its own class. E.g. say you want a state transition that occurs after a delay:

class Delay : public Transition { Q_OBJECT
   int m_delay;
   QBasicTimer m_timer;
   void timerEvent(QTimerEvent * ev) {
      if (m_timer.timerId() != ev->timerId()) return;
      m_timer.stop();
      trigger();
   }
public:
   Delay(QState * s, QState * d, int ms) : Transition(s, d), m_delay(ms) {
      connect(s, &QState::entered, this, [this]{ m_timer.start(m_delay, this);});
   }
};

最后,我们可以提供一个简单的 UI 来测试和可视化机器的行为.调试输出复制在 QPlainTextEdit 中以方便使用.

Finally, we can offer a simple UI to test and visualize the behavior of the machine. The debug output is duplicated in a QPlainTextEdit for ease of use.

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   MeasController ctl;
   QWidget w;
   QGridLayout layout{&w};
   QPushButton start{"Start"};
   QPushButton moveDown{"Move Down"};
   QPlainTextEdit log;
   log.setReadOnly(true);
   layout.addWidget(&start, 0, 0);
   layout.addWidget(&moveDown, 0, 1);
   layout.addWidget(&log, 1, 0, 1, 2);

   QObject::connect(&start, &QPushButton::clicked, &ctl, &MeasController::startZ);
   QObject::connect(&moveDown, &QPushButton::clicked, &ctl, &MeasController::moveDown);

   static QtMessageHandler handler = qInstallMessageHandler(
      +[](QtMsgType t, const QMessageLogContext& c, const QString & msg){
      static QPointer<QPlainTextEdit> log{[]{
         for (auto w : qApp->topLevelWidgets())
            for (auto log : w->findChildren<QPlainTextEdit*>()) return log;
         Q_ASSERT(false);
      }()};
      if (log) log->appendPlainText(msg);
      handler(t, c, msg);
   });

   w.show();
   return app.exec();
}

#include "main.moc"

完整示例到此结束.这是一个较长的示例,风格有些类似.

This concludes the complete example. This is a longer example in a somewhat similar style.

这篇关于QState::assignProperty 不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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