1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
|
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example pinball
\title Qt SCXML: Pinball Example
\ingroup examples-qtscxml
\brief Demonstrates how the internal
logic of the application can be encapsulated in a scxml file.
This example demonstrates a clear separation
between chosen user interface, which may be easily replaced,
and internal logic encapsulated in the scxml file,
which may stay the same for different user interface.
\section1 Pinball Features
\image pinball.png Screenshot of the Pinball example
\e {Pinball Example} mimics a pinball game.
The targets on the pinball table are substituted here by GUI controls,
mainly by push buttons. Display elements, including current
score, highscore and targets' lights are substituted by labels.
Usually targets' lights state changes very often during game:
they get turned on or off, they blink slowly, fast or
with any intermediate speed indicating a game (or a certain target)
entered some temporary state. The state of each target light
is presented here as an enabled or a disabled label.
There is no real ball here, but one can easily imagine that
pressing a target's button can mimic hitting a real pinball
target with a ball.
Our pinball contains the following features:
\list
\li Initially and when the game ends the pinball table
goes into \c offState. In that state all lights on the table
blink slowly (with 1s interval).
\li After pressing the \uicontrol START button the pinball table
goes into \c onState. All lights get turned off and the
pinball table is ready to be played.
\li Whenever the table is in \c onState and the inattentive player
presses the \uicontrol {BALL OUT} button, the game ends
and enters into the \c offState. If the player's score is
higher than the current highscore, the highscore is updated.
\li The goal is to collect the \uicontrol JACKPOT. In order to do that,
the player must hit all five \uicontrol CRAZY letters twice.
He has unlimited time for collecting it for the first time.
However, after he has collected all the letters for the first time,
he enters the \c hurryState and he must quickly collect
all the letters again within 5s. If the time passed and
the letters were not collected again, the player must
start collecting the letters from scratch.
\li Scores:
\list
\li 1.000 per letter hit when not in \c hurryState.
\li 10.000 per letter hit when in \c hurryState.
\li 100.000 bonus for all 5 letters when not in \c hurryState.
\li 1.000.000 bonus for all 5 letters when in \c hurryState (\uicontrol JACKPOT)
\endlist
\li When not in \c hurryState, the letters already hit should blink
with intermediate speed (500ms). Letters not hit yet should stay off.
\li When in \c hurryState, the letters already hit should
stay on. Letters not hit yet should blink fast (200ms).
In addition the \uicontrol HURRY light should blink with the same speed.
\li When jackpot gets collected the \uicontrol JACKPOT light should stay on.
\endlist
\section1 SCXML Part: Internal Logic Description
The pinball.scxml file describes the internal logic implemented for
pinball game.
\quotefromfile pinball/pinball.scxml
\skipto scxml
\printuntil guiControl
\dots 12
\skipto /^\ {8}<\//
\printuntil internalState
\dots 12
\skipto /^\ {8}<\//
\printuntil scxml
In this example we've chosen ecmascript data model
("datamodel" attribute of <scxml> element).
This data model enables declaring variables with
initial values, which can be modified later.
We declare two variables in our model: "highscore" and "score",
with initial values of 0 (two <data> elements inside <datamodel> element).
We also define a root parallel state "global",
with two child states: \c guiControl and \c internalState,
which are also parallel. Since the top \c global state is
parallel, it means that whenever a \c global state is active
all of its direct children are also active. In fact,
the top level \c global is always active, meaning
all of its children are always active, too.
The \c global state in our example doesn't play any other role apart from
collecting its two children and making them both active at a time.
\quotefromfile pinball/pinball.scxml
\skipto guiControl
\printuntil rLight
\dots 16
\skipto /^\ {12}<\//
\printuntil aLight
\dots 16
\skipto /^\ {12}<\//
\printuntil zLight
\dots 16
\skipto /^\ {12}<\//
\printuntil yLight
\dots 16
\skipto /^\ {12}<\//
\printuntil hurryLight
\dots 16
\skipto /^\ {12}<\//
\printuntil jackpotLight
\dots 16
\skipto /^\ {12}<\//
\printuntil gameOverLight
\dots 16
\skipto /^\ {12}<\//
\printuntil /^\ {8}<\//
The \c guiControl part is responsible for maintaining the current
state of every light control, which is visible on the pinball table.
We have eight different lights, five for letters: C, R, A, Z and Y,
and three for whole texts: \uicontrol HURRY!, \uicontrol JACKPOT
and \uicontrol {GAME OVER}. Every light
corresponds to its state, e.g. for light of letter \uicontrol C we have
a \c cLight state and so on. Every light state has two child states
indicating whether the light is on or off, e.g. \c cLight state
contains \c cLightOn and \c cLightOff states. The content
for other light states, which is omitted here, is analogous.
As mentioned before, \c guiControl state is always active, and since
it is of parallel type, all its direct children are always active too.
So, \c cLight state is always active, however,
only one of \c cLightOn or \c cLightOff it active at a time.
The same applies to other children of \c guiControl state.
In addition, we define transitions between on and off substates, e.g.
whenever the active state is \c cLightOn and someone posts \c turnOffC event,
we change the active substate of \c cLight into \c cLightOff, and vice versa:
whenever the active state is \c cLightOff and we receive \c turnOnC event,
we change the active substate of \c cLight into \c cLightOn.
In case of our application, we use instances of QLabel class in C++
to pretend real lights on the table. Whenever the light transitions
into "on" or "off" state we enable or disable the particular label
accordingly. The glue connection between the state machine and the GUI
part of the application will be shown in \l {cpp} {cpp code} later on. For now
it is enough to realize that changes to active states inside
the state machine will serve as external interface of the state machine,
so that other parts of the application (e.g. GUI part) can listen to.
All of the mentioned events which switch the state of every light
will be generated by this state machine inside \c internalState
in reaction to running timers or to external triggers.
\quotefromfile pinball/pinball.scxml
\skipto internalState
\printuntil logicalState
\dots 16
\skipto /^\ {12}<\//
\printuntil workflow
\dots 16
\skipto /^\ {12}<\//
\printuntil /^\ {8}<\//
The \c internalState consists of two main parts: \c logicalState and \c workflow.
The \c logicalState holds the definitions for modes the game is able
to go into and logical states of collected targets, while \c workflow state
implements generator for light blinking and calculates most of new states
machine should go into depending on incoming events and on currently active states.
As mentioned already, \c internalState is always active, and since
it is of a parallel type, \c logicalState and \c workflow children are always active too.
\quotefromfile pinball/pinball.scxml
\skipto logicalState
\printuntil letterState
\dots 20
\skipto /^\ {16}<\//
\printuntil modeState
\dots 20
\skipto /^\ {16}<\//
\printuntil /^\ {12}<\//
The \c logicalState consist of two parts: \c letterState and \c modeState.
As previously mentioned, \c logicalState is always active, and since
it is of parallel type, \c letterState and \c modeState children are always active too.
Now let's look at the first part of it, the \c letterState:
\quotefromfile pinball/pinball.scxml
\skipto letterState
\printuntil lettersState
\printuntil rLetter
\dots 28
\skipto /^\ {24}<\//
\printuntil aLetter
\dots 28
\skipto /^\ {24}<\//
\printuntil zLetter
\dots 28
\skipto /^\ {24}<\//
\printuntil yLetter
\dots 28
\skipto /^\ {24}<\//
\printuntil /^\ {16}<\//
It contains one parallel \c lettersState.
The \c lettersState maintains the logical state of
buttons pretending targets which were pressed by the user.
Please notice the difference between lettersStates and lightStates
which were mentioned earlier. E.g. the letter state for \uicontrol C letter
holds the info if the target for \uicontrol C letter was hit or not,
while the light state for \uicontrol C letter holds the info if the
light for the target for \uicontrol C letter should be currently on or off.
In the real pinball game these states are usually orthogonal,
e.g. if you didn't hit any target yet, the target is blinking,
indicating that it's currently worth to hit it. This blinking
means the light state switches between on or off in some short time interval,
while the target state is continouosly off, since it was not hit yet.
The author of a pinball table can decide e.g. that
after hitting the target (so after the target state goes to on)
he turns target's light continouosly off or on or just increase
of decrease the interval of blinking.
As mentioned before, \c letterState is always active, so it means
that its only child \c lettersState should always be active too. However,
there is an exception here: for a short while the \c lettersState may
end up being \e {not active}. This happens when the transition for
\c lettersState is being performed. This transition is triggered when
the \c resetLetters event will appear and it instructs the state machine
to exit \c lettersState and all its descendant states and reenter
\c lettersState and set up all its descendant states with their initial states.
In short, the \c resetLetters event resets the \c lettersState and all its
descendant states to the default configuration.
The \c lettersState contains five direct substates which
correspond to five different letters. The content
for other letters states than C, which is omitted here, is analogous.
The \c cLetter contains two substates reflecting its off and on states:
\c cLetterOff and \c cLetterOn. The "cLetter" state inside its parallel
parent \c lettersState is always active (under condition that
\c lettersState is active, too, what was described before), however,
only one of its child states is active at a time: \c cLetterOff or \c cLetterOn.
The initial substate of \c cLetter state is \c cLetterOff meaning
that whenever \c cLetter state is being activated (what happens
initially and after \c resetLetters event) its active
substate will be set to \c cLetterOff.
The \c cLetterOff defines a transition, which will be triggered by
the \c {cLetterTriggered} event. This transition activates the \c cLetterOn,
the other child of \c cLetter, only when the machine is in \c onState
(which will be defined later, but in short: when the pinball game is running).
The \c {cLetterTriggered} event is expected to be an event posted into the state machine
from outside of the state machine. This event should be generated when
the ball hits the \uicontrol C letter target. In our example we mimic
it by the pressing \uicontrol C letter button.
The \c cLetterOn state defines additional action. The action is defined
inside \c onentry element of the state, which means it will be executed
when the state machine enters this state. The action will generate
the event \c letterOn, which is common for all other letter states
defined in \c lettersState. This event will be used in further part
for updating the current score. So, whenever any of letters will switch
its state from off to on (but not vice versa), the \c letterOn
event will be posted.
The \c cLetterOn state is defined as a final state, which means that
whenever this state is activated the \c {done.state.cLetter} event
will be automatically posted by the state machine. Moreover,
when all \c lettersState children reach their final state,
the state machine will automatically post \c {done.state.lettersState} event.
Later, we will make use of it.
\quotefromfile pinball/pinball.scxml
\skipto modeState
\printuntil offState
\dots 24
\skipto /^\ {20}<\//
\printuntil onState
\dots 24
\skipto /^\ {20}<\//
\printuntil /^\ {16}<\//
The \c modeState consists of two substates, \c offState and \c onState.
The \c offState describes what should happen when the pinball game
is not yet started or when it is over,
while \c onState represents the logic appropriate for the active game.
\quotefromfile pinball/pinball.scxml
\skipto offState
\printuntil /^\ {20}<\//
Whenever the pinball application starts or when the game ends the machine goes into the
\c offState. Entering that state invokes some actions, which are
enclosed inside <onentry> element. First, we update the highScore
variable in case the current highScore value is less than current score value.
This is being checked inside the "cond" attribute of <if> element
(please note that we need to escape the "<" character with "<").
When being in off state, we still want to show the last reached score,
so we don't clear it here - we will do that when we enter the on state.
Next, we raise two events: \c resetLetters for logical reset of
all letter which were possibly hit during last game and \c update
for immediate generation of light blink and update of all lights.
When the machine is in \c offState it is ready to go into the
\c onState if only the \c startTriggered event occurs, which is described
by <transition> element. This event is expected to be generated externally
after pressing the \uicontrol START button on the pinball table.
\skipto onState
\printuntil /^\ {20}<\//
The fist thing which is being done by the state machine after in enters
the \c onState is clearing the current score variable. The \c onState
is of parallel type and consists of two direct child states:
\c hurryState and \c jackpotState, which both are always active as long as
their parent "onState" is active too. Both \c hurryState and \c jackpotState
contain two substates which reflects the off and the on state of each of them.
Only one substate of \c hurryState and one substate of \c jackpotState
can be acitve at a time and initially the off substates are active.
Whenever we enter \c hurryStateOff or \c hurryStateOn we generate two events:
\c resetLetters and \c update (the same as we generate when entering the \c onState).
In addition when we enter the \c hurryStateOn we send a delayed event
\c goToHurryOff with a delay of 5s, marked with \c hurryId. It means, that after 5s we just
switch the state back to \c hurryStateOff. In this way we implement
the five seconds hurry feature of the pinball table.
We also define transitions: \c hurryStateOff -> \c hurryStateOn
when \c goToHurryOn event occurs and the opposite,
\c hurryStateOn -> \c hurryStateOff when \c goToHurryOff event occurs.
When we exit the \c hurryStateOn we cancel possibly pending delayed
event which was marked with \c hurryId. This is important in case
the 5s time hasn't elapsed yet, but we've collected all the five letters
in the hurry state - we then collect the jackpot and want the pending
timer to finish.
The substates of \c jackpotState generate the request for update the state
of lights. The \c jackpotStateOff defines the transition to \c jackpotStateOn
when the \c goForJackpot event occurs. The opposite transition isn't
needed, since when the jackpot gets collected the corresponding light
is left lit until the end of game. When the new game starts, the \c jackpotState
is entered again which causes that its initial active substate will be
\c jackpotStateOff.
In addition, the \c onState defines one transition in reaction to
\c ballOutTriggered event which instructs the machine to go into the \c offState.
The \c ballOutTriggered event is expected to be an event posted into the state machine
from outside of the state machine. This event should be generated when
the ball gets out of playing area of the table. In our example we mimic
it by the pressing \uicontrol {BALL OUT} button. Posting the event from outside of state
machine will be shown in \l {cpp} {cpp code} later on.
\quotefromfile pinball/pinball.scxml
\skipto workflow
\printuntil letterOn
\dots 20
\skipto /^\ {16}<\//
\printuntil lettersState
\dots 20
\skipto /^\ {16}<\//
\printuntil updateLights
\dots 20
\skipto /^\ {16}<\//
\printuntil /^\ {12}<\//
The \c workflow state is responsible for generator for light blinking,
which is defined in its \c lightImpulseGenerator substate, and
for reacting for events which were posted so far from other parts
of the state machine.
The \c lightImpulseGenerator contains two child states:
\c lightImpulseOn and \c lightImpulseOff, with only one active at a time.
Whenever the delayed \c lightImpulse event is being delivered, it immediately
cause the transition from \c lightImpluseOn into \c lightImpulseOff or vice versa,
depending on the state the machine was in. In effect, the \c lightImpulseGenerator
toggles between its on and off state. These transitions are defined inside
\c lightImpulseGenerator, so it means that during this toggling the machine
also exits \c lightImpulseGenerator and reenters it immediately afterwards.
Entering \c lightImpulseGenerator cause generation of the \c update event.
The \c update event triggers the targetless transition and posts two other
events: \c scheduleNewImpulse and \c updateLights. The first one,
\c scheduleNewImpulse returns back to the \c lightImpulseGenerator and
it posts delayed \c lightImpulse event. After the desired delay,
the \c lightImpulse gets delivered to \c lightImpulseGenerator back
and it causes to toggle its substate again. In this way the machine
gets into the cycle. The current delay of the \c lightImpulse
event depends on the state in which the machine was in time of posting that delayed event.
Please also notice, that the next \c scheduleNewImpulse
event may occur on demand, before the next delayed \c lightImpulse
event gets delivered, so for that case we cancel any possible pending event.
\quotefromfile pinball/pinball.scxml
\skipto workflow
\skipto letterOn
\printuntil /^\ {16}<\//
\printuntil /^\ {16}<\//
Whenever we receive the \c letterOn event we update the current score.
The transition for the \c letterOn event is targetless, since we just
listen for that event and update the internal data accordingly
without changing any active state. The new score is being
increased by 1.000 or 10.000 points, depending if we currently are
in \c hurryStateOff or \c hurryStateOn.
After the score is updated we generate the \c updateLights event
in order to update immediately letters' lights accordingly.
We don't generate "update" here, since we don't want to toggle
light impulse now, but just update the lights according to
the current impulse state.
We also intercept the \c {done.state.lettersState} event,
which is being generated when all the letters were hit.
Depending on which state we are currently in, we grant
a small bonus of 100.000 or a big one of 1.000.000 (jackpot).
In addition we toggle \c hurryState substate by
sending \c goToHurryOn or \c goToHurryOff event.
When all letters were collected being in \c hurryStateOn
we also raise the \c goForJackpot event which instructs
the machine to activate the \c jackpotStateOn.
\skipto updateLights
\printuntil rLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil aLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil zLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil yLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil rLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil aLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil zLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil yLetterOn
\dots 32
\skipto /^\ {28}<\//
\printuntil /^\ {16}<\//
When we receive \c updateLights event, we first want to send a
\c updateScore signal outside of the state machine. We pass
to the signal current value of highscore and score variables.
This signal is received by the cpp part.
Next, depending if we are in \c jackpotStateOn or \c jackpotStateOff
we send the \c turnOnJackpot or the \c turnOffJackpot signal,
which instructs the \c guiControl state to transition to
\c jackpotLightOn or \c jackpotLightOff respectively.
When the machine is in \e idle state, i.e. in off state
or when the game is on, but no interaction is being performed,
the \c updateLights event is delivered periodically
during the game, each time with \c lightImpulseOn or
\c lightImpulseOff state toggled. Depending on the
current state of light impulse, and depending also
on which of \c offState, \c hurryStateOff or \c hurryStateOn
is active we turn on or off all the lights accordingly
to the description of the pinball table.
\section1 GUI Part: User Interface Description
The GUI part of the application consists of a mainwindow.ui
file which describes the static user interface of the game.
\target cpp
\section1 CPP Part: Glue GUI with SCXML
The CPP part of the application consists of a
MainWindow class which glues the GUI part with the SCXML part.
The class is declared in mainwindow.h.
\quotefromfile pinball/mainwindow.h
\skipto Pinball
\printuntil };
The MainWindow class holds the pointer to the
\c {Pinball *m_machine} which is the state machine
class automatically generated by Qt out of SCMXL file
and the pointer to the \c {Ui::MainWindow *m_ui} which
describes the GUI part. It also declares two helper methods.
\quotefromfile pinball/mainwindow.cpp
\skipto #include
\printuntil /}$/
The constructor of the MainWindow class
instantiates the GUI part of the application
and stores the pointer to the passed \c Pinball state machine.
It also initializes the GUI part and glues the
GUI part with the state machine by connecting
their communication interfaces together.
The \c initAndConnect() method connects
the state with the corresponding GUI widget by
binding its activity with the enablement of the widget,
so that whenever the state is active its corresponding
widget is enabled and whenever the state is inactive
the widget is disabled. We do that for all lights, targets
and description lables.
We also connect to the \c updateScore propagated
by the state machine and update the score displays
with the values passed with the event.
The info about hitting any GUI target needs to be passed
to the state machine and we do that by connecting
all target buttons' \c clicked signals to the lambda expressions
which submit corresponding event into the state machine.
\quotefromfile pinball/main.cpp
\skipto #include
\printuntil /}$/
Finally, there is main.cpp file with the main() function
inside which we instantiate the
\c app application object, \c Pinball state machine
and \c MainWindow GUI class. We initialize and start the state machine,
show the main window and execute the application.
*/
|