Developpez.com

Qt

Choisissez la catégorie, puis la rubrique :

Viadeo Twitter Facebook Share on Google+   
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

Système de coordonnées

Le système de coordonnées est contrôlé par la classe QPainter. Avec les classes QPaintDevice et QPaintEngine, QPainter compose les bases du système de dessin de Qt, Arthur. QPainter est utilisé pour les opérations de dessin, QPaintDevice est une abstraction d'un espace à deux dimensions qui peut être peint avec un QPainter et QPaintEngine fournit l'interface que le QPainter utilise pour dessiner sur les différents types de périphériques.

La classe QPaintDevice est la classe de base pour les objets pouvant être dessinés. Ses fonctionnalités de dessin sont héritées dans les classes QWidget, QPixmap, QPicture, QImage et QPrinter. L'origine du système de coordonnées par défaut d'un périphérique de dessin est le coin supérieur gauche. Les valeurs de l'axe des x augmentent vers la droite et les valeurs de l'axe des y augmentent vers le bas. L'unité par défaut est le pixel pour les périphériques basés sur les pixels et le point (1/72 de pouce) pour les imprimantes.

La correspondance entre les coordonnées logiques de QPainter et les coordonnées physiques de QPaintDevice est gérée par la matrice de transformation de QPainter, sa zone d'affichage et sa « fenêtre ». Les systèmes de coordonnées logiques et physiques coïncident par défaut. QPainter gère aussi les transformations de coordonnées (par exemple, les rotations et les redimensionnements).

Rendu

Représentation logique

La taille (largeur et hauteur) d'une primitive graphique correspond toujours à son modèle mathématique, ignorant la largeur du pinceau avec lequel la primitive est rendue :

image image
QRect(1, 2, 6, 4) QLine(2, 7, 6, 1)

Anticrénelage du dessin

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

L'énumération RenderHint est utilisée pour spécifier à QPainter des drapeaux qui peuvent être respectés ou non par le moteur. La valeur QPainter::Antialiasing indique que le moteur doit si possible appliquer un anticrénelage aux bords des primitives, c'est-à-dire adoucir les bords par l'utilisation de différentes intensités de couleurs.

Mais par défaut, le QPainter n'active pas l'anticrénelage et d'autres règles sont appliquées : lors du rendu avec un pinceau de largeur 1, les pixels seront rendus à droite en dessous des points définis mathématiquement. Par exemple :

image image
 QPainter painter(this);
 
 
 
 painter.setPen(Qt::darkGreen);
 
 painter.drawRect(1, 2, 6, 4);
 QPainter painter(this);
 
 
 
 painter.setPen(Qt::darkGreen);
 
 painter.drawLine(2, 7, 6, 1);

Lors du rendu avec un nombre pair de pixels, les pixels seront rendus symétriquement autour des points définis mathématiquement, alors que le rendu avec un pinceau ayant un nombre impair de pixels dessinera les pixels libres en dessous et à droite du point défini mathématiquement, tout comme dans le cas du pixel unique. Voir les diagrammes de QRectF ci-dessous pour des exemples concrets.

QRectF
image image
Représentation logique pinceau de largeur 1 pixel
image image
pinceau de largeur 2 pixels pinceau de largeur 3 pixels

Notez que pour des raisons historiques, la valeur de retour des fonctions QRect::right() et QRect::bottom() dévie du véritable coin inférieur droit du rectangle.

La fonction right() de QRect retourne left() + width() - 1 et la fonction bottom() retourne top() + height() - 1. Le point vert en bas à droite sur les diagrammes indique les coordonnées retournées par cette fonction.

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

Autre solution, utilisez QRect, et x() + width() et y() + height() pour trouver le coin inférieur droit et ainsi éviter les fonctions right() et bottom().

Anticrénelage du rendu

Si vous définissez l'indicateur anticrénelage du QPainter, les pixels seront dessinés symétriquement des deux côtés des points définis mathématiquement:

image image
 QPainter painter(this);
 
 painter.setRenderHint(
 
     QPainter::Antialiasing);
 
 painter.setPen(Qt::darkGreen);
 
 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, le QPainter opère sur les systèmes de coordonnées des périphériques (généralement des pixels), mais il gère également les transformations affines de coordonnées.

Vous pouvez redimensionner le système de coordonnées grâce à la fonction QPainter::scale(), vous pouvez le tourner avec la fonction QPainter::rotate() et le déplacer (c'est-à-dire ajouter un décalage aux points) avec la fonction QPainter::translate().

image image image image
nop rotate() scale() translate()

Vous pouvez aussi changer l'angle entre les axes du système de coordonnées avec la fonction QPainter::shear(). Voir la démonstration Transformations affines pour une présentation d'un système de coordonnées non orthogonal. Toutes les opérations de transformation agissent sur la matrice de transformation du QPainter que vous pouvez récupérer à l'aide de la fonction QPainter::worldTransform(). Une matrice transforme un point dans le plan en un autre point.

Si vous avez besoin des mêmes transformations plusieurs fois, vous pouvez aussi utiliser les objets QTransform et les fonctions QPainter::worldTransform() et QPainter::setWorldTransform(). Vous pouvez sauvegarder à n'importe quel moment la matrice de transformation de QPainter en appelant la fonction QPainter::save() qui sauvegarde la matrice dans une pile interne. La fonction QPainter::restore() restaure la matrice en la retirant de la pile.

Un besoin fréquent pour les matrices de transformation se manifeste lors de la réutilisation du même code de dessin pour des périphériques de dessin variés. Les imprimantes possèdent de hautes résolutions, par exemple, 600 points par pouce, alors que les résolutions des écrans varient souvent entre 72 et 100 points par pouce.

Exemple d'horloge analogique

image

L'exemple de l'horloge analogique montre comment afficher le contenu d'un widget personnalisé en utilisant la matrice de transformation du QPainter. Qt est fourni avec une explication détaillée de cet exemple. Nous allons ici nous limiter à l'examen de la fonction paintEvent() de l'exemple et voir comment nous pouvons utiliser la matrice de transformation (c'est-à-dire les fonctions de matrices de QPainter) pour dessiner l'horloge. Nous recommandons de compiler et d'exécuter l'exemple avant de lire la suite. En particulier, essayez de redimensionner la fenêtre à différentes tailles.

 void AnalogClock::paintEvent(QPaintEvent *)
 
 {
 
     static const QPoint hourHand[3] = {
 
         QPoint(7, 8),
 
         QPoint(-7, 8),
 
         QPoint(0, -40)
 
     };
 
     static const QPoint minuteHand[3] = {
 
         QPoint(7, 8),
 
         QPoint(-7, 8),
 
         QPoint(0, -70)
 
     };
 
 
 
     QColor hourColor(127, 0, 127);
 
     QColor minuteColor(0, 127, 127, 191);
 
 
 
     int side = qMin(width(), height());
 
     QTime time = QTime::currentTime();
 
 
 
     QPainter painter(this);
 
     painter.setRenderHint(QPainter::Antialiasing);
 
     painter.translate(width() / 2, height() / 2);
 
     painter.scale(side / 200.0, side / 200.0);

Premièrement, nous définissons le QPainter. Nous déplaçons le système de coordonnées afin que le point (0, 0) corresponde au centre du widget, au lieu d'être le coin supérieur gauche. Nous multiplions la taille du système de coordonnées par le facteur side / 100, où side est la plus petite valeur entre la largeur du widget et sa hauteur. Nous souhaitons que l'horloge soit carrée, même si le périphérique ne l'est pas. Cela nous donnera une zone carrée de 200 × 200 ayant son origine (0,0) au centre, sur laquelle nous pouvons dessiner. Nous dessinerons dans le carré le plus grand possible entrant dans le widget.

Voir aussi la section conversion Fenêtre-Viewport.

     painter.save();
 
     painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
 
     painter.drawConvexPolygon(hourHand, 3);
 
     painter.restore();

Nous dessinons l'aiguille des heures de l'horloge en tournant le système de coordonnées puis en appelant QPainter::drawConvexPolygon(). Grâce à la rotation, l'aiguille pointe dans la bonne direction.

Le polygone est spécifié sous forme d'un tableau alternant les valeurs x et y, conservées dans la variable statique hourHand (définie au début de la fonction), et qui correspond aux quatre points (2, 0), (0, 2), (-2, 0) et (0, -25).

Les appels aux fonctions QPainter::save() et QPainter::restore() entourant le code garantissent que le code suivant ne va pas perturber les transformations que nous avons utilisées.

     painter.save();
 
     painter.rotate(6.0 * (time.minute() + time.second() / 60.0));
 
     painter.drawConvexPolygon(minuteHand, 3);
 
     painter.restore();

Nous dessinons de la même façon l'aiguille des minutes, définie par les quatre points (1, 0), (0, 1), (-1, 0) et (0, -40). Ces coordonnées spécifient une aiguille plus petite et plus longue que l'aiguille des heures.

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

Pour terminer, nous dessinons le cadre de l'horloge, qui est constitué de 12 petites lignes à 30 degrés d'intervalle. À la fin, le QPainter est tourné d'une manière qui n'est pas très utile mais cela importe peu car nous avons terminé le dessin.

 

Pour une démonstration des capacités de Qt à effectuer des transformations affines sur les opérations de dessin, voir la démonstration Transformations affines qui permet à l'utilisateur d'expérimenter les opérations de transformation. Voir aussi l'exemple Transformations qui présente l'influence des transformations dans le rendu des primitives graphiques par QPainter. En particulier, l'exemple montre la manière dont l'ordre des transformations affecte le résultat.

Pour plus d'information à propos des matrices de transformations, voyez la documentation de QTransform.

Conversion fenêtre-afficheur

Lors du rendu avec QPainter, nous spécifions les points en utilisant des coordonnées logiques qui sont ensuite converties en coordonnées physiques du périphérique de dessin.

Cette correspondance entre les coordonnées logiques et physiques est gérée par la transformation de monde worldTransform() du QPainter (décrite dans la section Transformations), et l'afficheur (viewport) et la fenêtre de QPainter. L'afficheur représente les coordonnées physiques spécifiant un rectangle arbitraire. La « fenêtre » représente le même rectangle mais dans les coordonnées logiques. Par défaut les systèmes de coordonnées logiques et physiques coïncident et sont équivalents à celui du périphérique de dessin.

Avec la conversion fenêtre-afficheur vous pouvez faire en sorte que le système de coordonnées logiques corresponde à vos préférences. Le mécanisme peut aussi être utilisé pour rendre le code de dessin indépendant du périphérique de dessin. Vous pouvez, par exemple, faire s'étendre le système de coordonnées logiques de (-50, -50) à (50, 50) avec le centre en (0, 0) en appelant la fonction QPainter::setWindow():

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

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

En définissant le rectangle de la « fenêtre » ou de l'afficheur, vous réalisez une transformation linéaire des coordonnées. Notez que chaque coin de la « fenêtre » correspond aux coins de l'afficheur et inversement. Pour cette raison, il est en général conseillé de conserver les mêmes proportions pour l'afficheur et pour la « fenêtre » afin d'éviter les déformations :

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

Si nous rendons le système de coordonnées logiques carré, nous devrions aussi rendre l'afficheur carré en utilisant la fonction QPainter::setViewport(). Dans l'exemple ci-dessus, nous le définissons comme le carré le plus grand rentrant dans le rectangle du périphérique de dessin. En prenant la taille du périphérique de dessin en considération lors de la définition de la fenêtre ou de l'afficheur, il est possible de conserver un code de dessin indépendant du périphérique de dessin.

Notez que la conversion fenêtre-afficheur n'est qu'une transformation linéaire, c'est-à-dire qu'elle n'effectue pas de clipping. Cela signifie que si vous dessinez en dehors de la « fenêtre » définie, votre dessin sera toujours transformé vers l'afficheur en utilisant la même algèbre linéaire.

image

L'afficheur, la « fenêtre » et la matrice de transformation déterminent les conversions entre les coordonnées logiques du QPainter et les coordonnées physiques du périphérique de dessin. Par défaut, la matrice de transformation de monde est la matrice identité et les paramètres de la « fenêtre » et de l'afficheur sont équivalents aux paramètres du périphérique de dessin, c'est à dire que les systèmes de coordonnées du monde, de la « fenêtre » et du périphérique sont équivalents, mais comme nous l'avons vu, les systèmes peuvent être manipulés en utilisant les opérations de transformation et la conversion fenêtre-afficheur. L'illustration ci-dessus décrit ce processus.

Voir aussi l'exemple d'horloge analogique et l'exemple de transformations.

Remerciements

Merci à Alexandre Laurent pour la traduction, ainsi qu'à Ilya Diallo et Claude Leloup pour la relecture !

Warning: include(): https:// wrapper is disabled in the server configuration by allow_url_include=0 in /home/developpez/www/developpez-com/upload/qt/doc/bs.php on line 4 Warning: include(https://qt.developpez.com/index/rightColumn): failed to open stream: no suitable wrapper could be found in /home/developpez/www/developpez-com/upload/qt/doc/bs.php on line 4 Warning: include(): Failed opening 'https://qt.developpez.com/index/rightColumn' for inclusion (include_path='.:/usr/php53/lib/php') in /home/developpez/www/developpez-com/upload/qt/doc/bs.php on line 4
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.7
Copyright © 2019 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur, une redirection cassée ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
Responsable bénévole de la rubrique Qt : Thibaut Cuvelier -

Partenaire : Hébergement Web