Sur cette page

Exemple de dessin de base

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

QPainter Cette classe effectue un traçage de bas niveau sur des widgets et d'autres périphériques de dessin. Elle permet de dessiner tout type d'éléments, des simples lignes aux formes complexes telles que des secteurs circulaires et des cordes. Elle permet également de dessiner du texte aligné et des images bitmap. Par défaut, elle dessine dans un système de coordonnées « naturel », mais elle peut également effectuer des transformations de vue et de monde.

Fenêtre proposant différentes options pour modifier le dessin

L’exemple fournit une zone de rendu affichant la forme actuellement active et permet à l’utilisateur de manipuler la forme rendue ainsi que son apparence à l’aide des paramètres de ` 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 indications de rendu (Antialiasing) de ` QPainter`. De plus, l’utilisateur peut faire pivoter une forme (Transformations) ; en arrière-plan, nous utilisons la capacité d’ QPainter à manipuler le système de coordonnées pour effectuer la rotation.

L'exemple « Basic Drawing » se compose de deux classes :

  • RenderArea est un widget personnalisé qui affiche plusieurs copies de la forme actuellement active.
  • Window est la fenêtre principale de l’application, qui affiche un widget « RenderArea » ainsi que plusieurs widgets de paramètres.

Nous allons d’abord passer en revue la classe ` Window `, puis nous nous intéresserons à la classe ` RenderArea `.

Définition de la classe Window

La classe `Window` hérite de ` QWidget` et correspond à la fenêtre principale de l’application, qui affiche 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 mettant à jour le widget ` RenderArea ` : le slot ` shapeChanged() ` met à jour le widget ` RenderArea ` lorsque l’utilisateur modifie la forme actuellement active. Nous appelons le slot ` penChanged() ` lorsque l’un des paramètres du stylet de ` QPainter` change. Enfin, le slot ` brushChanged() ` met à jour le widget ` RenderArea ` lorsque l’utilisateur modifie le style de pinceau du peintre.

Implémentation de la classe Window

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 affichera la forme actuellement active. Ensuite, nous créons la liste déroulante Shape et y 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 est un objet de type ` QPen ` ; la classe ` QPen ` définit la manière dont un « peintre » doit tracer les lignes et les contours des formes. Un stylo possède plusieurs propriétés : largeur, style, extrémité et jonction.

La largeur d’un stylo peut être nulle 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 de la manière la plus fluide possible, même si cela n’est peut-être pas mathématiquement correct.

Nous créons un ` 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 « plein » (Qt::SolidLine). Définir le style sur « aucun » (Qt::NoPen) indique au moteur de dessin de ne pas tracer de lignes ni de contours. L’embout du stylo définit la manière dont les extrémités des lignes sont dessinées. Quant à la jonction du stylo, elle définit la manière dont deux lignes se rejoignent lorsque plusieurs lignes connectées sont tracées. Les paramètres « cap » et « join » ne s’appliquent qu’aux lignes d’une largeur d’au moins 1 pixel.

Nous créons des objets ` QComboBox` pour chacun des paramètres ` Pen Style`, ` Pen Cap ` et ` Pen Join `, et y ajoutons les éléments associés (c'est-à-dire respectivement les valeurs des énumérations ` Qt::PenStyle`, ` Qt::PenCapStyle ` et ` Qt::PenJoinStyle `).

    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 y 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 afin de créer des lignes plus régulières et moins irrégulières ; il peut être appliqué à l’aide des indications de rendu d’ QPainter. Les QPainter::RenderHints servent à spécifier des indicateurs à QPainter qui peuvent être respectés ou non par un moteur donné.

Il suffit de créer un fichier 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 pivotée en trois dimensions.

Nous utilisons les fonctions QPainter::translate(), QPainter::rotate() et QPainter::scale() pour implémenter 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 relions ensuite les widgets de paramètres à leurs slots 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 mise en page 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 actuellement 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 correspondant au rôle donné dans l’index spécifié de la liste déroulante. Nous utilisons ` QComboBox::currentIndex()` pour récupérer l’index de la forme, et le rôle est défini par l’énumération ` Qt::ItemDataRole ` ; ` IdRole ` est un alias de ` Qt::UserRole`.

Notez que ` Qt::UserRole ` n’est que le premier rôle pouvant être utilisé à des fins spécifiques à l’application. Si vous avez besoin de stocker différentes données sous 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'`. Cependant, il est recommandé, en matière de bonnes pratiques de programmation, de donner à chaque rôle son propre nom : « myFirstRole = Qt::UserRole + 1 » et « mySecondRole = 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 objet QVariant; nous devons donc les convertir en RenderArea::Shape. S’il n’y a pas de données pour le rôle donné, la fonction renvoie QMetaType::UnknownType.

Enfin, 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 stylet. Là encore, nous utilisons la fonction QComboBox::itemData() pour récupérer les paramètres, puis nous appelons le slot RenderArea::setPen() afin de 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 du pinceau, 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 « brush » correspond à un remplissage en dégradé, des actions spécifiques 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 remplissages 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 pinceau est « Qt::LinearGradientPattern », on crée d’abord un objet ` QLinearGradient ` avec une zone d’interpolation comprise entre les coordonnées passées en arguments au constructeur. Les positions sont spécifiées à l’aide de coordonnées logiques. On définit ensuite 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 (comprise entre 0 et 1) et d’une QColor. L’ensemble des points d’arrêt décrit la manière dont la zone du dégradé doit être remplie. Un dégradé peut comporter un nombre arbitraire de points d’arrêt.

Enfin, nous appelons le slot ` RenderArea::setBrush() ` pour mettre à jour le pinceau 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 : concernant le constructeur de ` QRadialGradient `, le premier argument correspond au centre, et le second au rayon du dégradé radial. Le troisième argument est facultatif, mais peut servir à définir le point focal du dégradé à 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 pinceau est « Qt::TexturePattern », nous créons un « QBrush » à partir d’un « QPixmap ». Nous appelons ensuite le slot « RenderArea::setBrush() » pour mettre à jour le widget « RenderArea » avec le pinceau nouvellement créé.

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

Sinon, nous créons simplement un pinceau avec le style donné et de couleur verte, puis nous appelons le slot « RenderArea::setBrush() » pour mettre à jour le widget « RenderArea » avec le pinceau nouvellement créé.

Définition de la classe RenderArea

La classe ` RenderArea ` hérite de ` QWidget` et affiche plusieurs copies de la forme actuellement active à l’aide d’un ` 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;
};

Nous définissons tout d’abord une énumération publique ` Shape ` pour répertorier les différentes formes pouvant être rendues par le widget (c’est-à-dire les formes pouvant être rendues par un ` QPainter`). Ensuite, nous réimplémentons le constructeur ainsi que deux des fonctions publiques de ` QWidget` : ` minimumSizeHint()` et ` sizeHint()`.

Nous réimplémentons également la fonction QWidget::paintEvent() afin de pouvoir dessiner la forme actuellement active selon les paramètres spécifiés.

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

Implémentation 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 définissons sa forme comme étant une « Polygon », sa propriété « antialiased » sur « 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 spécifiant le pinceau issu de la propriété « palette » du widget qui sera utilisé pour dessiner l’arrière-plan. « QPalette::Base » est généralement blanc.

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

L’ 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é correspond à une taille non 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’existe pas de mise en page pour le widget, et renvoie sinon la taille préférée de la mise en page.

Notre réimplémentation de la fonction renvoie une instance de ` QSize ` d’une largeur de 400 pixels et d’une hauteur de 200 pixels.

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

RenderArea Elle 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é correspond à une taille non valide, aucune taille n’est recommandée.

L’implémentation par défaut de ` QWidget::minimumSizeHint()` renvoie une taille non valide s’il n’existe aucune mise en page pour le widget, et renvoie la taille minimale de la mise en page dans le cas contraire.

Notre réimplémentation de la fonction renvoie un 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 slots publics ` setShape()`, ` setPen() ` et ` setBrush() ` sont appelés chaque fois que nous souhaitons modifier la forme, le stylo ou le pinceau d’un widget ` RenderArea `. Nous définissons la forme, le stylo ou le pinceau en fonction du paramètre du slot, puis nous appelons ` QWidget::update()` pour que les modifications soient visibles dans le widget ` RenderArea `.

Le slot QWidget::update() ne provoque pas un rafraîchissement immédiat ; il planifie plutôt un événement de dessin qui sera traité lorsque Qt reviendra à 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, puis nous appelons le slot QWidget::update() pour que les modifications soient 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;

Ensuite, nous réimplémentons 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 objets ` QPoint`. Nous utilisons ce vecteur pour afficher les formes ` Points`, ` Polyline ` et ` Polygon `. Ensuite, nous créons un objet ` 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 un « QPainterPath ». La classe « QPainterPath » fournit un conteneur pour les opérations de dessin, permettant de construire et de réutiliser des formes graphiques. Un tracé de dessin est un objet composé d’un certain nombre d’éléments graphiques de base, tels que des rectangles, des ellipses, des lignes et des courbes. Pour plus d’informations sur la classe ` QPainterPath `, consultez l’exemple « Tracés de dessin ». Dans cet exemple, nous créons un tracé de dessin composé d’une ligne droite et d’une courbe de Bézier.

De plus, nous définissons un angle de départ et une longueur d’arc que nous utiliserons lors du tracé des 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 », puis configurons le stylo et le pinceau du «painter» en fonction de ceux de « RenderArea ». Si l’option « Antialiasing » est cochée, nous définissons également les indications de rendu du «painter». « QPainter::Antialiasing » indique que le moteur doit, si possible, appliquer un anticrénelage aux bords des primitives.

    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 rendons les multiples copies de la forme de l’ RenderArea. Le nombre de copies dépend de la taille du widget RenderArea, et nous calculons leurs positions à l’aide de deux boucles for ainsi que de la hauteur et de la largeur du widget.

Pour chaque copie, nous enregistrons d’abord l’état actuel du peintre en instanciant un ` QPainterStateGuard`. Ensuite, nous translatons le système de coordonnées, à l’aide de la fonction ` QPainter::translate()`, vers 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 rendues les unes sur les autres 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 le faire pivoter 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(). Enfin, nous ramenons le système de coordonnées à sa position initiale, avant qu’il ne soit pivoté et mis à l’échelle.

Désormais, lors du rendu de la forme, celle-ci apparaîtra comme si elle avait été pivoté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 l’ RenderArea, puis nous l’affichons à l’aide de la fonction de dessin associée QPainter:

Avant de commencer le rendu, nous avons enregistré l’état actuel du peintre (en plaçant cet état sur une pile). La raison en est que nous calculons la position de chaque copie de forme par rapport à un même point du système de coordonnées. Lorsque nous translatons le système de coordonnées, nous perdons la référence à ce point à moins d'enregistrer 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 dessiner une copie de la forme, l’état initial du peintre est restauré par ` QPainterStateGuard`, qui appelle la fonction ` QPainter::restore()`. De cette manière, nous nous assurons que la copie suivante de la forme sera dessinée à la bonne position.

Nous pourrions réinitialiser le système de coordonnées à l’aide de ` QPainter::translate()` au lieu d’enregistrer l’état du peintre. Mais comme, en plus de translater le système de coordonnées (lorsque l’option du paramètre ` Transformation ` est cochée), nous effectuons également une rotation et une mise à l’échelle de ce système, la solution la plus simple consiste à enregistrer 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.