Sur cette page

Horloge analogique

L'exemple « Horloge analogique » montre comment afficher le contenu d'un widget personnalisé.

Horloge analogique avec aiguilles des heures, des minutes et des secondes

Capture d'écran de l'exemple « Horloge analogique »

Cet exemple montre également comment les fonctionnalités de transformation et de mise à l'échelle d'QPainter peuvent être utilisées pour faciliter le dessin de widgets personnalisés.

Définition de la classe AnalogClock

La classe ` AnalogClock ` fournit un widget d’horloge doté d’aiguilles des heures, des minutes et des secondes, qui se met automatiquement à jour toutes les secondes. Nous créons une sous-classe de ` QWidget ` et réimplémentons la fonction standard ` paintEvent()` pour dessiner le cadran de l’horloge :

class AnalogClock : public QWidget
{
    Q_OBJECT

public:
    AnalogClock(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
};

Implémentation de la classe AnalogClock

Lors de la construction du widget, nous configurons un temporisateur d’une seconde pour suivre l’heure actuelle, et nous le connectons au slot standard ` update()` afin que le cadran de l’horloge soit mis à jour lorsque le temporisateur émet le signal ` timeout()`. Enfin, nous redimensionnons le widget pour qu’il s’affiche à une taille raisonnable.

AnalogClock::AnalogClock(QWidget *parent)
    : QWidget(parent)
{
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
    timer->start(1000);

    setWindowTitle(tr("Analog Clock"));
    resize(200, 200);
}

La fonction ` paintEvent() ` est appelée chaque fois que le contenu du widget doit être mis à jour. Cela se produit lorsque le widget s’affiche pour la première fois, ainsi que lorsqu’il est masqué puis réapparu, mais elle est également exécutée lorsque le slot ` update()` du widget est appelé. Comme nous avons connecté le signal ` timeout()` du minuteur à ce slot, celui-ci sera appelé au moins une fois par seconde.

Avant de configurer le « painter » et de dessiner l’horloge, nous définissons d’abord trois listes d’ QPoints et trois QColors qui seront utilisées pour les aiguilles des heures, des minutes et des secondes. Nous utilisons la fonction ` palette()` pour obtenir des couleurs adaptées au reste de la fenêtre, aussi bien en mode clair qu’en mode sombre. Les aiguilles des heures et des minutes sont dessinées dans la couleur de premier plan, tandis que l’aiguille des secondes est dessinée dans la couleur d’accent.

Nous déterminons également la longueur du côté le plus court du widget afin de pouvoir y intégrer le cadran de l’horloge. Il est également utile de déterminer l’heure actuelle avant de commencer le dessin.

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());

Le contenu des widgets personnalisés est dessiné à l’aide d’un ` QPainter`. Les `painters` peuvent être utilisés pour dessiner sur n’importe quel ` QPaintDevice`, mais ils sont généralement associés à des widgets ; nous transmettons donc l’instance du widget au constructeur du `painter`.

    QPainter painter(this);
    QTime time = QTime::currentTime();

Nous appelons ` QPainter::setRenderHint()` avec ` QPainter::Antialiasing ` pour activer l’anticrénelage. Cela rend le tracé des lignes diagonales beaucoup plus fluide.

    painter.setRenderHint(QPainter::Antialiasing);

La translation déplace l’origine au centre du widget, et l’opération de mise à l’échelle garantit que les opérations de dessin suivantes sont mises à l’échelle pour s’adapter aux dimensions du widget. Nous utilisons un facteur d’échelle qui nous permet d’utiliser des coordonnées x et y comprises entre -100 et 100, et qui garantit que celles-ci restent dans les limites de la longueur du côté le plus court du widget.

    painter.translate(width() / 2, height() / 2);
    painter.scale(side / 200.0, side / 200.0);

Pour simplifier notre code, nous allons dessiner un cadran de taille fixe qui sera positionné et mis à l'échelle de manière à se trouver au centre du widget.

Le « painter » se charge de toutes les transformations effectuées lors de l’événement de dessin et garantit que tout est dessiné correctement. Confier les transformations au « painter » est souvent plus simple que d’effectuer des calculs manuels uniquement pour dessiner le contenu d’un widget personnalisé.

Affiche le système de coordonnées et l'origine du widget sur le cadran de l'horloge

Nous définissons le stylo sur « Qt::NoPen » (sans contour) car nous ne voulons pas de contour, et nous utilisons un pinceau plein avec la couleur appropriée pour afficher les heures. Les pinceaux sont utilisés pour remplir des polygones et d’autres formes géométriques.

    painter.setPen(Qt::NoPen);
    painter.setBrush(hourColor);

Nous dessinons d’abord l’aiguille des heures, à l’aide d’une formule qui fait pivoter le système de coordonnées dans le sens inverse des aiguilles d’une montre d’un nombre de degrés déterminé par l’heure et la minute actuelles. Cela signifie que l’aiguille s’affichera pivotée dans le sens des aiguilles d’une montre de l’angle requis. Nous enregistrons et restaurons la matrice de transformation avant et après la rotation en instanciant un ` QPainterStateGuard `, car nous souhaitons placer l’aiguille des minutes sans avoir à tenir compte des rotations précédentes.

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

Nous dessinons des repères autour du pourtour de l’horloge pour chaque heure, dans la même couleur que l’aiguille des heures. Nous dessinons chaque repère, puis faisons pivoter le système de coordonnées afin que le moteur de rendu soit prêt pour le suivant.

    for (int i = 0; i < 12; ++i) {
        painter.drawRect(73, -3, 16, 6);
        painter.rotate(30.0);
    }

L’aiguille des minutes est pivotée et dessinée de la même manière que l’aiguille des heures.

    painter.setBrush(minuteColor);

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

Pour l’aiguille des secondes, nous procédons de la même manière et ajoutons deux cercles pour la mettre en valeur visuellement.

    painter.setBrush(secondsColor);

    {
        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);
    }

Enfin, nous dessinons des repères sur le pourtour du cadran, indiquant les minutes et les secondes. Cette fois-ci, nous les dessinons sous forme de lignes et définissons donc le stylo sur la couleur correspondante.

    painter.setPen(minuteColor);

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

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.