Sur cette page

Exemple de dessin de base

L'exemple de dessin de base montre comment afficher des primitives graphiques de base dans différents styles à l'aide de la classe QPainter.

QPainter Cette classe permet d'effectuer de la peinture de bas niveau sur des widgets et d'autres dispositifs de peinture. La classe peut tout dessiner, des simples lignes aux formes complexes telles que les tartes et les accords. Elle peut également dessiner du texte aligné et des pixmaps. Normalement, elle dessine dans un système de coordonnées "naturel", mais elle peut également effectuer des transformations de vue et de monde.

Fenêtre avec diverses options pour modifier le dessin

L'exemple fournit une zone de rendu, affichant la forme active, et permet à l'utilisateur de manipuler la forme rendue et son apparence à l'aide des paramètres QPainter: L'utilisateur peut changer la forme active (Shape) et modifier le stylo (Pen Width, Pen Style, Pen Cap, Pen Join), le pinceau (Brush Style) et les indices de rendu (Antialiasing) de QPainter. En outre, l'utilisateur peut faire pivoter une forme (Transformations) ; en coulisse, nous utilisons la capacité de QPainter à manipuler le système de coordonnées pour effectuer la rotation.

L'exemple de dessin de base se compose de deux classes :

  • RenderArea est un widget personnalisé qui rend de multiples copies de la forme active.
  • Window est la fenêtre principale de l'application qui affiche un widget RenderArea en plus de plusieurs widgets de paramètres.

Nous examinerons d'abord la classe Window, puis la classe RenderArea.

Définition de la classe Window

La classe Window hérite de la classe QWidget et constitue la fenêtre principale de l'application affichant un widget RenderArea ainsi que plusieurs widgets de paramètres.

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private slots:
    void shapeChanged();
    void penChanged();
    void brushChanged();

private:
    RenderArea *renderArea;
    QLabel *shapeLabel;
    QLabel *penWidthLabel;
    QLabel *penStyleLabel;
    QLabel *penCapLabel;
    QLabel *penJoinLabel;
    QLabel *brushStyleLabel;
    QLabel *otherOptionsLabel;
    QComboBox *shapeComboBox;
    QSpinBox *penWidthSpinBox;
    QComboBox *penStyleComboBox;
    QComboBox *penCapComboBox;
    QComboBox *penJoinComboBox;
    QComboBox *brushStyleComboBox;
    QCheckBox *antialiasingCheckBox;
    QCheckBox *transformationsCheckBox;
};

Nous déclarons les différents widgets, ainsi que trois slots privés qui mettent à jour le widget RenderArea: Le slot shapeChanged() met à jour le widget RenderArea lorsque l'utilisateur modifie la forme active. Nous appelons l'emplacement penChanged() lorsque l'un des paramètres du stylo de QPainter change. Le slot brushChanged() met à jour le widget RenderArea lorsque l'utilisateur modifie le style de pinceau du peintre.

Mise en œuvre de la classe de fenêtre

Dans le constructeur, nous créons et initialisons les différents widgets apparaissant dans la fenêtre principale de l'application.

Window::Window()
{
    renderArea = new RenderArea;

    shapeComboBox = new QComboBox;
    shapeComboBox->addItem(tr("Polygon"), RenderArea::Polygon);
    shapeComboBox->addItem(tr("Rectangle"), RenderArea::Rect);
    shapeComboBox->addItem(tr("Rounded Rectangle"), RenderArea::RoundedRect);
    shapeComboBox->addItem(tr("Ellipse"), RenderArea::Ellipse);
    shapeComboBox->addItem(tr("Pie"), RenderArea::Pie);
    shapeComboBox->addItem(tr("Chord"), RenderArea::Chord);
    shapeComboBox->addItem(tr("Path"), RenderArea::Path);
    shapeComboBox->addItem(tr("Line"), RenderArea::Line);
    shapeComboBox->addItem(tr("Polyline"), RenderArea::Polyline);
    shapeComboBox->addItem(tr("Arc"), RenderArea::Arc);
    shapeComboBox->addItem(tr("Points"), RenderArea::Points);
    shapeComboBox->addItem(tr("Text"), RenderArea::Text);
    shapeComboBox->addItem(tr("Pixmap"), RenderArea::Pixmap);

    shapeLabel = new QLabel(tr("&Shape:"));
    shapeLabel->setBuddy(shapeComboBox);

Nous créons d'abord le widget RenderArea qui rendra la forme actuellement active. Ensuite, nous créons la boîte combobox Shape et ajoutons les éléments associés (c'est-à-dire les différentes formes qu'un QPainter peut dessiner).

    penWidthSpinBox = new QSpinBox;
    penWidthSpinBox->setRange(0, 20);
    penWidthSpinBox->setSpecialValueText(tr("0 (cosmetic pen)"));

    penWidthLabel = new QLabel(tr("Pen &Width:"));
    penWidthLabel->setBuddy(penWidthSpinBox);

QPainterLe stylo d'un peintre est un objet QPen; la classe QPen définit la manière dont un peintre doit dessiner les lignes et les contours des formes. Un stylo possède plusieurs propriétés : Largeur, style, capuchon et jointure.

La largeur d'un stylo peut être égale ou supérieure à zéro, mais la largeur la plus courante est zéro. Notez que cela ne signifie pas 0 pixel, mais implique que la forme est dessinée aussi doucement que possible, même si ce n'est pas mathématiquement correct.

Nous créons un site QSpinBox pour le paramètre Pen Width.

    penStyleComboBox = new QComboBox;
    penStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidLine));
    penStyleComboBox->addItem(tr("Dash"), static_cast<int>(Qt::DashLine));
    penStyleComboBox->addItem(tr("Dot"), static_cast<int>(Qt::DotLine));
    penStyleComboBox->addItem(tr("Dash Dot"), static_cast<int>(Qt::DashDotLine));
    penStyleComboBox->addItem(tr("Dash Dot Dot"), static_cast<int>(Qt::DashDotDotLine));
    penStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoPen));

    penStyleLabel = new QLabel(tr("&Pen Style:"));
    penStyleLabel->setBuddy(penStyleComboBox);

    penCapComboBox = new QComboBox;
    penCapComboBox->addItem(tr("Flat"), Qt::FlatCap);
    penCapComboBox->addItem(tr("Square"), Qt::SquareCap);
    penCapComboBox->addItem(tr("Round"), Qt::RoundCap);

    penCapLabel = new QLabel(tr("Pen &Cap:"));
    penCapLabel->setBuddy(penCapComboBox);

    penJoinComboBox = new QComboBox;
    penJoinComboBox->addItem(tr("Miter"), Qt::MiterJoin);
    penJoinComboBox->addItem(tr("Bevel"), Qt::BevelJoin);
    penJoinComboBox->addItem(tr("Round"), Qt::RoundJoin);

    penJoinLabel = new QLabel(tr("Pen &Join:"));
    penJoinLabel->setBuddy(penJoinComboBox);

Le style de stylo définit le type de ligne. Le style par défaut est solide (Qt::SolidLine). Le fait de définir le style sur none (Qt::NoPen) indique au peintre de ne pas dessiner de lignes ou de contours. Le capuchon du stylo définit la manière dont les extrémités des lignes sont dessinées. Et la jonction du stylo définit la manière dont deux lignes se rejoignent lorsque plusieurs lignes connectées sont dessinées. Le capuchon et la jonction ne s'appliquent qu'aux lignes d'une largeur égale ou supérieure à 1 pixel.

Nous créons des classes QComboBoxpour chacun des paramètres Pen Style, Pen Cap et Pen Join et ajoutons les éléments associés (c'est-à-dire les valeurs des enums Qt::PenStyle, Qt::PenCapStyle et Qt::PenJoinStyle respectivement).

    brushStyleComboBox = new QComboBox;
    brushStyleComboBox->addItem(tr("Linear Gradient"),
            static_cast<int>(Qt::LinearGradientPattern));
    brushStyleComboBox->addItem(tr("Radial Gradient"),
            static_cast<int>(Qt::RadialGradientPattern));
    brushStyleComboBox->addItem(tr("Conical Gradient"),
            static_cast<int>(Qt::ConicalGradientPattern));
    brushStyleComboBox->addItem(tr("Texture"), static_cast<int>(Qt::TexturePattern));
    brushStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidPattern));
    brushStyleComboBox->addItem(tr("Horizontal"), static_cast<int>(Qt::HorPattern));
    brushStyleComboBox->addItem(tr("Vertical"), static_cast<int>(Qt::VerPattern));
    brushStyleComboBox->addItem(tr("Cross"), static_cast<int>(Qt::CrossPattern));
    brushStyleComboBox->addItem(tr("Backward Diagonal"), static_cast<int>(Qt::BDiagPattern));
    brushStyleComboBox->addItem(tr("Forward Diagonal"), static_cast<int>(Qt::FDiagPattern));
    brushStyleComboBox->addItem(tr("Diagonal Cross"), static_cast<int>(Qt::DiagCrossPattern));
    brushStyleComboBox->addItem(tr("Dense 1"), static_cast<int>(Qt::Dense1Pattern));
    brushStyleComboBox->addItem(tr("Dense 2"), static_cast<int>(Qt::Dense2Pattern));
    brushStyleComboBox->addItem(tr("Dense 3"), static_cast<int>(Qt::Dense3Pattern));
    brushStyleComboBox->addItem(tr("Dense 4"), static_cast<int>(Qt::Dense4Pattern));
    brushStyleComboBox->addItem(tr("Dense 5"), static_cast<int>(Qt::Dense5Pattern));
    brushStyleComboBox->addItem(tr("Dense 6"), static_cast<int>(Qt::Dense6Pattern));
    brushStyleComboBox->addItem(tr("Dense 7"), static_cast<int>(Qt::Dense7Pattern));
    brushStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoBrush));

    brushStyleLabel = new QLabel(tr("&Brush:"));
    brushStyleLabel->setBuddy(brushStyleComboBox);

La classe QBrush définit le motif de remplissage des formes dessinées par un QPainter. Le style de pinceau par défaut est Qt::NoBrush. Ce style indique au peintre de ne pas remplir les formes. Le style standard de remplissage est Qt::SolidPattern.

Nous créons un QComboBox pour le paramètre Brush Style et ajoutons les éléments associés (c'est-à-dire les valeurs de l'énumération Qt::BrushStyle ).

    otherOptionsLabel = new QLabel(tr("Options:"));
    antialiasingCheckBox = new QCheckBox(tr("&Antialiasing"));

L'anticrénelage est une fonctionnalité qui "lisse" les pixels pour créer des lignes plus régulières et moins irrégulières, et qui peut être appliquée à l'aide des conseils de rendu de QPainter. QPainter::RenderHints est utilisé pour spécifier des drapeaux à QPainter qui peuvent être respectés ou non par un moteur donné.

Nous créons simplement un QCheckBox pour l'option Antialiasing.

    transformationsCheckBox = new QCheckBox(tr("&Transformations"));

L'option Transformations implique une manipulation du système de coordonnées qui donnera l'impression que la forme rendue est tournée en trois dimensions.

Nous utilisons les fonctions QPainter::translate(), QPainter::rotate() et QPainter::scale() pour mettre en œuvre cette fonctionnalité représentée dans la fenêtre principale de l'application par un simple QCheckBox.

    connect(shapeComboBox, &QComboBox::activated,
            this, &Window::shapeChanged);
    connect(penWidthSpinBox, &QSpinBox::valueChanged,
            this, &Window::penChanged);
    connect(penStyleComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(penCapComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(penJoinComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(brushStyleComboBox, &QComboBox::activated,
            this, &Window::brushChanged);
    connect(antialiasingCheckBox, &QAbstractButton::toggled,
            renderArea, &RenderArea::setAntialiased);
    connect(transformationsCheckBox, &QAbstractButton::toggled,
            renderArea, &RenderArea::setTransformed);

Nous connectons ensuite les widgets de paramètres avec leurs emplacements associés à l'aide de la fonction statique QObject::connect(), en veillant à ce que le widget RenderArea soit mis à jour chaque fois que l'utilisateur modifie la forme ou l'un des autres paramètres.

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setColumnStretch(0, 1);
    mainLayout->setColumnStretch(3, 1);
    mainLayout->addWidget(renderArea, 0, 0, 1, 4);
    mainLayout->addWidget(shapeLabel, 2, 0, Qt::AlignRight);
    mainLayout->addWidget(shapeComboBox, 2, 1);
    mainLayout->addWidget(penWidthLabel, 3, 0, Qt::AlignRight);
    mainLayout->addWidget(penWidthSpinBox, 3, 1);
    mainLayout->addWidget(penStyleLabel, 4, 0, Qt::AlignRight);
    mainLayout->addWidget(penStyleComboBox, 4, 1);
    mainLayout->addWidget(penCapLabel, 3, 2, Qt::AlignRight);
    mainLayout->addWidget(penCapComboBox, 3, 3);
    mainLayout->addWidget(penJoinLabel, 2, 2, Qt::AlignRight);
    mainLayout->addWidget(penJoinComboBox, 2, 3);
    mainLayout->addWidget(brushStyleLabel, 4, 2, Qt::AlignRight);
    mainLayout->addWidget(brushStyleComboBox, 4, 3);
    mainLayout->addWidget(otherOptionsLabel, 5, 0, Qt::AlignRight);
    mainLayout->addWidget(antialiasingCheckBox, 5, 1, 1, 1, Qt::AlignRight);
    mainLayout->addWidget(transformationsCheckBox, 5, 2, 1, 2, Qt::AlignRight);
    setLayout(mainLayout);

    shapeChanged();
    penChanged();
    brushChanged();
    antialiasingCheckBox->setChecked(true);

    setWindowTitle(tr("Basic Drawing"));
}

Enfin, nous ajoutons les différents widgets à une disposition et appelons les slots shapeChanged(), penChanged(), et brushChanged() pour initialiser l'application. Nous activons également l'anticrénelage.

void Window::shapeChanged()
{
    RenderArea::Shape shape = RenderArea::Shape(shapeComboBox->itemData(
            shapeComboBox->currentIndex(), IdRole).toInt());
    renderArea->setShape(shape);
}

Le slot shapeChanged() est appelé chaque fois que l'utilisateur modifie la forme active.

Nous récupérons d'abord la forme choisie par l'utilisateur à l'aide de la fonction QComboBox::itemData(). Cette fonction renvoie les données relatives au rôle donné dans l'index donné du combobox. Nous utilisons QComboBox::currentIndex() pour récupérer l'index de la forme, et le rôle est défini par l'enum Qt::ItemDataRole; IdRole est un alias pour Qt::UserRole.

Notez que Qt::UserRole n'est que le premier rôle qui peut être utilisé à des fins spécifiques à l'application. Si vous devez stocker différentes données dans le même index, vous pouvez utiliser différents rôles en incrémentant simplement la valeur de Qt::UserRole, par exemple : 'Qt::UserRole + 1' et 'Qt::UserRole + 2'. Toutefois, une bonne pratique de programmation consiste à donner à chaque rôle son propre nom : "monPremierRôle = Qt::UserRole + 1" et "monDeuxièmeRôle = Qt::UserRole + 2". Même si nous n'avons besoin que d'un seul rôle dans cet exemple particulier, nous ajoutons la ligne de code suivante au début du fichier window.cpp.

const int IdRole = Qt::UserRole;

La fonction QComboBox::itemData() renvoie les données sous la forme d'un QVariant, nous devons donc convertir les données en RenderArea::Shape. S'il n'y a pas de données pour le rôle donné, la fonction renvoie QMetaType::UnknownType.

À la fin, nous appelons le slot RenderArea::setShape() pour mettre à jour le widget RenderArea.

void Window::penChanged()
{
    int width = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
            penStyleComboBox->currentIndex(), IdRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
            penCapComboBox->currentIndex(), IdRole).toInt());
    Qt::PenJoinStyle join = Qt::PenJoinStyle(penJoinComboBox->itemData(
            penJoinComboBox->currentIndex(), IdRole).toInt());

    renderArea->setPen(QPen(Qt::blue, width, style, cap, join));
}

Nous appelons le slot penChanged() chaque fois que l'utilisateur modifie l'un des paramètres du stylo. Nous utilisons à nouveau la fonction QComboBox::itemData() pour récupérer les paramètres, puis nous appelons le slot RenderArea::setPen() pour mettre à jour le widget RenderArea.

void Window::brushChanged()
{
    Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox->itemData(

Le slot brushChanged() est appelé chaque fois que l'utilisateur modifie le paramètre de la brosse, que nous récupérons à l'aide de la fonction QComboBox::itemData() comme précédemment.

    if (style == Qt::LinearGradientPattern) {
        QLinearGradient linearGradient(0, 0, 100, 100);
        linearGradient.setColorAt(0.0, Qt::white);
        linearGradient.setColorAt(0.2, Qt::green);
        linearGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(linearGradient);

Si le paramètre de la brosse est un remplissage en dégradé, des actions spéciales sont nécessaires.

La classe QGradient est utilisée en combinaison avec QBrush pour spécifier les remplissages en dégradé. Qt prend actuellement en charge trois types de remplissage en dégradé : linéaire, radial et conique. Chacun d'entre eux est représenté par une sous-classe de QGradient: QLinearGradient, QRadialGradient et QConicalGradient.

Ainsi, si le style de brosse est Qt::LinearGradientPattern, nous créons d'abord un objet QLinearGradient avec une zone d'interpolation entre les coordonnées transmises en tant qu'arguments au constructeur. Les positions sont spécifiées à l'aide de coordonnées logiques. Ensuite, nous définissons les couleurs du dégradé à l'aide de la fonction QGradient::setColorAt(). Les couleurs sont définies à l'aide de points d'arrêt composés d'une position (entre 0 et 1) et d'une adresse QColor. L'ensemble des points d'arrêt décrit la manière dont la zone de gradient doit être remplie. Un dégradé peut avoir un nombre arbitraire de points d'arrêt.

Enfin, nous appelons le slot RenderArea::setBrush() pour mettre à jour la brosse du widget RenderArea avec l'objet QLinearGradient.

    } else if (style == Qt::RadialGradientPattern) {
        QRadialGradient radialGradient(50, 50, 50, 70, 70);
        radialGradient.setColorAt(0.0, Qt::white);
        radialGradient.setColorAt(0.2, Qt::green);
        radialGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(radialGradient);
    } else if (style == Qt::ConicalGradientPattern) {
        QConicalGradient conicalGradient(50, 50, 150);
        conicalGradient.setColorAt(0.0, Qt::white);
        conicalGradient.setColorAt(0.2, Qt::green);
        conicalGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(conicalGradient);

Un schéma d'actions similaire à celui utilisé pour QLinearGradient est utilisé dans les cas de Qt::RadialGradientPattern et Qt::ConicalGradientPattern.

La seule différence réside dans les arguments transmis au constructeur : Dans le cas du constructeur QRadialGradient, le premier argument est le centre, et le second le rayon du gradient radial. Le troisième argument est facultatif, mais peut être utilisé pour définir le point focal du gradient à l'intérieur du cercle (le point focal par défaut est le centre du cercle). En ce qui concerne le constructeur QConicalGradient, le premier argument spécifie le centre du cône et le second l'angle de départ de l'interpolation.

    } else if (style == Qt::TexturePattern) {
        renderArea->setBrush(QBrush(QPixmap(":/images/brick.png")));

Si le style de brosse est Qt::TexturePattern, nous créons un QBrush à partir d'un QPixmap, puis nous appelons le slot RenderArea::setBrush() pour mettre à jour le widget RenderArea avec la nouvelle brosse créée.

    } else {
        renderArea->setBrush(QBrush(Qt::green, style));
    }
}

Sinon, nous créons simplement une brosse avec le style donné et une couleur verte, puis nous appelons le slot RenderArea::setBrush() pour mettre à jour le widget RenderArea avec la brosse nouvellement créée.

Définition de la classe RenderArea

La classe RenderArea hérite de QWidget et rend de multiples copies de la forme active à l'aide d'une zone de rendu QPainter.

class RenderArea : public QWidget
{
    Q_OBJECT

public:
    enum Shape { Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc,
                 Chord, Pie, Path, Text, Pixmap };

    explicit RenderArea(QWidget *parent = nullptr);

    QSize minimumSizeHint() const override;
    QSize sizeHint() const override;

public slots:
    void setShape(Shape shape);
    void setPen(const QPen &pen);
    void setBrush(const QBrush &brush);
    void setAntialiased(bool antialiased);
    void setTransformed(bool transformed);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Shape shape;
    QPen pen;
    QBrush brush;
    bool antialiased;
    bool transformed;
    QPixmap pixmap;
};

Tout d'abord, nous définissons un enum Shape public pour contenir les différentes formes qui peuvent être rendues par le widget (c'est-à-dire les formes qui peuvent être rendues par un QPainter). Nous réimplémentons ensuite le constructeur ainsi que deux des fonctions publiques de QWidget: minimumSizeHint() et sizeHint().

Nous réimplémentons également la fonction QWidget::paintEvent() pour pouvoir dessiner la forme active en fonction des paramètres spécifiés.

Nous déclarons plusieurs slots privés : Le slot setShape() modifie la forme de RenderArea, les slots setPen() et setBrush() modifient le stylo et le pinceau du widget, et les slots setAntialiased() et setTransformed() modifient les propriétés respectives du widget.

Mise en œuvre de la classe RenderArea

Dans le constructeur, nous initialisons certaines variables du widget.

RenderArea::RenderArea(QWidget *parent)
    : QWidget(parent)
{
    shape = Polygon;
    antialiased = false;
    transformed = false;
    pixmap.load(":/images/qt-logo.png");

    setBackgroundRole(QPalette::Base);
    setAutoFillBackground(true);
}

Nous fixons sa forme à Polygon, sa propriété antialiased à false et nous chargeons une image dans la variable pixmap du widget. Enfin, nous définissons le rôle d'arrière-plan du widget, en déterminant la brosse de la variable palette qui sera utilisée pour le rendu de l'arrière-plan. QPalette::Base est généralement blanc.

QSize RenderArea::sizeHint() const
{
    return QSize(400, 200);
}

La propriété RenderArea hérite de la propriété sizeHint de QWidget qui contient la taille recommandée pour le widget. Si la valeur de cette propriété n'est pas valide, aucune taille n'est recommandée.

L'implémentation par défaut de la fonction QWidget::sizeHint() renvoie une taille non valide s'il n'y a pas de disposition pour le widget, et renvoie la taille préférée de la disposition dans le cas contraire.

Notre réimplémentation de la fonction renvoie un QSize d'une largeur de 400 pixels et d'une hauteur de 200 pixels.

QSize RenderArea::minimumSizeHint() const
{
    return QSize(100, 100);
}

RenderArea hérite également de la propriété minimumSizeHint de QWidget qui contient la taille minimale recommandée pour le widget. Là encore, si la valeur de cette propriété n'est pas valide, aucune taille n'est recommandée.

L'implémentation par défaut de QWidget::minimumSizeHint() renvoie une taille non valide s'il n'y a pas de disposition pour le widget, et renvoie la taille minimale de la disposition dans le cas contraire.

Notre réimplémentation de la fonction renvoie une page QSize d'une largeur de 100 pixels et d'une hauteur de 100 pixels.

void RenderArea::setShape(Shape shape)
{
    this->shape = shape;
    update();
}

void RenderArea::setPen(const QPen &pen)
{
    this->pen = pen;
    update();
}

void RenderArea::setBrush(const QBrush &brush)
{
    this->brush = brush;
    update();
}

Les emplacements publics setShape(), setPen() et setBrush() sont appelés chaque fois que nous voulons modifier la forme, la plume ou la brosse d'un widget RenderArea. Nous définissons la forme, le stylo ou la brosse en fonction du paramètre de l'emplacement et appelons QWidget::update() pour rendre les modifications visibles dans le widget RenderArea.

Le slot QWidget::update() ne provoque pas un repeint immédiat, mais planifie un événement de peinture qui sera traité lorsque Qt reviendra dans la boucle d'événements principale.

void RenderArea::setAntialiased(bool antialiased)
{
    this->antialiased = antialiased;
    update();
}

void RenderArea::setTransformed(bool transformed)
{
    this->transformed = transformed;
    update();
}

Avec les slots setAntialiased() et setTransformed(), nous modifions l'état des propriétés en fonction du paramètre du slot, et appelons le slot QWidget::update() pour rendre les changements visibles dans le widget RenderArea.

void RenderArea::paintEvent(QPaintEvent * /* event */)
{
    static const QPoint points[4] = {
        QPoint(10, 80),
        QPoint(20, 10),
        QPoint(80, 30),
        QPoint(90, 70)
    };

    QRect rect(10, 20, 80, 60);

    QPainterPath path;
    path.moveTo(20, 80);
    path.lineTo(20, 30);
    path.cubicTo(80, 0, 50, 50, 80, 80);

    int startAngle = 20 * 16;
    int arcLength = 120 * 16;

Nous réimplémentons ensuite la fonction QWidget::paintEvent(). La première chose que nous faisons est de créer les objets graphiques dont nous aurons besoin pour dessiner les différentes formes.

Nous créons un vecteur de quatre QPoints. Nous utilisons ce vecteur pour rendre les formes Points, Polyline et Polygon. Nous créons ensuite un QRect, définissant un rectangle dans le plan, que nous utilisons comme rectangle de délimitation pour toutes les formes, à l'exception de Path et Pixmap.

Nous créons également une classe QPainterPath. La classe QPainterPath fournit un conteneur pour les opérations de peinture, ce qui permet de construire et de réutiliser des formes graphiques. Un chemin de peinture est un objet composé d'un certain nombre de blocs graphiques, tels que des rectangles, des ellipses, des lignes et des courbes. Pour plus d'informations sur la classe QPainterPath, voir l'exemple Painter Paths. Dans cet exemple, nous créons un chemin de peintre composé d'une ligne droite et d'une courbe de Bézier.

En outre, nous définissons un angle de départ et une longueur d'arc que nous utiliserons pour dessiner les formes Arc, Chord et Pie.

    QPainter painter(this);
    painter.setPen(pen);
    painter.setBrush(brush);
    if (antialiased)
        painter.setRenderHint(QPainter::Antialiasing, true);

Nous créons un QPainter pour le widget RenderArea et définissons le stylo et la brosse du peintre en fonction du stylo et de la brosse de RenderArea. Si l'option de paramètre Antialiasing est cochée, nous définissons également les indices de rendu du peintre. QPainter::Antialiasing indique que le moteur doit antialiaser les bords des primitives si possible.

    for (int x = 0; x < width(); x += 100) {
        for (int y = 0; y < height(); y += 100) {
            QPainterStateGuard guard(&painter);
            painter.translate(x, y);

Enfin, nous effectuons le rendu des multiples copies de la forme de RenderArea. Le nombre de copies dépend de la taille du widget RenderArea, et nous calculons leurs positions en utilisant deux boucles for et la hauteur et la largeur des widgets.

Pour chaque copie, nous sauvegardons d'abord l'état actuel du peintre en instanciant un QPainterStateGuard. Ensuite, nous traduisons le système de coordonnées, à l'aide de la fonction QPainter::translate(), à la position déterminée par les variables des boucles for. Si nous omettons cette translation du système de coordonnées, toutes les copies de la forme seront superposées dans le coin supérieur gauche du widget RenderArea.

            if (transformed) {
                painter.translate(50, 50);
                painter.rotate(60.0);
                painter.scale(0.6, 0.9);
                painter.translate(-50, -50);
            }

Si l'option du paramètre Transformations est cochée, nous effectuons une translation supplémentaire du système de coordonnées avant de faire pivoter le système de coordonnées de 60 degrés dans le sens des aiguilles d'une montre à l'aide de la fonction QPainter::rotate() et de réduire sa taille à l'aide de la fonction QPainter::scale(). Au final, nous ramenons le système de coordonnées à l'endroit où il se trouvait avant la rotation et la mise à l'échelle.

Désormais, lors du rendu de la forme, celle-ci apparaîtra comme si elle avait été tournée en trois dimensions.

            switch (shape) {
            case Line:
                painter.drawLine(rect.bottomLeft(), rect.topRight());
                break;
            case Points:
                painter.drawPoints(points, 4);
                break;
            case Polyline:
                painter.drawPolyline(points, 4);
                break;
            case Polygon:
                painter.drawPolygon(points, 4);
                break;
            case Rect:
                painter.drawRect(rect);
                break;
            case RoundedRect:
                painter.drawRoundedRect(rect, 25, 25, Qt::RelativeSize);
                break;
            case Ellipse:
                painter.drawEllipse(rect);
                break;
            case Arc:
                painter.drawArc(rect, startAngle, arcLength);
                break;
            case Chord:
                painter.drawChord(rect, startAngle, arcLength);
                break;
            case Pie:
                painter.drawPie(rect, startAngle, arcLength);
                break;
            case Path:
                painter.drawPath(path);
                break;
            case Text:
                painter.drawText(rect,
                                 Qt::AlignCenter,
                                 tr("Qt by\nThe Qt Company"));
                break;
            case Pixmap:
                painter.drawPixmap(10, 10, pixmap);
            }

Ensuite, nous identifions la forme de RenderArea et la rendons à l'aide de la fonction de dessin QPainter associée :

Avant de commencer le rendu, nous avons sauvegardé l'état actuel du peintre (en le plaçant sur une pile). La raison en est que nous calculons la position de chaque copie de forme par rapport au même point du système de coordonnées. Lors de la translation du système de coordonnées, nous perdons la connaissance de ce point, sauf si nous sauvegardons l'état actuel du peintre avant de lancer le processus de translation.

        }
    }

    painter.setRenderHint(QPainter::Antialiasing, false);
    painter.setPen(palette().dark().color());
    painter.setBrush(Qt::NoBrush);
    painter.drawRect(QRect(0, 0, width() - 1, height() - 1));
}

Ensuite, lorsque nous avons fini de rendre une copie de la forme, l'état original du peintre est restauré par QPainterStateGuard, qui appelle la fonction QPainter::restore(). De cette manière, nous nous assurons que la prochaine copie de la forme sera rendue dans la bonne position.

Nous pourrions retraduire le système de coordonnées en utilisant QPainter::translate() au lieu de sauvegarder l'état du peintre. Mais étant donné qu'en plus de la translation du système de coordonnées (lorsque l'option du paramètre Transformation est cochée), nous effectuons une rotation et une mise à l'échelle du système de coordonnées, la solution la plus simple consiste à sauvegarder l'état actuel du peintre.

Exemple de projet @ code.qt.io

© 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.