Analoge Uhr

Das Beispiel der analogen Uhr zeigt, wie der Inhalt eines benutzerdefinierten Widgets gezeichnet werden kann.

Screenshot des Beispiels "Analoge Uhr

Dieses Beispiel zeigt auch, wie die Transformations- und Skalierungsfunktionen von QPainter verwendet werden können, um das Zeichnen von benutzerdefinierten Widgets zu erleichtern.

Definition der Klasse AnalogClock

Die Klasse AnalogClock bietet ein Uhren-Widget mit Stunden-, Minuten- und Sekundenzeiger, das automatisch jede Sekunde aktualisiert wird. Wir subclass QWidget und reimplementieren die Standardfunktion paintEvent(), um das Ziffernblatt zu zeichnen:

class AnalogClock : public QWidget
{
    Q_OBJECT

public:
    AnalogClock(QWidget *parent = nullptr);

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

Implementierung der Klasse AnalogClock

Wenn das Widget konstruiert ist, richten wir einen Ein-Sekunden-Timer ein, um die aktuelle Zeit zu verfolgen, und verbinden ihn mit dem Standard-Slot update(), so dass das Ziffernblatt aktualisiert wird, wenn der Timer das Signal timeout() ausgibt. Schließlich passen wir die Größe des Widgets so an, dass es in einer angemessenen Größe angezeigt wird.

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

Die Funktion paintEvent() wird immer dann aufgerufen, wenn der Inhalt des Widgets aktualisiert werden muss. Dies geschieht, wenn das Widget zum ersten Mal angezeigt wird, und wenn es abgedeckt und dann freigelegt wird, aber sie wird auch ausgeführt, wenn der update()-Slot des Widgets aufgerufen wird. Da wir das Signal timeout() des Timers mit diesem Slot verbunden haben, wird er mindestens einmal pro Sekunde aufgerufen.

Bevor wir den Painter einrichten und die Uhr zeichnen, definieren wir zunächst drei Listen mit QPoints und drei QColors, die für den Stunden-, Minuten- und Sekundenzeiger verwendet werden. Wir verwenden die Funktion palette(), um geeignete Farben zu erhalten, die in den Rest des Fensters passen, sowohl im hellen als auch im dunklen Modus. Der Stunden- und der Minutenzeiger werden in der Vordergrundfarbe gezeichnet, der Sekundenzeiger in der Akzentfarbe.

Wir bestimmen auch die Länge der kürzesten Seite des Widgets, damit das Ziffernblatt in das Widget passt. Es ist auch nützlich, die aktuelle Zeit zu bestimmen, bevor wir mit dem Zeichnen beginnen.

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

Der Inhalt von benutzerdefinierten Widgets wird mit einem QPainter gezeichnet. Painter können zum Zeichnen auf jedem QPaintDevice verwendet werden, aber sie werden normalerweise mit Widgets verwendet, also übergeben wir die Widget-Instanz an den Konstruktor des Painters.

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

Wir rufen QPainter::setRenderHint() mit QPainter::Antialiasing auf, um das Antialiasing zu aktivieren. Dies macht das Zeichnen von diagonalen Linien viel glatter.

    painter.setRenderHint(QPainter::Antialiasing);

Die Verschiebung verschiebt den Ursprung in die Mitte des Widgets, und die Skalierungsoperation stellt sicher, dass die folgenden Zeichenoperationen so skaliert werden, dass sie in das Widget passen. Wir verwenden einen Skalierungsfaktor, der es uns ermöglicht, x- und y-Koordinaten zwischen -100 und 100 zu verwenden, und der sicherstellt, dass diese innerhalb der Länge der kürzesten Seite des Widgets liegen.

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

Um unseren Code zu vereinfachen, zeichnen wir ein Zifferblatt fester Größe, das so positioniert und skaliert wird, dass es in der Mitte des Widgets liegt.

Der Painter kümmert sich um alle Transformationen, die während des Paint-Ereignisses vorgenommen werden, und stellt sicher, dass alles korrekt gezeichnet wird. Es ist oft einfacher, den Painter mit den Transformationen zu beauftragen, als manuelle Berechnungen durchzuführen, nur um den Inhalt eines benutzerdefinierten Widgets zu zeichnen.

Wir stellen den Stift auf Qt::NoPen ein, weil wir keine Umrisse wollen, und verwenden einen einfarbigen Pinsel mit der für die Anzeige von Stunden geeigneten Farbe. Pinsel werden beim Ausfüllen von Polygonen und anderen geometrischen Formen verwendet.

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

Zuerst wird der Stundenzeiger gezeichnet, wobei eine Formel verwendet wird, die das Koordinatensystem um eine von der aktuellen Stunde und Minute abhängige Anzahl von Grad gegen den Uhrzeigersinn dreht. Das bedeutet, dass der Zeiger im Uhrzeigersinn um den gewünschten Betrag gedreht dargestellt wird. Wir speichern die Transformationsmatrix vor und nach der Drehung und stellen sie wieder her, da wir den Minutenzeiger ohne Berücksichtigung früherer Drehungen platzieren wollen.

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

Wir zeichnen Markierungen um den Rand der Uhr für jede Stunde in der gleichen Farbe wie der Stundenzeiger. Wir zeichnen jede Markierung und drehen dann das Koordinatensystem so, dass der Maler für die nächste Markierung bereit ist.

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

Der Minutenzeiger wird gedreht und auf ähnliche Weise wie der Stundenzeiger gemalt.

    painter.setBrush(minuteColor);

    painter.save();
    painter.rotate(6.0 * time.minute());
    painter.drawConvexPolygon(minuteHand, 4);
    painter.restore();

Für den Sekundenzeiger machen wir dasselbe und fügen als visuelles Highlight zwei Zacken hinzu.

    painter.setBrush(secondsColor);

    painter.save();
    painter.rotate(6.0 * time.second());
    painter.drawConvexPolygon(secondsHand, 4);
    painter.drawEllipse(-3, -3, 6, 6);
    painter.drawEllipse(-5, -68, 10, 10);
    painter.restore();

Zum Schluss zeichnen wir Markierungen um den Rand der Uhr, die die Minuten und Sekunden anzeigen. Diesmal zeichnen wir sie als Linien und setzen den Stift daher auf die entsprechende Farbe.

    painter.setPen(minuteColor);

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

Beispielprojekt @ code.qt.io

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