1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the documentation of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:FDL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in a
|
---|
14 | ** written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Free Documentation License
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Free
|
---|
18 | ** Documentation License version 1.3 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file included in the packaging of this
|
---|
20 | ** file.
|
---|
21 | **
|
---|
22 | ** If you have questions regarding the use of this file, please contact
|
---|
23 | ** Nokia at [email protected].
|
---|
24 | ** $QT_END_LICENSE$
|
---|
25 | **
|
---|
26 | ****************************************************************************/
|
---|
27 |
|
---|
28 | /*!
|
---|
29 | \group statemachine
|
---|
30 | \title State Machine Classes
|
---|
31 | */
|
---|
32 |
|
---|
33 | /*!
|
---|
34 | \page statemachine-api.html
|
---|
35 | \title The State Machine Framework
|
---|
36 | \brief An overview of the State Machine framework for constructing and executing state graphs.
|
---|
37 |
|
---|
38 | \ingroup frameworks-technologies
|
---|
39 |
|
---|
40 | \tableofcontents
|
---|
41 |
|
---|
42 | The State Machine framework provides classes for creating and executing
|
---|
43 | state graphs. The concepts and notation are based on those from Harel's
|
---|
44 | \l{Statecharts: A visual formalism for complex systems}{Statecharts}, which
|
---|
45 | is also the basis of UML state diagrams. The semantics of state machine
|
---|
46 | execution are based on \l{State Chart XML: State Machine Notation for
|
---|
47 | Control Abstraction}{State Chart XML (SCXML)}.
|
---|
48 |
|
---|
49 | Statecharts provide a graphical way of modeling how a system reacts to
|
---|
50 | stimuli. This is done by defining the possible \e states that the system can
|
---|
51 | be in, and how the system can move from one state to another (\e transitions
|
---|
52 | between states). A key characteristic of event-driven systems (such as Qt
|
---|
53 | applications) is that behavior often depends not only on the last or current
|
---|
54 | event, but also the events that preceded it. With statecharts, this
|
---|
55 | information is easy to express.
|
---|
56 |
|
---|
57 | The State Machine framework provides an API and execution model that can be
|
---|
58 | used to effectively embed the elements and semantics of statecharts in Qt
|
---|
59 | applications. The framework integrates tightly with Qt's meta-object system;
|
---|
60 | for example, transitions between states can be triggered by signals, and
|
---|
61 | states can be configured to set properties and invoke methods on QObjects.
|
---|
62 | Qt's event system is used to drive the state machines.
|
---|
63 |
|
---|
64 | The state graph in the State Machine framework is hierarchical. States can be nested inside of
|
---|
65 | other states, and the current configuration of the state machine consists of the set of states
|
---|
66 | which are currently active. All the states in a valid configuration of the state machine will
|
---|
67 | have a common ancestor.
|
---|
68 |
|
---|
69 | \section1 Classes in the State Machine Framework
|
---|
70 |
|
---|
71 | These classes are provided by qt for creating event-driven state machines.
|
---|
72 |
|
---|
73 | \annotatedlist statemachine
|
---|
74 |
|
---|
75 | \section1 A Simple State Machine
|
---|
76 |
|
---|
77 | To demonstrate the core functionality of the State Machine API, let's look
|
---|
78 | at a small example: A state machine with three states, \c s1, \c s2 and \c
|
---|
79 | s3. The state machine is controlled by a single QPushButton; when the button
|
---|
80 | is clicked, the machine transitions to another state. Initially, the state
|
---|
81 | machine is in state \c s1. The statechart for this machine is as follows:
|
---|
82 |
|
---|
83 | \img statemachine-button.png
|
---|
84 | \omit
|
---|
85 | \caption This is a caption
|
---|
86 | \endomit
|
---|
87 |
|
---|
88 | The following snippet shows the code needed to create such a state machine.
|
---|
89 | First, we create the state machine and states:
|
---|
90 |
|
---|
91 | \snippet doc/src/snippets/statemachine/main.cpp 0
|
---|
92 |
|
---|
93 | Then, we create the transitions by using the QState::addTransition()
|
---|
94 | function:
|
---|
95 |
|
---|
96 | \snippet doc/src/snippets/statemachine/main.cpp 1
|
---|
97 |
|
---|
98 | Next, we add the states to the machine and set the machine's initial state:
|
---|
99 |
|
---|
100 | \snippet doc/src/snippets/statemachine/main.cpp 2
|
---|
101 |
|
---|
102 | Finally, we start the state machine:
|
---|
103 |
|
---|
104 | \snippet doc/src/snippets/statemachine/main.cpp 3
|
---|
105 |
|
---|
106 | The state machine executes asynchronously, i.e. it becomes part of your
|
---|
107 | application's event loop.
|
---|
108 |
|
---|
109 | \section1 Doing Useful Work on State Entry and Exit
|
---|
110 |
|
---|
111 | The above state machine merely transitions from one state to another, it
|
---|
112 | doesn't perform any operations. The QState::assignProperty() function can be
|
---|
113 | used to have a state set a property of a QObject when the state is
|
---|
114 | entered. In the following snippet, the value that should be assigned to a
|
---|
115 | QLabel's text property is specified for each state:
|
---|
116 |
|
---|
117 | \snippet doc/src/snippets/statemachine/main.cpp 4
|
---|
118 |
|
---|
119 | When any of the states is entered, the label's text will be changed
|
---|
120 | accordingly.
|
---|
121 |
|
---|
122 | The QState::entered() signal is emitted when the state is entered, and the
|
---|
123 | QState::exited() signal is emitted when the state is exited. In the
|
---|
124 | following snippet, the button's showMaximized() slot will be called when
|
---|
125 | state \c s3 is entered, and the button's showMinimized() slot will be called
|
---|
126 | when \c s3 is exited:
|
---|
127 |
|
---|
128 | \snippet doc/src/snippets/statemachine/main.cpp 5
|
---|
129 |
|
---|
130 | Custom states can reimplement QAbstractState::onEntry() and
|
---|
131 | QAbstractState::onExit().
|
---|
132 |
|
---|
133 | \section1 State Machines That Finish
|
---|
134 |
|
---|
135 | The state machine defined in the previous section never finishes. In order
|
---|
136 | for a state machine to be able to finish, it needs to have a top-level \e
|
---|
137 | final state (QFinalState object). When the state machine enters a top-level
|
---|
138 | final state, the machine will emit the QStateMachine::finished() signal and
|
---|
139 | halt.
|
---|
140 |
|
---|
141 | All you need to do to introduce a final state in the graph is create a
|
---|
142 | QFinalState object and use it as the target of one or more transitions.
|
---|
143 |
|
---|
144 | \section1 Sharing Transitions By Grouping States
|
---|
145 |
|
---|
146 | Assume we wanted the user to be able to quit the application at any time by
|
---|
147 | clicking a Quit button. In order to achieve this, we need to create a final
|
---|
148 | state and make it the target of a transition associated with the Quit
|
---|
149 | button's clicked() signal. We could add a transition from each of \c s1, \c
|
---|
150 | s2 and \c s3; however, this seems redundant, and one would also have to
|
---|
151 | remember to add such a transition from every new state that is added in the
|
---|
152 | future.
|
---|
153 |
|
---|
154 | We can achieve the same behavior (namely that clicking the Quit button quits
|
---|
155 | the state machine, regardless of which state the state machine is in) by
|
---|
156 | grouping states \c s1, \c s2 and \c s3. This is done by creating a new
|
---|
157 | top-level state and making the three original states children of the new
|
---|
158 | state. The following diagram shows the new state machine.
|
---|
159 |
|
---|
160 | \img statemachine-button-nested.png
|
---|
161 | \omit
|
---|
162 | \caption This is a caption
|
---|
163 | \endomit
|
---|
164 |
|
---|
165 | The three original states have been renamed \c s11, \c s12 and \c s13 to
|
---|
166 | reflect that they are now children of the new top-level state, \c s1. Child
|
---|
167 | states implicitly inherit the transitions of their parent state. This means
|
---|
168 | it is now sufficient to add a single transition from \c s1 to the final
|
---|
169 | state \c s2. New states added to \c s1 will also automatically inherit this
|
---|
170 | transition.
|
---|
171 |
|
---|
172 | All that's needed to group states is to specify the proper parent when the
|
---|
173 | state is created. You also need to specify which of the child states is the
|
---|
174 | initial one (i.e. which child state the state machine should enter when the
|
---|
175 | parent state is the target of a transition).
|
---|
176 |
|
---|
177 | \snippet doc/src/snippets/statemachine/main2.cpp 0
|
---|
178 |
|
---|
179 | \snippet doc/src/snippets/statemachine/main2.cpp 1
|
---|
180 |
|
---|
181 | In this case we want the application to quit when the state machine is
|
---|
182 | finished, so the machine's finished() signal is connected to the
|
---|
183 | application's quit() slot.
|
---|
184 |
|
---|
185 | A child state can override an inherited transition. For example, the
|
---|
186 | following code adds a transition that effectively causes the Quit button to
|
---|
187 | be ignored when the state machine is in state \c s12.
|
---|
188 |
|
---|
189 | \snippet doc/src/snippets/statemachine/main2.cpp 2
|
---|
190 |
|
---|
191 | A transition can have any state as its target, i.e. the target state does
|
---|
192 | not have to be on the same level in the state hierarchy as the source state.
|
---|
193 |
|
---|
194 | \section1 Using History States to Save and Restore the Current State
|
---|
195 |
|
---|
196 | Imagine that we wanted to add an "interrupt" mechanism to the example
|
---|
197 | discussed in the previous section; the user should be able to click a button
|
---|
198 | to have the state machine perform some non-related task, after which the
|
---|
199 | state machine should resume whatever it was doing before (i.e. return to the
|
---|
200 | old state, which is one of \c s11, \c s12 and \c s13 in this case).
|
---|
201 |
|
---|
202 | Such behavior can easily be modeled using \e{history states}. A history
|
---|
203 | state (QHistoryState object) is a pseudo-state that represents the child
|
---|
204 | state that the parent state was in the last time the parent state was
|
---|
205 | exited.
|
---|
206 |
|
---|
207 | A history state is created as a child of the state for which we wish to
|
---|
208 | record the current child state; when the state machine detects the presence
|
---|
209 | of such a state at runtime, it automatically records the current (real)
|
---|
210 | child state when the parent state is exited. A transition to the history
|
---|
211 | state is in fact a transition to the child state that the state machine had
|
---|
212 | previously saved; the state machine automatically "forwards" the transition
|
---|
213 | to the real child state.
|
---|
214 |
|
---|
215 | The following diagram shows the state machine after the interrupt mechanism
|
---|
216 | has been added.
|
---|
217 |
|
---|
218 | \img statemachine-button-history.png
|
---|
219 | \omit
|
---|
220 | \caption This is a caption
|
---|
221 | \endomit
|
---|
222 |
|
---|
223 | The following code shows how it can be implemented; in this example we
|
---|
224 | simply display a message box when \c s3 is entered, then immediately return
|
---|
225 | to the previous child state of \c s1 via the history state.
|
---|
226 |
|
---|
227 | \snippet doc/src/snippets/statemachine/main2.cpp 3
|
---|
228 |
|
---|
229 | \section1 Using Parallel States to Avoid a Combinatorial Explosion of States
|
---|
230 |
|
---|
231 | Assume that you wanted to model a set of mutually exclusive properties of a
|
---|
232 | car in a single state machine. Let's say the properties we are interested in
|
---|
233 | are Clean vs Dirty, and Moving vs Not moving. It would take four mutually
|
---|
234 | exclusive states and eight transitions to be able to represent and freely
|
---|
235 | move between all possible combinations.
|
---|
236 |
|
---|
237 | \img statemachine-nonparallel.png
|
---|
238 | \omit
|
---|
239 | \caption This is a caption
|
---|
240 | \endomit
|
---|
241 |
|
---|
242 | If we added a third property (say, Red vs Blue), the total number of states
|
---|
243 | would double, to eight; and if we added a fourth property (say, Enclosed vs
|
---|
244 | Convertible), the total number of states would double again, to 16.
|
---|
245 |
|
---|
246 | Using parallel states, the total number of states and transitions grows
|
---|
247 | linearly as we add more properties, instead of exponentially. Furthermore,
|
---|
248 | states can be added to or removed from the parallel state without affecting
|
---|
249 | any of their sibling states.
|
---|
250 |
|
---|
251 | \img statemachine-parallel.png
|
---|
252 | \omit
|
---|
253 | \caption This is a caption
|
---|
254 | \endomit
|
---|
255 |
|
---|
256 | To create a parallel state group, pass QState::ParallelStates to the QState
|
---|
257 | constructor.
|
---|
258 |
|
---|
259 | \snippet doc/src/snippets/statemachine/main3.cpp 0
|
---|
260 |
|
---|
261 | When a parallel state group is entered, all its child states will be
|
---|
262 | simultaneously entered. Transitions within the individual child states
|
---|
263 | operate normally. However, any of the child states may take a transition which exits the parent
|
---|
264 | state. When this happens, the parent state and all of its child states are exited.
|
---|
265 |
|
---|
266 | The parallelism in the State Machine framework follows an interleaved semantics. All parallel
|
---|
267 | operations will be executed in a single, atomic step of the event processing, so no event can
|
---|
268 | interrupt the parallel operations. However, events will still be processed sequentially, since
|
---|
269 | the machine itself is single threaded. As an example: Consider the situation where there are two
|
---|
270 | transitions that exit the same parallel state group, and their conditions become true
|
---|
271 | simultaneously. In this case, the event that is processed last of the two will not have any
|
---|
272 | effect, since the first event will already have caused the machine to exit from the parallel
|
---|
273 | state.
|
---|
274 |
|
---|
275 | \section1 Detecting that a Composite State has Finished
|
---|
276 |
|
---|
277 | A child state can be final (a QFinalState object); when a final child state
|
---|
278 | is entered, the parent state emits the QState::finished() signal. The
|
---|
279 | following diagram shows a composite state \c s1 which does some processing
|
---|
280 | before entering a final state:
|
---|
281 |
|
---|
282 | \img statemachine-finished.png
|
---|
283 | \omit
|
---|
284 | \caption This is a caption
|
---|
285 | \endomit
|
---|
286 |
|
---|
287 | When \c s1 's final state is entered, \c s1 will automatically emit
|
---|
288 | finished(). We use a signal transition to cause this event to trigger a
|
---|
289 | state change:
|
---|
290 |
|
---|
291 | \snippet doc/src/snippets/statemachine/main3.cpp 1
|
---|
292 |
|
---|
293 | Using final states in composite states is useful when you want to hide the
|
---|
294 | internal details of a composite state; i.e. the only thing the outside world
|
---|
295 | should be able to do is enter the state, and get a notification when the
|
---|
296 | state has completed its work. This is a very powerful abstraction and
|
---|
297 | encapsulation mechanism when building complex (deeply nested) state
|
---|
298 | machines. (In the above example, you could of course create a transition
|
---|
299 | directly from \c s1 's \c done state rather than relying on \c s1 's
|
---|
300 | finished() signal, but with the consequence that implementation details of
|
---|
301 | \c s1 are exposed and depended on).
|
---|
302 |
|
---|
303 | For parallel state groups, the QState::finished() signal is emitted when \e
|
---|
304 | all the child states have entered final states.
|
---|
305 |
|
---|
306 | \section1 Targetless Transitions
|
---|
307 |
|
---|
308 | A transition need not have a target state. A transition without a target can
|
---|
309 | be triggered the same way as any other transition; the difference is that
|
---|
310 | when a targetless transition is triggered, it doesn't cause any state
|
---|
311 | changes. This allows you to react to a signal or event when your machine is
|
---|
|
---|