重载 paintEvent 方法
paintEvent的作用
paintEvent
方法是一个重要的事件处理函数,用于自定义控件的绘制。
它在控件需要重新绘制时被调用,例如在窗口被遮挡后重新显示、控件大小改变、或调用 update() 方法时。
- 自定义绘制: paintEvent 允许开发者在控件上绘制自定义内容,比如图形、文本、图像等。通过重写这个方法,可以实现复杂的自定义界面。
- 处理绘制事件: 当控件需要更新其显示内容时,Qt 会自动调用 paintEvent。这包括窗口的重绘、控件的状态变化等。
- 使用 QPainter: 在 paintEvent 中,通常会使用 QPainter 类来执行绘制操作。QPainter 提供了丰富的绘图功能,包括绘制线条、矩形、圆形、文本等。
paintEvent 的函数原型
代码语言:python代码运行次数:0复制def paintEvent(self, event: QPaintEvent):
# 自定义绘制代码
paintEvent示例代码
代码语言:python代码运行次数:0复制from __future__ import annotations
from PySide6.QtCore import QRect, Qt
from PySide6.QtGui import QColor, QFont, QPainter
from PySide6.QtWidgets import QApplication, QWidget
class CustomWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Paint Event Example")
self.resize(400, 400)
def paintEvent(self, event):
painter = QPainter(self)
# 设置绘制颜色
painter.setBrush(QColor(100, 150, 200))
painter.setPen(Qt.GlobalColor.black)
# 绘制一个矩形
rect = QRect(50, 100, 300, 200)
painter.drawRect(rect)
# 绘制文本
font = QFont('ComicShannsMono Nerd Font', 20)
painter.setFont(font)
painter.drawText(rect, Qt.AlignmentFlag.AlignCenter,
'Hello PySide6 paintEvent')
if __name__ == '__main__':
app = QApplication([])
# 创建并显示自定义窗口
custom_widget = CustomWidget()
custom_widget.show()
app.exec()
运行效果
自定义QWidget 实现 PowerBar
示例代码
代码语言:python代码运行次数:0复制from __future__ import annotations
from PySide6.QtCore import QRect, QSize, Qt
from PySide6.QtGui import QBrush, QPainter, QPaintEvent
from PySide6.QtWidgets import QApplication, QDial, QSizePolicy, QVBoxLayout, QWidget
class PowerBar(QWidget):
def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
# 显式的设置控件的最小大小,这个值是静态值
self.setMinimumSize(100, 100)
# setSizePolicy 接受两个参数,分别表示控件在水平方向和垂直方向上的大小策略
# QSizePolicy.Policy.MinimumExpanding 表示控件的最小扩展策略
# 具体来说,这意味着控件可以在其最小大小的基础上扩展,但不会小于其最小大小
# 换句话说,控件会尽量占据可用空间,但不会小于其定义的最小尺寸
# Fixed # 0x0 控件的大小是固定的,不会随布局的变化而改变
# Minimum # 0x1 控件的大小可以缩小到其最小大小,但不会小于这个最小值
# MinimumExpanding # 0x3 控件可以扩展到其最小大小以上,但不会小于其最小大小
# Maximum # 0x4 控件的大小可以扩展到其最大大小,但不会超过这个最大值
# Preferred # 0x5 控件的大小是其首选大小,布局会尽量使控件达到这个大小,但可以根据可用空间进行调整
# Expanding # 0x7 控件可以扩展以填充可用空间,但没有最小或最大限制
# Ignored # 0xd 控件的大小策略被忽略,布局管理器不会考虑这个控件的大小
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
def sizeHint(self) -> QSize:
# 当布局管理器计算控件的大小时,它会首先检查控件的最小大小
# 如果最小大小大于 sizeHint 返回的建议大小,布局管理器将使用最小大小,而不是建议大小
# return QSize(50, 50)
return QSize(100, 100)
# 重载 paintEvent 方法
def paintEvent(self, event: QPaintEvent):
# 通过合理地使用 update() 和 repaint() 方法,可以有效地控制控件的重绘,确保用户界面始终保持最新状态
painter = QPainter(self)
brush = QBrush()
brush.setStyle(Qt.BrushStyle.Dense3Pattern)
brush.setColor(Qt.GlobalColor.darkGreen)
rect = QRect(0, 0, painter.device().width(), painter.device().height())
painter.fillRect(rect, brush)
class CustomWidget(QWidget):
def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
self.v_layout = QVBoxLayout()
self.bar = PowerBar()
self.dial = QDial()
self.dial.setRange(0, 100)
self.dial.setValue(0)
self.v_layout.addWidget(self.bar)
self.v_layout.addWidget(self.dial)
self.setLayout(self.v_layout)
if __name__ == "__main__":
app = QApplication()
ins = CustomWidget()
ins.show()
app.exec()
运行效果
更完整的PowerBar
示例代码
代码语言:python代码运行次数:0复制from __future__ import annotations
from PySide6.QtCore import QRect, QSize, Qt
from PySide6.QtGui import QBrush, QFont, QPainter, QPaintEvent
from PySide6.QtWidgets import QApplication, QDial, QLabel, QSizePolicy, QVBoxLayout, QWidget
class PowerBar(QWidget):
def __init__(self, parent: QWidget, min_value: int = 0, max_value: int = 100):
super().__init__(parent)
self.min_dial_value = min_value
self.max_dial_value = max_value
self.current_value = self.min_dial_value
# 显式的设置控件的最小大小,这个值是静态值
self.setMinimumSize(100, 100)
# setSizePolicy 接受两个参数,分别表示控件在水平方向和垂直方向上的大小策略
# QSizePolicy.Policy.MinimumExpanding 表示控件的最小扩展策略
# 具体来说,这意味着控件可以在其最小大小的基础上扩展,但不会小于其最小大小
# 换句话说,控件会尽量占据可用空间,但不会小于其定义的最小尺寸
# Fixed # 0x0 控件的大小是固定的,不会随布局的变化而改变
# Minimum # 0x1 控件的大小可以缩小到其最小大小,但不会小于这个最小值
# MinimumExpanding # 0x3 控件可以扩展到其最小大小以上,但不会小于其最小大小
# Maximum # 0x4 控件的大小可以扩展到其最大大小,但不会超过这个最大值
# Preferred # 0x5 控件的大小是其首选大小,布局会尽量使控件达到这个大小,但可以根据可用空间进行调整
# Expanding # 0x7 控件可以扩展以填充可用空间,但没有最小或最大限制
# Ignored # 0xd 控件的大小策略被忽略,布局管理器不会考虑这个控件的大小
self.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
def sizeHint(self) -> QSize:
# 当布局管理器计算控件的大小时,它会首先检查控件的最小大小
# 如果最小大小大于 sizeHint 返回的建议大小,布局管理器将使用最小大小,而不是建议大小
# return QSize(50, 50)
return QSize(300, 500)
# 重载 paintEvent 方法
def paintEvent(self, event: QPaintEvent):
# 通过合理地使用 update() 和 repaint() 方法,可以有效地控制控件的重绘,确保用户界面始终保持最新状态
painter = QPainter(self)
brush = QBrush()
brush.setStyle(Qt.BrushStyle.SolidPattern)
brush.setColor(Qt.GlobalColor.gray)
rect = QRect(0, 0, painter.device().width(), painter.device().height())
painter.fillRect(rect, brush)
# 绘制对应比例面积的矩形
percent = self.current_value / self.max_dial_value
real_height = int(percent * painter.device().height())
y_pos = painter.device().height() - real_height
dial_percent_rect = QRect(0,
y_pos,
painter.device().width(),
real_height)
dial_percent_brush = QBrush()
dial_percent_brush.setStyle(Qt.BrushStyle.SolidPattern)
if percent <= 0:
dial_percent_brush.setColor(Qt.GlobalColor.darkGray)
elif percent <= 0.3:
dial_percent_brush.setColor(Qt.GlobalColor.darkGreen)
elif 0.3 < percent <= 0.6:
dial_percent_brush.setColor(Qt.GlobalColor.darkYellow)
elif 0.6 < percent <= 0.8:
dial_percent_brush.setColor(Qt.GlobalColor.darkBlue)
else:
dial_percent_brush.setColor(Qt.GlobalColor.darkRed)
painter.fillRect(dial_percent_rect, dial_percent_brush)
painter.end()
def re_draw(self, current_value: int):
self.current_value = current_value # 更新当前值
self.update()
class CustomWidget(QWidget):
def __init__(self, parent: QWidget | None = None):
super().__init__(parent)
self.v_layout = QVBoxLayout()
self.min_value = 0
self.max_value = 100
self.bar = PowerBar(self, self.min_value, self.max_value)
self.label = QLabel('初始状态')
self.label.setFont(QFont('ComicShannsMono Nerd Font Propo', 22))
self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter)
self.label.setStyleSheet("background-color: gray;")
self.dial = QDial()
self.dial.setRange(self.min_value, self.max_value)
self.dial.setValue(self.min_value)
self.dial.valueChanged.connect(self.bar.re_draw)
self.dial.valueChanged.connect(self.update_label)
self.v_layout.addWidget(self.bar)
self.v_layout.addWidget(self.label)
self.v_layout.addWidget(self.dial)
self.setLayout(self.v_layout)
def update_label(self):
if self.dial.value() <= 0:
self.label.setStyleSheet("background-color: gray;")
elif self.dial.value() <= 30:
self.label.setStyleSheet("background-color: darkgreen;")
elif 30 < self.dial.value() <= 60:
self.label.setStyleSheet("background-color: #CCCC00;")
elif 60 < self.dial.value() <= 80:
self.label.setStyleSheet("background-color: darkblue;")
else:
self.label.setStyleSheet("background-color: darkred;")
self.label.setText(f'Power Percent: {self.dial.value()}%')
if __name__ == "__main__":
app = QApplication()
ins = CustomWidget()
ins.show()
app.exec()
上述代码实现的效果为,当旋转 dial 时,灰色矩形中会由下向上升起带有颜色的矩形,矩形高度随着 dial 值的变化而变化:
代码语言:python代码运行次数:0复制 # 绘制对应比例面积的矩形
percent = self.current_value / self.max_dial_value
real_height = int(percent * painter.device().height())
y_pos = painter.device().height() - real_height
dial_percent_rect = QRect(0,
y_pos,
painter.device().width(),
real_height)
这段代码对应以下的逻辑示意图: