警告

本节包含自动从C++翻译到Python的代码片段,可能包含错误。

透明背景#

示例展示了如何创建一个具有透明背景的圆形窗口。

../_images/shapedclock-example.png

将背景设置为透明的窗口小部件将对所有未绘制的像素进行透明处理,并且背景将以小于100%的不透明度通过 painted 的像素闪耀。完全没有涂鸦的像素也不会接收到任何鼠标输入。这可以用来自定义顶级小部件的形状。在大多数窗口系统中,设置某些窗口标志将导致窗口装饰(标题栏,窗口框架,按钮)被禁用,从而可以创建特别形状的窗口。在这个例子中,我们使用这个功能来创建包含一个模拟时钟的圆形窗口。

由于此示例的窗口没有文件菜单或关闭按钮,我们提供了一个带有退出项的上下文菜单,以便可以关闭示例。在窗口上单击鼠标右键以打开此菜单。

ShapedClock 类定义#

ShapedClock 类基于在 模拟时钟 示例中定义的 AnalogClock 类。整个类定义如下

class ShapedClock(QWidget):

    Q_OBJECT
# public
    ShapedClock(QWidget parent = None)
    QSize sizeHint() override
# protected
    def mouseMoveEvent(event):
    def mousePressEvent(event):
    def paintEvent(event):
# private
    dragPosition = QPoint()

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

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

ShapedClock 类实现#

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

def __init__(self, parent):
    super().__init__(parent, Qt.FramelessWindowHint | Qt.WindowSystemMenuHint)

    setAttribute(Qt.WA_TranslucentBackground)
    timer = QTimer(self)
    timer.timeout.connect(this, QOverload<>::of(&ShapedClock::update))
    timer.start(1000)
    quitAction = QAction(tr("Exit"), self)
    quitAction.setShortcut(tr("Ctrl+Q"))
    quitAction.triggered.connect(qApp.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()处理器。

def mousePressEvent(self, event):

    if event.button() == Qt.LeftButton:
        dragPosition = event.globalPosition().toPoint() - frameGeometry().topLeft()
        event.accept()

如果鼠标左键在小部件上按下,我们记录从小部件框架(即使隐藏)的左上角到鼠标点击发生点的全局(屏幕)坐标之间的位移。如果用户在按住左键的同时移动鼠标,将使用这个位移。由于我们处理了事件,因此通过调用其accept()函数来接受它。

../_images/shapedclock-dragging.png

如果鼠标在小部件上移动,将调用mouseMoveEvent()处理器。

def mouseMoveEvent(self, event):

    if event.buttons()  Qt.LeftButton:
        move(event.globalPosition().toPoint() - dragPosition)
        event.accept()

当鼠标移动时,如果按住左键,则将小部件的左上角移动到通过从全局坐标中的当前光标位置减去dragPosition得到的点。如果我们拖动小部件,我们也接受这个事件。

paintEvent()函数基本上与Analog Clock示例中描述的相同。唯一的不同之处在于我们使用QPainter::drawEllipse()来绘制圆形钟面。我们将画家的不透明度降低到90%,并使用调色板默认的背景颜色。

def paintEvent(self, arg__0):

    QPoint hourHand[4] = {
        QPoint(5, 14),
        QPoint(-5, 14),
        QPoint(-4, -71),
        QPoint(4, -71)

    QPoint minuteHand[4] = {
        QPoint(4, 14),
        QPoint(-4, 14),
        QPoint(-3, -89),
        QPoint(3, -89)

    QPoint secondsHand[4] = {
       QPoint(1, 14),
       QPoint(-1, 14),
       QPoint(-1, -89),
       QPoint(1, -89)

    hourColor = QColor(palette().color(QPalette.Text))
    minuteColor = QColor(palette().color(QPalette.Text))
    secondsColor = QColor(palette().color(QPalette.Accent))
    side = qMin(width(), height())
    painter = QPainter(self)
    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)
    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 i in range(0, 12):
        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 j in range(0, 60):
        painter.drawLine(92, 0, 96, 0)
        painter.rotate(6.0)

最后,我们实现小部件的sizeHint(),这样当它首次显示时就会有一个合理的默认大小。

def sizeHint(self):

    return QSize(200, 200)

示例项目 @ code.qt.io