Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Stopping a QStateMachine with unconditional transitions
QtWS25 Last Chance

Stopping a QStateMachine with unconditional transitions

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 4 Posters 1.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    kain
    wrote on last edited by
    #1

    I am trying to model a process with QStateMachine which I am able to interrupt using some kind of 'stop'-Signal. For testing purpose I build this simple StateMachine:

    StateMachineWrapper::StateMachineWrapper(QObject *parent) : QObject(parent)
    {
        sm = new QStateMachine(this);
    
        QState *running = new QState(sm);
        QState *stopped = new QState(sm);
    
        QState* stateA = new QState(running);
        QState* stateB = new QState(running);
        QState* stateC = new QState(running);
    
        stateA->addTransition(stateB);
        stateB->addTransition(stateC);
        stateC->addTransition(stateA);
        running->addTransition(this, &StateMachineWrapper::stopSM, stopped);
    
        connect(stateA, &QState::entered, this, &StateMachineWrapper::onA);
        connect(stateB, &QState::entered, this, &StateMachineWrapper::onB);
        connect(stateC, &QState::entered, this, &StateMachineWrapper::onC);
        connect(stopped, &QState::entered, this, &StateMachineWrapper::onStopped);
    
        sm->setInitialState(running);
        running->setInitialState(stateA);
    }
    
    void StateMachineWrapper::onA()
    {
        qDebug() << "State A";
    }
    void StateMachineWrapper::onB()
    {
        qDebug() << "State B";
    }
    void StateMachineWrapper::onC()
    {
        qDebug() << "State C";
    }
    
    void StateMachineWrapper::onStopped()
    {
        qDebug() << "State Stopped";
    }
    
    void StateMachineWrapper::onStartSM()
    {
        sm->start();
    }
    
    void StateMachineWrapper::onStopSM()
    {
        qDebug() << "Trying to Stop SM";
        emit stopSM();
    }
    

    Basically the StateMachine has a loop consisting of the three states A, B and C. These are nested in a parent "Running"-QState. In a real-world application this might be some control loop or just some process containing many states.

    I want to be able to stop this process using a signal. To achieve that I created a "Stopped"-state with a transition that should trigger on a specific signal (here: stopSM).

    However this does not work because the QStateMachine does not seem to return to the EventLoop I guess. I know I could achieve that If I would create an event for every existing transition in that StateMachine and would not use unconditional transitions. But this would also mean a big overhead If my StateMachine consists of > 100 States.

    Is there any way of making my StateMachine react to external signals like the 'stopped'-Signal in the above example?

    Pablo J. RoginaP 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      What do you mean by it doesn't return control to the event loop ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • K Offline
        K Offline
        kain
        wrote on last edited by
        #3

        Well, I wrote a minimal sample-application like this:

        void MainWindow::on_pushButton_clicked()
        {
            wrapper = new StateMachineWrapper();
            QThread* thread = new QThread();
            connect(thread, &QThread::started, wrapper, &StateMachineWrapper::onStartSM);
            connect(this, &MainWindow::stop, wrapper, &StateMachineWrapper::onStopSM);
            wrapper->moveToThread(thread);
            thread->start();
        }
        
        void MainWindow::on_pushButton_2_clicked()
        {
            emit stop();
        }
        
        void MainWindow::on_pushButton_3_clicked()
        {
            emit stop();
        }
        

        The SM starts without any problems and outputs A,B,C,A,B,C,... as expected.
        But when I want to stop the StateMachine by emitting the 'stopSM' signal, it does not work. I would expect the StateMachine to transition to the "stopped" state because I added that transition on the 'running' state.

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          What version of Qt are you using ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          0
          • K Offline
            K Offline
            kain
            wrote on last edited by
            #5

            I am using Qt 5.11.2 and MinGW 5.3.0

            Another question I have is: Can Transitions somehow be priorized? So for example I want a SignalTransition to be of an higher priority as unconditional transitions.
            Background: I have processes with a lot of steps, so I would like to be able to use unconditional transitions for this. However, to be able to react to external signals or even be able to stop this process (like in my above example) I need those unconditional transitions to be of a lower priority.

            1 Reply Last reply
            0
            • K kain

              I am trying to model a process with QStateMachine which I am able to interrupt using some kind of 'stop'-Signal. For testing purpose I build this simple StateMachine:

              StateMachineWrapper::StateMachineWrapper(QObject *parent) : QObject(parent)
              {
                  sm = new QStateMachine(this);
              
                  QState *running = new QState(sm);
                  QState *stopped = new QState(sm);
              
                  QState* stateA = new QState(running);
                  QState* stateB = new QState(running);
                  QState* stateC = new QState(running);
              
                  stateA->addTransition(stateB);
                  stateB->addTransition(stateC);
                  stateC->addTransition(stateA);
                  running->addTransition(this, &StateMachineWrapper::stopSM, stopped);
              
                  connect(stateA, &QState::entered, this, &StateMachineWrapper::onA);
                  connect(stateB, &QState::entered, this, &StateMachineWrapper::onB);
                  connect(stateC, &QState::entered, this, &StateMachineWrapper::onC);
                  connect(stopped, &QState::entered, this, &StateMachineWrapper::onStopped);
              
                  sm->setInitialState(running);
                  running->setInitialState(stateA);
              }
              
              void StateMachineWrapper::onA()
              {
                  qDebug() << "State A";
              }
              void StateMachineWrapper::onB()
              {
                  qDebug() << "State B";
              }
              void StateMachineWrapper::onC()
              {
                  qDebug() << "State C";
              }
              
              void StateMachineWrapper::onStopped()
              {
                  qDebug() << "State Stopped";
              }
              
              void StateMachineWrapper::onStartSM()
              {
                  sm->start();
              }
              
              void StateMachineWrapper::onStopSM()
              {
                  qDebug() << "Trying to Stop SM";
                  emit stopSM();
              }
              

              Basically the StateMachine has a loop consisting of the three states A, B and C. These are nested in a parent "Running"-QState. In a real-world application this might be some control loop or just some process containing many states.

              I want to be able to stop this process using a signal. To achieve that I created a "Stopped"-state with a transition that should trigger on a specific signal (here: stopSM).

              However this does not work because the QStateMachine does not seem to return to the EventLoop I guess. I know I could achieve that If I would create an event for every existing transition in that StateMachine and would not use unconditional transitions. But this would also mean a big overhead If my StateMachine consists of > 100 States.

              Is there any way of making my StateMachine react to external signals like the 'stopped'-Signal in the above example?

              Pablo J. RoginaP Offline
              Pablo J. RoginaP Offline
              Pablo J. Rogina
              wrote on last edited by
              #6

              @kain

              void StateMachineWrapper::onStopSM()
              {
              qDebug() << "Trying to Stop SM";
              emit stopSM();
              }

              I'm not sure why in this method onStopSM() you don't directly stop the state machine with

              sm->stop()
              

              the same way you start the state machine in method onStartSM() (that you even know works!). The only thing missing I see in the StateMachineWrapper class is to connect the state machine stopped() signal to do the final tasks as you need

              Upvote the answer(s) that helped you solve the issue
              Use "Topic Tools" button to mark your post as Solved
              Add screenshots via postimage.org
              Don't ask support requests via chat/PM. Please use the forum so others can benefit from the solution in the future

              1 Reply Last reply
              0
              • K Offline
                K Offline
                kain
                wrote on last edited by kain
                #7

                I don't want to stop the whole statemachine. I got 2 parent states: 'running' and 'stopped'. The states A,B and C are substates of 'running'.
                In a real-world application this might be a "paused" or "error" state or whatever process I want to be able to interact with.

                Edit: I just tried to stop the StateMachine as you suggested. It does not work either.

                1 Reply Last reply
                0
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Would you mind providing your test application as a complete project ? This would allow to test it locally in the same conditions as you.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • K Offline
                    K Offline
                    kain
                    wrote on last edited by kain
                    #9

                    Sure. I Attached the whole TestApp
                    (I could not upload here due to missing priviliges, so I instead uploaded it here:
                    https://ufile.io/u3kbd )

                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      MrShawn
                      wrote on last edited by
                      #10

                      @kain said in Stopping a QStateMachine with unconditional transitions:

                      However this does not work because the QStateMachine does not seem to return to the EventLoop I guess. I know I could achieve that If I would create an event for every existing transition in that StateMachine and would not use unconditional transitions. But this would also mean a big overhead If my StateMachine consists of > 100 States.

                      Looking at your code, you're probably right about never making it to the event loop. Why do you need to have a different event for each state if it is unconditional anyways?

                      Create one signal in your wrapper to swap states, and emit it in each on entry slot you have.

                      StateMachineWrapper::StateMachineWrapper(QObject *parent) : QObject(parent)
                      {
                          sm = new QStateMachine(this);
                      
                          QState *running = new QState(sm);
                          QState *stopped = new QState(sm);
                      
                          QState* stateA = new QState(running);
                          QState* stateB = new QState(running);
                          QState* stateC = new QState(running);
                      
                          stateA->addTransition(this, &StateMachineWrapper::sig_nextState, stateB);
                          stateB->addTransition(this, &StateMachineWrapper::sig_nextState, stateC);
                          stateC->addTransition(this, &StateMachineWrapper::sig_nextState, stateA);
                          running->addTransition(this, &StateMachineWrapper::stopSM, stopped);
                      
                          connect(stateA, &QState::entered, this, &StateMachineWrapper::onA);
                          connect(stateB, &QState::entered, this, &StateMachineWrapper::onB);
                          connect(stateC, &QState::entered, this, &StateMachineWrapper::onC);
                          connect(stopped, &QState::entered, this, &StateMachineWrapper::onStopped);
                      
                          sm->setInitialState(running);
                          running->setInitialState(stateA);
                      }
                      
                      void StateMachineWrapper::onA()
                      {
                          qDebug() << "State A";
                          emit sig_nextState();
                      }
                      void StateMachineWrapper::onB()
                      {
                          qDebug() << "State B";
                          emit sig_nextState();
                      }
                      void StateMachineWrapper::onC()
                      {
                          qDebug() << "State C";
                          emit sig_nextState();
                      }
                      
                      void StateMachineWrapper::onStopped()
                      {
                          qDebug() << "State Stopped";
                      }
                      
                      void StateMachineWrapper::onStartSM()
                      {
                          sm->start();
                      }
                      
                      void StateMachineWrapper::onStopSM()
                      {
                          qDebug() << "Trying to Stop SM";
                          emit stopSM();
                      }
                      
                      1 Reply Last reply
                      0
                      • K Offline
                        K Offline
                        kain
                        wrote on last edited by
                        #11

                        Thank you, I solved it this way already. I was just curios, if I could use QStateMachine in a more 'implicit' way.

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved