[Qt-interest] QStateMachine API and design?

Sean Harmer sean.harmer at maps-technology.com
Thu Nov 4 15:51:06 CET 2010


Hi,

On Thursday 04 November 2010 14:03:22 Stephen Kelly wrote:
> Yes. I haven't figured out what parallel states are for though tbh.

We have mainly made use of them where in some hardware devices we have 
multiple subsystems which are more or less independent (although you can still 
have some dependencies between them using guardded transitions). 

For example we have a SM with 15 subsystems representing 15 pieces of a type 
of pcb and an overall "system mode" state. All of these are arranged under a 
top-level state as 16 parallel states. Each of these then has substates in the 
usual manner. The system mode state and each pcb are mostly independent but 
are part of the same SM because the system mode initiates actions on each of 
the pcb's and the pcb's notify progress by posting events to the SM. Then all 
pcb's have performed a required task then the system mode gets updated.

NB: In turn each of the pcb's are QObjects that themselves contain a SM that 
manages the inner logic of the pcb's. I find it helps to keep the SM's 
relatively self-contained and as simple as possible rather than having a 
single monolithic one.

Applying this to a GUI, you could envisage using parallel states if you have 
several widgets on a form that are more or less independent. For example a 
dialog with two tab widgets on them. The current tab for each tab widget could 
be managed by a SM containing two parallel states.

> >> * Why is there no notification when the 'current state' is changed?
> > 
> > I have filed a bug for this after discussion with Kent:
> > 
> > http://bugreports.qt.nokia.com/browse/QTBUG-9380
> > 
> > Please vote for it or file a MR. It should be asimple change and it is on
> > my list but time as always is short. A graphical SM debugger would also
> > be *very* nice but that's a much larger task.
> 
> Done.

Cool thanks.

> I connect to the entered() signal as you suggested and connect it to a
> custom stateChanged() slot on the statemachine itself, while the transition
> updates the state Q_PROPERTY of the state machine.
> 
> http://websvn.kde.org/trunk/KDE/kdepim/mobile/lib/statemachinebuilder.cpp?v
> iew=markup
> 
> > Or you can implement the
> > onEntry()/onExit() functions in your own custom states. We have largely
> > gone for custom states and used overridden onEntry() functions. Not sure
> > if any of these would map cleanly onto a Qml scene though as I have not
> > tried it in anger yet.
> 
> I would be interested in how you think it could work, or to see what one of
> your onEntry implementations look like.

OK. I'll see if I can put together a simple Qml + SM example. I may have some 
time this evening.

Here is a sample onEntry() implementation as an example. It is nothing 
special:

void MeasureAtState::onEntry( QEvent* e )
{
    FrChildState::onEntry( e );

    emit measurementStarted();

    // Kick off a measurement.
    childSystem()->measure( m_measureSettings );
}

In this case the MeasureAtState is derived from FrChildState which in turn is 
derived from a simple State helper class which I have attached to this mail. 
The State class has it's onEntry()/onExit() functions overridden to print out 
a qDebug() msg of as the SM enters/exits states (along with the parent 
states). I found this to be invaluable during debugging SMs.

In the above example it was not possible to hook up the MeasureAtState 
instance's entered() signal to the childSystem()'s measure signal as we needed 
to pass in some arguments to the measure() function. I admit that this leads 
to some coupling between the SM and the objects it is managing but that is OK 
IMO as the whole thing is then encapsulated in another QObject class anyway. 
The wider application never deals with the SM directly.

In such cases we have also taken the approach of having a custom class be 
responsible for creating its child states rather than having all states and 
parent-child relationships set up by a single function. Something like this:

ModeState::ModeState( FrSystem* system, QState* parent )
    : FrSystemState( system, "FrSystem Mode", parent ),
      m_starting( new StartingState( system, this ) ),
      m_running( new RunningState( system, this ) ),
      m_pausing( new PausingState( system, this ) ),
      m_error( new ErrorState( system, this ) )
{
    setInitialState( m_starting );

    // Final state
    QFinalState* final = new QFinalState( this );

    // As soon as the starting state has done its work we immediately 
transition
    // to the running state.
    m_starting->addTransition( m_running );

    // We then need transitions to/from the paused state...
    PausingTransition* pausingTransition = new PausingTransition( system, 
m_running );
    pausingTransition->setTargetState( m_pausing );
    m_running->addTransition( pausingTransition );

    CustomEventTransition* unpauseTransition =
            new CustomEventTransition( QEvent::Type( QEvent::User + 
FrHardware::UnpauseEventOffset ), m_pausing );
    unpauseTransition->setTargetState( m_running );
    m_pausing->addTransition( unpauseTransition );

    // Adding transitions to/from the error state...
    ErrorTransition* errorTransition = new ErrorTransition( system, m_running 
);
    errorTransition->setTargetState( m_error );
    m_running->addTransition( errorTransition );
    m_error->addTransition( m_running );

    // Add a transition from the running state to the final state when all 
enabled probes are in the notMeasuring state
    StoppingTransition* trans1 = new StoppingTransition( system, m_running );
    trans1->setTargetState( final );
    m_running->addTransition( trans1 );
}

Then in turn the RunningState ctor is responsible for creating its nested 
child states etc.

I find that this makes it easier to understand, debug and maintain a sub-tree 
of states more easily. Both approaches are valid though.

Hope this helps a litle.

Cheers,

Sean
-------------- next part --------------
A non-text attachment was scrubbed...
Name: state.cpp
Type: text/x-c++src
Size: 1126 bytes
Desc: not available
Url : http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20101104/dc443bc8/attachment.bin 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: state.h
Type: text/x-chdr
Size: 668 bytes
Desc: not available
Url : http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20101104/dc443bc8/attachment-0001.bin 


More information about the Qt-interest-old mailing list