Sur cette page

Système de coordonnées

Le système de coordonnées est géré par la classe ` QPainter `. Avec les classes ` QPaintDevice ` et ` QPaintEngine `, ` QPainter ` constitue la base du système de dessin de Qt, Arthur. ` QPainter ` est utilisé pour effectuer des opérations de dessin, ` QPaintDevice ` est une abstraction d’un espace bidimensionnel sur lequel il est possible de dessiner à l’aide d’un ` QPainter`, et ` QPaintEngine ` fournit l’interface que le dessinateur utilise pour dessiner sur différents types de périphériques.

La classe ` QPaintDevice ` est la classe de base des objets pouvant être peints : ses capacités de dessin sont héritées par les classes ` QWidget`, ` QImage`, ` QPixmap`, ` QPicture` et ` QOpenGLPaintDevice `. Le système de coordonnées par défaut d’un périphérique de peinture a son origine dans le coin supérieur gauche. Les valeurs x augmentent vers la droite et les valeurs y augmentent vers le bas. L’unité par défaut est un pixel sur les périphériques basés sur les pixels et un point (1/72 de pouce) sur les imprimantes.

La mise en correspondance des coordonnées logiques QPainter avec les coordonnées physiques QPaintDevice est gérée par la matrice de transformation, la fenêtre d’affichage et la « fenêtre » d’ QPainter. Les systèmes de coordonnées logiques et physiques coïncident par défaut. QPainter prend également en charge les transformations de coordonnées (par exemple, la rotation et la mise à l’échelle).

Rendu

Représentation logique

La taille (largeur et hauteur) d’une primitive graphique correspond toujours à son modèle mathématique, sans tenir compte de la largeur du stylo utilisé pour son rendu :

Rectangle sur une grille de coordonnées allant de (1,2) à (7,6)Droite sur une grille de coordonnées allant de (2, 7) à (6, 1)
QRect(QPoint(1, 2), QPoint(7, 6))QLine(QPoint(2, 7), QPoint(6, 1))
QLine(2, 7, 6, 1)
QRect(QPoint(1, 2), QSize(6, 4))
QRect(1, 2, 6, 4)

Peinture avec aliasing

Lors du dessin, le rendu des pixels est contrôlé par l'indicateur de rendu QPainter::Antialiasing.

L’énumération RenderHint est utilisée pour spécifier des indicateurs à QPainter qui peuvent être respectés ou non par un moteur donné. La valeur QPainter::Antialiasing indique que le moteur doit, si possible, lisser les bords des primitives, c’est-à-dire adoucir les bords en utilisant différentes intensités de couleur.

Mais par défaut, le tracé présente un aliasing et d’autres règles s’appliquent : lors du rendu avec un stylo d’un pixel de large, les pixels seront rendus à droite et en dessous des points définis mathématiquement. Par exemple :

Rectangle avec anticrénelage, dont les pixels sont affichés à droite et en dessousLigne présentant un effet d'aliasing, avec des pixels affichés à droite et en dessous
QPainter painter(this);

painter.setPen(Qt::darkGreen);
// Using the (x  y  w  h) overload
painter.drawRect(1, 2, 6, 4);
QPainter painter(this);

painter.setPen(Qt::darkGreen);
painter.drawLine(2, 7, 6, 1);

lors d’un rendu avec un stylo comportant un nombre pair de pixels, les pixels seront rendus symétriquement autour des points mathématiquement définis, tandis que lors d’un rendu avec un stylo comportant un nombre impair de pixels, le pixel supplémentaire sera rendu à droite et en dessous du point mathématique, comme dans le cas d’un pixel. Consultez les schémas QRectF ci-dessous pour des exemples concrets.

QRectF
Rectangle indiquant ses dimensions width() et height()Rectangle tracé avec un stylo d'un pixel indiquant les coordonnées des coins
Représentation logiqueStylo d’un pixel de large
Rectangle tracé avec un stylo de deux pixels, indiquant les coordonnées des coinsRectangle tracé avec un stylo de trois pixels, indiquant les coordonnées des coins
Stylo de deux pixels de largeStylo de trois pixels de large

Notez que, pour des raisons historiques, la valeur renvoyée par les fonctions ` QRect::right()` et ` QRect::bottom()` s'écarte du véritable coin inférieur droit du rectangle.

QRectLa fonction ` right()` renvoie ` left()` + ` width()` - 1 et la fonction ` bottom()` renvoie ` top()` + ` height()` - 1. Le point vert en bas à droite des schémas indique les coordonnées renvoyées par ces fonctions.

Nous vous recommandons d’utiliser simplement QRectF à la place : la classe QRectF définit un rectangle dans le plan en utilisant des coordonnées à virgule flottante pour plus de précision (QRect utilise des coordonnées entières), et les fonctions QRectF::right() et QRectF::bottom() renvoient bien le véritable coin inférieur droit.

Sinon, en utilisant QRect, appliquez x() + width() et y() + height() pour trouver le coin inférieur droit, et évitez les fonctions right() et bottom().

Rendu anti-aliasing

Si vous définissez l'indicateur de rendu « anti-aliasing » d'QPainter, les pixels seront rendus symétriquement de part et d'autre des points définis mathématiquement :

Rectangle lissé avec rendu symétrique des pixelsLigne lissée avec rendu symétrique des pixels
QPainter painter(this);
painter.setRenderHint(
    QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
// Using the (x  y  w  h) overload
painter.drawRect(1, 2, 6, 4);
QPainter painter(this);
painter.setRenderHint(
    QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
painter.drawLine(2, 7, 6, 1);

Transformations

Par défaut, l’ QPainter opère sur le système de coordonnées propre au périphérique associé, mais il prend également entièrement en charge les transformations de coordonnées affines.

Vous pouvez mettre à l'échelle le système de coordonnées d'un décalage donné à l'aide de la fonction ` QPainter::scale()`, le faire pivoter dans le sens des aiguilles d'une montre à l'aide de la fonction ` QPainter::rotate()` et le translater (c'est-à-dire ajouter un décalage donné aux points) à l'aide de la fonction ` QPainter::translate()`.

Vous pouvez également faire pivoter le système de coordonnées autour de l’origine à l’aide de la fonction QPainter::shear(). Toutes les opérations de transformation s’appliquent à la matrice de transformation de QPainter, que vous pouvez récupérer à l’aide de la fonction QPainter::worldTransform(). Une matrice transforme un point du plan en un autre point.

Si vous avez besoin d’appliquer les mêmes transformations à plusieurs reprises, vous pouvez également utiliser des objets QTransform ainsi que les fonctions QPainter::worldTransform() et QPainter::setWorldTransform(). Vous pouvez à tout moment enregistrer la matrice de transformation de QPainter en appelant la fonction QPainter::save(), qui enregistre la matrice dans une pile interne. La fonction QPainter::restore() la récupère ensuite.

La matrice de transformation est souvent nécessaire lorsqu’on réutilise le même code de dessin sur divers périphériques d’affichage. Sans transformations, les résultats sont étroitement liés à la résolution du périphérique d’affichage. Les imprimantes ont une haute résolution, par exemple 600 points par pouce, tandis que les écrans ont souvent une résolution comprise entre 72 et 100 points par pouce.

Exemple d’horloge analogique
Widget d'horloge analogique affichant les aiguilles des heures et des minutesL’exemple « Horloge analogique » montre comment dessiner le contenu d’un widget personnalisé à l’aide de la matrice de transformation d’ QPainter.

Nous vous recommandons de compiler et d’exécuter cet exemple avant de poursuivre votre lecture. Essayez notamment de redimensionner la fenêtre à différentes tailles.

void AnalogClock::paintEvent(QPaintEvent *)
{
    static const QPoint hourHand[4] = {
        QPoint(5, 14),
        QPoint(-5, 14),
        QPoint(-4, -71),
        QPoint(4, -71)
    };
    static const QPoint minuteHand[4] = {
        QPoint(4, 14),
        QPoint(-4, 14),
        QPoint(-3, -89),
        QPoint(3, -89)
    };

    static const QPoint secondsHand[4] = {
       QPoint(1, 14),
       QPoint(-1, 14),
       QPoint(-1, -89),
       QPoint(1, -89)
    };

    const QColor hourColor(palette().color(QPalette::Text));
    const QColor minuteColor(palette().color(QPalette::Text));
    const QColor secondsColor(palette().color(QPalette::Accent));

    int side = qMin(width(), height());

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.translate(width() / 2, height() / 2);
    painter.scale(side / 200.0, side / 200.0);

Nous translatons le système de coordonnées de manière à ce que le point (0, 0) se trouve au centre du widget, et non dans le coin supérieur gauche. Nous mettons également le système à l'échelle en le divisant par side / 200, où side correspond soit à la largeur, soit à la hauteur du widget, selon la plus petite des deux. Nous voulons que l'horloge soit carrée, même si l'appareil ne l'est pas.

Cela nous donnera une zone carrée de 200 x 200, avec l’origine (0, 0) au centre, sur laquelle nous pourrons dessiner. Ce que nous dessinerons s’affichera dans le plus grand carré possible pouvant tenir dans le widget.

Voir également la section « Conversion fenêtre-fenêtre de visual isation ».

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
        painter.drawConvexPolygon(hourHand, 4);
    }

Nous dessinons l'aiguille des heures de l'horloge en faisant pivoter le système de coordonnées et en appelant la fonction ` QPainter::drawConvexPolygon()`. Grâce à cette rotation, elle est dessinée dans la bonne direction.

Le polygone est spécifié sous la forme d’un tableau de valeurs x et y alternées, stockées dans la variable statique ` hourHand ` (définie au début de la fonction), qui correspond aux trois points (7, 8), (-7, 8), (0, -40).

Les appels à ` QPainter::save()` et ` QPainter::restore()` encadrant le code garantissent que le code qui suit ne sera pas perturbé par les transformations que nous avons utilisées.

    painter.setBrush(minuteColor);

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.minute());
        painter.drawConvexPolygon(minuteHand, 4);
    }

Ensuite, nous dessinons les index horaires du cadran, qui se compose de douze courtes lignes espacées de 30 degrés. Une fois cette boucle terminée, le « painter » a effectué un tour complet pour revenir à son état d’origine ; nous n’avons donc pas besoin d’enregistrer ni de restaurer l’état.

    {
        QPainterStateGuard guard(&painter);
        painter.rotate(6.0 * time.second());
        painter.drawConvexPolygon(secondsHand, 4);
        painter.drawEllipse(-3, -3, 6, 6);
        painter.drawEllipse(-5, -68, 10, 10);
    }

Nous procédons de la même manière pour l’aiguille des minutes de l’horloge, qui est définie par les trois points (7, 8), (-7, 8), (0, -70). Ces coordonnées définissent une aiguille plus fine et plus longue que l’aiguille des minutes.

    for (int j = 0; j < 60; ++j) {
        painter.drawLine(92, 0, 96, 0);
        painter.rotate(6.0);
    }

Enfin, nous dessinons les repères des minutes sur le cadran, qui se compose de soixante courtes lignes espacées de 6 degrés. Nous sautons un repère de minute sur cinq, car nous ne voulons pas dessiner par-dessus les repères des heures. À la fin de cette opération, l’objet « painter » est pivoté d’une manière qui n’est pas très utile, mais comme nous avons terminé le dessin, cela n’a pas d’importance.

Pour plus d’informations sur la matrice de transformation, consultez la documentation d’ QTransform.

Conversion fenêtre-fenêtre de visualisation

Lorsqu’on dessine avec QPainter, on spécifie des points à l’aide de coordonnées logiques qui sont ensuite converties en coordonnées physiques du périphérique de dessin.

La mise en correspondance des coordonnées logiques avec les coordonnées physiques est gérée par la fonction de transformation mondiale d’ QPainter, ` worldTransform()` (décrite dans la section « Transformations » ), ainsi que par les fonctions ` viewport()` et ` window()` d’ QPainter. La fenêtre de visualisation représente les coordonnées physiques en spécifiant un rectangle arbitraire. La « fenêtre » décrit ce même rectangle en coordonnées logiques. Par défaut, les systèmes de coordonnées logiques et physiques coïncident et sont équivalents au rectangle du périphérique de dessin.

La conversion fenêtre-fenêtre de visualisation vous permet d’adapter le système de coordonnées logiques à vos préférences. Ce mécanisme peut également être utilisé pour rendre le code de dessin indépendant du périphérique de dessin. Vous pouvez, par exemple, faire en sorte que les coordonnées logiques s'étendent de (-50, -50) à (50, 50) avec (0, 0) au centre en appelant la fonction ` QPainter::setWindow()` :

QPainter painter(this);
painter.setWindow(QRect(-50, -50, 100, 100));

Désormais, les coordonnées logiques (-50, -50) correspondent aux coordonnées physiques (0, 0) du périphérique de dessin. Indépendamment du périphérique de dessin, votre code de dessin fonctionnera toujours sur les coordonnées logiques spécifiées.

En définissant la « fenêtre » ou le rectangle de la fenêtre d’affichage, vous effectuez une transformation linéaire des coordonnées. Notez que chaque coin de la « fenêtre » correspond au coin correspondant de la fenêtre d’affichage, et inversement. C’est pourquoi il est généralement recommandé de conserver le même rapport d’aspect entre la fenêtre d’affichage et la « fenêtre » afin d’éviter toute déformation :

int side = qMin(width(), height());
int x = (width() - side / 2);
int y = (height() - side / 2);

painter.setViewport(x, y, side, side);

Si nous définissons le système de coordonnées logiques comme un carré, nous devons également définir la fenêtre d’affichage comme un carré à l’aide de la fonction ` QPainter::setViewport()`. Dans l’exemple ci-dessus, nous le rendons équivalent au plus grand carré pouvant s’insérer dans le rectangle du périphérique de dessin. En tenant compte de la taille du périphérique de dessin lors de la configuration de la fenêtre ou de la fenêtre d’affichage, il est possible de maintenir le code de dessin indépendant du périphérique de dessin.

Notez que la conversion fenêtre-fenêtre de visualisation n'est qu'une transformation linéaire, c'est-à-dire qu'elle n'effectue pas de découpage. Cela signifie que si vous dessinez en dehors de la « fenêtre » actuellement définie, votre dessin est tout de même transformé pour s'adapter à la fenêtre de visualisation en utilisant la même approche algébrique linéaire.

Illustration montrant comment les coordonnées sont mappées à l'aide de la fenêtre d'affichage, de la « fenêtre » et de la matrice de transformation

La fenêtre de visualisation, la « fenêtre » et la matrice de transformation déterminent comment les coordonnées logiques d'QPainter s sont mappées aux coordonnées physiques du périphérique de dessin. Par défaut, la matrice de transformation mondiale est la matrice identité, et les paramètres de la « fenêtre » et de la fenêtre d'affichage sont équivalents à ceux du périphérique de dessin ; en d'autres termes, les systèmes de coordonnées mondiaux, de la « fenêtre » et du périphérique sont équivalents. Cependant, comme nous l'avons vu, ces systèmes peuvent être manipulés à l'aide d'opérations de transformation et de la conversion fenêtre-fenêtre d'affichage. L’illustration ci-dessus décrit ce processus.

Voir aussi Horloge analogique.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.