Objectif : Dessiner en 2D dans une application GUI/Qt.
Il existe deux approches pour dessiner en 2D dans Qt :
Graphics View
QPainter
Le framework Graphics View se décompose en 3 parties essentielles :
L’architecture Graphics View offre une approche basée sur le pattern modèle-vue. Plusieurs vues peuvent observer une scène unique constituée d’éléments de différentes formes géométriques.
La classe QGraphicsScene
fournit la scène pour l’architecture Graphics View. La scène a les responsabilités suivantes :
La classe QGraphicsView
fournit la vue “widget” qui permet de visualiser le contenu d’une scène.
La classe QGraphicsItem
est la classe de base pour les éléments graphiques dans une scène.
Elle fournit plusieurs éléments standard pour les formes typiques, telles que :
QGraphicsRectItem
),QGraphicsEllipseItem
) etQGraphicsTextItem
).Mais les fonctionnalités les plus puissantes seront disponibles lorsque on écrira un élément personnalisé. Entre autres choses, QGraphicsItem
supporte les fonctionnalités suivantes : les événements souris et clavier, le glissez et déposez (drag and drop), le groupement d’éléments, la détection des collisions.
La scène sert de conteneur pour les objets QGraphicsItem
. La classe QGraphicsView
fournit la vue “widget” qui permet de visualiser le contenu d’une scène.
Exemple simple :
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QgraphicsView>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsView view(&scene);
view.show();
return app.exec();
}
En collaboration avec les classes QPaintDevice
et QPaintEngine
, QPainter
est la base du système de dessin de Qt :
QPainter
est la classe utilisée pour effectuer les opérations de dessin.QPaintDevice
représente un dispositif qui peut être peint en utilisant un QPainter
.QPaintEngine
fournit le moteur de rendu et l’interface (abstraite) que QPainter
utilise pour dessiner sur différents types de dispositifs suivant la plate-forme utilisée.La classe QPainter
est la classe de base de dessin bas niveau sur les widgets et les autres dispositifs de dessins. QPainter
fournit des fonctions hautement optimisées : il peut tout dessiner des lignes simples à des formes complexes. QPainter
peut fonctionner sur n’importe quel objet qui hérite de la classe QPaintDevice
.
L’utilisation courante de QPainter
est à l’intérieur de la méthode paintEvent()
d’un widget : construire, personnaliser (par exemple le pinceau), dessiner et détruire l’objet QPainter
après le dessin.
Lorsqu’un widget doit être dessiné ou redessiné, la méthode paintEvent()
est appelée. Pour se dessiner, la majorité des widgets de Qt utilise un QPainter
lors de l’appel à cette fonction.
La classe QPainter
permet de dessiner sur toutes les classes graphique de Qt : QCustomRasterPaintDevice
, QGLFramebufferObject
, QGLPixelBuffer
, QImage
, QPicture
, QPixmap
, QPrinter
, QSvgGenerator
et bien évidemment QWidget
.
Le QPaintDevice
est un espace à deux dimensions dans lequel on peut dessiner. L’origine du système de coordonnées par défaut d’un QPaintDevice
est le coin haut gauche (0,0). Les coordonnées en x s’incrémentent vers la droite et les coordonnées en y vers le bas. L’unité par défaut est d’un pixel et d’un point (1/72 d’un pouce (inch)) pour les imprimantes.
Par défaut, il y a correspondance entre les coordonnées logiques du QPainter
et coordonnées physiques du QPaintDevice
.
Un widget est “repeint” (dessiné ou redessiné) :
Lorsque l’on le lui demande explicitement en appelant :
repaint()
entraîne un rafraichissement immédiatupdate()
met une demande de rafraîchissement en file d’attenteDans tous les cas, c’est la méthode paintEvent()
qui est appelée : void paintEvent(QPaintEvent* e);
Pour dessiner dans un widget, il faut donc redéfinir QWidget::paintEvent()
.
Exemple simple :
#include <QtGui>
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent = 0 ) : QWidget( parent ) {}
void paintEvent(QPaintEvent* e)
{
QWidget::paintEvent(e); // effectue le comportement standard
QPainter painter(this); // construire
painter.setPen( QPen(Qt::red, 2) ); // personnaliser
painter.drawRect( 25, 15, 120, 60 ); // dessiner
} // détruire
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MyWidget myWidget;
myWidget.show();
return app.exec();
}
Remarque : Certains widgets sont basés sur QAbstractScrollArea
, dont le but est de permettre l’affichage d’un widget plus grand que sa zone d’affichage, en ajoutant des barres de défilement, par exemple. Appliquer un painter directement sur celle-ci n’aura pas l’effet souhaité. Cette classe implémente la méthode viewport()
qui permet d’accéder au widget qui est réellement affiché. Il faut donc appliquer le painter sur celui-ci. Les widgets concernés sont QAbstractItemView
, QGraphicsView
, QMdiArea
, QPlainTextEdit
, QScrollArea
, QTextEdit
, QTextBrowser
, QColumnView
, QHeaderView
, QListView
, QTableView
, QTreeView
, QHelpContentWidget
, QTreeWidget
, QTableWidget
, QHelpIndexWidget
, QListWidget
, QUndoView
.
void paintEvent(QPaintEvent* e)
{
if (this->viewport())
{
QPainter painter(this->viewport()); // construire
painter.setPen( QPen(Qt::red, 2) ); // personnaliser
painter.drawRect( 25, 15, 120, 60 ); // dessiner
} // détruire
}
La classe QPainter
fournit de nombreuses méthodes dessiner et personnaliser son dessin :
setPen()
: lignes et contourssetBrush()
: remplissagesetFont()
: textesetTransform()
, etc. : transformations affinessetClipRect/Path/Region()
: clipping (découpage)setCompositionMode()
: compositiondrawPoint()
, drawPoints()
, drawLine()
, drawLines()
, drawRect()
, drawRects()
, drawArc()
, drawEllipse()
, drawPolygon()
, drawPolyline()
, etc … et drawPath()
pour des chemins complexesfillRect()
, fillPath()
translate()
, rotate()
, scale()
…drawText()
, drawPixmap()
, drawImage()
, drawPicture()
Conjointement à la classe QPainter
, on utilise de nombreuses autres classes utiles :
QPoint
, QLine
, QRect
, QPolygon
QPointF
, QLineF
, …QPainterPath
QRegion
QPen
QBrush
QColor
Exemple simple :
#include <QtGui>
class MyWidget : public QWidget
{
public:
MyWidget( QWidget *parent = 0 ) : QWidget( parent ) {}
void paintEvent(QPaintEvent* e)
{
QPainter painter(this);
QPen pen;
QBrush brush;
brush.setStyle(Qt::CrossPattern);
brush.setColor(Qt::blue);
pen.setStyle(Qt::DashDotLine);
pen.setWidth(3);
pen.setBrush(Qt::green);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);
painter.setPen(pen);
painter.setBrush(brush);
painter.drawLine( 25, 15, 145, 75 );
painter.setPen( QPen(Qt::red, 2) );
painter.drawRect( 25, 15, 120, 60 );
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MyWidget myWidget;
myWidget.show();
return app.exec();
}
Remarque : On peut aussi utiliser begin()
pour commencer à dessiner et end()
pour terminer. Tous les paramètres du QPainter
(setPen()
, setBrush()
, etc …) sont réinitialisés à leur valeur par défaut lorsque begin()
est appelée.
QPainter painter;
QPen pen;
pen.setStyle(Qt::SolidLine);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);
painter.begin(this);
pen.setBrush(Qt::white);
pen.setWidth(2);
painter.setPen(pen);
painter.drawLine(0, 0, 0, h);
painter.drawLine(0, h, w, h);
painter.drawLine(w, h, w, 0);
painter.drawLine(w, 0, 0, 0);
painter.end();
Il est donc possible de dessiner :
QPointF p(1.0, 0.0);
QPainter(this);
painter.drawPoint(p)
Voir aussi : QPainter::drawPoints()
.
QLineF line(10.0, 80.0, 90.0, 20.0);
QPainter(this);
painter.drawLine(line);
static const QPointF points[3] = {
QPointF(10.0, 80.0),
QPointF(20.0, 10.0),
QPointF(80.0,30.0)
};
QPainter painter(this);
painter.drawPolyline(points, 3);
static const QPointF points[4] = {
QPointF(10.0, 80.0),
QPointF(20.0, 10.0),
QPointF(80.0, 30.0),
QPointF(90.0, 70.0)};
QPainter painter(this);
painter.drawPolygon(points, 4);
Voir aussi : QPainter::drawConvexPolygon()
.
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
QPainter painter(this);
painter.drawRect(rectangle);
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
QPainter painter(this);
painter.drawRoundRect(rectangle);
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
QPainter painter(this);
painter.drawEllipse(rectangle);
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
int startAngle = 30 * 16;
int spanAngle = 120 * 16;
QPainter painter(this);
painter.drawArc(rectangle, startAngle, spanAngle);
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
int startAngle = 30 * 16;
int spanAngle = 120 * 16;
QPainter painter(this);
painter.drawPie(rectangle, startAngle, spanAngle);
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
int startAngle = 30 * 16;
int spanAngle = 120 * 16;
QPainter painter(this);
painter.drawChord(rect, startAngle, spanAngle);
QPainterPath path;
path.moveTo(20, 80);
path.lineTo(20, 30);
path.cubicTo(80, 0, 50, 50, 80, 80);
QPainter painter(this);
painter.drawPath(path);
Voir QPainter::setClipRegion()
, QPainter::setCLipPath()
QRect rect(0, 0, 170, 45);
QPainter painter(this);
painter.drawText(rect, Qt::AlignCenter, "Qt by\nTrolltech");
Qt fournit quatre classes de traitement des données d’image : QImage
, QPixmap
, les QBitmap
et QPicture
:
QImage
fournit une représentation d’image indépendante du matériel qui permet un accès direct aux pixels. QImage est conçu et optimisé pour les E/S.QPixmap
est conçu et optimisé pour afficher les images sur l’écran.QBitmap
n’est qu’une classe de commodité qui hérite QPixmap
.QPicture
est un dispositif permettant d’enregistrer des commandes d’un QPainter
et de les “rejouer”.Ces 4 classes héritent de QPaintDevice
et on peut donc dessiner dedans avec un QPainter
.
Elles possèdent aussi les méthodes load()
et save()
d’accès aux fichiers (dont les principaux formats sont supportés).
QRect rect(0, 0, 170, 45);
QPainter painter(this);
QPixmap pixmap;
pixmap.load("qt-logo.png");
painter.drawPixmap(45, 50, pixmap);