本页内容

半透明背景

本示例演示了如何制作一个具有半透明背景的圆形窗口。

叠加在文字上的半透明时钟

将背景设置为半透明的控件,其所有未填充的像素将呈现透明状态,而背景光会透过不透明度低于100%的像素透出。完全未填充的像素也不会接收任何鼠标输入。此特性可用于自定义顶级控件的形状。 在大多数窗口系统中,设置特定的窗口标志会禁用窗口装饰(标题栏、窗口边框、按钮),从而允许创建特殊形状的窗口。在本示例中,我们利用此功能创建了一个包含指针式时钟的圆形窗口。

由于本示例中的窗口未提供“File ”菜单或关闭按钮,因此我们提供了一个包含“Exit ”选项的上下文菜单,以便关闭该示例。在窗口上单击鼠标右键即可打开此菜单。

ShapedClock 类定义

ShapedClock 类基于“模拟时钟”示例中定义的AnalogClock 类。该类的完整定义如下:

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

paintEvent() 的实现会在半透明背景(即钟面)上绘制一个模拟时钟。此外,我们还实现了sizeHint() 方法,这样就无需显式调整控件的大小。

由于包含时钟控件的窗口没有标题栏,我们为mouseMoveEvent()和mousePressEvent()提供了实现,以便允许用户在屏幕上拖动时钟。dragPosition 变量让我们能够跟踪用户上次在控件上点击的位置。

ShapedClock 类的实现

ShapedClock 构造函数会设置一个定时器,并将其连接到小部件的 update() 槽。此外,我们还向小部件添加了一个操作,当右键单击小部件时,该操作将自动通过上下文菜单提供。

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

通过设置Qt::WA_TranslucentBackground 控件属性,我们请求创建一个透明窗口。通过在控件上设置Qt::FramelessWindowHint 标志,我们告知窗口管理器该控件不应显示窗口边框。因此,我们需要提供一种方式,让用户能够在屏幕上移动时钟。

鼠标按钮事件会被传递到mousePressEvent() 处理程序:

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

如果在控件上方按下鼠标左键,我们会记录控件框架左上角(即使框架被隐藏)与鼠标点击点之间以全局(屏幕)坐标系为基准的位移量。当用户按住鼠标左键移动鼠标时,将使用该位移量。 由于我们对该事件进行了处理,因此通过调用其accept()函数来接受该事件。

相对于表盘的拖拽位置

当鼠标在小部件上移动时,会调用mouseMoveEvent() 处理程序。

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

如果在移动鼠标时按住左键,则小部件的左上角会移动到由全局坐标系中当前光标位置减去dragPosition 所给出的点。如果拖动小部件,我们也会捕获该事件。

paintEvent() 函数与“模拟时钟”示例中描述的函数基本相同。唯一新增之处是,我们使用QPainter::drawEllipse()来绘制一个圆形的表盘。我们将绘图器的透明度降低至90%,并使用调色板的默认背景色。

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

最后,我们为该控件实现sizeHint() 方法,以便其在首次显示时具有合理的默认大小:

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

示例项目 @ 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.