Towards Animations · Machines à états (1/2): Etat Une solution appropriée pour « maintenir...

Preview:

Citation preview

Towards Animations

gilles.bailly@telecom-paristech.fr

créditsEric Lecolinet

gconftool-2 --type Boolean --set /desktop/gnome/interface/menus_have_icons True

Mes icones ne s’affichaient pas dans mon menu…

C’était la faute d'Ubuntu

Signaux et Slots• Comment connecter un signal à un slot ?

• QObject::connect() • Quel code pour déclarer / implémenter un slot ?

• public slots: (.h) • Est ce qu’un slot peut retourner une valeur ?

• Oui • Quel code pour déclarer / implémenter un signal ?

• signals (.h) • emit (.cpp)

• Où et Quand j’utilise Q_OBJECT ? • Premiere élèment dans la class (.h) • signals et/ou slots

Compilation & QMake

• Quand est ce que je fais make ? • à chaque changement dans le code

• Quand est ce que je fais qmake ? • à chaque changement concernant les signaux et les

slots • Quand est ce que je fais qmake -project

• quand je rajoute / supprime un fichier

QWidgets• Quelle est la différence entre l’arbre d’héritage et l’arbre

d’instanciation? • héritage de classe (A hérite de B) • héritage d’instances (A contient B)

• Quelle classe utiliser pour créer une fenêtre • QMainWindow

• Quels sont les différents layouts? • QVBoxLayout; QHBoxLayout; QGridLayout; QFormLayout

• Comment afficher une trace? • #include <QDebug> • qdebug() << “ma trace”;

Dessin• De quelle classe doit-on hériter?

• QWidget • Quelle méthode doit-on réimplémenter?

• void paintEvent(QPaintEvent *e); • Comment demander le réaffichage d’un widget

• update() • repaint()

• Quelle est la différence entre update() et repaint()? • update() indique qu’une zone est à réafficher

(mais l’affichage n’est pas instantané) • repaint() réaffiche immédiatement

(mais peut introduire de la latence) • Comment dessiner dans paintEvent(QPaintEvent*)

• instancier un QPainter: QPainter(this)

9

Animations

State Machine Properties

State Machine

gilles.bailly@telecom-paristech.fr

Objectifs

• Introduction aux machines à états• création de votre machine à états avec Qt• Aller un peu plus loin…

Introduction

2

State Machine Framework

• Qu’est ce que c’est ?• API C++

• Créer, interprétater, exécuter

• Hierarchical Finite State Machine (HFSMs)

• W3C SCXML

• Pourquoi ?• Comportement

• QEvent

• void mousePressEvent(QMouseEvent*);

Difficile • à comprendre

• à maintenir

• à étendre

Solution• Machine à états

Constat

Prenons du recul

Machines à états finis

Example : rubber banding (repris de Scott Hudson - CMU)

Quel est le problème ?

Accept the press for endpoint p1;

P2 = P1;

Draw line P1-P2;

Repeat

Erase line P1-P2;

P2 = current_position();

Draw line P1-P2;

Until release event;

Act on line input;

Machines à états finis

Rubber banding

Problème - pas compatible avec la gestion événementielle

Accept the press for endpoint p1;

P2 = P1;

Draw line P1-P2;

Repeat

Erase line P1-P2;

P2 = current_position();

Draw line P1-P2;

Until release event;

Act on line input;

Rappel : gestion des événements

void paintEvent(QPaintEvent* e) { ......}

Cycle événement / réaffichage - on ne doit pas bloquer ce cycle ni ignorer les (autres) événements

événements

boucle de gestiondes événements

void mySlot() ...... update(); }

Définition d’une machine à états

(indépendent de Qt)

3

Machines à états (1/2): Etat

Une solution appropriée pour « maintenir l’état » !- simple et efficace pour modéliser les comportements- évite les «spaghettis de callbacks»- évite la multiplication des variables d’état- évite les erreurs ! - passage facile d’une représentation visuelle au code source- divers outils, UML, QState

Etat Etat de départ Etat final

NB: en IHM généralement pas d’état final : on revient à l’état initial

Machines à états (2/2) : Transitions

Représentées par des arcs avec un label : Evénement / Action Si on est dans l’état A et cet événement se produit

• cette action est effectuée• puis on passe dans l’état B

Remarque : les actions sont parfois sur les états • i.e. quand on entre / sort de l’état • au lieu d’être sur les transitions

B A

Mouse_Dn / Draw_Line()

Machines à états

Retour à notre «ligne élastique»

Accept the press for endpoint p1;

P2 = P1;

Draw line P1-P2;

Repeat

Erase line P1-P2;

P2 = current_position();

Draw line P1-P2;

Until release event;

Act on line input;

A

B

C

Machines à états

Accept the press for endpoint p1;

P2 = P1;

Draw line P1-P2;

Repeat

Erase line P1-P2;

P2 = current_position();

Draw line P1-P2;

Until release event;

Act on line input;

A

B

C

Press / A

Move / B

Release / C

Brainstorming <30s>

Quelle est la machine à état d’un bouton?

Machines à états

Autre exemple : bouton

Press-inside / A

Leave / B Enter / C

Release / E

Release / D

A: highlight button B: unhighlight button C: highlight button D: do button action E: do nothing

Machines à états

- Evénements : de plus ou moins haut niveau• peuvent aussi être des timeouts (cf. QTimer)

- Gardes • condition supplémentaire • notation : prédicat : événement / action • exemple: button.enabled: press-inside / A

Remarques

State fsm_transition(state, event) {

switch(state) {

case 1:

switch(event.kind) {

case MouseMove:

action_B();

state = 1;

case MouseRelease:

action_C()

state = 2;

}

break;

case 0:

switch(eventt.kind) {

case ...

}

}

return state;

}

SwingStates

Implementation Java - Caroline Appert, LRI

- http://swingstates.sourceforge.net/

- classe Canvas qui facilite le dessin interactif

- exemples d’interactions avancées

Créer sa machine à états avec Qt

4

QState QAbstractTransition

QStateMachine

QStateMachine machine;

QState *s1 = new QState(); QState *s2 = new QState(); QState *s3 = new QState();

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

machine.addState(s1); machine.addState(s2); machine.addState(s3); machine.setInitialState(s1);

machine.start();

s1->assignProperty(label, "text", "In state s1"); s2->assignProperty(button, "geometry", QRectF(0,0,50,50));

Property

connect(s3, SIGNAL(entered()), button, SLOT(showMaximized())); connect(s3, SIGNAL(exited()), button, SLOT(showMinimized()));

Signaux sur entrée /sortie d’états

QAbstractTransition * trans = s1->addTransition(obj, SiGNAL(mySignal(), s2);

connect(trans, SIGNAL(triggered()), myObject, SLOT(mySlot()));

Appel de méthode sur une transition

Questions/réponses• Quelles sont les trois composants (classes) d’une

machine à états • QStateMachine; QState; QAbstractTransition

• Comment traduire ca?

• s1->addTransition(button, SIGNAL(clicked()), s2); • Est ce que les actions sont exécutés sur les états ou

sur les transitions? • Transitions • Mais aussi à l’entrée et à la sortie des états

Un peu plus loin avec les machines à états

5 - machines à états hiérarchiques- types de transitions- transitions maisons

Hiérarchies// s11, s12, s13 sont groupés dans s1 QState *s1 = new QState( ); QState *s11 = new QState(s1); QState *s12 = new QState(s1); QState *s13 = new QState(s1); s11->addTransition(button, SIGNAL(clicked( )), s12); …etc… s1->setInitialState(s11); // s2 est un étal final QFinalState *s2 = new QFinalState( );

// les enfants de s1 héritent de la transition s1 -> s2 s1->addTransition(quitButton, SIGNAL(clicked( )), s2);

mac->addState(s1); mac->addState(s2); mac->setInitialState(s1); mac->start( );

HiérarchiesAvantages

- permettent de simplifier les schémas- les états enfants héritent des transitions des états parents

Exemple 2 - 4 radio boutons exclusifs - noter les transitions de l'état parent vers les enfants

solution hiérarchiquesolution « plate »

Types de transitionsPour les signaux

- QSignalTransition : c'est ce qu'on a utilisé jusqu'à présent via :s1->addTransition(button, SIGNAL(clicked( )), s2);

Pour les événements - QEventTransition- QKeyEventTransition- QMouseEventTransition

On peut aussi créer ses propres transitions - en dérivant QAbstractTransition

Header « maison » Transitions.hbutton.clicked / this.doIt()

QAbstractTransition * trans = A->addTransition(button, SiGNAL(clicked(), B);

connect(trans, SIGNAL(triggered()), this, SLOT(doIt())); C’est un peu verbeux…

addTrans(A,B, button, SiGNAL(clicked(), this, SLOT(doIt()));

Header « maison » Transitions.h Ce header définit

- des fonctions simplifiées pour les cas courants- cas des signaux :

QSignalTransition* addTrans( QState* from, QState* to, QObject* sender, const char* signal )

QSignalTransition* addTrans( QState* from, QState* to, QObject* sender, const char* signal, QObject* receiver, const char* slot )

addTrans(A,B, button, SiGNAL(clicked(), this, SLOT(doIt()));

Transitions sur les événementsEvénements quelconques QEventTransition* addEventTrans( QState* from, QState* to, QObject* source, QEvent::Type eventType )

QEventTransition* addEventTrans( QState* from, QState* to, QObject* source, QEvent::Type eventType, QObject* receiver, const char* slot )

Exemple

addEventTrans(out, in, widget, QEvent::Enter); addEventTrans(in, out, widget, QEvent::Leave );

Note - compatible avec événements ad hoc

(définis par le programmeur)

widget.Enter

widget.Leave

in out

Transitions sur les événements

// transition de s1 vers s2 quand le bouton gauche de la souris est pressé sur «canvas» addMouseTrans( s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton, this, SLOT(newLine( )) );

addMouseTrans( s2, s2, canvas, QEvent::MouseMove, Qt::NoButton, // ATT: NoButton pour MouseMove ! this, SLOT(adjustLine( )) );

addMouseTrans( s2, s1, canvas, QEvent::MouseButtonRelease, Qt::LeftButton );

Evénements souris - même principe- on peut spécifier les boutons

Transitions sur les événements

// il faut appuyer LeftButton et RightButton addMouseTrans( s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton | Qt::RightButton );

// double clic addMouseTrans( s1, s2, canvas, QEvent::MouseButtonDblClick, Qt::LeftButton );

// il faut appuyer SHIFT et LeftButton addMouseTrans( s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton ) ->setModifierMask( Qt::ShiftModifier );

Evénements souris (2) - variantes- modifieurs clavier

Transitions sur les événements

Problème addMouseTrans( s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton, this, SLOT(newLine( )) );

addMouseTrans( s2, s2, canvas, QEvent::MouseMove, Qt::NoButton, this, SLOT(adjustLine( )) );

addMouseTrans( s2, s1, canvas, QEvent::MouseButtonRelease, Qt::LeftButton );

newLine() et adjustLine() n'ont pas de paramètre QEvent !

=> comment peuvent-elles accéder à la position du curseur ?

Position du curseurSolution 1 QPoint cursorPos(QWidget* w) { return w->mapFromGlobal(QCursor::pos( )); }

- attention : petit décalage possible sur systèmes asynchrones (e.g. X11 en réseau)

Position du curseurSolution 2

- variante de addMouseTrans( ) - pos contiendra la position du curseur mise à jour à chaque transition

QPoint pos;

addMouseTrans( s2, s2, canvas, QEvent::MouseMove, Qt::NoButton, pos);

Properties

gilles.bailly@telecom-paristech.fr

Animations

State Machine Properties

QProperty

• The Meta-object system (QObject) • Introspection des objects

• utile pour QtDesigner

51

QWidget* w = new QLabel();Question : Quels sont les propriétés de w?

Réponse : w->dynamicProperties();

QProperty class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Type m_var READ var WRITE setVar)

public:void setVar(const Type& v){ m_var = v;}const Type& var(){ return m_var; }

protected:Type m_var;

};

//Type parmis : // int, float, QRect, QPos, QSize, etc.

QPushButton *button = new QPushButton;QObject *object = button;

button->setFlat(true);object->setProperty("flat", true); lent

rapide

Animations

gilles.bailly@telecom-paristech.fr

Animations

• Définition: • Une série d’images. Chaque image est montrée

pendant un temps fixe. • Approches

• QTimer • QTimeLine • QAnimation framework

56

QTimerQTimer* timer = new QTimer(); connect(timer, SIGNAL( timeout() ),

this, SLOT( doSomething() ) ); timer->setSingleShot(false); //est répété indéfiniment timer->start(1000); … timer->stop();

57

Simple, mais peu puissant

QTimeLine

58

QProgressBar *progress = new QProgressBar(); progress->setRange(0,100);

QTimeLine* timeLine = new QTimeLine(1000); //duration = 1s (1000ms) timeLine->setFrameRange(0,100); //par défaut [0;40] connect(timeLine, SIGNAL( frameChange(int) ),

progress, SLOT( setValue(int) ) ); timeLine->setLoopCount(10); //répété 10 fois

timeLine->setCurveShape(Qt::EaseInOutCurve); //par défaut.

un peu plus puissant

59

//Linear, InQuad, OutQuad, InOutQuad, ..., Custom (45 différentes)

QTimeLine: CurveShape

Animation Framework

• permet d’animer des Objects (et donc des QWidgets)

• S’appuie sur les QProperty • peut aussi s’appuyer sur les machines à

états. • Plus de fonctionnalités

• ex: groupes

60

Plus simple, plus puissant, meilleure intégration

QPropertyAnimation animation(myWidget, "geometry"); animation.setDuration(10000); animation.setStartValue(QRect(0, 0, 100, 30)); animation.setEndValue(QRect(250, 250, 100, 30));

animation.setEasingCurve(QEasingCurve::InOutSine); //Linear, InQuad, OutQuad, InOutQuad, ..., Custom (45 différentes)

animation.start();//animation.stop();

QProperty

Animation linéaire par défaut

Exemple

QPropertyAnimation animation(myWidget, "geometry"); animation.setDuration(10000); animation.setStartValue(QRect(0, 0, 100, 30)); animation.setEndValue(QRect(250, 250, 100, 30));

animation.setKeyValueAt(0,QRect(0, 0, 100, 30)); animation.setKeyValueAt(0.8,QRect(100, 150, 100, 30)); animation.setKeyValueAt(1,QRect(250, 250, 100, 30));

animation.setEasingCurve(QEasingCurve::InOutSine); //Linear, InQuad, OutQuad, InOutQuad, ..., Custom (45 différentes)

animation.start();//animation.stop();

QProperty

Exemple

La base de toute animation

(start, stop, pause)

Contrôler les propriétés des widgets (color; size; etc.)

Gérer des groupes d'animations

Animation Framework

• Peut être utilisé tout seul… …mais aussi avec les machines à états

64

Animation & State Machine

QState *s1 = new QState(); s1->assignProperty(button, "geometry", QRect(0, 0, 100, 30));

QState *2 = new QState(); s2->assignProperty(button, "geometry", QRect(250, 250, 100, 30));

QSignalTransition *trans1 = s1->addTransition(button, SIGNAL(clicked()), s2);

trans1->addAnimation(new QPropertyAnimation(button, "geometry"));

QSignalTransition *trans2 = state2->addTransition(button, SIGNAL(clicked()), s1);

trans2->addAnimation(new QPropertyAnimation(button, "geometry"));

Questions/réponses• Quelles sont les 3 approches pour les animations

• QTimer; QTimeLine; QAnimationFramework • Sur quels concepts s’appuient le

Animation Framework • QProperty and QStateMachine

• Comment définit on une QProperty • Q_OBJECT • QPROPERTY(Type m_var READ var WRITE setVar)

QStateMachine machine;

QState *s1 = new QState(); QState *s2 = new QState(); QState *s3 = new QState();

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

machine.addState(s1); machine.addState(s2); machine.addState(s3); machine.setInitialState(s1);

machine.start();

Qt Designer

gilles.bailly@telecom-paristech.fr

Génère une description xml de votre interface (.ui)

QProperties

Génère une description xml de votre interface (.ui)

Comment intégrer les ".ui" dans votre programme?

3 approches• Approche 1

– On « charge » notre widget dans notre programme

• Approche 2 – Héritage simple

• Approche 3 – Héritage multiple

Approche 1

• Fichiers – main.cpp – calculatorform.ui

– calculator.pro

TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp

Approche 1

• main.cpp #include "ui_calculatorform.h"

int main (int argc, char *argv[]) { Qapplication app(argc, argv); Qwidget *widget = new Qwidget(); Ui::CalculatorForm ui; ui.setupUi(widget);

widget->show(); return app.exec(); }

uic: transforme un fichier xml (myform.ui) en un fichier d’entête (ui_myform_.h

Approche 1

• Avantages : – rapide et facile à intégrer

• Inconvénients : – peu de flexibilité – Comment afficher le résultat dans le Qlabel ?

Approche 2: héritage simple

• Fichiers – main.cpp – calculatorform.ui – calculatorform.h – calculator.pro

TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp HEADERS = calculatorform.h

• main.cpp#include "calculatorform.h" (pas de ui)

int main (int argc, char *argv[]) { Qapplication app(argc, argv); calculator widget;

widget.show(); return app.exec(); }

Approche 2: héritage simple

• calculatorForm.h#include "ui_calculatorform.h"

Class Calculator : public QWidget { Q_OBJECT public: CalculatorForm(Qwidget *parent =0);

private slots: void on_inputSpinBox1_valueChanged(int value); void on_inputSpinBox2_valueChanged(int value); private Ui::CalculatorForm ui; }

Auto-connect

Approche 2: héritage simple

• calculatorForm.cpp

CalculatorForm::CalculatorForm(Qwidget *parent):Qwidget( parent) { ui.setupUi(this); }

void CalculatorForm::on_inputSpinBox1_valueChanged(int value) { QString res =QString::number(value + ui.inputSpinBox2->value()); ui.outputWidget->setText(res); }

Approche 2: héritage simple

• Avantages : – Plus flexible

• Inconvénients : – Un peu plus de code …

Approche 2: héritage simple

Approche 3 : héritage multiple

• Fichiers – main.cpp – calculatorform.ui – calculatorform.h – calculator.pro

TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp HEADERS = calculatorform.h

Pareil que l’approche 2

• main.cpp#include "calculatorform.h » (pas de ui)

int main (int argc, char *argv[]) { Qapplication app(argc, argv); CalculatorForm widget;

widget.show(); return app.exec(); }

Approche 3 : héritage multiple

• calculatorForm.h

#include "ui_calculatorform.h"

Class CalculatorFrom : public QWidget, private Ui::CalculatorForm { Q_OBJECT public: CalculatorForm(Qwidget *parent =0);

}

Approche 3 : héritage multiple

• calculatorForm.cpp

CalculatorForm::CalculatorForm(Qwidget *parent):Qwidget( parent) { setupUi(this); (différent de ui.setupUi(this))

inputSpinBox1->…. (acces direct aux éléments) }

Approche 3 : héritage multiple

• Avantages : – Encore plus flexible

• Acces direct aux variables – Fichier généré automatiquement dans Qt

Creator • Inconvénients :

– Un peu plus de code …

Approche 3 : héritage multiple

Recommended