En esta página

Fondo translúcido

El ejemplo muestra cómo hacer una ventana redonda con un fondo translúcido.

Reloj translúcido superpuesto al texto

Los widgets que configuren su fondo como translúcido serán transparentes para todos los píxeles no pintados, y el fondo brillará a través de los píxeles pintados con una opacidad inferior al 100%. Los píxeles que no estén pintados tampoco recibirán ninguna entrada del ratón. Esto se puede utilizar para personalizar las formas de los widgets de nivel superior. En la mayoría de los sistemas de ventanas, la configuración de determinados indicadores de ventana hará que se desactive la decoración de la ventana (barra de título, marco de ventana, botones), lo que permitirá crear ventanas con formas especiales. En este ejemplo, utilizamos esta función para crear una ventana circular que contiene un reloj analógico.

Dado que la ventana de este ejemplo no proporciona un menú File ni un botón de cierre, proporcionamos un menú contextual con una entrada Exit para poder cerrar el ejemplo. Pulse el botón derecho del ratón sobre la ventana para abrir este menú.

Definición de la clase ShapedClock

La clase ShapedClock está basada en la clase AnalogClock definida en el ejemplo Reloj Analógico. A continuación se presenta la definición completa de la clase:

class ShapedClock : public QWidget
{
    Q_OBJECT

public:
    ShapedClock(QWidget *parent = nullptr);
    QSize sizeHint() const override;

protected:
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void paintEvent(QPaintEvent *event) override;

private:
    QPoint dragPosition;
};

La implementación paintEvent() dibuja un reloj analógico sobre un fondo semitransparente (la esfera del reloj). Además, implementamos sizeHint() para no tener que redimensionar el widget explícitamente.

Dado que la ventana que contiene el widget del reloj no tendrá barra de título, proporcionamos implementaciones para mouseMoveEvent() y mousePressEvent() para permitir que el reloj se arrastre por la pantalla. La variable dragPosition nos permite mantener un registro de dónde hizo clic el usuario por última vez en el widget.

Implementación de la clase ShapedClock

El constructor ShapedClock configura un temporizador y lo conecta a la ranura update() del widget. Además, añadimos una acción al widget, que estará disponible automáticamente a través de un menú contextual al hacer clic con el botón derecho sobre el widget.

ShapedClock::ShapedClock(QWidget *parent)
    : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint)
{
    setAttribute(Qt::WA_TranslucentBackground);
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, QOverload<>::of(&ShapedClock::update));
    timer->start(1000);

    QAction *quitAction = new QAction(tr("E&xit"), this);
    quitAction->setShortcut(tr("Ctrl+Q"));
    connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit);
    addAction(quitAction);

    setContextMenuPolicy(Qt::ActionsContextMenu);
    setToolTip(tr("Drag the clock with the left mouse button.\n"
                  "Use the right mouse button to open a context menu."));
    setWindowTitle(tr("Shaped Analog Clock"));
}

Solicitamos una ventana transparente estableciendo el atributo de widget Qt::WA_TranslucentBackground. Informamos al gestor de ventanas de que el widget no debe estar decorado con un marco de ventana estableciendo la bandera Qt::FramelessWindowHint en el widget. Como resultado, necesitamos proporcionar una forma para que el usuario mueva el reloj por la pantalla.

Los eventos del botón del ratón se envían al manejador mousePressEvent():

void ShapedClock::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
        event->accept();
    }
}

Si se pulsa el botón izquierdo del ratón sobre el widget, registramos el desplazamiento en coordenadas globales (pantalla) entre la posición superior izquierda del marco del widget (incluso cuando está oculto) y el punto donde se produjo el clic del ratón. Este desplazamiento se utilizará si el usuario mueve el ratón mientras mantiene pulsado el botón izquierdo. Como hemos actuado sobre el evento, lo aceptamos llamando a su función accept().

La posición de arrastre con respecto a la esfera del reloj

El manejador mouseMoveEvent() es llamado si el ratón se desplaza sobre el widget.

void ShapedClock::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        move(event->globalPosition().toPoint() - dragPosition);
        event->accept();
    }
}

Si se mantiene pulsado el botón izquierdo mientras se mueve el ratón, la esquina superior izquierda del widget se desplaza al punto dado por la resta de dragPosition a la posición actual del cursor en coordenadas globales. Si arrastramos el widget, también aceptamos el evento.

La función paintEvent() es principalmente la misma que la descrita en el ejemplo del Reloj Analógico. El único añadido es que utilizamos QPainter::drawEllipse() para dibujar una esfera de reloj redonda. Reducimos la opacidad del pintor al 90%, y utilizamos el color de fondo por defecto de la paleta.

void ShapedClock::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());
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.translate(width() / 2, height() / 2);
    painter.scale(side / 200.0, side / 200.0);

    painter.setPen(Qt::NoPen);
    painter.setBrush(palette().window());
    painter.setOpacity(0.9);
    painter.drawEllipse(QPoint(0, 0), 98, 98);
    painter.setOpacity(1.0);

    QTime time = QTime::currentTime();
    painter.setPen(Qt::NoPen);
    painter.setBrush(hourColor);

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

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

    painter.setBrush(minuteColor);

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

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

    painter.setPen(minuteColor);

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

Por último, implementamos sizeHint() para que el widget tenga un tamaño razonable por defecto cuando se muestre por primera vez:

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

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.