En esta página

Reloj analógico

El ejemplo Reloj analógico muestra cómo dibujar el contenido de un widget personalizado.

Reloj analógico con agujas de horas, minutos y segundos

Captura de pantalla del ejemplo Reloj analógico

Este ejemplo también demuestra cómo pueden utilizarse las funciones de transformación y escalado de QPainter para facilitar el dibujo de widgets personalizados.

Definición de la clase AnalogClock

La clase AnalogClock proporciona un widget de reloj con manecillas de hora, minutos y segundos que se actualiza automáticamente cada segundo. Subclasificamos QWidget y reimplementamos la función estándar paintEvent() para dibujar la esfera del reloj:

class AnalogClock : public QWidget
{
    Q_OBJECT

public:
    AnalogClock(QWidget *parent = nullptr);

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

Implementación de la clase AnalogClock

Cuando se construye el widget, configuramos un temporizador de un segundo para controlar la hora actual, y lo conectamos a la ranura estándar update() para que la esfera del reloj se actualice cuando el temporizador emita la señal timeout(). Por último, redimensionamos el widget para que se muestre a un tamaño razonable.

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 función paintEvent() es llamada cada vez que el contenido del widget necesita ser actualizado. Esto ocurre cuando el widget se muestra por primera vez, y cuando se cubre y luego se expone, pero también se ejecuta cuando se llama a la ranura update() del widget. Como hemos conectado la señal timeout() del temporizador a esta ranura, será llamada al menos una vez por segundo.

Antes de configurar el pintor y dibujar el reloj, definimos tres listas de QPoints y tres QColors que se utilizarán para las agujas de la hora, los minutos y los segundos. Utilizamos la función palette() para obtener colores apropiados que se ajusten al resto de la ventana, tanto en modo claro como oscuro. Las manecillas de las horas y los minutos se dibujan en el color de primer plano, la manecilla de los segundos se dibuja en el color de acento.

También determinamos la longitud del lado más corto del widget para poder encajar la esfera del reloj dentro del widget. También es útil determinar la hora actual antes de empezar a dibujar.

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

El contenido de los widgets personalizados se dibuja con un QPainter. Los pintores se pueden utilizar para dibujar en cualquier QPaintDevice, pero normalmente se utilizan con widgets, por lo que pasamos la instancia del widget al constructor del pintor.

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

Llamamos a QPainter::setRenderHint() con QPainter::Antialiasing para activar el antialiasing. Esto hace que el dibujo de líneas diagonales sea mucho más suave.

    painter.setRenderHint(QPainter::Antialiasing);

La traslación mueve el origen al centro del widget, y la operación de escala asegura que las siguientes operaciones de dibujo se ajusten a la escala del widget. Usamos un factor de escala que nos permite usar coordenadas x e y entre -100 y 100, y que asegura que éstas se encuentran dentro de la longitud del lado más corto del widget.

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

Para simplificar nuestro código, dibujaremos una esfera de reloj de tamaño fijo que se colocará y escalará de forma que quede en el centro del widget.

El pintor se encarga de todas las transformaciones realizadas durante el evento paint, y se asegura de que todo se dibuja correctamente. Dejar que el pintor se encargue de las transformaciones es a menudo más fácil que realizar cálculos manuales sólo para dibujar el contenido de un widget personalizado.

Muestra el sistema de coordenadas del widget y el origen en la esfera del reloj

Configuramos la pluma para que sea Qt::NoPen porque no queremos ningún contorno, y utilizamos un pincel sólido con el color apropiado para mostrar las horas. Los pinceles se utilizan para rellenar polígonos y otras formas geométricas.

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

Primero dibujamos la aguja de las horas, utilizando una fórmula que gira el sistema de coordenadas en sentido contrario a las agujas del reloj un número de grados determinado por la hora y los minutos actuales. Esto significa que la aguja se mostrará girada en el sentido de las agujas del reloj la cantidad necesaria. Guardamos y restauramos la matriz de transformación antes y después de la rotación instanciando un QPainterStateGuard porque queremos colocar el minutero sin tener en cuenta ninguna rotación anterior.

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

Dibujamos marcadores alrededor del borde del reloj para cada hora en el mismo color que la aguja horaria. Dibujamos cada marcador y luego rotamos el sistema de coordenadas para que el pintor esté listo para el siguiente.

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

La aguja de los minutos se gira y se pinta de forma similar a la aguja de las horas.

    painter.setBrush(minuteColor);

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

Para el segundero hacemos lo mismo y añadimos dos círculos como realce visual.

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

Por último, dibujamos marcadores alrededor del borde del reloj, indicando los minutos y los segundos. Esta vez los dibujamos como líneas y por lo tanto ajustamos el lápiz al color respectivo.

    painter.setPen(minuteColor);

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

Proyecto de ejemplo @ 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.