You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt6中重写QSpinBox的paintEvent实现顶层绘制

QSpinBox子类化绘制问题解决方案

QSpinBox是个复合控件,内部包含子控件(比如用来显示数值的QLineEdit)。父控件的paintEvent执行完后,子控件会接着自己绘制,所以你在父类paintEvent之后画的矩形,会被QLineEdit这类子控件盖住,这就是你遇到的问题根源。

问题1:让绘制内容位于QSpinBox所有元素之上

方法1:添加顶层覆盖控件

在CustomSpinBox内部加一个透明的子控件,让它完全覆盖整个SpinBox区域,然后在这个覆盖控件的paintEvent里画内容。这个覆盖控件会处于所有子控件的最顶层,所以画的内容能盖住所有元素。

代码示例:

import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QSpinBox, QWidget
from PyQt6.QtGui import QPaintEvent, QBrush, QColor, QPainter


class OverlayWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 让覆盖控件不拦截鼠标操作,保证SpinBox的箭头还能点
        self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
        self.setGeometry(parent.rect())

    def paintEvent(self, e: QPaintEvent) -> None:
        painter = QPainter(self)
        painter.setBrush(QBrush(QColor('orange'), Qt.BrushStyle.SolidPattern))
        painter.drawRect(0, 0, self.width(), self.height())


class CustomSpinBox(QSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.overlay = OverlayWidget(self)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        # 控件尺寸变化时,让覆盖控件跟着调整大小
        self.overlay.setGeometry(self.rect())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    window.setGeometry(500, 200, 400, 300)

    spin_box = CustomSpinBox(window)
    spin_box.move(100, 100)
    spin_box.setFixedSize(120, 40)

    window.show()
    app.exec()

方法2:用事件过滤器监听子控件绘制

给SpinBox的所有子控件装个事件过滤器,等子控件画完之后,再画你的覆盖内容。这种方法不用额外加控件,但逻辑稍复杂:

import sys
from PyQt6.QtCore import Qt, QEvent
from PyQt6.QtWidgets import QApplication, QSpinBox, QWidget
from PyQt6.QtGui import QPaintEvent, QBrush, QColor, QPainter


class CustomSpinBox(QSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 给所有子控件安装事件过滤器
        for child in self.children():
            child.installEventFilter(self)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Type.Paint:
            # 先让子控件完成绘制
            result = super().eventFilter(obj, event)
            # 再画覆盖内容
            painter = QPainter(self)
            painter.setBrush(QBrush(QColor('orange'), Qt.BrushStyle.SolidPattern))
            painter.drawRect(0, 0, self.width(), self.height())
            return result
        return super().eventFilter(obj, event)

    def paintEvent(self, e: QPaintEvent) -> None:
        super().paintEvent(e)
        # 这里也要画一次,覆盖SpinBox自身的箭头等区域
        painter = QPainter(self)
        painter.setBrush(QBrush(QColor('orange'), Qt.BrushStyle.SolidPattern))
        painter.drawRect(0, 0, self.width(), self.height())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    window.setGeometry(500, 200, 400, 300)

    spin_box = CustomSpinBox(window)
    spin_box.move(100, 100)
    spin_box.setFixedSize(120, 40)

    window.show()
    app.exec()

问题2:完全不绘制原内容

要彻底隐藏SpinBox的原始内容,需要两步:

  1. 不调用父类的paintEvent,避免画控件的框架和箭头;
  2. 隐藏内部的数值显示子控件(QLineEdit)。

代码示例:

import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QSpinBox, QWidget
from PyQt6.QtGui import QPaintEvent, QBrush, QColor, QPainter, QFont, QFontMetrics


class CustomSpinBox(QSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 隐藏默认的数值输入框
        self.lineEdit().hide()

    def paintEvent(self, e: QPaintEvent) -> None:
        # 不调用super().paintEvent(e),完全自定义绘制
        painter = QPainter(self)
        # 画橙色背景
        painter.setBrush(QBrush(QColor('orange'), Qt.BrushStyle.SolidPattern))
        painter.drawRect(0, 0, self.width(), self.height())
        
        # 可选:自己画当前数值(如果需要保留数值显示)
        painter.setPen(Qt.GlobalColor.black)
        font = QFont()
        font.setPointSize(12)
        painter.setFont(font)
        text = str(self.value())
        metrics = QFontMetrics(font)
        # 让文本居中显示
        x = (self.width() - metrics.horizontalAdvance(text)) // 2
        y = (self.height() + metrics.ascent()) // 2
        painter.drawText(x, y, text)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()
    window.setGeometry(500, 200, 400, 300)

    spin_box = CustomSpinBox(window)
    spin_box.move(100, 100)
    spin_box.setFixedSize(120, 40)
    spin_box.setValue(50)  # 设置初始值

    window.show()
    app.exec()

内容的提问来源于stack exchange,提问作者Jimmy Lander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:21:02