Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
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
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);
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
• 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();
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