×
Namespaces

Variants
Actions
(Difference between revisions)

Archived:Combining Qt Animation and State Machine Frameworks

From Nokia Developer Wiki
Jump to: navigation, search
somnathbanik (Talk | contribs)
hamishwillee (Talk | contribs)
m (Text replace - "<code cpp>" to "<code cpp-qt>")
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
[[Category:Qt]]
+
{{Archived|timestamp=20120306051545|user=[[User:Hamishwillee|&lt;br /&gt;----]]|[[:Category:Qt Quick|Qt Quick]] should be used for all UI development on mobile devices. The approach described in this article (using C++ for the Qt app UI) is deprecated.}}
{{CodeSnippet
+
[[Category:Qt C++ UI]][[Category:MeeGo Harmattan]][[Category:Symbian]][[Category:Code Examples]][[Category:Animation]]
|id=
+
 
|platform=Symbian
+
{{ArticleMetaData <!-- v1.2 -->
|devices=N8
+
|sourcecode= [[Media:StateMachineQt.zip]] <!-- Link to example source code (e.g. [[Media:The Code Example ZIP.zip]]) -->
|category=Qt
+
|installfile= <!-- Link to SIS (or WGZ etc.) installation file (e.g. [[Media:The Installation File.sis]]) -->
|subcategory= UI
+
|devices= N8         <!-- Devices tested against (e.g. ''devices=N95, N8'') -->
|creationdate= 24th May, 2011
+
|sdk= <!-- SDK(s) built and tested against (e.g. [http://linktosdkdownload/ Nokia Qt SDK 1.1]) -->
|keywords= QStateMachine, QPropertyAnimation
+
|platform= Symbian <!-- Compatible platforms (e.g. ''Symbian^1 and later, Qt 4.6 and later'') -->
 +
|devicecompatability= <!-- Compatible devices (e.g.: ''All* (must have internal GPS)'' ) -->
 +
|dependencies= <!-- Any other/external dependencies e.g.: Google Maps Api v1.0 -->
 +
|signing= <!-- Signing requirements - one of: Self-Signed, DevCert, Manufacturer -->
 +
|capabilities= <!-- Capabilities required by the article/code example (e.g. Location, NetworkServices. -->
 +
|keywords= QStateMachine, QPropertyAnimation   <!-- APIs, classes and methods (e.g. ''QSystemScreenSaver, QList, CBase'' -->
 +
|language= <!-- Language category code for non-English topics - e.g. Lang-Chinese -->
 +
|translated-by= <!-- [[User:XXXX]] -->
 +
|translated-from-title= <!-- Title only -->
 +
|translated-from-id= <!-- Id of translated revision -->
 +
|review-by= <!-- After re-review: [[User:username]] -->
 +
|review-timestamp= <!-- After re-review: YYYYMMDD -->
 +
|update-by= <!-- After significant update: [[User:username]]-->
 +
|update-timestamp= <!-- After significant update: YYYYMMDD -->
 +
|creationdate= 20110524
 +
|author= [[User:somnathbanik]]
 +
<!-- The following are not in current metadata -->
 +
|subcategory= UI
 
}}
 
}}
+
{{Abstract|This article demonstrates how to combine Qt's Animation Framework and State Machine Framework to animate standard widgets.}}
+
 
==Overview ==
+
Firstly it shows how to implement and application’s logic using a state machine.  Then it demonstrates how to use the animation and state machine frameworks together, animating standard widgets rather than graphics items.
{{Abstract| This article demonstrate how to combine Animation and State Machine Frameworks.}} We will see how to implements the application’s logic using a state machine rather than managing it all ourselvesAnd finally we will add  both the animation and state machine frameworks to show how they can be used together, and where the animation is of standard widgets rather than of the graphics items.
+
 
+
==Animation Framework Concepts==
+
Qt’s animation framework is quite interesting and the concepts are easy to understand.  The framework is based on [http://doc.qt.nokia.com/4.7/qobject.html QObject] and Qt’s property system. The simplest approach is to create a [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] for each property of each [http://doc.qt.nokia.com/4.7/qobject.html QObject] we want to animate, and to give the property animation a duration, an initial value, and a final value.  
+
 
==Animation Framework In Use==
+
In this example we give a duration of ''5000 milliseconds'' and initial and final geometry specified as [http://doc.qt.nokia.com/4.7/qrect.html QRect]. When the animation starts the object is immediately set to the initial geometry and then its geometry will be changed over a 5 second period to reach the final geometry. So if the initial width is ''50'' pixels and the final width is ''360'' pixels then the width would be ''112'' pixels after ''1'' second,  ''174'' pixels after ''2'' seconds,'' 236'' pixels after ''3'' seconds, ''298'' pixels after'' 4'' seconds and finally'' 360'' pixels after ''5'' seconds. In this case the increment is ''62'' pixels per second - calculated from the difference between the final and initial widths divided by the duration  
Qt’s animation framework is quite interesting and the concepts are easy to understand.  Practically the framework is based on [http://doc.qt.nokia.com/4.7/qobject.html QObject] and Qt’s property system. The simplest approach is to create a [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] for each property of each [http://doc.qt.nokia.com/4.7/qobject.html QObject] we want to animate, and to give the property animation a duration, an initial value, and a final value.  
+
In this example we give a duration of ''5000 milliseconds'' and initial and final geometrics specified as [http://doc.qt.nokia.com/4.7/qrect.html QRect]. When the animation starts the object is immediately set to the initial geometry and then its geometry will be changed over a 5 second period to reach the final geometry. So if the initial width is ''50'' pixels and the final width is ''360'' then the width would be ''112'' pixels after ''1'' second,  ''174'' pixels after ''2'' seconds,'' 236'' pixels after ''3'' seconds, ''298'' pixels after'' 4'' seconds and finally'' 360'' pixels after ''5'' seconds. In this case the increment is'' 62'' pixels per second which is calculated from the difference between the final and initial widths divided by the duration  
+
 
ie ''(360- 50)/5 = 62''.
 
ie ''(360- 50)/5 = 62''.
Qt uses a much more better time granularity then seconds , so the actual change of width might be from'' 50'' pixels to ''52'' pixels to ''54'' pixels and so on. We will also use the [http://doc.qt.nokia.com/4.7/qeasingcurve.html QEasingCurve] class which offers over forty different interpolation graphs.  
+
 
+
Qt uses a much finer time granularity than seconds, so the actual change of width might be from'' 50'' pixels to ''52'' pixels to ''54'' pixels and so on. We will also use the [http://doc.qt.nokia.com/4.7/qeasingcurve.html QEasingCurve] class which offers over forty different interpolation graphs.  
+
+
==State Machine Framework In Use==
+
Using Qt’s state machine framework we can maintain the state of an application. And like the animation framework is is heavily dependent on [http://doc.qt.nokia.com/4.7/qobject.html QObject] and Qt’s property system. To set up a state machine we start by creating a [http://doc.qt.nokia.com/4.7/qstatemachine.html QStateMachine]. Then we create the states we nee (of [http://doc.qt.nokia.com/4.7/qstate.html QState] or [http://doc.qt.nokia.com/4.7/qfinalstate.html QFinalState]) and for each state we assign property ''(QObject, property, value)'', so that the state machine set the property to the given value. Once the states have been set up then we create the transitions which specify how the state machine goes from one state to another. When ever there is a change in the state it emits an {{Icode|exited()}} signal to the state it left  behind, it emits  {{Icode|entered()}} signal to the state it enters  and  emits a {{Icode|finished ()}} signal if the state is completed. Now when everything is set up we tell the state machine which state to use as  its initial state and then call {{Icode|QStateMachine::start()}} to start the things.  There are many functionality of state machine, but here we have used very basic of it.
+
+
+
 
   
 
   
 +
== State Machine Framework Concepts ==
 +
Using Qt’s state machine framework we can define the states of an application and the transitions and triggers between them. Like the animation framework, the state machine framework is heavily dependent on [http://doc.qt.nokia.com/4.7/qobject.html QObject] and Qt’s property system.
 +
 +
To set up a state machine we start by creating a [http://doc.qt.nokia.com/4.7/qstatemachine.html QStateMachine]. Then we create the states we need ([http://doc.qt.nokia.com/4.7/qstate.html QState] or [http://doc.qt.nokia.com/4.7/qfinalstate.html QFinalState]). For easch state we assign property values ''(QObject, property, value)'', these are the values that the state machine will change the properties to when in the state.
 +
 +
Once the states have been set up then we create the transitions which define how the state machine changes from one state to another. When ever there is a change in the state it emits an {{Icode|exited()}} signal to the state it left behind, it emits {{Icode|entered()}} signal to the state it enters and emits a {{Icode|finished ()}} signal if the state is completed. Now when everything is set up we tell the state machine which state to use as its initial state and then call {{Icode|QStateMachine::start()}} to start the machine. 
 +
 +
The state machine offers a lot more functionality - we've only used the very basic functionality here.
 
   
 
   
 
==Basic Idea==  
 
==Basic Idea==  
To demonstrate the core functionality of the State Machine, we will create an example, where a state machine has three states, '''s1''', '''s2''' and '''s3'''. The state machine is controlled by a single [http://doc.qt.nokia.com/4.7/qpushbutton.html QPushButton] when the button is clicked, the machine transitions to another state. Initially, the state machine is in state '''s1''', also along with the state change we animate the button from an initial position to a final position.  
+
The example is a state machine with three states, '''s1''', '''s2''' and '''s3''', where '''s1''' is the initial state and '''s3''' is the final state. The state machine has a single [http://doc.qt.nokia.com/4.7/qpushbutton.html QPushButton] which has a different position in each state. When the button is clicked, the state machine moves to the next state and animates the button to its next position.  
+
The three screenshots recorded while changing the states. 
+
 
   
 
   
[[Image:StateMachine1.png|120x320px]] [[Image:StateMachine2.png|120x320px]] [[Image:StateMachine3.png|120x320px]]  
+
The three screenshots below were recorded while changing the states. 
 +
 
 +
[[File:StateMachine1.png|250px]] [[File:StateMachine2.png|250px]] [[File:StateMachine3.png|250px]]  
 
   
 
   
<code cpp>
 
 
==  Class Definition==
 
==  Class Definition==
 +
 +
<code cpp-qt>
 
#ifndef STATEMACHINEQT_H
 
#ifndef STATEMACHINEQT_H
 
#define STATEMACHINEQT_H
 
#define STATEMACHINEQT_H
Line 71: Line 89:
 
</code>
 
</code>
 
The slots {{Icode|animate1/2/3()}} are used to animate the button from one position to another.  
 
The slots {{Icode|animate1/2/3()}} are used to animate the button from one position to another.  
 +
 
The [http://doc.qt.nokia.com/4.7/qstatemachine.html QStateMachine] class provides a hierarchical finite state machine. It is part of [http://doc.qt.nokia.com/4.7/statemachine-api.html The State Machine Framework]. The [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] class animates Qt properties.  [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] interpolates over Qt properties. As property values are stored in [http://doc.qt.nokia.com/4.7/qvariant.html#qVariantSetValue QVariants], the class inherits [http://doc.qt.nokia.com/4.7/qvariantanimation.html QVariantAnimation], and supports animation of the same variant types as its super class.  
 
The [http://doc.qt.nokia.com/4.7/qstatemachine.html QStateMachine] class provides a hierarchical finite state machine. It is part of [http://doc.qt.nokia.com/4.7/statemachine-api.html The State Machine Framework]. The [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] class animates Qt properties.  [http://doc.qt.nokia.com/4.7/qpropertyanimation.html QPropertyAnimation] interpolates over Qt properties. As property values are stored in [http://doc.qt.nokia.com/4.7/qvariant.html#qVariantSetValue QVariants], the class inherits [http://doc.qt.nokia.com/4.7/qvariantanimation.html QVariantAnimation], and supports animation of the same variant types as its super class.  
 
 
 
   
 
   
 
== Class Implementation ==
 
== Class Implementation ==
 
   
 
   
First we create a button of size 50x50.  
+
First we create a button of size 50x50.  
<code cpp>
+
<code cpp-qt>
 
iButton = new QPushButton(this);
 
iButton = new QPushButton(this);
    iButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
iButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    iButton->setFixedSize(50,50);
+
iButton->setFixedSize(50,50);
    iButton->show();
+
iButton->show();
 
</code>
 
</code>
 
   
 
   
Then we create the state machine and states .
+
Then we create the state machine and states:
<code cpp>
+
<code cpp-qt>
    machine = new QStateMachine(this);
+
machine = new QStateMachine(this);
    QState *s1 = new QState();
+
QState *s1 = new QState();
    s1->assignProperty(iButton, "text", "S1");
+
s1->assignProperty(iButton, "text", "S1");
    QState *s2 = new QState();
+
QState *s2 = new QState();
    s2->assignProperty(iButton, "text", "S2");
+
s2->assignProperty(iButton, "text", "S2");
    QState *s3 = new QState();
+
QState *s3 = new QState();
      s3->assignProperty(iButton, "text", "S3");
+
s3->assignProperty(iButton, "text", "S3");
 
</code>
 
</code>
 
   
 
   
The {{Icode|QState::assignProperty()}} function is used to have a state set a property of a [http://doc.qt.nokia.com/4.7/qobject.html QObject] when the state is entered.
+
The {{Icode|QState::assignProperty()}} function is used to have a state set a property of a [http://doc.qt.nokia.com/4.7/qobject.html QObject] when the state is entered.   
   
+
 
   
 
   
 
Then we create the transitions by using the {{Icode|QState::addTransition()}} function.
 
Then we create the transitions by using the {{Icode|QState::addTransition()}} function.
<code cpp>
+
<code cpp-qt>
    s1->addTransition(iButton, SIGNAL(clicked()), s2);
+
s1->addTransition(iButton, SIGNAL(clicked()), s2);
    s2->addTransition(iButton, SIGNAL(clicked()), s3);
+
s2->addTransition(iButton, SIGNAL(clicked()), s3);
    s3->addTransition(iButton, SIGNAL(clicked()), s1);
+
s3->addTransition(iButton, SIGNAL(clicked()), s1);
+
 
</code>
 
</code>
 
   
 
   
Next, we add the states to the machine and set the machine's initial state:
+
Next, we add the states to the machine and set the machine's initial state:
<code cpp>
+
<code cpp-qt>
    machine->addState(s1);
+
machine->addState(s1);
    machine->addState(s2);
+
machine->addState(s2);
    machine->addState(s3);
+
machine->addState(s3);
    machine->setInitialState(s1);
+
machine->setInitialState(s1);
 
</code>
 
</code>
 
Finally, we start the state machine:
 
Finally, we start the state machine:
<code cpp>
+
<code cpp-qt>
 
machine->start();
 
machine->start();
 
</code>
 
</code>
 
   
 
   
The {{Icode|QState::entered()}} signal is emitted when the state is entered
+
The {{Icode|QState::entered()}} signal is emitted when the state is entered:
<code cpp>
+
<code cpp-qt>
    QObject::connect(s1, SIGNAL(entered()), this, SLOT(animate1()));
+
QObject::connect(s1, SIGNAL(entered()), this, SLOT(animate1()));
    QObject::connect(s2, SIGNAL(entered()), this, SLOT(animate2()));
+
QObject::connect(s2, SIGNAL(entered()), this, SLOT(animate2()));
    QObject::connect(s3, SIGNAL(entered()), this, SLOT(animate3()));
+
QObject::connect(s3, SIGNAL(entered()), this, SLOT(animate3()));
 
</code>
 
</code>
In the following snippet, the widget {{Icode|animation()}} slot will be called when states are entered.  
+
In the following snippet, the widget {{Icode|animation()}} slot will be called when states are entered.  
 
   
 
   
 
   
 
   
<code cpp>
+
<code cpp-qt>
        animation = new QPropertyAnimation(iButton, "geometry");
+
animation = new QPropertyAnimation(iButton, "geometry");
        animation->setDuration(5000);
+
animation->setDuration(5000);
        animation->setStartValue(QRect(0,0, iButton->width(),iButton-> height()));
+
animation->setStartValue(QRect(0,0, iButton->width(),iButton-> height()));
        animation->setEndValue(QRect(310,135, iButton->width(),iButton->height()));
+
animation->setEndValue(QRect(310,135, iButton->width(),iButton->height()));
        animation->setEasingCurve(QEasingCurve::OutBounce);
+
animation->setEasingCurve(QEasingCurve::OutBounce);
        animation->start();
+
animation->start();
 
</code>
 
</code>
 
   
 
   
The button is animated from ''(0,0)'' position to ''(310,135)'' position while entering to ''state 1''.  and so as the others. We have also added an easing effect with {{Icode|setEasingCurve()}} function. Finally {{Icode|start()}} is called to start the animation.  
+
The button is animated from ''(0,0)'' position to ''(310,135)'' position while entering to ''state 1'' (similar animations happen when entering the other states). We have also added an easing effect with {{Icode|setEasingCurve()}} function which makes the animation "bounce" before it settles. Finally {{Icode|start()}} is called to start the animation.  
+
+
 
   
 
   
 
==Source Code==
 
==Source Code==
The full source code presented in this article is available here [[File:StateMachineQt.zip]]
+
The full source code for this article is available here: [[File:StateMachineQt.zip]]
 +
 
 +
==Related Articles==
 +
 
 +
* [[How to use QStateMachine in Qt]]
 +
* [[Archived:Using QStateMachine and QState]]

Latest revision as of 04:13, 11 October 2012

Archived.pngArchived: This article is archived because it is not considered relevant for third-party developers creating commercial solutions today. If you think this article is still relevant, let us know by adding the template {{ReviewForRemovalFromArchive|user=~~~~|write your reason here}}.

Qt Quick should be used for all UI development on mobile devices. The approach described in this article (using C++ for the Qt app UI) is deprecated.

Article Metadata
Code ExampleTested with
Devices(s): N8
Compatibility
Platform(s): Symbian
Symbian
Article
Keywords: QStateMachine, QPropertyAnimation
Created: somnathbanik (24 May 2011)
Last edited: hamishwillee (11 Oct 2012)

This article demonstrates how to combine Qt's Animation Framework and State Machine Framework to animate standard widgets.

Firstly it shows how to implement and application’s logic using a state machine. Then it demonstrates how to use the animation and state machine frameworks together, animating standard widgets rather than graphics items.

Contents

[edit] Animation Framework Concepts

Qt’s animation framework is quite interesting and the concepts are easy to understand. The framework is based on QObject and Qt’s property system. The simplest approach is to create a QPropertyAnimation for each property of each QObject we want to animate, and to give the property animation a duration, an initial value, and a final value.

In this example we give a duration of 5000 milliseconds and initial and final geometry specified as QRect. When the animation starts the object is immediately set to the initial geometry and then its geometry will be changed over a 5 second period to reach the final geometry. So if the initial width is 50 pixels and the final width is 360 pixels then the width would be 112 pixels after 1 second, 174 pixels after 2 seconds, 236 pixels after 3 seconds, 298 pixels after 4 seconds and finally 360 pixels after 5 seconds. In this case the increment is 62 pixels per second - calculated from the difference between the final and initial widths divided by the duration ie (360- 50)/5 = 62.

Qt uses a much finer time granularity than seconds, so the actual change of width might be from 50 pixels to 52 pixels to 54 pixels and so on. We will also use the QEasingCurve class which offers over forty different interpolation graphs.

[edit] State Machine Framework Concepts

Using Qt’s state machine framework we can define the states of an application and the transitions and triggers between them. Like the animation framework, the state machine framework is heavily dependent on QObject and Qt’s property system.

To set up a state machine we start by creating a QStateMachine. Then we create the states we need (QState or QFinalState). For easch state we assign property values (QObject, property, value), these are the values that the state machine will change the properties to when in the state.

Once the states have been set up then we create the transitions which define how the state machine changes from one state to another. When ever there is a change in the state it emits an exited() signal to the state it left behind, it emits entered() signal to the state it enters and emits a finished () signal if the state is completed. Now when everything is set up we tell the state machine which state to use as its initial state and then call QStateMachine::start() to start the machine.

The state machine offers a lot more functionality - we've only used the very basic functionality here.

[edit] Basic Idea

The example is a state machine with three states, s1, s2 and s3, where s1 is the initial state and s3 is the final state. The state machine has a single QPushButton which has a different position in each state. When the button is clicked, the state machine moves to the next state and animates the button to its next position.

The three screenshots below were recorded while changing the states.

StateMachine1.png StateMachine2.png StateMachine3.png

[edit] Class Definition

#ifndef STATEMACHINEQT_H
#define STATEMACHINEQT_H
#include <QMainWindow>
#include "QPushButton"
#include "QVBoxLayout"
#include "QStateMachine"
#include <QPropertyAnimation>
#include "QEventTransition"
#include "QMessageBox"
namespace Ui {
class StateMachineQt;
}
class StateMachineQt : public QMainWindow
{
Q_OBJECT
public:
explicit StateMachineQt(QWidget *parent = 0);
~StateMachineQt();
private:
Ui::StateMachineQt *ui;
public:
QPushButton *iButton;
QStateMachine *machine;
QPropertyAnimation *animation;
public slots:
void animate1();
void animate2();
void animate3();
};
#endif // STATEMACHINEQT_H

The slots animate1/2/3() are used to animate the button from one position to another.

The QStateMachine class provides a hierarchical finite state machine. It is part of The State Machine Framework. The QPropertyAnimation class animates Qt properties. QPropertyAnimation interpolates over Qt properties. As property values are stored in QVariants, the class inherits QVariantAnimation, and supports animation of the same variant types as its super class.

[edit] Class Implementation

First we create a button of size 50x50.

iButton = new QPushButton(this);
iButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
iButton->setFixedSize(50,50);
iButton->show();

Then we create the state machine and states:

machine = new QStateMachine(this);
QState *s1 = new QState();
s1->assignProperty(iButton, "text", "S1");
QState *s2 = new QState();
s2->assignProperty(iButton, "text", "S2");
QState *s3 = new QState();
s3->assignProperty(iButton, "text", "S3");

The QState::assignProperty() function is used to have a state set a property of a QObject when the state is entered.

Then we create the transitions by using the QState::addTransition() function.

s1->addTransition(iButton, SIGNAL(clicked()), s2);
s2->addTransition(iButton, SIGNAL(clicked()), s3);
s3->addTransition(iButton, SIGNAL(clicked()), s1);

Next, we add the states to the machine and set the machine's initial state:

machine->addState(s1);
machine->addState(s2);
machine->addState(s3);
machine->setInitialState(s1);

Finally, we start the state machine:

machine->start();

The QState::entered() signal is emitted when the state is entered:

QObject::connect(s1, SIGNAL(entered()), this, SLOT(animate1()));
QObject::connect(s2, SIGNAL(entered()), this, SLOT(animate2()));
QObject::connect(s3, SIGNAL(entered()), this, SLOT(animate3()));

In the following snippet, the widget animation() slot will be called when states are entered.


animation = new QPropertyAnimation(iButton, "geometry");
animation->setDuration(5000);
animation->setStartValue(QRect(0,0, iButton->width(),iButton-> height()));
animation->setEndValue(QRect(310,135, iButton->width(),iButton->height()));
animation->setEasingCurve(QEasingCurve::OutBounce);
animation->start();

The button is animated from (0,0) position to (310,135) position while entering to state 1 (similar animations happen when entering the other states). We have also added an easing effect with setEasingCurve() function which makes the animation "bounce" before it settles. Finally start() is called to start the animation.

[edit] Source Code

The full source code for this article is available here: File:StateMachineQt.zip

[edit] Related Articles

This page was last modified on 11 October 2012, at 04:13.
167 page views in the last 30 days.