Qt-Lecolinet

download Qt-Lecolinet

of 29

Transcript of Qt-Lecolinet

Toolkit Qt Petit cours Qtbases 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 QtToolkit 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, outilsExtensions 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

LiensLiens 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 widgetsArbre d'hritage

Hello Word! : premire version#include #include 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 #include 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 // inclure headers adquats ! #include #include #include 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 slotsUn 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 slotsModularit, exibilit- le mme signal peut tre connect plusieurs slots - plusieurs signaux peuvent tre connects un mme slot

ConnexionTypage fort- les types des paramtres des signaux et des slots doivent tre les mmes - un slot peut avoir moins de paramtres qu'un signalQObject::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 slotDans 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 slotDans 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 slotDans le chier source de l'implmentation (.cpp)void BankAccount::setBalance(int newBalance) { if ( newBalance != curBalance ) { curBalance = newBalance; emit balanceChanged(curBalance); } }

ConnexionConnexion simpleBankAccount 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 !

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

CompilationMeta 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 principaleDans 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 principaleActions- les actions peuvent sajouter la fois dans les menus et les toolbarsQToolBar* toolbar = addToolBar( tr("File") ); toolbar->addAction(new_action);

Botes de dialogueQFileDialog, QMessageBox

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

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

Bote de dialogue modaleSolution gnraleQFileDialog 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]; ... }

QStringCodage 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 simplieQString 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

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

QTextStreamQTextStream- 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 > : exemples : outStream > 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 );

LayoutProblmes- internationalisation - retaillage - complexit du code

Layout : exempleQVBoxLayout *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

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

RessourcesFichier sourceQAction* 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 images/copy.png images/cut.png images/new.png images/open.png images/paste.png images/save.png

Graphique 2DDeux modles- QPainter : modle fonctionnel - Graphics View : modle objet

Graphique 2DDeux modles- QPainter : modle fonctionnel - Graphics View : modle objetQGraphicsScene 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 2DQIcon QPushButton *button = new QPushButton( "&Find Address", parent ); button->setIcon( QIcon(":/images/new.png")) ); - cf chiers de ressources .qrc

Graphique 3D3D- 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 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 #include #include #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 : mainint 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 Box3DGLuint 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 interactifIllustr avec Qt

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

Gestion des vnementsvoid 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 widgetRednir QWidget::paintEvent()#include class MyWidget : public QWidget { public: MyWidget(QWidget* parent); protected: void paintEvent(QPaintEvent* e ); };____________________________________________________________________________________________________________________________________________________________________________

Dtecter les vnements- lorsquon presse un boutonHeader (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 #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 vnementsHeader (chier .h)#include #include class MyWidget : public QWidget { public: MyWidget(); protected: void mousePressEvent(QMouseEvent*); }; #include void MyWidget:: mousePressEvent(QMouseEvent* e) { ! if (e.button() == Qt::LeftButton) { ...... ...... ...... ! update(); // demande de rafraichissement }

SynthsemousePressEvent(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

QPainterAttributs- 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, QPictureQGLPixelBuffer, QGLFrameBufferObject

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

QPainterClasses 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: QPenAttributs- style : type de ligne - width : paisseur (0 = cosmetique) - brush : attributs du pinceau (couleur...) - capStyle : terminaisons - joinStyle : jointures Cap Style Qt::PenStyle

Pinceau: QPenExempleQPen 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: QBrushAttributs- style - color - gradient - texture Qt::BrushStyle

Remplissage: QBrushAttributs- 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: gradientsType- lineaire - radial - conique QLinearGradient QRadialGradient

CompositionModes 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 afnesTransformations- 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 AntialiasingRender hints- hint = option de rendu effet non garanti dpend de limplmentation et du matriel - QPainter::setRenderingHints()

Antialiasing et cordonnesEpaisseurs impaire- pixels dessins droite et en dessous NoteQRect::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::AntialiasingQPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::darkGreen); painter.drawLine(2, 7, 6, 1);

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

QPainterPathQPointF 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 !

QPainterPathQPainterPath 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

ImagesTypes 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 pixelsFormat 32 bits : accs directQImage 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 mmoireQImage 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);

QPictureEnregistrer 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 dafchageSVG- 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

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

Picking / InteractionExemple- 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 centrQRect 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 lafchageProblmes classiques :- Flicking - Tearing - Lag

FlickingFlicking- 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 bufferingDouble buffering- solution au icking : dessin dans back buffer recopie dans front buffer - par dfaut avec Qt4

Double bufferingPage 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 bufferingTriple buffering- comme double buffering mais plus rapide - un back buffer est toujours disponible mme avec synchro

Performance de lafchageLatence- 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

XORPrincipe- afchage en mode XOR trs efcace pour dplacements interactifs

Machines tatset 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 nisExample : 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 nisRubber bandingAccept 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 vnementsvoid mySlot()

Machines tatsUne 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 tatsTransitions- 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 tatsRetour notre ligne lastiqueAccept the press for endpoint p1; P2 = P1; Draw line P1-P2; Repeat

A

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

B

B

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

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

Machines tatsMove / B Press / A Release / C

Machines tatsAutre exemple : boutonRelease / 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 vnementsstate = start_state; for ( ; ; ) { raw_evt = wait_for_event(); evt = transform_event(raw_evt); state = fsm_transition(state, evt); }

Machines tatsImplmentation en durMove / 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 tatsUtilisation- prfrer lutilisation doutils existants - bass sur des tables dtats et de transitions

Qt State MachineQt : 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 tatsQStateMachine * 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 tatsPour 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 paralllesPour viter lexplosion combinatoireQState *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 transitionsPour les signaux : QSignalTransitions1->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 canvasaddTrans(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 transitionsPour 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 transitionsUne solution : - crer une sous-classe de QMouseEventTransition qui rednit eventTest( ) - pour sauvegarder currentPos, la position de la sourisclass 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(e); QEvent* realEvent = we->event( ); // le vrai vnment souris switch (realEvent->type( )) { case QEvent::MouseMove: canvas->currentPos = static_cast(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 conseilFaire 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 propritsassignPropertys1->assignProperty( label, "text", "In state s1" ); s1->assignProperty( button, "geometry", QRectF(0, 0, 50, 50) );

Exemple- quatre radio boutons exclusifs

base des animationsQState *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 dinteractionExemples 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 dinteractionModes 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 dinteractionMode temporel- actions interprtes dune manire spcique tant que le mode est activ mode tracer plusieurs traits - syntaxe : verbe (objets)*

Modes dinteractionModieurs- 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 dinteractionCas 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.proTEMPLATE = 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 DesignerAuto-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 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(this, "spinBox1"); ui_spinBox2 = qFindChild(this, "spinBox2"); ui_label1 = qFindChild(this, "label1");