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
|
---|
312 | in a certain state, without having to leave that state. Example:
|
---|
313 |
|
---|
314 | \code
|
---|
315 | QStateMachine machine;
|
---|
316 | QState *s1 = new QState(&machine);
|
---|
317 |
|
---|
318 | QPushButton button;
|
---|
319 | QSignalTransition *trans = new QSignalTransition(&button, SIGNAL(clicked()));
|
---|
320 | s1->addTransition(trans);
|
---|
321 |
|
---|
322 | QMessageBox msgBox;
|
---|
323 | msgBox.setText("The button was clicked; carry on.");
|
---|
324 | QObject::connect(trans, SIGNAL(triggered()), &msgBox, SLOT(exec()));
|
---|
325 |
|
---|
326 | machine.setInitialState(s1);
|
---|
327 | \endcode
|
---|
328 |
|
---|
329 | The message box will be displayed each time the button is clicked, but the
|
---|
330 | state machine will remain in its current state (s1). If the target state
|
---|
331 | were explicitly set to s1, however, s1 would be exited and re-entered each
|
---|
332 | time (e.g. the QAbstractState::entered() and QAbstractState::exited()
|
---|
333 | signals would be emitted).
|
---|
334 |
|
---|
335 | \section1 Events, Transitions and Guards
|
---|
336 |
|
---|
337 | A QStateMachine runs its own event loop. For signal transitions
|
---|
338 | (QSignalTransition objects), QStateMachine automatically posts a
|
---|
339 | QStateMachine::SignalEvent to itself when it intercepts the corresponding
|
---|
340 | signal; similarly, for QObject event transitions (QEventTransition objects)
|
---|
341 | a QStateMachine::WrappedEvent is posted.
|
---|
342 |
|
---|
343 | You can post your own events to the state machine using
|
---|
344 | QStateMachine::postEvent().
|
---|
345 |
|
---|
346 | When posting a custom event to the state machine, you typically also have
|
---|
347 | one or more custom transitions that can be triggered from events of that
|
---|
348 | type. To create such a transition, you subclass QAbstractTransition and
|
---|
349 | reimplement QAbstractTransition::eventTest(), where you check if an event
|
---|
350 | matches your event type (and optionally other criteria, e.g. attributes of
|
---|
351 | the event object).
|
---|
352 |
|
---|
353 | Here we define our own custom event type, \c StringEvent, for posting
|
---|
354 | strings to the state machine:
|
---|
355 |
|
---|
356 | \snippet doc/src/snippets/statemachine/main4.cpp 0
|
---|
357 |
|
---|
358 | Next, we define a transition that only triggers when the event's string
|
---|
359 | matches a particular string (a \e guarded transition):
|
---|
360 |
|
---|
361 | \snippet doc/src/snippets/statemachine/main4.cpp 1
|
---|
362 |
|
---|
363 | In the eventTest() reimplementation, we first check if the event type is the
|
---|
364 | desired one; if so, we cast the event to a StringEvent and perform the
|
---|
365 | string comparison.
|
---|
366 |
|
---|
367 | The following is a statechart that uses the custom event and transition:
|
---|
368 |
|
---|
369 | \img statemachine-customevents.png
|
---|
370 | \omit
|
---|
371 | \caption This is a caption
|
---|
372 | \endomit
|
---|
373 |
|
---|
374 | Here's what the implementation of the statechart looks like:
|
---|
375 |
|
---|
376 | \snippet doc/src/snippets/statemachine/main4.cpp 2
|
---|
377 |
|
---|
378 | Once the machine is started, we can post events to it.
|
---|
379 |
|
---|
380 | \snippet doc/src/snippets/statemachine/main4.cpp 3
|
---|
381 |
|
---|
382 | An event that is not handled by any relevant transition will be silently
|
---|
383 | consumed by the state machine. It can be useful to group states and provide
|
---|
384 | a default handling of such events; for example, as illustrated in the
|
---|
385 | following statechart:
|
---|
386 |
|
---|
387 | \img statemachine-customevents2.png
|
---|
388 | \omit
|
---|
389 | \caption This is a caption
|
---|
390 | \endomit
|
---|
391 |
|
---|
392 | For deeply nested statecharts, you can add such "fallback" transitions at
|
---|
393 | the level of granularity that's most appropriate.
|
---|
394 |
|
---|
395 | \section1 Using Restore Policy To Automatically Restore Properties
|
---|
396 |
|
---|
397 | In some state machines it can be useful to focus the attention on assigning properties in states,
|
---|
398 | not on restoring them when the state is no longer active. If you know that a property should
|
---|
399 | always be restored to its initial value when the machine enters a state that does not explicitly
|
---|
400 | give the property a value, you can set the global restore policy to
|
---|
401 | QStateMachine::RestoreProperties.
|
---|
402 |
|
---|
403 | \code
|
---|
404 | QStateMachine machine;
|
---|
405 | machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
|
---|
406 | \endcode
|
---|
407 |
|
---|
408 | When this restore policy is set, the machine will automatically restore all properties. If it
|
---|
409 | enters a state where a given property is not set, it will first search the hierarchy of ancestors
|
---|
410 | to see if the property is defined there. If it is, the property will be restored to the value
|
---|
411 | defined by the closest ancestor. If not, it will be restored to its initial value (i.e. the
|
---|
412 | value of the property before any property assignments in states were executed.)
|
---|
413 |
|
---|
414 | Take the following code:
|
---|
415 |
|
---|
416 | \snippet doc/src/snippets/statemachine/main5.cpp 0
|
---|
417 |
|
---|
418 | Lets say the property \c fooBar is 0.0 when the machine starts. When the machine is in state
|
---|
419 | \c s1, the property will be 1.0, since the state explicitly assigns this value to it. When the
|
---|
420 | machine is in state \c s2, no value is explicitly defined for the property, so it will implicitly
|
---|
421 | be restored to 0.0.
|
---|
422 |
|
---|
423 | If we are using nested states, the parent defines a value for the property which is inherited by
|
---|
424 | all descendants that do not explicitly assign a value to the property.
|
---|
425 |
|
---|
426 | \snippet doc/src/snippets/statemachine/main5.cpp 2
|
---|
427 |
|
---|
428 | Here \c s1 has two children: \c s2 and \c s3. When \c s2 is entered, the property \c fooBar
|
---|
429 | will have the value 2.0, since this is explicitly defined for the state. When the machine is in
|
---|
430 | state \c s3, no value is defined for the state, but \c s1 defines the property to be 1.0, so this
|
---|
431 | is the value that will be assigned to \c fooBar.
|
---|
432 |
|
---|
433 | \section1 Animating Property Assignments
|
---|
434 |
|
---|
435 | The State Machine API connects with the Animation API in Qt to allow automatically animating
|
---|
436 | properties as they are assigned in states.
|
---|
437 |
|
---|
438 | Say we have the following code:
|
---|
439 |
|
---|
440 | \snippet doc/src/snippets/statemachine/main5.cpp 3
|
---|
441 |
|
---|
442 | Here we define two states of a user interface. In \c s1 the \c button is small, and in \c s2
|
---|
443 | it is bigger. If we click the button to transition from \c s1 to \c s2, the geometry of the button
|
---|
444 | will be set immediately when a given state has been entered. If we want the transition to be
|
---|
445 | smooth, however, all we need to do is make a QPropertyAnimation and add this to the transition
|
---|
446 | object.
|
---|
447 |
|
---|
448 | \snippet doc/src/snippets/statemachine/main5.cpp 4
|
---|
449 |
|
---|
450 | Adding an animation for the property in question means that the property assignment will no
|
---|
451 | longer take immediate effect when the state has been entered. Instead, the animation will start
|
---|
452 | playing when the state has been entered and smoothly animate the property assignment. Since we
|
---|
453 | do not set the start value or end value of the animation, these will be set implicitly. The
|
---|
454 | start value of the animation will be the property's current value when the animation starts, and
|
---|
455 | the end value will be set based on the property assignments defined for the state.
|
---|
456 |
|
---|
457 | If the global restore policy of the state machine is set to QStateMachine::RestoreProperties,
|
---|
458 | it is possible to also add animations for the property restorations.
|
---|
459 |
|
---|
460 | \section1 Detecting That All Properties Have Been Set In A State
|
---|
461 |
|
---|
462 | When animations are used to assign properties, a state no longer defines the exact values that a
|
---|
463 | property will have when the machine is in the given state. While the animation is running, the
|
---|
464 | property can potentially have any value, depending on the animation.
|
---|
465 |
|
---|
466 | In some cases, it can be useful to be able to detect when the property has actually been assigned
|
---|
467 | the value defined by a state.
|
---|
468 |
|
---|
469 | Say we have the following code:
|
---|
470 |
|
---|
471 | \snippet doc/src/snippets/statemachine/main5.cpp 5
|
---|
472 |
|
---|
473 | When \c button is clicked, the machine will transition into state \c s2, which will set the
|
---|
474 | geometry of the button, and then pop up a message box to alert the user that the geometry has
|
---|
475 | been changed.
|
---|
476 |
|
---|
477 | In the normal case, where animations are not used, this will operate as expected. However, if
|
---|
478 | an animation for the \c geometry of \c button is set on the transition between \c s1 and \c s2,
|
---|
479 | the animation will be started when \c s2 is entered, but the \c geometry property will not
|
---|
480 | actually reach its defined value before the animation is finished running. In this case, the
|
---|
481 | message box will pop up before the geometry of the button has actually been set.
|
---|
482 |
|
---|
483 | To ensure that the message box does not pop up until the geometry actually reaches its final
|
---|
484 | value, we can use the state's propertiesAssigned() signal. The propertiesAssigned() signal will be
|
---|
485 | emitted when the property is assigned its final value, whether this is done immediately or
|
---|
486 | after the animation has finished playing.
|
---|
487 |
|
---|
488 | \snippet doc/src/snippets/statemachine/main5.cpp 6
|
---|
489 |
|
---|
490 | In this example, when \c button is clicked, the machine will enter \c s2. It will remain in state
|
---|
491 | \c s2 until the \c geometry property has been set to \c QRect(0, 0, 50, 50). Then it will
|
---|
492 | transition into \c s3. When \c s3 is entered, the message box will pop up. If the transition into
|
---|
493 | \c s2 has an animation for the \c geometry property, then the machine will stay in \c s2 until the
|
---|
494 | animation has finished playing. If there is no such animation, it will simply set the property and
|
---|
495 | immediately enter state \c s3.
|
---|
496 |
|
---|
497 | Either way, when the machine is in state \c s3, you are guaranteed that the property \c geometry
|
---|
498 | has been assigned the defined value.
|
---|
499 |
|
---|
500 | If the global restore policy is set to QStateMachine::RestoreProperties, the state will not emit
|
---|
501 | the propertiesAssigned() signal until these have been executed as well.
|
---|
502 |
|
---|
503 | \section1 What Happens If A State Is Exited Before The Animation Has Finished
|
---|
504 |
|
---|
505 | If a state has property assignments, and the transition into the state has animations for the
|
---|
506 | properties, the state can potentially be exited before the properties have been assigned to the
|
---|
507 | values defines by the state. This is true in particular when there are transitions out from the
|
---|
508 | state that do not depend on the propertiesAssigned signal, as described in the previous section.
|
---|
509 |
|
---|
510 | The State Machine API guarantees that a property assigned by the state machine either:
|
---|
511 | \list
|
---|
512 | \o Has a value explicitly assigned to the property.
|
---|
513 | \o Is currently being animated into a value explicitly assigned to the property.
|
---|
514 | \endlist
|
---|
515 |
|
---|
516 | When a state is exited prior to the animation finishing, the behavior of the state machine depends
|
---|
517 | on the target state of the transition. If the target state explicitly assigns a value to the
|
---|
518 | property, no additional action will be taken. The property will be assigned the value defined by
|
---|
519 | the target state.
|
---|
520 |
|
---|
521 | If the target state does not assign any value to the property, there are two
|
---|
522 | options: By default, the property will be assigned the value defined by the state it is leaving
|
---|
523 | (the value it would have been assigned if the animation had been permitted to finish playing). If
|
---|
524 | a global restore policy is set, however, this will take precedence, and the property will be
|
---|
525 | restored as usual.
|
---|
526 |
|
---|
527 | \section1 Default Animations
|
---|
528 |
|
---|
529 | As described earlier, you can add animations to transitions to make sure property assignments
|
---|
530 | in the target state are animated. If you want a specific animation to be used for a given property
|
---|
531 | regardless of which transition is taken, you can add it as a default animation to the state
|
---|
532 | machine. This is in particular useful when the properties assigned (or restored) by specific
|
---|
533 | states is not known when the machine is constructed.
|
---|
534 |
|
---|
535 | \code
|
---|
536 | QState *s1 = new QState();
|
---|
537 | QState *s2 = new QState();
|
---|
538 |
|
---|
539 | s2->assignProperty(object, "fooBar", 2.0);
|
---|
540 | s1->addTransition(s2);
|
---|
541 |
|
---|
542 | QStateMachine machine;
|
---|
543 | machine.setInitialState(s1);
|
---|
544 | machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));
|
---|
545 | \endcode
|
---|
546 |
|
---|
547 | When the machine is in state \c s2, the machine will play the default animation for the
|
---|
548 | property \c fooBar since this property is assigned by \c s2.
|
---|
549 |
|
---|
550 | Note that animations explicitly set on transitions will take precedence over any default
|
---|
551 | animation for the given property.
|
---|
552 | */
|
---|