1. 必须重写 paintEvent()
根据官方文档的说明,如果希望自定义的 QWidget 派生类能够响应 StyleSheet 中定义的样式,就必须用如下代码重写 paintEvent() 方法。(其实应该叫做实现 paintEvent(),因为 QWidget::paintEvent() 本来是个空函数,啥都没做)
换成 Python + PySide6 语法就是:
class CustomWidget(QWidget): def __init__(self, parent: QWidget = None): super().__init__(parent) ... def paintEvent(self, event): opt = QStyleOption() opt.initFrom(self) painter = QPainter(self) self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget, opt, painter, self) pass
2. paintEvent() 的作用
首先需要知道,所有 QWidget 派生类需要绘制的时候,都是调用 paintEvent() 方法进行绘制!(但 QWidget::paintEvent() 是空函数,具体实现都由派生类自己来做,比如 QLabel::paintEvent())
① 如果继承自某个内建 widget(比如 QLabel, QPushButton 这些),那么重写该方法将会覆盖父类的绘制行为。
② 如果该 widget 还有 child widgets 的话,在执行完自己的 paintEvent() 之后,还会接着自动调用所有 child widgets 的 paintEvent()!
(其实并不是所有,只需要调用与该 event.rect() 有交集的 child widgets 的 paintEvent() 方法。)
③ 对于由内建 widget 组合而成的自定义 widget。比如说我新建了一个 CustomWidget,它其实是由 QLabel + QPushButton 组合起来的。
那么根据 ② 中所述,是可以不需要重写 paintEvent() 方法的。child widgets 会自行绘制自己。
④ 但是!如果不重写 paintEvent() 的话,自定义的 widget 就没办法响应 StyleSheet 中与其相匹配的样式。即在 qss 文件中,或者直接通过 setStyleSheet() 设置的
CustomWidget { background-color: #acdbb7 }
就无法生效。3. 响应 StyleSheet 属性选择器的样式
CustomWidget[clicked="true"] { background-color: #dc5f5f }
这是 StyleSheet 属性选择器的例子。
要想让自定义控件 CustomWidget 的实例响应这个 [clicked=”true”] 属性选择器。除了 CustomWidget 必须重写 paintEvent() 外,还必须在代码中给 CutomWidget 实例设置 clicked “属性”,还必须调用 QStyle.polish() 方法来重新加载 StyleSheet。
widget.setProperty('clicked', True) widget.style().polish(widget)
4. 示例代码
QSS = """ CustomWidget { background-color: #acdbb7 } CustomWidget[clicked="true"] { background-color: #dc5f5f } """ class CustomWidget(QWidget): def __init__(self, title: str, parent: QWidget = None): super().__init__(parent) # 1. 图标 self.label_icon = QLabel(self) self.label_icon.setPixmap(self.style().standardIcon(QStyle.StandardPixmap.SP_TitleBarMenuButton).pixmap(32, 32)) # 2. title self.label_title = QLabel(self) self.label_title.setText(title) self.horizontalLayout = QHBoxLayout(self) self.horizontalLayout.addWidget(self.label_icon) self.horizontalLayout.addWidget(self.label_title) pass def paintEvent(self, event): opt = QStyleOption() opt.initFrom(self) painter = QPainter(self) self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget, opt, painter, self) pass def mousePressEvent(self, ev): self.setProperty('clicked', True) self.style().polish(self) pass if __name__ == '__main__': app = QApplication(sys.argv) app.setStyleSheet(QSS) main_window = QMainWindow() main_window.setCentralWidget(CustomWidget('测试')) main_window.show() sys.exit(app.exec()) pass