In the comments to my previous post, a few pointed out that the states & transitions API we’re researching seems generally useful (i.e. outside of Kinetic context). Hence, we’ve released a stand-alone state machine add-on that you can play with. It’s been tested with Qt 4.4 and 4.5. Enjoy, and don’t be shy about giving feedback.
No related posts.
12 comments
Great work! Exactly what I needed.
Good to see it decoupled from the kinetics framework.
Just a very little remark, I think the images of the state diagrams in the documentation are a “bit” too large.
This is great. I’ve been researching statemachines for use in my dialogs, and having one with this tight integration into Qt would be awesome (it still helps to know Boost.Statechart, though, for the cases where you can’t afford the Qt dependency).
After reading the framework main page, and some of the QAS subclasses, I have two questions:
1. QtHistoryState: I assume it’s what UML calls “shallow history” (H), and not “deep history” (H*), yes?
2. I found no obvious way to add guards to transitions. In my experience, guards are absolutely essential, esp. in GUI programming.
I also have two suggestions:
3. The handling of concurrent states is awkward, because you need the extra parent state (in UML, you add child states to “regions” in the parent state, and regions are not states). I suggest a syntax like this:
s = new QState;
sa1 = new QState( s->region( 0 ) );
sa2 = new QState( s->region( 0 ) );
s->region( 0 )->setInitalState( sa1 );
sb1 = new QState( s->region( 1 ) ); // calling region(x) where x != makes ‘s’ implicitly a concurrent state
QState::region() would return something like QConcurrentStateRegion with a subset of the QState interface (setInitialState(), and the like)
4. In the documentation, please use proper UML syntax, and not ellipses and stuff. The history state is (H) or (H*) (that’s an H in a circle), etc. The syntax is almost identical, but using UML will help those that already know it while at the same time still not intimidate those that don’t.
Thanks,
Marc
When looking through (part of) the API, one thing that strikes me as odd is the use of the invokeMethod in the states. Why can’t I just use the normal Qt signal/slot mechanism here? It would make so much more sense if the state just emitted a signal when it’s state is entered and when it’s state is exited. It would fit the normal API better. I understand that the current way is potentially more powerfull, but it would be nice to at least offer the default Qt mechanism for those that don’t need to invoke complex methods with a whole list of arguments.
This would be great. I’ve been researching statemachines for use in my dialogs, and having one with this tight integration into Qt would be awesome (it still helps to know Boost.Statechart, though, for the cases where you can’t afford the Qt dependency).
After reading the framework main page, and some of the QAS subclasses, I have two questions:
1. QtHistoryState: I assume it’s what UML calls “shallow history” (H), and not “deep history” (H*), yes?
2. I found no obvious way to add guards to transitions. In my experience, guards are absolutely essential, esp. in GUI programming.
I also have two suggestions:
3. The handling of concurrent states is awkward, because you need the extra parent state (in UML, you add child states to “regions” in the parent state, and regions are not states). I suggest a syntax like this:
s = new QState;
sa1 = new QState( s->region( 0 ) );
sa2 = new QState( s->region( 0 ) );
s->region( 0 )->setInitalState( sa1 );
sb1 = new QState( s->region( 1 ) ); // calling region(x) where x != makes ‘s’ implicitly a concurrent state
QState::region() would return something like QConcurrentStateRegion with a subset of the QState interface (setInitialState(), and the like)
4. In the documentation, please use proper UML syntax, and not ellipses and stuff. The history state is (H) or (H*) (that’s an H in a circle), etc. The syntax is almost identical, but using UML will help those that already know it while at the same time still not intimidate those that don’t.
Thanks,
Marc
Hi Andre. Yes, this is something we have debated in previous API iterations; originally there were QState::entered() and exited() signals.
But consider how it looks now (no signals & slots):
state->invokeMethodOnEntry(button, “showMaximized”);
versus:
QObject::connect(state, SIGNAL(entered()), button, SLOT(showMaximized()));
How is the second form better?
I don’t understand the comment about “[offering] the default Qt mechanism for those that don’t need to invoke complex methods with a whole list of arguments”. The argument list for invokeMethodOnEntry() is optional.
We either need to offer one approach or the other, not both; supporting both would just be confusing.
I give you some reasons why the second one is better:
1) IDEs like Visual Studio and QtCreator can autocomplete the SLOT
2) Although the SLOT-Macro just expands to 2_maximized, you don’t imediately see that it is a string constant. It leaves the fact that lookup is done using a string constant as an implementation detail.
3) You bring a second way to connect something to the same framework that already has sigs&slots.
A reason against the second one:
-1) You can get “showMaximized” from the QMetaObject but you cannot use SLOT to connect to a slot that you dont know about during compile time and would have to make a “dynamic” version of SLOT that evaluates on runtime. I have done that for one project. If you ever plan to add such a dynamic SLOT, think about a version that does not take a string but a QMetaMethod. Same with Signals if you want to connect to a signal that is attached to a property using the NOTIFY-Keyword.
Feel to contact me for more information about the project I spoke in -1.
Looking through the API a bit more, I also miss slots to trigger transitions. The only thing I can do is add create the transition using QState::addTransition() with an object and a signal, but it is not obvious how to do that later, or how to make two different signals trigger a certain transition.
I agree with Axel Jäger when he sais that it is confusing to support a different connect mechanism into a framework that already has a perfectly valid and working one. What’s more, if you use the standard signals and slots, you make it possible for signals to be disconnected later on, and you introduce the option to choose the type of connection (for multithreaded applications that is important!) Why throw all that away by introducing something that looks much more like a callback mechanism than like the Qt signal/slot system (even though it uses it). The semantics are different for no good reason, IMHO.
If you, Kent, ask why the connect statement is better, then I think it is time to ask that question for the whole of the Qt toolkit. Why is it using signals and slots if it is not better than callbacks? Personally, I *LIKE* the signal/slot mechanism. I bet Qt is not about to throw it away, so if only in the interest of consistancy, it would be good to keep using it whereever possible. One way in which the second form is better, is that it allows me to make the connections from another piece of code than where I setup the statemachine itself. That makes sense. First define the whole framework of possible states and their transitions, then later hook that up to the rest of your application like the GUI. Why would you think forcing me to mix those concers is better?
Andre: Transitions, like states, are passive objects. You define transitions, which are associated with certain events/signals, between states, and then let the state machine algorithm do the rest.
If you want two different signals to trigger a state change, you create two transitions, one per signal.
I like the signal/slot mechanism too. But I’m not convinced there’s a gain in using it in this case; I keep getting this mental image of “wavy states that are all over the place”.
Just because we have signals and slots in Qt doesn’t mean it has to be a core part of every API we do. There’s no reason to generalize this to say that it’s a question of whether to use signals and slots for the whole of Qt. We shouldn’t be afraid to try out different approaches, that’s all. Hopefully that will make us more confident that the final solution is good (whether it be signal+slot-based or not).
I don’t see how the current approach stops you from separating the creation of the states from the “hooking up” (“population”) of the states, though.
Anyway, thanks for very good feedback so far, and remember that the API is not set in stone. Keep it coming!
Any chance of releasing a version of Qt SCXML based on this framework? The version of the SCXML project in svn currently uses its own QStateMachine.
Chris, re. SCXML with QtStateMachine: we’re working on it… stay tuned!
Hi
I want to use QT to build a state machine
I have a question
What would be a clever way to change the state if all error flags == 0 and + list of signals are emited
The way i see it is every time error flag change, check all of the them and if all == 0
emit signal
Thank you
Ross
Comments on this entry are closed.