Qt SCXML Media Player QML Example (C++ Data Model)

Sends data to and receives it from a C++ data model.

Media Player QML Example (C++ Data Model) demonstrates how to access data from a C++ data model. The data model enables writing C++ code for expr attributes and <script> elements. The data part of the data model is backed by a subclass of QScxmlCppDataModel, for which the Qt SCXML compiler (qscxmlc) generates the dispatch methods.

The UI is created using Qt Quick.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

Using the C++ Data Model

We specify the data model as a value of the datamodel attribute of the <scxml> element in the SCXML file:

 <scxml
     xmlns="http://www.w3.org/2005/07/scxml"
     version="1.0"
     name="MediaPlayerStateMachine"
     initial="stopped"
     datamodel="cplusplus:TheDataModel:thedatamodel.h"

The format of the datamodel attribute is: cplusplus:<class-name>:<classdef-header>. Therefore, we need a file called thedatamodel.h that contains a subclass of QScxmlCppDataModel:

 #include "qscxmlcppdatamodel.h"

 class TheDataModel: public QScxmlCppDataModel
 {
     Q_OBJECT
     Q_SCXML_DATAMODEL

QScxmlCppDataModel derives from QObject, so we add the Q_OBJECT macro in the private section of the definition, right after the opening bracket. We then place the Q_SCXML_DATAMODEL macro after Q_OBJECT. The macro expands to the declaration of virtual methods, the implementation of which is generated by the Qt SCXML compiler.

In the SCXML file, we specify C++ statements in the <script> element and use the expr attribute to access the data model:

     <state id="stopped">
         <transition event="tap" cond="isValidMedia()" target="playing"/>
     </state>

     <state id="playing">
         <onentry>
             <script>
                 media = eventData().value(QStringLiteral(&quot;media&quot;)).toString();
             </script>
             <send event="playbackStarted">
                 <param name="media" expr="media"/>
             </send>
         </onentry>

         <onexit>
             <send event="playbackStopped">
                 <param name="media" expr="media"/>
             </send>
         </onexit>

         <transition event="tap" cond="!isValidMedia() || media == eventData().value(QStringLiteral(&quot;media&quot;))" target="stopped"/>
         <transition event="tap" cond="isValidMedia() &amp;&amp; media != eventData().value(QStringLiteral(&quot;media&quot;))" target="playing"/>
     </state>

The Qt SCXML compiler generates the various evaluateTo methods and converts the expressions and scripts into lambdas inside those methods in mediaplayer-cppdatamodel.cpp:

 bool TheDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) {
 ....
     return [this]()->bool{ return isValidMedia(); }();
 ....
 }

 QVariant TheDataModel::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) {
 ....
         return [this]()->QVariant{ return media; }();
 ....
 }

 void TheDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) {
 ....
         [this]()->void{ media = eventData().value(QStringLiteral("media")).toString(); }();
 ....
 }

Example project @ code.qt.io