How to change state from the state object itself not outside?

877 views Asked by At
    class ListenState : public QState
    {
    public:
        ListenState();
        ~ListenState();

    signals:
        void nextState();

    public slots:
        void getSettings();
    };

The cpp file is

ListenState::ListenState()
{
    qDebug() << "Entering ListenState";
}

ListenState::~ListenState()
{
    qDebug() << "Leaving ListenState";
}

void ListenState::getSettings()
{
    Commands cmd;

    cmd.getSettings();

    emit exited( QEvent::None ); // i want to change state now
}

What I want to do is when getSettings() is called, I want to change the state to next one. I thought I would emit exited() but it doesn't build. I tried to create my own signal nextState() but that doesn't compile either if I emit in this function.

With above code the error is:

ListenState.cpp:23: error: C2664: 'QAbstractState::exited' : cannot convert parameter 1 from 'QEvent::Type' to 'QAbstractState::QPrivateSignal' No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

If I emit my own signal with emit nextState(); the error is:

ListenState.obj:-1: error: LNK2001: unresolved external symbol "public: void __thiscall ListenState::nextState(void)" (?nextState@ListenState@@QAEXXZ)

Is there a way to trigger trasition from one state to another when I am in originating state?

1

There are 1 answers

0
Kuba hasn't forgotten Monica On

First, a state's lifetime has very little to do with when a state is entered or exited. States usually exist as long as the state machine exists, or they may be created and destroyed on-the-fly. You're hooking up a state's constructor and destructor with the expectation that they'll be invoked when a state is entered or exited. This is not the case.

To check when a state has been entered or exited, you could use the following:

void exposeStateTransitions(QState * state, QString name) {
  if (name.isEmpty()) name = state->objectName();
  QObject::connect(state, &QState::entered, []{
    qDebug() << "state" << name << "was entered";
  });
  QObject::connect(state, &QState::exited, []{
    qDebug() << "state" << name << "was exited";
  });
}

Secondly, states can only be changed by the use of the transition objects. You need to create a transition object for the desired transition, and provide a signal or an event to trigger it:

class ListenState : public QState {
  Q_OBJECT
  QSignalTransition m_transition;
  Q_SIGNAL void settingsTransition();
public:
  ListenState(QState * settingsTarget, QState * parent = 0) : 
    QState(parent), m_transition(this, SIGNAL(settingsTransition())
  {
    m_transition.setTargetState(settingsTarget);
    addTransition(&m_transition);
  }
  void getSettings() {
    ...
    emit settingsTransition();
  }
};

If you wish, you can trigger the transition on the fly, too:

class ListenState : public QState {
  Q_OBJECT
  QSignalTransition m_transition;
  Q_SIGNAL void settingsTransition();
public:
  ListenState(QState * parent = 0) : 
    QState(parent), m_transition(this, SIGNAL(settingsTransition())
  {
    addTransition(&m_transition);
  }
  void getSettings(QState * target) {
    ...
    m_transition.setTargetState(target);
    emit settingsTransition();
  }
};

You can use events instead of signals:

class ListenState : public QState {
  QEventTransition m_transition;
public:
  ListenState(QState * parent = 0) : 
    QState(parent), m_transition(this, QEvent::Leave) {
    addTransition(&m_transition);
  }
  void getSettings(QState * target) {
    ...
    m_transition->setTargetState(target);
    QCoreApplication::postEvent(this, new QEvent(QEvent::Leave));
  }
};