0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
4 |
** All rights reserved.
|
|
5 |
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
6 |
**
|
|
7 |
** This file is part of the documentation of the Qt Toolkit.
|
|
8 |
**
|
|
9 |
** $QT_BEGIN_LICENSE:LGPL$
|
|
10 |
** No Commercial Usage
|
|
11 |
** This file contains pre-release code and may not be distributed.
|
|
12 |
** You may use this file in accordance with the terms and conditions
|
|
13 |
** contained in the Technology Preview License Agreement accompanying
|
|
14 |
** this package.
|
|
15 |
**
|
|
16 |
** GNU Lesser General Public License Usage
|
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
18 |
** General Public License version 2.1 as published by the Free Software
|
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
20 |
** packaging of this file. Please review the following information to
|
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
23 |
**
|
|
24 |
** In addition, as a special exception, Nokia gives you certain additional
|
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
27 |
**
|
|
28 |
** If you have questions regarding the use of this file, please contact
|
|
29 |
** Nokia at qt-info@nokia.com.
|
|
30 |
**
|
|
31 |
**
|
|
32 |
**
|
|
33 |
**
|
|
34 |
**
|
|
35 |
**
|
|
36 |
**
|
|
37 |
**
|
|
38 |
** $QT_END_LICENSE$
|
|
39 |
**
|
|
40 |
****************************************************************************/
|
|
41 |
|
|
42 |
/*!
|
|
43 |
\group statemachine
|
|
44 |
\title State Machine Classes
|
|
45 |
*/
|
|
46 |
|
|
47 |
/*!
|
|
48 |
\page statemachine-api.html
|
|
49 |
\title The State Machine Framework
|
|
50 |
\brief An overview of the State Machine framework for constructing and executing state graphs.
|
|
51 |
|
|
52 |
\ingroup frameworks-technologies
|
|
53 |
|
|
54 |
\tableofcontents
|
|
55 |
|
|
56 |
The State Machine framework provides classes for creating and executing
|
|
57 |
state graphs. The concepts and notation are based on those from Harel's
|
|
58 |
\l{Statecharts: A visual formalism for complex systems}{Statecharts}, which
|
|
59 |
is also the basis of UML state diagrams. The semantics of state machine
|
|
60 |
execution are based on \l{State Chart XML: State Machine Notation for
|
|
61 |
Control Abstraction}{State Chart XML (SCXML)}.
|
|
62 |
|
|
63 |
Statecharts provide a graphical way of modeling how a system reacts to
|
|
64 |
stimuli. This is done by defining the possible \e states that the system can
|
|
65 |
be in, and how the system can move from one state to another (\e transitions
|
|
66 |
between states). A key characteristic of event-driven systems (such as Qt
|
|
67 |
applications) is that behavior often depends not only on the last or current
|
|
68 |
event, but also the events that preceded it. With statecharts, this
|
|
69 |
information is easy to express.
|
|
70 |
|
|
71 |
The State Machine framework provides an API and execution model that can be
|
|
72 |
used to effectively embed the elements and semantics of statecharts in Qt
|
|
73 |
applications. The framework integrates tightly with Qt's meta-object system;
|
|
74 |
for example, transitions between states can be triggered by signals, and
|
|
75 |
states can be configured to set properties and invoke methods on QObjects.
|
|
76 |
Qt's event system is used to drive the state machines.
|
|
77 |
|
|
78 |
\section1 Classes in the State Machine Framework
|
|
79 |
|
|
80 |
These classes are provided by qt for creating event-driven state machines.
|
|
81 |
|
|
82 |
\annotatedlist statemachine
|
|
83 |
|
|
84 |
\section1 A Simple State Machine
|
|
85 |
|
|
86 |
To demonstrate the core functionality of the State Machine API, let's look
|
|
87 |
at a small example: A state machine with three states, \c s1, \c s2 and \c
|
|
88 |
s3. The state machine is controlled by a single QPushButton; when the button
|
|
89 |
is clicked, the machine transitions to another state. Initially, the state
|
|
90 |
machine is in state \c s1. The statechart for this machine is as follows:
|
|
91 |
|
|
92 |
\img statemachine-button.png
|
|
93 |
\omit
|
|
94 |
\caption This is a caption
|
|
95 |
\endomit
|
|
96 |
|
|
97 |
The following snippet shows the code needed to create such a state machine.
|
|
98 |
First, we create the state machine and states:
|
|
99 |
|
|
100 |
\snippet doc/src/snippets/statemachine/main.cpp 0
|
|
101 |
|
|
102 |
Then, we create the transitions by using the QState::addTransition()
|
|
103 |
function:
|
|
104 |
|
|
105 |
\snippet doc/src/snippets/statemachine/main.cpp 1
|
|
106 |
|
|
107 |
Next, we add the states to the machine and set the machine's initial state:
|
|
108 |
|
|
109 |
\snippet doc/src/snippets/statemachine/main.cpp 2
|
|
110 |
|
|
111 |
Finally, we start the state machine:
|
|
112 |
|
|
113 |
\snippet doc/src/snippets/statemachine/main.cpp 3
|
|
114 |
|
|
115 |
The state machine executes asynchronously, i.e. it becomes part of your
|
|
116 |
application's event loop.
|
|
117 |
|
|
118 |
\section1 Doing Useful Work on State Entry and Exit
|
|
119 |
|
|
120 |
The above state machine merely transitions from one state to another, it
|
|
121 |
doesn't perform any operations. The QState::assignProperty() function can be
|
|
122 |
used to have a state set a property of a QObject when the state is
|
|
123 |
entered. In the following snippet, the value that should be assigned to a
|
|
124 |
QLabel's text property is specified for each state:
|
|
125 |
|
|
126 |
\snippet doc/src/snippets/statemachine/main.cpp 4
|
|
127 |
|
|
128 |
When any of the states is entered, the label's text will be changed
|
|
129 |
accordingly.
|
|
130 |
|
|
131 |
The QState::entered() signal is emitted when the state is entered, and the
|
|
132 |
QState::exited() signal is emitted when the state is exited. In the
|
|
133 |
following snippet, the button's showMaximized() slot will be called when
|
|
134 |
state \c s3 is entered, and the button's showMinimized() slot will be called
|
|
135 |
when \c s3 is exited:
|
|
136 |
|
|
137 |
\snippet doc/src/snippets/statemachine/main.cpp 5
|
|
138 |
|
|
139 |
Custom states can reimplement QAbstractState::onEntry() and
|
|
140 |
QAbstractState::onExit().
|
|
141 |
|
|
142 |
\section1 State Machines That Finish
|
|
143 |
|
|
144 |
The state machine defined in the previous section never finishes. In order
|
|
145 |
for a state machine to be able to finish, it needs to have a top-level \e
|
|
146 |
final state (QFinalState object). When the state machine enters a top-level
|
|
147 |
final state, the machine will emit the QStateMachine::finished() signal and
|
|
148 |
halt.
|
|
149 |
|
|
150 |
All you need to do to introduce a final state in the graph is create a
|
|
151 |
QFinalState object and use it as the target of one or more transitions.
|
|
152 |
|
|
153 |
\section1 Sharing Transitions By Grouping States
|
|
154 |
|
|
155 |
Assume we wanted the user to be able to quit the application at any time by
|
|
156 |
clicking a Quit button. In order to achieve this, we need to create a final
|
|
157 |
state and make it the target of a transition associated with the Quit
|
|
158 |
button's clicked() signal. We could add a transition from each of \c s1, \c
|
|
159 |
s2 and \c s3; however, this seems redundant, and one would also have to
|
|
160 |
remember to add such a transition from every new state that is added in the
|
|
161 |
future.
|
|
162 |
|
|
163 |
We can achieve the same behavior (namely that clicking the Quit button quits
|
|
164 |
the state machine, regardless of which state the state machine is in) by
|
|
165 |
grouping states \c s1, \c s2 and \c s3. This is done by creating a new
|
|
166 |
top-level state and making the three original states children of the new
|
|
167 |
state. The following diagram shows the new state machine.
|
|
168 |
|
|
169 |
\img statemachine-button-nested.png
|
|
170 |
\omit
|
|
171 |
\caption This is a caption
|
|
172 |
\endomit
|
|
173 |
|
|
174 |
The three original states have been renamed \c s11, \c s12 and \c s13 to
|
|
175 |
reflect that they are now children of the new top-level state, \c s1. Child
|
|
176 |
states implicitly inherit the transitions of their parent state. This means
|
|
177 |
it is now sufficient to add a single transition from \c s1 to the final
|
|
178 |
state \c s2. New states added to \c s1 will also automatically inherit this
|
|
179 |
transition.
|
|
180 |
|
|
181 |
All that's needed to group states is to specify the proper parent when the
|
|
182 |
state is created. You also need to specify which of the child states is the
|
|
183 |
initial one (i.e. which child state the state machine should enter when the
|
|
184 |
parent state is the target of a transition).
|
|
185 |
|
|
186 |
\snippet doc/src/snippets/statemachine/main2.cpp 0
|
|
187 |
|
|
188 |
\snippet doc/src/snippets/statemachine/main2.cpp 1
|
|
189 |
|
|
190 |
In this case we want the application to quit when the state machine is
|
|
191 |
finished, so the machine's finished() signal is connected to the
|
|
192 |
application's quit() slot.
|
|
193 |
|
|
194 |
A child state can override an inherited transition. For example, the
|
|
195 |
following code adds a transition that effectively causes the Quit button to
|
|
196 |
be ignored when the state machine is in state \c s12.
|
|
197 |
|
|
198 |
\snippet doc/src/snippets/statemachine/main2.cpp 2
|
|
199 |
|
|
200 |
A transition can have any state as its target, i.e. the target state does
|
|
201 |
not have to be on the same level in the state hierarchy as the source state.
|
|
202 |
|
|
203 |
\section1 Using History States to Save and Restore the Current State
|
|
204 |
|
|
205 |
Imagine that we wanted to add an "interrupt" mechanism to the example
|
|
206 |
discussed in the previous section; the user should be able to click a button
|
|
207 |
to have the state machine perform some non-related task, after which the
|
|
208 |
state machine should resume whatever it was doing before (i.e. return to the
|
|
209 |
old state, which is one of \c s11, \c s12 and \c s13 in this case).
|
|
210 |
|
|
211 |
Such behavior can easily be modeled using \e{history states}. A history
|
|
212 |
state (QHistoryState object) is a pseudo-state that represents the child
|
|
213 |
state that the parent state was in the last time the parent state was
|
|
214 |
exited.
|
|
215 |
|
|
216 |
A history state is created as a child of the state for which we wish to
|
|
217 |
record the current child state; when the state machine detects the presence
|
|
218 |
of such a state at runtime, it automatically records the current (real)
|
|
219 |
child state when the parent state is exited. A transition to the history
|
|
220 |
state is in fact a transition to the child state that the state machine had
|
|
221 |
previously saved; the state machine automatically "forwards" the transition
|
|
222 |
to the real child state.
|
|
223 |
|
|
224 |
The following diagram shows the state machine after the interrupt mechanism
|
|
225 |
has been added.
|
|
226 |
|
|
227 |
\img statemachine-button-history.png
|
|
228 |
\omit
|
|
229 |
\caption This is a caption
|
|
230 |
\endomit
|
|
231 |
|
|
232 |
The following code shows how it can be implemented; in this example we
|
|
233 |
simply display a message box when \c s3 is entered, then immediately return
|
|
234 |
to the previous child state of \c s1 via the history state.
|
|
235 |
|
|
236 |
\snippet doc/src/snippets/statemachine/main2.cpp 3
|
|
237 |
|
|
238 |
\section1 Using Parallel States to Avoid a Combinatorial Explosion of States
|
|
239 |
|
|
240 |
Assume that you wanted to model a set of mutually exclusive properties of a
|
|
241 |
car in a single state machine. Let's say the properties we are interested in
|
|
242 |
are Clean vs Dirty, and Moving vs Not moving. It would take four mutually
|
|
243 |
exclusive states and eight transitions to be able to represent and freely
|
|
244 |
move between all possible combinations.
|
|
245 |
|
|
246 |
\img statemachine-nonparallel.png
|
|
247 |
\omit
|
|
248 |
\caption This is a caption
|
|
249 |
\endomit
|
|
250 |
|
|
251 |
If we added a third property (say, Red vs Blue), the total number of states
|
|
252 |
would double, to eight; and if we added a fourth property (say, Enclosed vs
|
|
253 |
Convertible), the total number of states would double again, to 16.
|
|
254 |
|
|
255 |
Using parallel states, the total number of states and transitions grows
|
|
256 |
linearly as we add more properties, instead of exponentially. Furthermore,
|
|
257 |
states can be added to or removed from the parallel state without affecting
|
|
258 |
any of their sibling states.
|
|
259 |
|
|
260 |
\img statemachine-parallel.png
|
|
261 |
\omit
|
|
262 |
\caption This is a caption
|
|
263 |
\endomit
|
|
264 |
|
|
265 |
To create a parallel state group, pass QState::ParallelStates to the QState
|
|
266 |
constructor.
|
|
267 |
|
|
268 |
\snippet doc/src/snippets/statemachine/main3.cpp 0
|
|
269 |
|
|
270 |
When a parallel state group is entered, all its child states will be
|
|
271 |
simultaneously entered. Transitions within the individual child states
|
|
272 |
operate normally. However, any of the child states may take a transition
|
|
273 |
outside the parent state. When this happens, the parent state and all of its
|
|
274 |
child states are exited.
|
|
275 |
|
|
276 |
\section1 Detecting that a Composite State has Finished
|
|
277 |
|
|
278 |
A child state can be final (a QFinalState object); when a final child state
|
|
279 |
is entered, the parent state emits the QState::finished() signal. The
|
|
280 |
following diagram shows a composite state \c s1 which does some processing
|
|
281 |
before entering a final state:
|
|
282 |
|
|
283 |
\img statemachine-finished.png
|
|
284 |
\omit
|
|
285 |
\caption This is a caption
|
|
286 |
\endomit
|
|
287 |
|
|
288 |
When \c s1 's final state is entered, \c s1 will automatically emit
|
|
289 |
finished(). We use a signal transition to cause this event to trigger a
|
|
290 |
state change:
|
|
291 |
|
|
292 |
\snippet doc/src/snippets/statemachine/main3.cpp 1
|
|
293 |
|
|
294 |
Using final states in composite states is useful when you want to hide the
|
|
295 |
internal details of a composite state; i.e. the only thing the outside world
|
|
296 |
should be able to do is enter the state, and get a notification when the
|
|
297 |
state has completed its work. This is a very powerful abstraction and
|
|
298 |
encapsulation mechanism when building complex (deeply nested) state
|
|
299 |
machines. (In the above example, you could of course create a transition
|
|
300 |
directly from \c s1 's \c done state rather than relying on \c s1 's
|
|
301 |
finished() signal, but with the consequence that implementation details of
|
|
302 |
\c s1 are exposed and depended on).
|
|
303 |
|
|
304 |
For parallel state groups, the QState::finished() signal is emitted when \e
|
|
305 |
all the child states have entered final states.
|
|
306 |
|
|
307 |
\section1 Targetless Transitions
|
|
308 |
|
|
309 |
A transition need not have a target state. A transition without a target can
|
|
310 |
be triggered the same way as any other transition; the difference is that
|
|
311 |
when a targetless transition is triggered, it doesn't cause any state
|
|
312 |
changes. This allows you to react to a signal or event when your machine is
|
|
313 |
in a certain state, without having to leave that state. Example:
|
|
314 |
|
|
315 |
\code
|
|
316 |
QStateMachine machine;
|
|
317 |
QState *s1 = new QState(&machine);
|
|
318 |
|
|
319 |
QPushButton button;
|
|
320 |
QSignalTransition *trans = new QSignalTransition(&button, SIGNAL(clicked()));
|
|
321 |
s1->addTransition(trans);
|
|
322 |
|
|
323 |
QMessageBox msgBox;
|
|
324 |
msgBox.setText("The button was clicked; carry on.");
|
|
325 |
QObject::connect(trans, SIGNAL(triggered()), &msgBox, SLOT(exec()));
|
|
326 |
|
|
327 |
machine.setInitialState(s1);
|
|
328 |
\endcode
|
|
329 |
|
|
330 |
The message box will be displayed each time the button is clicked, but the
|
|
331 |
state machine will remain in its current state (s1). If the target state
|
|
332 |
were explicitly set to s1, however, s1 would be exited and re-entered each
|
|
333 |
time (e.g. the QAbstractState::entered() and QAbstractState::exited()
|
|
334 |
signals would be emitted).
|
|
335 |
|
|
336 |
\section1 Events, Transitions and Guards
|
|
337 |
|
|
338 |
A QStateMachine runs its own event loop. For signal transitions
|
|
339 |
(QSignalTransition objects), QStateMachine automatically posts a
|
|
340 |
QStateMachine::SignalEvent to itself when it intercepts the corresponding
|
|
341 |
signal; similarly, for QObject event transitions (QEventTransition objects)
|
|
342 |
a QStateMachine::WrappedEvent is posted.
|
|
343 |
|
|
344 |
You can post your own events to the state machine using
|
|
345 |
QStateMachine::postEvent().
|
|
346 |
|
|
347 |
When posting a custom event to the state machine, you typically also have
|
|
348 |
one or more custom transitions that can be triggered from events of that
|
|
349 |
type. To create such a transition, you subclass QAbstractTransition and
|
|
350 |
reimplement QAbstractTransition::eventTest(), where you check if an event
|
|
351 |
matches your event type (and optionally other criteria, e.g. attributes of
|
|
352 |
the event object).
|
|
353 |
|
|
354 |
Here we define our own custom event type, \c StringEvent, for posting
|
|
355 |
strings to the state machine:
|
|
356 |
|
|
357 |
\snippet doc/src/snippets/statemachine/main4.cpp 0
|
|
358 |
|
|
359 |
Next, we define a transition that only triggers when the event's string
|
|
360 |
matches a particular string (a \e guarded transition):
|
|
361 |
|
|
362 |
\snippet doc/src/snippets/statemachine/main4.cpp 1
|
|
363 |
|
|
364 |
In the eventTest() reimplementation, we first check if the event type is the
|
|
365 |
desired one; if so, we cast the event to a StringEvent and perform the
|
|
366 |
string comparison.
|
|
367 |
|
|
368 |
The following is a statechart that uses the custom event and transition:
|
|
369 |
|
|
370 |
\img statemachine-customevents.png
|
|
371 |
\omit
|
|
372 |
\caption This is a caption
|
|
373 |
\endomit
|
|
374 |
|
|
375 |
Here's what the implementation of the statechart looks like:
|
|
376 |
|
|
377 |
\snippet doc/src/snippets/statemachine/main4.cpp 2
|
|
378 |
|
|
379 |
Once the machine is started, we can post events to it.
|
|
380 |
|
|
381 |
\snippet doc/src/snippets/statemachine/main4.cpp 3
|
|
382 |
|
|
383 |
An event that is not handled by any relevant transition will be silently
|
|
384 |
consumed by the state machine. It can be useful to group states and provide
|
|
385 |
a default handling of such events; for example, as illustrated in the
|
|
386 |
following statechart:
|
|
387 |
|
|
388 |
\img statemachine-customevents2.png
|
|
389 |
\omit
|
|
390 |
\caption This is a caption
|
|
391 |
\endomit
|
|
392 |
|
|
393 |
For deeply nested statecharts, you can add such "fallback" transitions at
|
|
394 |
the level of granularity that's most appropriate.
|
|
395 |
|
|
396 |
\section1 Using Restore Policy To Automatically Restore Properties
|
|
397 |
|
|
398 |
In some state machines it can be useful to focus the attention on assigning properties in states,
|
|
399 |
not on restoring them when the state is no longer active. If you know that a property should
|
|
400 |
always be restored to its initial value when the machine enters a state that does not explicitly
|
|
401 |
give the property a value, you can set the global restore policy to
|
|
402 |
QStateMachine::RestoreProperties.
|
|
403 |
|
|
404 |
\code
|
|
405 |
QStateMachine machine;
|
|
406 |
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
|
|
407 |
\endcode
|
|
408 |
|
|
409 |
When this restore policy is set, the machine will automatically restore all properties. If it
|
|
410 |
enters a state where a given property is not set, it will first search the hierarchy of ancestors
|
|
411 |
to see if the property is defined there. If it is, the property will be restored to the value
|
|
412 |
defined by the closest ancestor. If not, it will be restored to its initial value (i.e. the
|
|
413 |
value of the property before any property assignments in states were executed.)
|
|
414 |
|
|
415 |
Take the following code:
|
|
416 |
\code
|
|
417 |
QStateMachine machine;
|
|
418 |
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
|
|
419 |
|
|
420 |
QState *s1 = new QState();
|
|
421 |
s1->assignProperty(object, "fooBar", 1.0);
|
|
422 |
machine.addState(s1);
|
|
423 |
machine.setInitialState(s1);
|
|
424 |
|
|
425 |
QState *s2 = new QState();
|
|
426 |
machine.addState(s2);
|
|
427 |
\endcode
|
|
428 |
|
|
429 |
Lets say the property \c fooBar is 0.0 when the machine starts. When the machine is in state
|
|
430 |
\c s1, the property will be 1.0, since the state explicitly assigns this value to it. When the
|
|
431 |
machine is in state \c s2, no value is explicitly defined for the property, so it will implicitly
|
|
432 |
be restored to 0.0.
|
|
433 |
|
|
434 |
If we are using nested states, the parent defines a value for the property which is inherited by
|
|
435 |
all descendants that do not explicitly assign a value to the property.
|
|
436 |
\code
|
|
437 |
QStateMachine machine;
|
|
438 |
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
|
|
439 |
|
|
440 |
QState *s1 = new QState();
|
|
441 |
s1->assignProperty(object, "fooBar", 1.0);
|
|
442 |
machine.addState(s1);
|
|
443 |
machine.setInitialState(s1);
|
|
444 |
|
|
445 |
QState *s2 = new QState(s1);
|
|
446 |
s2->assignProperty(object, "fooBar", 2.0);
|
|
447 |
s1->setInitialState(s2);
|
|
448 |
|
|
449 |
QState *s3 = new QState(s1);
|
|
450 |
\endcode
|
|
451 |
|
|
452 |
Here \c s1 has two children: \c s2 and \c s3. When \c s2 is entered, the property \c fooBar
|
|
453 |
will have the value 2.0, since this is explicitly defined for the state. When the machine is in
|
|
454 |
state \c s3, no value is defined for the state, but \c s1 defines the property to be 1.0, so this
|
|
455 |
is the value that will be assigned to \c fooBar.
|
|
456 |
|
|
457 |
\section1 Animating Property Assignments
|
|
458 |
|
|
459 |
The State Machine API connects with the Animation API in Qt to allow automatically animating
|
|
460 |
properties as they are assigned in states.
|
|
461 |
|
|
462 |
Say we have the following code:
|
|
463 |
\code
|
|
464 |
QState *s1 = new QState();
|
|
465 |
QState *s2 = new QState();
|
|
466 |
|
|
467 |
s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
|
|
468 |
s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));
|
|
469 |
|
|
470 |
s1->addTransition(button, SIGNAL(clicked()), s2);
|
|
471 |
\endcode
|
|
472 |
|
|
473 |
Here we define two states of a user interface. In \c s1 the \c button is small, and in \c s2
|
|
474 |
it is bigger. If we click the button to transition from \c s1 to \c s2, the geometry of the button
|
|
475 |
will be set immediately when a given state has been entered. If we want the transition to be
|
|
476 |
smooth, however, all we need to do is make a QPropertyAnimation and add this to the transition
|
|
477 |
object.
|
|
478 |
|
|
479 |
\code
|
|
480 |
QState *s1 = new QState();
|
|
481 |
QState *s2 = new QState();
|
|
482 |
|
|
483 |
s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
|
|
484 |
s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));
|
|
485 |
|
|
486 |
QSignalTransition *transition = s1->addTransition(button, SIGNAL(clicked()), s2);
|
|
487 |
transition->addAnimation(new QPropertyAnimation(button, "geometry"));
|
|
488 |
\endcode
|
|
489 |
|
|
490 |
Adding an animation for the property in question means that the property assignment will no
|
|
491 |
longer take immediate effect when the state has been entered. Instead, the animation will start
|
|
492 |
playing when the state has been entered and smoothly animate the property assignment. Since we
|
|
493 |
do not set the start value or end value of the animation, these will be set implicitly. The
|
|
494 |
start value of the animation will be the property's current value when the animation starts, and
|
|
495 |
the end value will be set based on the property assignments defined for the state.
|
|
496 |
|
|
497 |
If the global restore policy of the state machine is set to QStateMachine::RestoreProperties,
|
|
498 |
it is possible to also add animations for the property restorations.
|
|
499 |
|
|
500 |
\section1 Detecting That All Properties Have Been Set In A State
|
|
501 |
|
|
502 |
When animations are used to assign properties, a state no longer defines the exact values that a
|
|
503 |
property will have when the machine is in the given state. While the animation is running, the
|
|
504 |
property can potentially have any value, depending on the animation.
|
|
505 |
|
|
506 |
In some cases, it can be useful to be able to detect when the property has actually been assigned
|
|
507 |
the value defined by a state. For this, we can use the state's polished() signal.
|
|
508 |
\code
|
|
509 |
QState *s1 = new QState();
|
|
510 |
s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
|
|
511 |
|
|
512 |
QState *s2 = new QState();
|
|
513 |
|
|
514 |
s1->addTransition(s1, SIGNAL(polished()), s2);
|
|
515 |
\endcode
|
|
516 |
|
|
517 |
The machine will be in state \c s1 until the \c geometry property has been set. Then it will
|
|
518 |
immediately transition into \c s2. If the transition into \c s1 has an animation for the \c
|
|
519 |
geometry property, then the machine will stay in \c s1 until the animation has finished. If there
|
|
520 |
is no animation, it will simply set the property and immediately enter state \c s2.
|
|
521 |
|
|
522 |
Either way, when the machine is in state \c s2, the property \c geometry has been assigned the
|
|
523 |
defined value.
|
|
524 |
|
|
525 |
If the global restore policy is set to QStateMachine::RestoreProperties, the state will not emit
|
|
526 |
the polished() signal until these have been executed as well.
|
|
527 |
|
|
528 |
\section1 What happens if a state is exited before the animation has finished
|
|
529 |
|
|
530 |
If a state has property assignments, and the transition into the state has animations for the
|
|
531 |
properties, the state can potentially be exited before the properties have been assigned to the
|
|
532 |
values defines by the state. This is true in particular when there are transitions out from the
|
|
533 |
state that do not depend on the state being polished, as described in the previous section.
|
|
534 |
|
|
535 |
The State Machine API guarantees that a property assigned by the state machine either:
|
|
536 |
\list
|
|
537 |
\o Has a value explicitly assigned to the property.
|
|
538 |
\o Is currently being animated into a value explicitly assigned to the property.
|
|
539 |
\endlist
|
|
540 |
|
|
541 |
When a state is exited prior to the animation finishing, the behavior of the state machine depends
|
|
542 |
on the target state of the transition. If the target state explicitly assigns a value to the
|
|
543 |
property, no additional action will be taken. The property will be assigned the value defined by
|
|
544 |
the target state.
|
|
545 |
|
|
546 |
If the target state does not assign any value to the property, there are two
|
|
547 |
options: By default, the property will be assigned the value defined by the state it is leaving
|
|
548 |
(the value it would have been assigned if the animation had been permitted to finish playing.) If
|
|
549 |
a global restore policy is set, however, this will take precedence, and the property will be
|
|
550 |
restored as usual.
|
|
551 |
|
|
552 |
\section1 Default Animations
|
|
553 |
|
|
554 |
As described earlier, you can add animations to transitions to make sure property assignments
|
|
555 |
in the target state are animated. If you want a specific animation to be used for a given property
|
|
556 |
regardless of which transition is taken, you can add it as a default animation to the state
|
|
557 |
machine. This is in particular useful when the properties assigned (or restored) by specific
|
|
558 |
states is not known when the machine is constructed.
|
|
559 |
|
|
560 |
\code
|
|
561 |
QState *s1 = new QState();
|
|
562 |
QState *s2 = new QState();
|
|
563 |
|
|
564 |
s2->assignProperty(object, "fooBar", 2.0);
|
|
565 |
s1->addTransition(s2);
|
|
566 |
|
|
567 |
QStateMachine machine;
|
|
568 |
machine.setInitialState(s1);
|
|
569 |
machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));
|
|
570 |
\endcode
|
|
571 |
|
|
572 |
When the machine is in state \c s2, the machine will play the default animation for the
|
|
573 |
property \c fooBar since this property is assigned by \c s2.
|
|
574 |
|
|
575 |
Note that animations explicitly set on transitions will take precedence over any default
|
|
576 |
animation for the given property.
|
|
577 |
*/
|