Vous êtes sur la page 1sur 29

Toolkit Qt Petit cours Qt

bases graphique designer Eric Lecolinet - Tlcom ParisTech www.telecom-paristech.fr/~elc Toolkit graphique complet en C++
- Puissant et relativement simple - Nombreux outils et extensions - Utilis dans de nombreux produits, base de l'environnement KDE - Dvelopp par TrollTech, puis Nokia - Licences LGPL (gratuite) et commerciale

Toolkit Qt
Toolkit graphique complet en C++
- Puissant et relativement simple - Nombreux outils et extensions - Utilis dans de nombreux produits, base de l'environnement KDE - Dvelopp par TrollTech, puis Nokia - Licences LGPL (gratuite) et commerciale

Extensions, outils
Extensions et outils
- internationalisation: QString et Unicode - support sockets, XML, SQL, etc. - Open GL multiplateformes - Outils de dveloppement:

Toolkit multi-plateformes
- Principaux OSs : Windows, Mac OS X, Linux/X11 - Plate-formes mobiles : Symbian, MeeGo, Embedded Linux, Windows Mobile

QtDesigner IDE QtCreator

Liens
Liens utiles
- Site Tlcom : http://www.telecom-paristech.fr/~elc/qt - Site Nokia: http://qt.nokia.com/ - Documentation Qt 4.6 : http://doc.qt.nokia.com/4.6/ http://doc.qt.nokia.com/4.6/classlist.html http://doc.qt.nokia.com/4.6/classes.html - Documentation Qt 4.7 : http://doc.qt.nokia.com/4.7/

Principaux widgets
Arbre d'hritage

Hello Word! : premire version


#include <QApplication> #include <QLabel> int main(int argc, char **argv) { QApplication* app = new QApplication(argc, argv); QLabel* hello = new QLabel("Hello Qt!"); hello->show( ); return app->exec( ); }

Deuxime version
#include <QApplication> #include <QLabel> int main(int argc, char **argv) { QApplication app(argc, argv); QLabel hello("Hello Qt!"); hello.resize(100, 30); hello.show( ); return app.exec( ); }

Remarques
- pointeurs C++ : attention aux -> et aux * - pas de ramasse miette ! - parent pass en argument ou NULL (par dfaut) pour "top-level" widgets - exec() lance la boucle de gestion des vnements

Remarques
- objets crs dans la pile => dtruits en n de fonction - la variable contient l'objet: . (point) pour accder aux champs - NB Qt3: il faudrait aussi: app.setMainWidget(&hello)

3e version avec conteneur


#include <QApplication> // inclure headers adquats ! #include <QPushButton> #include <QWidget> #include <QFont> int main(int argc, char **argv) { QApplication app(argc, argv); QWidget box; // ne pas mettre de ( ) box.resize(200, 120); QPushButton quitBtn("Quit", &box); quitBtn.resize(100, 50); quitBtn.move(50, 35); quitBtn.setFont( QFont("Times", 18, QFont::Bold) ); QObject::connect( &quitBtn, SIGNAL(clicked( )), &app, SLOT(quit( )) ); box.show(); return app.exec(); }

Signaux et slots
Un signal est mis vers le monde extrieur
- par un objet (QObject, QWidget...) - quand il change d'tat - on nindique pas qui il sadresse

Noter : - parent de quitBtn pass en argument - connect( ) ..

Un slot est un rcepteur


- un slot est une mthode

Les signaux sont connects des slots


- ils sont alors appels automatiquement

Signaux et slots
Modularit, exibilit
- le mme signal peut tre connect plusieurs slots - plusieurs signaux peuvent tre connects un mme slot

Connexion
Typage fort
- les types des paramtres des signaux et des slots doivent tre les mmes - un slot peut avoir moins de paramtres qu'un signal
QObject::connect( &quitBtn, SIGNAL(clicked( )), &app, SLOT(quit( )) );

De plus
- l'metteur n'a pas besoin de connatre le ou les rcepteur(s) - il ne sait pas si le signal est reu - le rcepteur n'a pas non plus besoin de connatre l'metteur => vers modle multi-agents ou programmation par composants

QObject::connect( &x, SIGNAL(balanceChanged(int)), &y, SLOT(setBalance(int)) ); QObject::connect( &x, SIGNAL(balanceChanged(int)), &app, SLOT(quit()) );

Remarques
- aspect central de Qt - diffre de l'habituel mcanisme des callbacks ou Listeners

Avantages / inconvnients
- SLOT et SIGNAL sont des macros = > phase de prcompilation

Dclaration de slot
Dans le chier header (.h)
class BankAccount : public QObject { Q_OBJECT private: int curBalance; public: BankAccount( ) { curBalance = 0; } int getBalance( ) const { return curBalance; } public slots: void setBalance( int newBalance ); signals: void balanceChanged( int newBalance ); };

Dnition de slot
Dans le chier source de l'implmentation (.cpp)
void BankAccount::setBalance(int newBalance) { if ( newBalance != curBalance ) { curBalance = newBalance; emit balanceChanged(curBalance); } }

- mot-cl emit pour prcompilateur - provoque l'mission du signal balanceChanged avec la nouvelle valeur de curBalance

- sous classe de QObject - mot-cls Q_OBJECT, slots et signals pour prcompilateur - signaux pas implments, slots doivent tre implments

Dnition de slot
Dans le chier source de l'implmentation (.cpp)
void BankAccount::setBalance(int newBalance) { if ( newBalance != curBalance ) { curBalance = newBalance; emit balanceChanged(curBalance); } }

Connexion
Connexion simple
BankAccount x, y; QObject::connect( &x, SIGNAL(balanceChanged(int)), &y, SLOT(setBalance(int)) ); x.setBalance( 2450 );

- x est mis 2450 - le signal balanceChanged() est mis - il est reu par le slot setBalance() de y - y est mis 2450

- mot-cl emit pour prcompilateur - provoque l'mission du signal balanceChanged avec la nouvelle valeur de curBalance - Attention : vrier que la valeur a effectivement chang pour viter les boucles innies !

Connexion
Connexion dans les deux sens
QObject::connect( &x, SIGNAL(balanceChanged(int)), &y, SLOT(setBalance(int)) ); QObject::connect( &y, SIGNAL(balanceChanged(int)), &x, SLOT(setBalance(int)) ); x.setBalance( 2450 );

Compilation
Meta Object Compiler (MOC)
- pr-processeur C++ - gnre du code supplmentaire (tables de signaux /slots) - permet aussi de rcuprer le nom de la classe et de faire des test d'hritage - attention: ne pas oublier le mot-cl Q_OBJECT

- Attention : bouclerait si on navait pas fait un test dans setBalance() !

Utilisation de qmake
- dans le rpertoire contenant les chiers sources faire: qmake -project qmake make // cre le chier xxx.pro (dcrit le projet) // cre le chier Makele (ou quivalent si IDE) // cre les chiers moc (un par chier ayant des slots), // et les chiers binaires (*.o et excutable

Fentre principale
(QMainWindow)
barre de menu barre doutils barre de statut zone centrale

Fentre principale
Dans le constr. de la classe lle de QMainWindow // menuBar() est une methode de QMainWindow QMenuBar* menubar = menuBar( ); QMenu* lemenu = menubar->addMenu( tr("&File") ); // new.png est un chier qui sera spci dans une chier de ressources .qrc QAction* new_action = new QAction( QIcon(":/images/new.png"), tr("&New..."), this ); new_action->setShortcut( tr("Ctrl+N") ); new_action->setToolTip( tr("New le") ); new_action->setStatusTip( tr("New le") ); lemenu->addAction( new_action ); // acclrateur clavier // bulle daide // barre de statut // rajouter laction au menu droulant

Zones prdnies pour

- (et dautres fonctionnalits...)

Usage
- crer une sous-classe de QMainWindow - crer ses enfants dans son constructeur

// connecter le signal un slot de this connect( new_action, SIGNAL(triggered( )), this, SLOT(open( )) );

Fentre principale
Actions
- les actions peuvent sajouter la fois dans les menus et les toolbars
QToolBar* toolbar = addToolBar( tr("File") ); toolbar->addAction(new_action);

Botes de dialogue
QFileDialog, QMessageBox

Widget central
QTextEdit* text = new QTextEdit(this); setCentralWidget( text );

tr( )
- permet de traduire le texte (localisation) - ( suivre)

Bote de dialogue modale


Solution gnrale
QFileDialog dialog (parent); dialog.setFilter ("Text les (*.txt)"); QStringList le_names; if (dialog.exec() == QDialog::Accepted) { le_names = dialog.selectedFiles(); QString rst_name = le_names[0]; ... }

QString
Codage Unicode 16 bits
- Suite de QChars 1 caractre = 1 QChar de 16 bits (cas usuel) 1 caractre = 2 QChars de 16 bits (pour valeurs > 65535) - Conversions dune QString : toAscii( ) : ASCII 8 bits

Solution simplie
QString leName = QFileDialog::getOpenFileName( this,

Note : dialog.exec() lance une boucle de gestion des vnements secondaire !

toLatin1( ) : Latin-1 (ISO 8859-1) 8 bits toUtf8( ) : UTF-8 Unicode multibyte (1 caractre = 1 4 octets) toLocal8Bit( ) : codage local 8 bits - qPrintable ( const QString & str ) quivalent : str.toLocal8Bit( ).constData( )

tr("Open Image"), "/home/jana", tr("Image Files (*.png *.jpg *.bmp)") );

// titre // rpertoire initial // ltre

QFile
QFile
- lecture, criture de chiers - exemples :
QFile le( leName ); if ( le.open( QIODevice::ReadOnly | QIODevice::Text) ) ...; if ( le.open( QIODevice::WriteOnly ) ) ...;

QTextStream
QTextStream
- lecture ou criture de texte depuis un QFile :
QTextStream stream( &le );

Amlioration des iostream du C++


- compatibles avec QString et Unicode (et autres codecs de caractres) - oprateurs << et >> : exemples :
outStream << string; inStream >> string; // attention : sarrte au premier espace // 0 signie : pas de limite de taille

- mais aussi :
QString readLine( maxlen = 0 ); QString readAll( ); // pratique mais nutiliser que pour des petits chiers

- codecs :
setCodec( codec ), setAutoDetectUnicode( bool );

Layout
Problmes
- internationalisation - retaillage - complexit du code

Layout : exemple
QVBoxLayout *v_layout = new QVBoxLayout( ); v_layout->addWidget( new QPushButton( "OK" ) ); v_layout->addWidget( new QPushButton( "Cancel" ) ); v_layout->addStretch( 1 ); v_layout->addWidget( new QPushButton( "Help" ) ); QListBox *country_list = new QListBox( this ); countryList->insertItem( "Canada" ); ...etc... QHBoxLayout *h_layout = new QHBoxLayout( ); h_layout->addWidget( country_list ); h_layout->addLayout( v_layout ); QVBoxLayout *top_layout = new QVBoxLayout( ); topLevelBox->addWidget( new QLabel( "Select a country", this ) ); topLevelBox->addLayout( h_layout ); window->setLayout( top_layout ); window->show( );

Notes sur layouts :


- peuvent tre embots - pas lis une hirarchie de conteneurs (cf. Java) - cf. le stretch

QHBoxLayout, QVBoxLayout, QGridLayout

QFormLayout

Styles
Emulation du "Look and Feel"
- Look and feel simul et paramtrable (comme Swing) - rapidit, exibilit, extensibilit, - pas restreint un "dnominateur commun"

Ressources
Fichier source
QAction* newAct = new QAction( QIcon(":/images/new.png"), tr("&New..."), this ); newAct->setShortcut( tr("Ctrl+N") ); // : signie: relatif au programme // tr( ) pour ventuelle traduction // lacclrateur clavier peut tre traduit

QStyle
- QApplication::setStyle( new MyCustomStyle );

Fichier .qrc
- cr la main ou par QtCreator
<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>images/copy.png</file> <file>images/cut.png</file> <file>images/new.png</file> <file>images/open.png</file> <file>images/paste.png</file> <file>images/save.png</file> </qresource> </RCC>

Graphique 2D
Deux modles
- QPainter : modle fonctionnel - Graphics View : modle objet

Graphique 2D
Deux modles
- QPainter : modle fonctionnel - Graphics View : modle objet
QGraphicsScene scene; QGraphicsRectItem *rect = scene.addRect( QRectF(0,0,100,100) ); QGraphicsItem *item = scene.itemAt(50, 50); // item == rect ..... QGraphicsView view( &scene ); view.show( );

QPainter
- modle graphique volu (rotations, indpendance / pilote ...) - dessin par appel de mthodes - similaire Graphics2D en Java - exemple : QPainter painter( this ); // this est un widget painter.setPen( QPen(red, 2, DashLine) ); painter.drawRect( 25, 15, 120, 60 ); - section ddie dans la suite du cours

Graphics View
- graphique structur reprsentation objet du dessin rafraichissement automatique, fonctions de picking ... - vues dun graphe de scne : QGraphicsScene QGraphicsView QGraphicsItem, etc.

Graphique 2D
QIcon
QPushButton *button = new QPushButton( "&Find Address", parent ); button->setIcon( QIcon(":/images/new.png")) ); - cf chiers de ressources .qrc

Graphique 3D
3D
- via OpenGL sur QGLWidget - 3 mthodes rednir :
virtual void initializeGL() virtual void paintGL() virtual void resizeGL(int w, int h)

QImage, QPixmap
- QImage: optimis accs/manipulation des pixels - QPixmap, QBitmap : optimiss pour afchage lcran - ( suivre)

- Note: QtGraphicsView est compatible avec OpenGL

OpenGL : header Box3D


#include <QGLWidget> class Box3D : public QGLWidget { Q_OBJECT GLuint object; GLoat rotX, rotY, rotZ; public: Box3D( QWidget *parent = 0); ~Box3D(); protected: virtual void initializeGL(); virtual void paintGL(); virtual void resizeGL( int w, int h ); virtual GLuint makeObject(); } public slots: void setRotationX(int deg) { rotX = deg; updateGL(); } void setRotationY(int deg) { rotY = deg; updateGL(); } void setRotationZ(int deg) { rotZ = deg; updateGL(); } };

OpenGL : main
#include <qapplication.h> #include <qslider.h> #include <qvbox.h> #include "box3d.h" void createSlider( QWidget * parent, Box3D * box3d, const char * slot) // cf. le type de slot ! { QSlider *slider = new QSlider(0, 360, 60, 0, QSlider::Horizontal, parent); slider->setTickmarks(QSlider::Below); QObject::connect( slider, SIGNAL(valueChanged(int)), box3d, slot);

OpenGL : main
int main(int argc, char **argv) { QApplication::setColorSpec(QApplication::CustomColor); QApplication app(argc, argv); if (!QGLFormat::hasOpenGL( )) qFatal("This system has no OpenGL support"); QVBox * parent = new QVBox( ); parent->setCaption("OpenGL Box"); parent->setMargin(11); parent->setSpacing(6); Box3D *box3d = new Box3D(parent); createSlider( parent, box3d, SLOT(setRotationX(int)) ); createSlider( parent, box3d, SLOT(setRotationY(int)) ); createSlider( parent, box3d, SLOT(setRotationZ(int)) ); parent->resize( 250, 250 ); parent->show( ); return app.exec( ); }

OpenGL : implmentation Box3D


#include "box3d.h" Box3D::Box3D( QWidget *parent ) : QGLWidget( parent ) { object = 0; rotX = rotY = rotZ = 0.0; } Box3D::~Box3D() { makeCurrent(); glDeleteLists(object, 1); } void Box3D::initializeGL() { qglClearColor( darkBlue ); object = makeObject(); glShadeModel(GL_FLAT); } void Box3D::paintGL() { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); glRotatef(rotX, 1.0, 0.0, 0.0); glRotatef(rotY, 0.0, 1.0, 0.0); glRotatef(rotZ, 0.0, 0.0, 1.0); glCallList(object); } void Box3D::resizeGL( int w, int h ) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0,1.0,-1.0,1.0,5.0,15.0); glMatrixMode( GL_MODELVIEW ); }

. . . .

OpenGL : implmentation Box3D


GLuint Box3D::makeObject() { GLuint list = glGenLists( 1 ); glNewList( list, GL_COMPILE ); qglColor( yellow ); glLineWidth( 2.0 ); glBegin( GL_LINE_LOOP ); glVertex3f( +1.5, +1.0, +0.8 ); glVertex3f( +1.5, +1.0, -0.8 ); /* ... */ glEnd(); glEndList(); return list; }

Dessin interactif
Illustr avec Qt

Rafrachissement
Le widget est repeint
- Lorsque une fentre passe au dessus - Lorsque lon dplace le composant - Lorsque lon le lui demande explicitement

Gestion des vnements


void mySlot()

vnements

boucle de gestion des vnements


}

Modle damaged / repaint


- update( ) met une demande de rafrachissement en le dattente - repaint( ) entrane un rafraichissement immdiat ( viter) Dans tous les cas, cest la mthode : void paintEvent (QPaintEvent* e)
void paintEvent(QPaintEvent* e) { ...... }

...... update(); ...... update(x, y, w, h); ...... update(region1); update(region2); ......

paintEvent est appele une seule fois


- au retour dans la boucle de gestion des vnements - les demandes de mise jour par update() sont empils et compacts

qui est appele

Dessiner dans un widget


Rednir QWidget::paintEvent()
#include <QWidget> class MyWidget : public QWidget { public: MyWidget(QWidget* parent); protected: void paintEvent(QPaintEvent* e ); };
____________________________________________________________________________________________________________________________________________________________________________

Dtecter les vnements


- lorsquon presse un bouton
Header (chier .h)

- lorsquon relche un bouton - lorsquon double-clique - lorsquon dplace la souris - en appuyant ou pas sur un bouton (mouseTracking)

void mousePressEvent( QMouseEvent* e);


void mouseReleaseEvent( QMouseEvent* e); void mouseDoubleClickEvent( QMouseEvent* e); void mouseMoveEvent( QMouseEvent* e); void setMouseTracking(bool) et bool hasMouseTracking ()

#include <QPainter> #include MyWidget

Implmentation (chier .cpp)

void MyWidget::paintEvent(QPaintEvent* e) { ! QWidget::paintEvent(e); // effectue le comportement standard (afcher le fond...) ! QPainter painter(this); // cre un Painter pour linstance de MyWidget ! painter.drawLine(50, 10, 100, 20); }

Attention : nutiliser QPainter que dans paintEvent() ( cause du double buffering)

Dtecter les vnements


Header (chier .h)
#include <QWidget> #include <QMouseEvent> class MyWidget : public QWidget { public: MyWidget(); protected: void mousePressEvent(QMouseEvent*); }; #include <MyWidget> void MyWidget:: mousePressEvent(QMouseEvent* e) { ! if (e.button() == Qt::LeftButton) { ...... ...... ...... ! update(); // demande de rafraichissement }

Synthse
mousePressEvent(QMouseEvent* e)

Implmentation (chier .cpp) vnements


}

...... ...... update();

boucle de gestion des vnements

mouseMoveEvent(QMouseEvent* e) ...... ...... update(); } mouseMoveEvent(QMouseEvent* e) ...... ...... update(); }

! ! }

paintEvent(QPaintEvent* e) { ! ! } QWidget::paintEvent(e); QPainter.painter(this); ......

QMouseEvent : permet de rcuprer - le bouton qui a dclench lvnement - ltat des autres boutons (cliqus ou non) - la position de la souris (globale et locale)

mouseReleaseEvent(QMouseEvent* e) ...... ...... update(); }

Dessin

QPainter
Attributs
- setPen( ) : lignes et contours - setBrush( ) : remplissage - setFont( ) : texte - setTransform( ), etc. : transformations afnes - setClipRect/Path/Region( ) : clipping (dcoupage) - setCompositionMode( ) : composition

QPainter : outil de dessin QPaintDevice : objet sur lequel on peut dessiner QPaintEngine : moteur de rendu On peut dessiner dans tout ce qui hrite de QPaintDevice

- en particulier QWidget - mais aussi: QPrinter, QPixmap, QImage, QPicture


QGLPixelBuffer, QGLFrameBufferObject

QPainter
Lignes et contours
- drawPoint(), drawPoints() - drawLine(), drawLines() - drawRect(), drawRects() - drawArc(), drawEllipse() - drawPolygon(), drawPolyline(), etc... - drawPath() : chemin complexe

QPainter
Classes utiles
- entiers: QPoint, QLine, QRect, QPolygon - ottants: QPointF, QLineF, ... - chemin complexe: QPainterPath - zone dafchage: QRegion

Remplissage
- llRect(), llPath()

Divers
- drawText() - drawPixmap(), drawImage(), drawPicture() - etc.

Pinceau: QPen
Attributs
- style : type de ligne - width : paisseur (0 = cosmetique) - brush : attributs du pinceau (couleur...) - capStyle : terminaisons - joinStyle : jointures Cap Style Qt::PenStyle

Pinceau: QPen
Exemple
QPen pen; // creates a default pen

pen.setStyle(Qt::DashDotLine); pen.setWidth(3); pen.setBrush(Qt::green); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin);

Qt::PenStyle

Cap Style
// dans PaintEvent() QPainter painter(this); painter.setPen(pen);

Join Style

Join Style

Remplissage: QBrush
Attributs
- style - color - gradient - texture Qt::BrushStyle

Remplissage: QBrush
Attributs
- style - color - gradient - texture

QBrush brush( ... ); ..... QPainter painter( this ); painter.setBrush( brush );

QColor
- modles RGB, HSV or CMYK - composante alpha (transparence) : alpha blending - couleurs prdnies: Qt::GlobalColor

Qt::GlobalColor

Remplissage: gradients
Type
- lineaire - radial - conique QLinearGradient QRadialGradient

Composition
Modes de composition
- oprateurs de Porter Duff - exemples : SourceOver : dfaut, permet alpha blending Source = SourceOver avec source opaque - diverses limitations selon implmentation et Paint Device

QLinearGradient gradient(QPointF(0, 0), QPointF(100, 100)); gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, Qt::blue); QBrush brush(gradient);

QConicalGradient

Mthode: QPainter::setCompositionMode( )

rptition: setSpread()

Dcoupage (clipping)
Dcoupage
- selon un rectangle, une rgion ou un path - QPainter::setClipping(), setClipRect(), setClipRegion(), setClipPath()

Transformations afnes
Transformations
- translate() - rotate() - scale()

Dmo !

QRegion
- united(), intersected(), subtracted(), xored()
QRegion r1(QRect(100, 100, 200, 80), QRegion::Ellipse); QRegion r2(QRect(100, 120, 90, 30)); QRegion r3 = r1.intersected(r2); QPainter painter(this); painter.setClipRegion(r3); ...etc... // paint clipped graphics // r1: elliptic region // r2: rectangular region // r3: intersection

- shear() - setTransform()
QPainter painter( this ); painter.setRenderHint( QPainter::Antialiasing ); painter.translate( width( ) / 2, height( ) / 2 ); painter.scale( side/200.0, side/200.0 ); painter.save( ); // empile ltat courant painter.rotate( 30.0 * ((time.hour() + time.minute() / 60.0)) ); painter.drawConvexPolygon( hourHand, 3 ); painter.restore( ); // dpile

Hints et Antialiasing
Render hints
- hint = option de rendu effet non garanti dpend de limplmentation et du matriel - QPainter::setRenderingHints()

Antialiasing et cordonnes
Epaisseurs impaire
- pixels dessins droite et en dessous Note
QRect::right() = left() + width() -1 QRect::bottom() = top() + height() -1 Mieux : QRectF (en ottant)

Dessin anti-alias
- pixels rpartis autour de la ligne idale

Anti-aliasing
- viter leffet descalier QPainter::Antialiasing
QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::darkGreen); painter.drawLine(2, 7, 6, 1);

QPainterPath
QPainterPath
- gure compose dune suite arbitraire de lignes et courbes afche par: QPainter::drawPath() peut aussi servir pour remplissage, prolage, dcoupage

QPainterPath
QPointF center, startPoint; QPainterPath myPath; myPath.moveTo( center ); myPath.arcTo( boundingRect, startAngle, sweepAngle ); QPainter painter( this ); painter.setBrush( myGradient ); painter.setPen( myPen ); painter.drawPath( myPath ); -----------------------------------------------------------------------------QPointF baseline(x, y); QPainterPath myPath; myPath.addText( baseline, myFont, "Qt" ); QPainter painter( this ); ... etc.... painter.drawPath( myPath );

Mthodes
- dplacements: moveTo(), arcMoveTo() - dessin: lineTo(), arcTo() - courbes de Bezier: quadTo(), cubicTo() - addRect(), addEllipse(), addPolygon(), addPath() ... - addText() - translate(), union, addition, soustraction... - et dautres encore ...

Dmo !

QPainterPath
QPainterPath path; path.addRect(20, 20, 60, 60); path.moveTo(0, 0); path.cubicTo(99, 0, 50, 50, 99, 99); path.cubicTo(0, 99, 50, 50, 0, 0); QPainter painter(this); painter.llRect(0, 0, 100, 100, Qt::white); painter.setPen(QPen(QColor(79, 106, 25), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); painter.setBrush(QColor(122, 163, 39)); painter.drawPath(path);

Surfaces dafchage

Qt::OddEvenFill (dfaut)

Qt::WindingFill

Images
Types dimages
- QImage: optimis pour E/S et accs/manipulation des pixels QPixmap, QBitmap : optimiss pour afchage lcran - QPicture: pour enregistrer et rejouer les commandes dun QPainter - dans tous les cas : on peut dessiner dedans avec un QPainter

Accs aux pixels


Format 32 bits : accs direct
QImage image(3, 3, QImage::Format_RGB32); QRgb value; value = qRgb(122, 163, 39); // 0xff7aa327 image.setPixel(0, 1, value); image.setPixel(1, 0, value)

Format 8 bits : index Entres/sorties


- load() / save() : depuis/vers un chier, principaux formats supports - loadFromData() : depuis la mmoire
QImage image(3, 3, QImage::Format_Indexed8); QRgb value; value = qRgb(122, 163, 39); // 0xff7aa327 image.setColor(0, value); value = qRgb(237, 187, 51); // 0xffedba31 image.setColor(1, value); image.setPixel(0, 1, 0); image.setPixel(1, 0, 1);

QPicture
Enregistrer et rejouer les commandes dun QPainter
- Enregistrer :
QPicture picture; QPainter painter; painter.begin( &picture ); painter.drawEllipse( 10,20, 80,70 ); painter.end( ); picture.save( "drawing.pic" ); // paint in picture // draw an ellipse // painting done // save picture

Autres surfaces dafchage


SVG
- QSvgWidget QSvgRenderer

OpenGL
- QGLWidget QGLPixelBuffer QGLFramebufferObject QSvgWidget

- Rejouer :
QPicture picture; picture.load( "drawing.pic" ); QPainter painter; painter.begin( &myImage ); painter.drawPicture( 0, 0, picture ); painter.end( ); // load picture // paint in myImage // draw the picture at (0,0) // painting done

Impression
- QPrinter

Picking
Picking avec QRect, QREctF
- intersects() - contains()

Picking / Interaction
Exemple
- teste si la souris est dans le rectangle quand on appuie sur le bouton de la souris - met jour la position du rectangle pour quil soit centr
QRect rect; // variable dinstance de mon Widget de dessin

Picking avec QPainterPath


- intersects(const QRectF & rectangle) - intersects(const QPainterPath & path) - contains(const QPointF & point) - contains(const QRectF & rectangle) - contains(const QPainterPath & path)

Retourne lintersection
- QPainterPath intersected(const QPainterPath & path)

void mousePressEvent(QMouseEvent* e) { if (rect.contains( e->pos() )) { rect.moveCenter( e->pos() ); update( ); } ... void paintEvent(QPaintEvent* e ) { QPainter painter( this ); ..... // specier attributs graphiques painter.drawRect( rect ); }

Performance de lafchage
Problmes classiques :
- Flicking - Tearing - Lag

Flicking
Flicking
- scintillement de lafchage - car loeil peroit les images intermdiaires - exemple : rafraichir cette page tout effacer (peindre en blanc) puis tout redessiner (effet plus fort si le fond est sombre)

source: AnandTech

Double buffering
Double buffering
- solution au icking : dessin dans back buffer recopie dans front buffer - par dfaut avec Qt4

Double buffering
Page ipping
- implmentation frquente du double buffering - pas de recopie des pixels, on change juste de buffer - vite le tearing si synchro avec lafchage (vsync)

Possible problme : Tearing


- limage apparait en 2 parties horizontales - recopie avant que le dessin soit complet => mlange de 2 images source: AnandTech

src: Oracle/JavaSE

Triple buffering
Triple buffering
- comme double buffering mais plus rapide - un back buffer est toujours disponible mme avec synchro

Performance de lafchage
Latence
- lafchage ne suit pas linteraction

Remdes
- rduire la quantit dlments rafcher clipping : mthodes rect() et region() de QPaintEvent - sauver le dessin dans une image et rafcher en bloc double buffering logiciel, composition de buffer + dessin - afchage en mode XOR source: AnandTech

XOR
Principe
- afchage en mode XOR trs efcace pour dplacements interactifs

Machines tats
et modes dinteraction

Afcher 2 fois pour effacer


- 1er afchage: pixel = bg ^ fg - 2eme afchage: pixel = (bg ^ fg) ^ fg = bg

Problmes
- couleurs alatoires - plus disponible avec Qt4

Exemple Java Graphics : - setColor(Color c1) - setPaintMode() : dessine avec c1 - setXORMode(Color c2) : change c1 et c2

Machines tats nis


Example : rubber banding (repris de Scott Hudson - CMU)
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 nis


Rubber banding
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;

Quel est le problme ?

Problme :
- pas compatible avec la gestion vnementielle

Rappel : gestion des vnements


void mySlot()

Machines tats
Une solution approprie pour maintenir ltat !
- simple et efcace pour modliser les comportements - vite les spaghettis de callbacks et la multiplication des variables dtat et des erreurs ! - passage facile dune reprsentation visuelle au code source - divers outils, UML, QState

vnements

boucle de gestion des vnements


}

...... update(); ...... update(x, y, w, h); ...... update(region1); update(region2); ......

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

Cycle vnement / rafchage


- on ne doit pas bloquer ce cycle ni ignorer les (autres) vnements - on veut viter les spaghettis de callbacks et la multiplication des variables dtat

Etat

Etat de dpart

Etat nal

NB: en IHM gnralement pas dtat nal : on revient ltat initial

Machines tats
Transitions
- Reprsentes par des arcs avec un label : Evnement / Action si on est dans ltat A et cet vnement se produit cette action est effectue puis on passe dans ltat B Mouse_Dn / Draw_Line()

Machines tats
Retour notre ligne lastique
Accept the press for endpoint p1; P2 = P1; Draw line P1-P2; Repeat

A
- Remarque : les actions sont parfois sur les tats (cas de Qt) quand on entre dans ltat quand on sort de ltat

Erase line P1-P2; P2 = current_position();

Draw line P1-P2; Until release event; C Act on line input;

Machines tats
Move / B Press / A Release / C

Machines tats
Autre exemple : bouton
Release / E Enter / C Press-inside / A Leave / B Release / D

Accept the press for endpoint p1; P2 = P1; A Draw line P1-P2; Repeat Erase line P1-P2; B P2 = current_position(); Draw line P1-P2; Until release event; C Act on line input;

A: B: C: D: E:

highlight button unhighlight button highlight button do button action do nothing

Machines tats
- Evnements : de plus ou moins haut niveau peuvent aussi tre des timeouts - Gardes condition supplmentaire notation : prdicat : vnement / action exemple: button.enabled: press-inside / A - Implmentation compatible avec la boucle de gestion des vnements
state = start_state; for ( ; ; ) { raw_evt = wait_for_event(); evt = transform_event(raw_evt); state = fsm_transition(state, evt); }

Machines tats
Implmentation en dur
Move / B

State fsm_transition(state, event) { switch(state) { case 1: switch(event.kind) { case MouseMove: action_B(); state = 1; case MouseRelease: action_C() state = 2;

Press / A

Release / C

state = start_state; for ( ; ; ) { raw_evt = wait_for_event(); evt = transform_event(raw_evt); state = fsm_transition(state, evt); } }

} break; case 0: switch(eventt.kind) { case ... } } return state;

Machines tats
Utilisation
- prfrer lutilisation doutils existants - bass sur des tables dtats et de transitions

Qt State Machine
Qt : State Machine Framework
- modle hierarchique : StateCharts - bass sur SCXML - permet : groupes dtats (hirarchies) tats parallles (pour viter lexplosion combinatoire) tats historiques (pour sauver et restaurer ltat courant) dinjecter ses propres vnement, etc. - sert galement pour faire des animations

Java
- SwingStates (C. Appert) - http://swingstates.sourceforge.net/ - classe Canvas qui facilite le dessin interactif - exemples dinteractions avances

Bouton trois tats


QStateMachine * mac = new QStateMachine( ); QState *s1 = new QState( ); QState *s2 = new QState( ); QState *s3 = new QState( ); // transition de s1 s2 sur un signal de button s1->addTransition(button, SIGNAL(clicked( )), s2); s2->addTransition(button, SIGNAL(clicked( )), s3); s3->addTransition(button, SIGNAL(clicked( )), s1); mac->addState(s1); mac->addState(s2); mac->addState(s3); mac->setInitialState(s1); // les actions sont sur les tats ! QObject::connect( s3, SIGNAL(entered( )), button, SLOT(showMaximized( )) ); QObject::connect( s3, SIGNAL(exited( )), button, SLOT(showMinimized( )) ); mac->start( ); // lancer la machine !

Grouper les tats


Pour crer des hirarchies
// s11, s12, s13 sont groups dans s1 QState *s1 = new QState( ); QState *s11 = new QState(s1); QState *s12 = new QState(s2); QState *s13 = new QState(s3); s1->setInitialState(s11); // s2 est un tal nal QFinalState *s2 = new QFinalState( ); // les enfants de s1 (s11, s12 et s13) hritent de la transition s1 -> s2 s1->addTransition( quitButton, SIGNAL(clicked( )), s2 ); // le signal nished() est mis quand on atteint ltat nal connect( mac, SIGNAL(nished( )), QApplication::instance( ), SLOT(quit( )) ); mac->addState(s1); mac->addState(s2); mac->setInitialState(s1); mac->start( );

Etats parallles
Pour viter lexplosion combinatoire
QState *s1 = new QState( QState::ParallelStates ); // s11 and s12 will be entered in parallel QState *s11 = new QState(s1); QState *s12 = new QState(s1);

Types de transitions
Pour les signaux : QSignalTransition
s1->addTransition(button, SIGNAL(clicked( )), s2);

// cf. exemples prcdents

Pour les vnements : QKeyEventTranslation et QMouseEventTransition


// transition de s1 vers s2 quand le bouton gauche de la souris est press sur canvas
addTrans(s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton); addTrans(s2, s2, canvas, QEvent::MouseMove, Qt::NoButton); addTrans(s2, s1, canvas, QEvent::MouseButtonRelease, Qt::LeftButton);

void addTrans(QState* from, QState* to, // une petite fonction utile QObject* object, QEvent::Type type, Qt::MouseButton button) { QMouseEventTransition* trans = new QMouseEventTransition(object, type, button, from); trans->setTargetState(to); from->addTransition(trans); }

Filtres de transitions
Pour ajouter des gardes ou des conditions complexes Ou toute autre action utile... Exemple :
// transitions :
addTrans(s1, s2, canvas, QEvent::MouseButtonPress, Qt::LeftButton); addTrans(s2, s2, canvas, QEvent::MouseMove, Qt::NoButton); addTrans(s2, s1, canvas, QEvent::MouseButtonRelease, Qt::LeftButton);

Filtres de transitions
Une solution : - crer une sous-classe de QMouseEventTransition qui rednit eventTest( ) - pour sauvegarder currentPos, la position de la souris
class MouseEventTrans : public QMouseEventTransition { Canvas* canvas; // canvas a une variable currentPos public: MouseEventTrans(Canvas* object, QEvent::Type type, Qt::MouseButton button, QState * srcState) : QMouseEventTransition(object, type, button, srcState), canvas(object) { } bool eventTest (QEvent * e) { if ( ! QMouseEventTransition::eventTest(e) ) return false;

// ne pas oublier cette ligne !

// actions sur les tats :


connect(s1, SIGNAL(exited()), canvas, SLOT(addLine())); connect(s2, SIGNAL(entered()), canvas, SLOT(changeLine()));

// quand on sort de s1
// quand on entre dans s2

QStateMachine::WrappedEvent* we = static_cast<QStateMachine::WrappedEvent*>(e); QEvent* realEvent = we->event( ); // le vrai vnment souris switch (realEvent->type( )) { case QEvent::MouseMove: canvas->currentPos = static_cast<QMouseEvent*>(realEvent)->pos( ); // sauver la position souris break; .... etc... } return true; // true signie que lvnement dclenche la transition (sinon il est ignor) } };

Problme : - addLine( ) et changeLine( ) nont pas dargument - comment rcuprer la position de la souris ?

Un bon conseil
Faire au plus simple !
- utiliser les groupes (hirarchies et paralllisme) pour simplier les schmas - rappel : les tats enfants hritent des transitions des tats parents

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

Exemple
- quatre radio boutons exclusifs

base des animations


QState *s1 = new QState(); QState *s2 = new QState(); s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100)); QSignalTransition *transition = s1->addTransition(button, SIGNAL(clicked()), s2); transition->addAnimation(new QPropertyAnimation(button, "geometry"));s

mauvaise solution

bonne solution

Syntaxe dinteraction
Exemples de syntaxe
- objet verbe mettre du texte en orange - (objet)* verbe dtruire plusieurs chiers - (objet)* verbe objet dplacer plusieurs chiers - verbe objet tracer un seul trait - verbe (objets)* tracer plusieurs traits de suite - verbe objet ou (objet)* verbe le surligneur de Word

Modes dinteraction
Modes spatiaux
- leffet dune action dpend de lendroit o elle est effectue boutons, menus... - avantage : visibilit dcouverte, mmorisation

Modes temporels
- leffet dune action dpend du moment o elle est effectue palettes doutils de dessin bote de dialogue modale - gnralement incontournables pour crer de nouveaux objets - inconvnient : visibilit dcouverte, erreurs (quel est ltat courant ?)

Modes dinteraction
Mode temporel
- actions interprtes dune manire spcique tant que le mode est activ mode tracer plusieurs traits - syntaxe : verbe (objets)*

Modes dinteraction
Modieurs
- modient le comportement usuel - touches Control, Shift, Alt... - boutons de la souris gauche : slectionner, dplacer, activer droite : menu contextuel - multitouch 1 doigt : comme la souris 2 doigts : dler la vue, zoomer, pivoter

Cas one shot


- naffecte que laction suivante mode tracer un seul trait - syntaxe : verbe objet

Quasi-mode (ou micro-mode)


- naffecte que laction en cours (mode auto-dsactiv la n de laction) drag and drop

Modes dinteraction
Cas des crans tactiles passifs
- pas de claviers (en gnral) => pas de modieurs ni de raccourcis clavier - pas de boutons de la souris ni de mode hover => moins dtats interactionnels, ambiguits - alternatives multitouch gestes 3D (acclromtre, gyroscope, magntomtre...)

Qt Designer

Cas des tablettes tactiles


- gnralement actives => prsence de boutons modaux - inconvnient : ncessitent un stylet spcial

Qt Designer
Fichiers
calculator.pro calculatorform.ui calculator.h calculator.cpp main.cpp

Qt Designer
fichiers gnres par QtCreator main.cpp
#include "calculator.h" int main (int argc, char *argv[]) { QApplication app(argc, argv); Calculator widget; .... widget.show(); return app.exec(); }

calculatorform.ui
fichier XML

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

Exemple
vido et code .ui

Qt Designer
#include "ui_calculatorform.h" class Calculator : public QWidget { Q_OBJECT public: Calculator(QWidget *parent =0); private : Ui::CalculatorForm * ui; };

Qt Designer
Comment afficher le rsultat du calcul dans le Qlabel ?

calculator.h

calculator.cpp
#include "calculator.h Calculator::Calculator(Qwidget *parent) : QWidget(parent) { ui->setupUi(this); }

#include "ui_calculatorform.h" class Calculator : public QWidget { Q_OBJECT public: Calculator(QWidget *parent =0); private : Ui::CalculatorForm * ui; private slots : void on_spinBox1_valueChanged(int value); void on_spinBox2_valueChanged(int value); }; #include "calculator.h Calculator::Calculator(Qwidget *parent) : QWidget(parent) { ui->setupUi(this); } void Calculator::on_spinBox1_valueChanged(int val) { QString res = QString::number(val); // ou: QString res = QString::number( ui->spinBox1->value() ); ui->label3->setText(res); } // label3 : nom du widget dans QtCreator

Qt Designer
Auto-connect on peut lier les signaux des objets crs interactivement avec nimporte quel slot : par auto-connexion void on_spinBox1_valueChanged(int) via le mode Edition Signaux/Slots de QtCreator

Variante
Mme principe mais en utilisant lhritage multiple
#include "ui_calculatorform.h" Class Calculator : public QWidget, private Ui::CalculatorForm { Q_OBJECT public: Calculator(Qwidget *parent =0); }; Calculator::Calculator(Qwidget *parent) : Qwidget(parent) { setupUi(this); // au lieu de ui->setupUi(this) } void Calculator::on_spinBox1_valueChanged(int val) { QString res = QString::number(val); label3->setText(res); } // au lieu de : ui->label3->setText(res); }

Chargement dynamique
#include <QtUiTools> Calculator::Calculator(QWidget *parent) : QWidget(parent) { QUiLoader loader; QFile file(":/forms/calculatorform.ui"); file.open(QFile::ReadOnly); QWidget *formWidget = loader.load(&file, this); file.close();

class Calculator : public QWidget { Q_OBJECT public: Calculator(QWidget *parent = 0); private: QSpinBox *ui_spinBox1; QSpinBox *ui_spinBox2; QLabel *ui_label1; }; ..... ui_spinBox1 = qFindChild<QSpinBox*>(this, "spinBox1"); ui_spinBox2 = qFindChild<QSpinBox*>(this, "spinBox2"); ui_label1 = qFindChild<QLabel*>(this, "label1");