summaryrefslogtreecommitdiffstats
path: root/examples/scxml
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@wisebot.com>2016-11-01 16:45:06 +0100
committerJarek Kobus <jaroslaw.kobus@qt.io>2016-11-14 09:52:37 +0000
commit1ce48f9b6a0263b5520c2d428123e34ba0501021 (patch)
tree33326a9374aa5ddd21e51c47a3ef784627872cd5 /examples/scxml
parentffe78e265671978c9c8decd5e7bb54ed32a0ac08 (diff)
Add docs for sudoku example
Change-Id: I956a0c8d030475ff7ec00dabfeffd980918fde2a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'examples/scxml')
-rw-r--r--examples/scxml/sudoku/doc/images/sudoku.pngbin0 -> 18609 bytes
-rw-r--r--examples/scxml/sudoku/doc/src/sudoku.qdoc394
-rw-r--r--examples/scxml/sudoku/mainwindow.cpp1
-rw-r--r--examples/scxml/sudoku/sudoku.js2
-rw-r--r--examples/scxml/sudoku/sudoku.scxml3
5 files changed, 396 insertions, 4 deletions
diff --git a/examples/scxml/sudoku/doc/images/sudoku.png b/examples/scxml/sudoku/doc/images/sudoku.png
new file mode 100644
index 0000000..ef6d113
--- /dev/null
+++ b/examples/scxml/sudoku/doc/images/sudoku.png
Binary files differ
diff --git a/examples/scxml/sudoku/doc/src/sudoku.qdoc b/examples/scxml/sudoku/doc/src/sudoku.qdoc
new file mode 100644
index 0000000..d1b6369
--- /dev/null
+++ b/examples/scxml/sudoku/doc/src/sudoku.qdoc
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** 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 sudoku
+ \title Qt SCXML Sudoku Example
+ \ingroup examples-qtscxml
+ \brief Presents the use of SCXML in a sudoku game.
+
+ \e {Sudoku} demonstrates how an SCXML file may be used in a game.
+
+ \include examples-run.qdocinc
+
+ \section1 Sudoku Features
+
+ \image sudoku.png Screenshot of the Sudoku example
+
+ Our sudoku contains the following features:
+ \list
+ \li Initially and when the game ends, the sudoku
+ enters the \c idle state. In that state the players
+ can see if their last game finished successfully or not.
+ The state machine is then in one of two child states
+ of the \c idle state: \c solved or \c unsolved,
+ respectively. In the \c idle state the players
+ can also choose the sudoku grid they would like to solve.
+ The grid is disabled and the user interaction
+ is ignored.
+ \li After players click the \uicontrol Start button, the sudoku
+ enters the \c playing state and is ready for the user
+ interaction on the board.
+ \li When the game is in the \c playing state and the players
+ click the \uicontrol Stop button, the game ends
+ and enters the \c unsolved child state of the \c idle state.
+ If the players have solved the current puzzle successfully,
+ the game automatically ends and enters the \c solved child state
+ of the \c idle state indicating success.
+ \li The board consist of 81 buttons, laid out in a 9x9 grid.
+ The buttons with initial values given remain disabled
+ during the game. The players can only interact
+ with buttons initially empty. Each
+ click on the button increases its value by one.
+ \li During the game the \uicontrol Undo button
+ is available for the players' convenience.
+ \endlist
+
+ \section1 SCXML Part: Internal Logic Description
+
+ The \e sudoku.scxml file describes the internal structure of
+ the states the sudoku game can be in, defines the transitions
+ between states, and triggers the appropriate script
+ functions when the transitions take place. It also
+ communicates with the GUI part by sending events and listening
+ to the upcoming events and reacting to them.
+
+ We use the ECMAScript data model:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto scxml
+ \printuntil ecmascript
+
+ We declare the following variables:
+
+ \printuntil </datamodel>
+
+ \table
+ \header
+ \li Variable
+ \li Description
+ \row
+ \li \c initState
+ \li Holds the initial state of the current game. It is a
+ two-dimensional array of 9x9 cells that contain initial sudoku
+ numbers. The value of zero means the cell is initially empty.
+ \row
+ \li \c currentState
+ \li Holds the current state of the game being played. It is
+ similar to the \c initState variable and initially
+ contains the same content. However, when the players start
+ entering the numbers into the empty cells, this variable
+ is being updated accordingly, while the \c initState variable
+ remains unchanged.
+ \row
+ \li \c undoStack
+ \li Holds the history of players' moves. It is a vector of
+ the cells' coordinates that were touched last. Each
+ new modification during a game adds a pair of
+ x and y coordinates to that vector.
+ \endtable
+
+ The variables above are shared with the script helper functions
+ defined in the \c sudoku.js file:
+
+ \printuntil sudoku.js
+
+ We call some of the functions defined there
+ when taking transitions or in reaction to the
+ events sent by the GUI.
+
+ All the possible states mentioned before are defined in a
+ root state \c game.
+
+ \printuntil idle
+ \dots 12
+ \skipto id="unsolved"
+ \printuntil playing
+ \dots 12
+ \skipto /^\ {8}<\//
+ \printline state
+ \dots 8
+ \skipto /^\ {4}<\//
+ \printline state
+
+ When the sudoku example is started, the state
+ machine enters the \c game state and stays in this
+ state until the application exits. When entering this
+ state, we raise internally the \c restart event.
+ This event is also being raised whenever the players
+ change the current sudoku grid or when they start
+ the game by pressing the \uicontrol Start button.
+ We do not want to send it when they have finished
+ the current game because we still want to show the
+ filled grid from the last game play. So, this event
+ is being raised from three different contexts
+ and is captured internally once in a targetless
+ transition of the \c game state:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto <transition event="restart">
+ \printuntil </transition>
+
+ When we catch the \c restart event, we call
+ a helper \c restart() script method, defined in the \c sudoku.js
+ file and raise internally an additional \c update event.
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto restart
+ \printuntil }
+
+ The \c restart() function assigns the \c initState into the \c currentState
+ variable and clears the \c undoStack variable.
+
+ The \c update event is raised internally whenever we want to notify the GUI
+ that the grid contents have been changed and that
+ the GUI should update itself according to the passed values. This event
+ is caught in another targetless transition of the \c game state:
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto <transition event="update">
+ \printuntil </transition>
+
+ We send the external event \c updateGUI,
+ which is being intercepted in the \l {cpp}{C++ code}.
+ The \c updateGUI event is equipped
+ with additional data, specified inside \c <param>
+ elements. We pass two parameters, which
+ are accessible externally through the
+ \c currentState and \c initState names.
+ The actual values passed for them
+ equal the datamodel's \c currentState
+ and \c initState variables, respectively, which
+ are specified by the \c expr attributes.
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto idle
+ \printuntil </state>
+
+ When in \c idle state, we react to two
+ events, which may be sent by the GUI part:
+ \c start and \c setup. Whenever we receive the
+ \c start event, we just transition to the \c playing
+ state. When we receive the \c setup event, we
+ expect that the GUI part has sent us the new grid
+ to be solved. The grid's new initial state is expected
+ to be passed through the \c initState field of
+ \c _event.data. We assign the passed value to the
+ \c initState variable defined in our datamodel and
+ restart the grid's contents.
+
+ \skipto playing
+ \printuntil </transition>
+ \dots 12
+ \skipto </state>
+ \printline </state>
+
+ Whenever we enter the \c playing state, we reset
+ the grid's contents since we could have been still
+ showing the contents from the previous game play.
+ In the \c playing state we react to possible
+ events sent from the GUI: \c tap, \c undo, and \c stop.
+
+ The \c tap event is sent when the players
+ press one of the enabled sudoku cells.
+ This event is expected to contain additional data
+ specifying the cell's coordinates, which are passed
+ through the \c x and \c y fields of \c _event.data.
+ First, we check if the passed coordinates are valid
+ by invoking the \c isValidPosition() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto isValidPosition
+ \printuntil }
+
+ We ensure the coordinates are not negative nor bigger than our grid.
+ In addition, we check if the coordinates point to an
+ initially empty cell, since we can not modify
+ the cells initially given by the grid description.
+
+ When we have ensured the passed coordinates are correct,
+ we call \c calculateCurrentState() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto calculateCurrentState
+ \printuntil }
+
+ This function increments the value of the passed
+ grid's cell and adds the new move to the
+ undo stack history.
+
+ Right after the \c calculateCurrentState() function
+ finishes its execution, we check whether the grid is
+ already solved by calling the \c isSolved() script function:
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto isOK
+ \printuntil return true
+ \printuntil }
+ \skipto isSolved
+ \printuntil return true
+ \printuntil }
+
+ The \c isSolved() function returns \c true if the
+ grid is properly solved. Since we need to check
+ each row, each column, and each 3x3 square, we define
+ the \c isOK() helper function. This function takes
+ the vector of numbers and returns \c true if the passed vector
+ contains unique numbers and no number equals zero, meaning
+ there is no empty cell. The main loop of the \c isSolved() is invoked
+ nine times. In every iteration, we construct three vectors of numbers
+ representing a row, a column, and a square of the grid and call \c isOK()
+ for them. When all 27 vectors are OK, the grid is solved properly
+ and we return \c true.
+
+ Coming back to our SCXML file, in case \c isSolved()
+ returns \c true, we raise the \c solved event internally.
+ The last instruction in case of a proper move is to raise
+ the \c update event, since we need to notify the GUI
+ about the grid's change.
+
+ \quotefromfile sudoku/sudoku.scxml
+ \skipto id="playing"
+ \printline playing
+ \dots 12
+ \skipto undo
+ \printuntil </state>
+
+ When in the \c playing state, we also react to the \c undo
+ event sent from the GUI. In this case, we call the \c undo()
+ script function and notify the GUI about the need of an update.
+
+ \quotefromfile sudoku/sudoku.js
+ \skipto function undo
+ \printuntil }
+
+ The \c undo() function removes the last move from
+ the history, if there was any, and decrements the current value
+ for the cell described by the coordinates taken from this move.
+
+ The \c playing state is also ready for the \c stop
+ event sent by the GUI when the players press the \uicontrol Stop
+ button. In this case, we simply activate the \c idle state.
+
+ In addition, we intercept the \c solved event
+ sent internally and activate the \c solved state in this case.
+
+ \target cpp
+ \section1 C++ Part: Constructing the GUI
+
+ The C++ part of the application consists of a
+ \c MainWindow class which constructs the GUI and glues it with the SCXML part.
+ The class is declared in \e mainwindow.h.
+
+ \quotefromfile sudoku/mainwindow.h
+ \skipto MainWindow
+ \printuntil };
+
+ The \c MainWindow class holds the pointer to the
+ \c {QScxmlStateMachine *m_machine}, which is the state machine
+ class automatically generated by Qt out of the \c sudoku.scxml file.
+ It also holds the pointers to some GUI elements.
+
+ \quotefromfile sudoku/mainwindow.cpp
+ \skipto MainWindow
+ \printuntil {
+
+ The constructor of the \c MainWindow class
+ instantiates the GUI part of the application
+ and stores the pointer to the passed state machine.
+ It also initializes the GUI part and glues the
+ GUI part to the state machine by connecting
+ their communication interfaces together.
+
+ \skipto clicked
+ \printuntil });
+
+ First, we create 81 buttons and connect
+ their \c clicked signal to a lambda expression
+ that submits the \c tap event to the state machine
+ passing the button's coordinates.
+
+ Later, we add some horizontal and vertical lines
+ to the grid in order to group buttons in 3x3 boxes.
+
+ \skipto clicked
+ \printuntil });
+
+ We create the \uicontrol {Start / Stop} button and
+ connect its clicked signal to a lambda expression
+ which submits the \c stop or \c start event
+ depending on whether the machine is in \c playing state
+ or not, respectively.
+
+ We create a label informing whether the grid is solved
+ or not, and an \uicontrol Undo button, which submits the
+ \c undo event whenever it is clicked.
+
+ \skipto clicked
+ \printuntil });
+
+ Then we create a combobox that is filled with
+ grid names to be solved. These grids are
+ read from the \c :/data directory of the application
+ compiled-in resources.
+
+ \skipto currentIndexChanged
+ \printuntil setInitialValues
+
+ Whenever the players change the grid in the combobox,
+ we read the grid contents storing it in the
+ variant map under the \c initValues key as a
+ list of lists of int variants and we submit the
+ \c setup event to the state machine passing
+ the grid's contents. Initially, we read the first
+ available grid from the list and pass it directly
+ to the sudoku state machine as the initial grid.
+
+ \skipto connectToState
+ \printline playing
+ \dots 8
+ \skipto });
+ \printuntil connectToEvent
+ \dots 8
+ \skipto });
+ \printline });
+
+ Later, we connect to the signals that are being sent
+ whenever the machine enters or leaves the \c playing
+ or \c solved states, and we update some GUI parts accordingly.
+ We also connect to the state machine's \c updateGUI event
+ and update all the buttons' values according to the passed cells' states.
+
+ \quotefromfile sudoku/main.cpp
+ \skipto #include
+ \printuntil /\}$/
+
+ In the \c main() function in the \c main.cpp file, we instantiate the
+ \c app application object, \c Sudoku state machine,
+ and \c MainWindow GUI class. We start the state machine,
+ show the main window, and execute the application.
+*/
diff --git a/examples/scxml/sudoku/mainwindow.cpp b/examples/scxml/sudoku/mainwindow.cpp
index 574b5ca..3a13010 100644
--- a/examples/scxml/sudoku/mainwindow.cpp
+++ b/examples/scxml/sudoku/mainwindow.cpp
@@ -158,6 +158,7 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
m_undoButton = new QToolButton(this);
m_undoButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_undoButton->setText(tr("Undo"));
+ m_undoButton->setEnabled(false);
layout->addWidget(m_undoButton, Size + 3, 8, 1, 3);
connect(m_undoButton, &QAbstractButton::clicked,
diff --git a/examples/scxml/sudoku/sudoku.js b/examples/scxml/sudoku/sudoku.js
index afd9691..21a1ed8 100644
--- a/examples/scxml/sudoku/sudoku.js
+++ b/examples/scxml/sudoku/sudoku.js
@@ -68,7 +68,7 @@ function isValidPosition() {
function calculateCurrentState() {
if (isValidPosition() === false)
- return
+ return;
var x = _event.data.x;
var y = _event.data.y;
var currentValue = currentState[x][y];
diff --git a/examples/scxml/sudoku/sudoku.scxml b/examples/scxml/sudoku/sudoku.scxml
index ac447cc..eb36283 100644
--- a/examples/scxml/sudoku/sudoku.scxml
+++ b/examples/scxml/sudoku/sudoku.scxml
@@ -63,9 +63,6 @@
<raise event="restart"/>
</onentry>
<state id="idle">
- <onentry>
- <raise event="update"/>
- </onentry>
<transition event="start" target="playing"/>
<transition event="setup" target="unsolved">
<assign location="initState" expr="_event.data.initState"/>