Transluzenter Hintergrund

Das Beispiel zeigt, wie man ein rundes Fenster mit einem durchscheinenden Hintergrund erstellt.

Widgets, deren Hintergrund als durchscheinend eingestellt ist, sind für alle nicht gemalten Pixel transparent, und der Hintergrund scheint durch gemalte Pixel mit einer Deckkraft von weniger als 100% durch. Pixel, die überhaupt nicht gezeichnet werden, erhalten auch keine Mauseingaben. Dies kann verwendet werden, um die Formen von Widgets der obersten Ebene anzupassen. Bei den meisten Fenstersystemen führt das Setzen bestimmter Fensterflags dazu, dass die Fensterdekoration (Titelleiste, Fensterrahmen, Schaltflächen) deaktiviert wird, so dass speziell geformte Fenster erstellt werden können. In diesem Beispiel verwenden wir diese Funktion, um ein rundes Fenster zu erstellen, das eine analoge Uhr enthält.

Da das Fenster dieses Beispiels weder ein Menü File noch eine Schaltfläche zum Schließen bietet, stellen wir ein Kontextmenü mit einem Eintrag Exit bereit, über das das Beispiel geschlossen werden kann. Klicken Sie mit der rechten Maustaste auf das Fenster, um dieses Menü zu öffnen.

ShapedClock Klassendefinition

Die Klasse ShapedClock basiert auf der Klasse AnalogClock, die im Analog Clock Beispiel definiert wurde. Die gesamte Klassendefinition ist im Folgenden dargestellt:

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

Die Implementierung paintEvent() zeichnet eine analoge Uhr auf einem halbtransparenten Hintergrund (das Ziffernblatt). Außerdem implementieren wir sizeHint(), damit wir die Größe des Widgets nicht explizit ändern müssen.

Da das Fenster, das das Uhren-Widget enthält, keine Titelleiste hat, stellen wir Implementierungen für mouseMoveEvent() und mousePressEvent() zur Verfügung, damit die Uhr auf dem Bildschirm verschoben werden kann. Mit der Variable dragPosition können wir verfolgen, wo der Benutzer zuletzt auf das Widget geklickt hat.

Implementierung der Klasse ShapedClock

Der ShapedClock Konstruktor richtet einen Timer ein und verbindet ihn mit dem Update()-Slot des Widgets. Außerdem fügen wir dem Widget eine Aktion hinzu, die automatisch über ein Kontextmenü verfügbar wird, wenn man mit der rechten Maustaste auf das Widget klickt.

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

Wir fordern ein transparentes Fenster an, indem wir das Attribut Qt::WA_TranslucentBackground widget setzen. Wir teilen dem Fenstermanager mit, dass das Widget nicht mit einem Fensterrahmen dekoriert werden soll, indem wir das Qt::FramelessWindowHint Flag für das Widget setzen. Infolgedessen müssen wir dem Benutzer eine Möglichkeit bieten, die Uhr auf dem Bildschirm zu bewegen.

Maustastenereignisse werden an den Handler mousePressEvent() übergeben:

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

Wenn die linke Maustaste über dem Widget gedrückt wird, zeichnen wir die Verschiebung in globalen (Bildschirm-)Koordinaten zwischen der oberen linken Position des Widget-Rahmens (auch wenn dieser ausgeblendet ist) und dem Punkt auf, an dem der Mausklick erfolgte. Diese Verschiebung wird verwendet, wenn der Benutzer die Maus bewegt, während er die linke Taste gedrückt hält. Da wir auf das Ereignis reagiert haben, akzeptieren wir es, indem wir seine Funktion accept() aufrufen.

Der mouseMoveEvent() Handler wird aufgerufen, wenn die Maus über das Widget bewegt wird.

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

Wenn die linke Taste gedrückt gehalten wird, während die Maus bewegt wird, wird die linke obere Ecke des Widgets an den Punkt verschoben, der sich aus der Subtraktion von dragPosition von der aktuellen Cursorposition in globalen Koordinaten ergibt. Wenn wir das Widget ziehen, akzeptieren wir auch das Ereignis.

Die Funktion paintEvent() ist im Wesentlichen die gleiche wie im Beispiel der analogen Uhr beschrieben. Der einzige Zusatz ist, dass wir QPainter::drawEllipse() verwenden, um ein rundes Zifferblatt zu zeichnen. Wir reduzieren die Deckkraft des Malers auf 90 % und verwenden die Standard-Hintergrundfarbe der Palette.

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

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

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

    painter.setBrush(minuteColor);

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

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

    painter.setPen(minuteColor);

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

Schließlich implementieren wir sizeHint() für das Widget, damit es eine vernünftige Standardgröße erhält, wenn es zum ersten Mal angezeigt wird:

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

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.