PyQt 实现炫酷仪表盘源码分享
一、源码分享 1、源码分享 2、效果展示 3、库安装 二、实现原理 1、QPainter 概述 1.1 主要特性 1.2 基本使用模式 2、QPainter 构造函数与设备管理 2.1 构造函数 2.2 设备管理方法 3、绘图状态管理 3.1 画笔(QPen)设置 3.2 画刷(QBrush)设置 3.3 字体(QFont)设置 3.4 绘图状态保存与恢复 4、基本图形绘制方法 4.1 点与线 4.2 矩形绘制 4.3 椭圆与圆形 4.4 多边形绘制 5、文本绘制方法 5.1 基本文本绘制 5.2 文本测量与布局 5.3 文本路径绘制 6、图像绘制方法 6.1 基本图像绘制 6.2 图像变换与效果 7、路径绘制(QPainterPath) 7.1 创建与绘制路径 7.2 路径操作 8、坐标变换与视图 8.1 基本变换 8.2 矩阵变换 8.3 视图与窗口 9、高级绘制特性 9.1 渲染提示 9.2 合成模式 9.3 裁剪区域 9.4 不透明度与渐变 9.5 性能优化技巧 9.6 调试与问题排查
一、源码分享
1、源码分享
import sysimport mathfrom PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget,QSlider, QVBoxLayout)from PyQt6.QtGui import (QPainter, QRadialGradient, QColor, QFont, QFontMetrics, QFontDatabase, QPen)from PyQt6.QtCore import Qt, QPointF, QRectFclass SpeedMeterWidget(QWidget):def __init__(self, parent=None):super().__init__(parent)# 仪表盘参数 和QML完全一致self.maxValue = 260self.minValue = 0self.currentValue = 130self.redValue = 200self.bg_color = QColor("#00020414")# 数字字体,自行替换Digitall.ttffont_id = QFontDatabase.addApplicationFont("image/Digitall.ttf")font_family = QFontDatabase.applicationFontFamilies(font_id)[0]self.digit_font = QFont(font_family)#self.digit_font = QFont("Arial", 40)def set_current_value(self, val):self.currentValue = valself.update() # 触发重绘def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)w = self.width()h = self.height()center_x = w / 2center_y = h / 2radius = min(w, h) / 2 * 0.9# 1. 背景填充painter.fillRect(0, 0, w, h, self.bg_color)# 2. 内层红色径向渐变圆环 修复版radial_grad = QRadialGradient(center_x, center_y, radius * 0.6)col_center = QColor("#E43223")col_edge = QColor("#00E43223")radial_grad.setColorAt(0, col_center)radial_grad.setColorAt(1, col_edge)painter.setBrush(radial_grad)painter.setPen(Qt.PenStyle.NoPen)painter.drawEllipse(QPointF(center_x, center_y), radius * 0.6, radius * 0.6)# 3. 中间黑色内圆painter.setBrush(Qt.GlobalColor.black)painter.drawEllipse(QPointF(center_x, center_y), radius * 0.5, radius * 0.5)# ====================== 绘制刻度线 ======================painter.save()start_angle = 45r = radiustotal_steps = 130rotate_step = (360 - start_angle * 2) / total_stepspainter.translate(center_x, center_y)painter.rotate(start_angle)for i in range(total_steps + 1):val = i * 2if val >= self.redValue:big_color = QColor("#FF0000")small_color = QColor("#FF0000")else:big_color = QColor("#B6C6D6")small_color = QColor("#525B62")if i % 10 == 0:painter.setPen(big_color)pen = painter.pen()pen.setWidth(9)pen.setCapStyle(Qt.PenCapStyle.RoundCap)painter.setPen(pen)painter.drawLine(QPointF(0, r), QPointF(0, r / 1.1))elif i % 5 == 0:painter.setPen(small_color)pen = painter.pen()pen.setWidth(8)pen.setCapStyle(Qt.PenCapStyle.RoundCap)painter.setPen(pen)painter.drawLine(QPointF(0, r), QPointF(0, r / 1.08))else:painter.setPen(small_color)pen = painter.pen()pen.setWidth(6)pen.setCapStyle(Qt.PenCapStyle.RoundCap)painter.setPen(pen)painter.drawLine(QPointF(0, r), QPointF(0, r / 1.05))painter.rotate(rotate_step)painter.restore()# ====================== 绘制数字 ======================painter.save()num_r = radius * 0.75num_gap_angle = (360 - start_angle * 2) / 13val_step = (self.maxValue - self.minValue) / 13current_draw_val = self.minValuestart_deg = start_anglenum_font = self.digit_fontnum_font.setPointSizeF(w * 0.04)painter.setFont(num_font)fm = QFontMetrics(num_font)for i in range(14):if current_draw_val >= self.redValue:painter.setPen(QColor("#FF0000"))else:painter.setPen(Qt.GlobalColor.white)# QML原始角度公式不变angle_deg = -90 + start_deg + num_gap_angle * irad = math.radians(angle_deg)x = -num_r * math.cos(rad)y = -num_r * math.sin(rad)painter.save()painter.translate(center_x + x, center_y + y)# painter.rotate(angle_deg)text = f"{int(current_draw_val)}"text_w = fm.horizontalAdvance(text)text_h = w * 0.06offset_y = text_h / 4painter.drawText(QPointF(-text_w / 2, offset_y), text)painter.restore()current_draw_val += val_steppainter.restore()# ====================== 绘制指针======================painter.save()deg_rotate = (270.0 / (self.maxValue - self.minValue)) * (self.currentValue - self.minValue) + 135painter.translate(center_x, center_y)painter.rotate(deg_rotate)pen = QPen()pen.setColor(Qt.GlobalColor.white)pen.setWidth(8)pen.setCapStyle(Qt.PenCapStyle.RoundCap)painter.setPen(pen)painter.drawLine(QPointF(radius * 0.5, 0), QPointF(radius * 0.9, 0))painter.restore()# ====================== 中心文字 ======================painter.save()painter.setPen(Qt.GlobalColor.white)# 字体尺寸统一num_font_size = w * 0.12big_font = self.digit_fontbig_font.setPointSizeF(num_font_size)painter.setFont(big_font)# 拆分百位、十位、个位val = self.currentValuehundred = val // 100ten = (val // 10) % 10unit = val % 10# 计算单个数字宽度,用于水平居中排布fm = QFontMetrics(big_font)single_w = fm.horizontalAdvance("0")gap = single_w * 0.001 # 数字间微小间距# 总宽度 = 百位宽 + 间距 + 十位宽 + 间距 + 个位宽total_text_width = single_w * 3 + gap * 2# 起始X坐标,保证整体居中start_x = center_x - total_text_width / 2# -------- 绘制百位 --------if hundred == 0:painter.setPen(QColor("#666666"))else:painter.setPen(Qt.GlobalColor.white)painter.drawText(QPointF(start_x, center_y), f"{hundred}")# -------- 绘制十位 --------if ten == 0 and hundred == 0:painter.setPen(QColor("#666666"))else:painter.setPen(Qt.GlobalColor.white)ten_x = start_x + single_w + gappainter.drawText(QPointF(ten_x, center_y), f"{ten}")# -------- 绘制个位 --------if unit == 0 and hundred == 0 and ten == 0:painter.setPen(QColor("#666666"))else:painter.setPen(Qt.GlobalColor.white)unit_x = ten_x + single_w + gappainter.drawText(QPointF(unit_x, center_y), f"{unit}")# KM/h 单独绘制,在数字正下方,不重叠unit_font = self.digit_fontunit_font.setPointSizeF(w * 0.05)painter.setFont(unit_font)rect_unit = QRectF(0, center_y + 50, w, h)painter.drawText(rect_unit, Qt.AlignmentFlag.AlignHCenter, "KM/h")painter.restore()class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Speedometer PyQt6")self.resize(800, 800)central_widget = QWidget()central_widget.setStyleSheet("background-color: #020414;")self.setCentralWidget(central_widget)layout = QVBoxLayout(central_widget)layout.setContentsMargins(0, 0, 0, 20)self.meter = SpeedMeterWidget()layout.addWidget(self.meter)self.slider = QSlider(Qt.Orientation.Horizontal)self.slider.setRange(0, 260)self.slider.setValue(130)layout.addWidget(self.slider)self.slider.valueChanged.connect(self.meter.set_current_value)if __name__ == "__main__":app = QApplication(sys.argv)win = MainWindow()win.show()sys.exit(app.exec())
2、效果展示




3、库安装

二、实现原理
主要通过QPainter来绘制。
1、QPainter 概述
QPainter 是 PyQt6 中用于执行所有绘图操作的核心类。它提供了丰富的 API 来绘制各种图形、文本、图像和路径,是自定义控件、图表、游戏和可视化应用的基础。
1.1 主要特性
- 设备无关
:可在 QWidget、QPixmap、QImage、QPrinter 等多种设备上绘制 - 坐标系系统
:支持世界坐标、窗口坐标和视口坐标的转换 - 绘图状态
:保存和恢复绘图状态(画笔、画刷、字体等) - 抗锯齿
:支持高质量的抗锯齿渲染 - 变换操作
:支持平移、旋转、缩放、剪切等几何变换
1.2 基本使用模式
from PyQt6.QtGui import QPainter, QPen, QBrush, QColorfrom PyQt6.QtWidgets import QWidgetclass MyWidget(QWidget):def paintEvent(self, event):painter = QPainter(self) # 创建 QPainter 对象painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 开启抗锯齿# 绘制操作...painter.drawRect(10, 10, 100, 50)# QPainter 会在离开作用域时自动结束绘制
2、QPainter 构造函数与设备管理
2.1 构造函数
# 方法1:先创建后绑定设备painter = QPainter()painter.begin(widget) # 开始绘制# ... 绘制操作painter.end() # 结束绘制# 方法2:创建时直接绑定设备(推荐)painter = QPainter(widget)# 方法3:在 QPixmap 上绘制pixmap = QPixmap(400, 300)painter = QPainter(pixmap)
2.2 设备管理方法
begin(QPaintDevice) | ||
end() | ||
isActive() | ||
device() | ||
setDevice(QPaintDevice) |
3、绘图状态管理
3.1 画笔(QPen)设置
from PyQt6.QtGui import QPenfrom PyQt6.QtCore import Qt# 创建画笔pen = QPen(QColor("#FF5733"), 2) # 颜色和宽度pen.setStyle(Qt.PenStyle.DashLine) # 虚线样式pen.setCapStyle(Qt.PenCapStyle.RoundCap) # 线帽样式pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin) # 连接样式painter.setPen(pen) # 应用画笔painter.setPen(Qt.GlobalColor.red) # 快捷设置(红色,宽度1)painter.setPen(Qt.PenStyle.NoPen) # 无边框
3.2 画刷(QBrush)设置
from PyQt6.QtGui import QBrush, QLinearGradient, QRadialGradient# 纯色画刷brush = QBrush(QColor("#3498db"))painter.setBrush(brush)# 渐变画刷gradient = QLinearGradient(0, 0, 100, 100)gradient.setColorAt(0, QColor("#1abc9c"))gradient.setColorAt(1, QColor("#2ecc71"))painter.setBrush(QBrush(gradient))# 纹理画刷texture_brush = QBrush(QPixmap("texture.png"))painter.setBrush(texture_brush)# 样式画刷painter.setBrush(Qt.BrushStyle.Dense4Pattern) # 密集图案
3.3 字体(QFont)设置
from PyQt6.QtGui import QFontfont = QFont("Arial", 12)font.setBold(True)font.setItalic(True)font.setUnderline(True)font.setPixelSize(16) # 像素大小font.setPointSize(10) # 点大小painter.setFont(font)
3.4 绘图状态保存与恢复
# 保存当前状态painter.save()# 修改状态painter.setPen(QColor("blue"))painter.setBrush(QColor("yellow"))painter.setFont(QFont("Times", 14))# 绘制...painter.drawRect(50, 50, 100, 100)# 恢复之前的状态painter.restore()# 继续使用原始状态绘制painter.drawRect(200, 50, 100, 100)
4、基本图形绘制方法
4.1 点与线
# 绘制单个点painter.drawPoint(10, 10)painter.drawPoint(QPoint(20, 20))# 绘制多个点points = [QPoint(10, 10), QPoint(20, 20), QPoint(30, 30)]painter.drawPoints(points)# 绘制直线painter.drawLine(10, 10, 100, 100)painter.drawLine(QPoint(10, 10), QPoint(100, 100))# 绘制多条线lines = [QLine(10, 10, 50, 50),QLine(60, 10, 100, 50),QLine(110, 10, 150, 50)]painter.drawLines(lines)# 绘制折线polyline = QPolygonF([QPointF(10, 10), QPointF(50, 50), QPointF(100, 10)])painter.drawPolyline(polyline)
4.2 矩形绘制
from PyQt6.QtCore import QRect, QRectF# 绘制矩形(多种方式)painter.drawRect(10, 10, 100, 50) # x, y, width, heightpainter.drawRect(QRect(10, 10, 100, 50))painter.drawRect(QRectF(10.5, 10.5, 100.0, 50.0))# 绘制圆角矩形painter.drawRoundedRect(10, 10, 100, 50, 10, 10) # xRadius, yRadiuspainter.drawRoundedRect(QRectF(10, 10, 100, 50), 15, 15)# 填充矩形(使用画刷)painter.fillRect(10, 10, 100, 50, QColor("#e74c3c"))painter.fillRect(QRect(10, 10, 100, 50), QBrush(QColor("#3498db")))# 清除矩形区域painter.eraseRect(10, 10, 100, 50)
4.3 椭圆与圆形
# 绘制椭圆painter.drawEllipse(10, 10, 100, 50) # 外接矩形painter.drawEllipse(QRect(10, 10, 100, 50))# 绘制圆形(宽高相等的椭圆)painter.drawEllipse(QPoint(50, 50), 30, 30) # 中心点,x半径,y半径painter.drawEllipse(QPointF(50.5, 50.5), 25.0, 25.0)# 绘制弧线painter.drawArc(10, 10, 100, 100, 30 * 16, 120 * 16) # 角度以1/16度为单位# 绘制弦painter.drawChord(10, 10, 100, 100, 30 * 16, 120 * 16)# 绘制饼图painter.drawPie(10, 10, 100, 100, 30 * 16, 120 * 16)
4.4 多边形绘制
from PyQt6.QtCore import QPolygon, QPolygonF# 创建多边形polygon = QPolygon()polygon << QPoint(10, 10) << QPoint(50, 10) << QPoint(50, 50) << QPoint(10, 50)# 绘制多边形painter.drawPolygon(polygon)# 使用 QPolygonF(浮点精度)polygon_f = QPolygonF()polygon_f << QPointF(10.5, 10.5) << QPointF(50.5, 10.5) << QPointF(50.5, 50.5)# 填充多边形painter.drawPolygon(polygon, Qt.FillRule.OddEvenFill)# 绘制凸多边形(性能更好)convex_polygon = QPolygon([QPoint(10, 10), QPoint(50, 10), QPoint(30, 50)])painter.drawConvexPolygon(convex_polygon)
5、文本绘制方法
5.1 基本文本绘制
# 在指定位置绘制文本painter.drawText(10, 30, "Hello PyQt6")# 在指定矩形内绘制文本rect = QRect(10, 10, 200, 50)painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, "居中文本")# 带对齐方式的文本painter.drawText(rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop, "左上对齐")# 使用 QPointpainter.drawText(QPoint(10, 30), "在点位置绘制")# 绘制富文本(HTML)painter.drawText(10, 50, "<b>粗体</b> <i>斜体</i> <font color='red'>红色</font>")
5.2 文本测量与布局
# 获取文本宽度text_width = painter.fontMetrics().horizontalAdvance("Hello World")# 获取文本高度text_height = painter.fontMetrics().height()# 获取文本边界矩形bounding_rect = painter.fontMetrics().boundingRect("Hello World")# 文本布局from PyQt6.QtGui import QTextLayouttext = "这是一个多行文本示例,会自动换行"layout = QTextLayout(text, painter.font())layout.beginLayout()while True:line = layout.createLine()if not line.isValid():breakline.setLineWidth(200) # 行宽# 绘制每一行...layout.endLayout()
5.3 文本路径绘制
from PyQt6.QtGui import QPainterPath# 创建文本路径path = QPainterPath()path.addText(10, 50, painter.font(), "路径文本")# 绘制文本路径painter.drawPath(path)# 填充文本路径painter.fillPath(path, QColor("#9b59b6"))# 描边文本路径painter.strokePath(path, QPen(QColor("#34495e"), 2))
6、图像绘制方法
6.1 基本图像绘制
from PyQt6.QtGui import QPixmap, QImage# 加载图像pixmap = QPixmap("image.png")image = QImage("photo.jpg")# 绘制图像(多种方式)painter.drawPixmap(10, 10, pixmap) # 在指定位置绘制painter.drawPixmap(QRect(10, 10, 100, 100), pixmap) # 缩放绘制painter.drawPixmap(QRect(10, 10, 100, 100), pixmap, pixmap.rect()) # 源矩形和目标矩形# 绘制 QImagepainter.drawImage(10, 10, image)painter.drawImage(QRect(10, 10, 200, 150), image)# 绘制图像的一部分source_rect = QRect(0, 0, 50, 50) # 源图像区域target_rect = QRect(10, 10, 100, 100) # 目标区域painter.drawPixmap(target_rect, pixmap, source_rect)
6.2 图像变换与效果
# 保存状态painter.save()# 平移后绘制painter.translate(50, 50)painter.drawPixmap(0, 0, pixmap)# 恢复状态painter.restore()# 旋转后绘制painter.save()painter.translate(100, 100) # 设置旋转中心painter.rotate(45) # 旋转45度painter.drawPixmap(-50, -50, pixmap) # 以中心点绘制painter.restore()# 缩放绘制painter.save()painter.scale(0.5, 0.5) # 缩小一半painter.drawPixmap(10, 10, pixmap)painter.restore()
7、路径绘制(QPainterPath)
7.1 创建与绘制路径
from PyQt6.QtGui import QPainterPath# 创建路径path = QPainterPath()# 移动起点path.moveTo(10, 10)# 添加直线path.lineTo(100, 10)path.lineTo(100, 100)path.lineTo(10, 100)path.closeSubpath() # 闭合路径# 添加曲线path.cubicTo(50, 50, 100, 150, 150, 50) # 三次贝塞尔曲线path.quadTo(100, 100, 150, 50) # 二次贝塞尔曲线# 添加圆弧path.arcTo(50, 50, 100, 100, 30, 120) # 角度为度# 添加椭圆path.addEllipse(200, 200, 50, 30)# 添加矩形path.addRect(10, 10, 100, 50)# 添加圆角矩形path.addRoundedRect(10, 10, 100, 50, 10, 10)# 绘制路径painter.drawPath(path)# 填充路径painter.fillPath(path, QColor("#f1c40f"))# 描边路径painter.strokePath(path, QPen(QColor("#2c3e50"), 3))
7.2 路径操作
# 路径合并path1 = QPainterPath()path1.addRect(10, 10, 50, 50)path2 = QPainterPath()path2.addEllipse(30, 30, 50, 50)# 并集union_path = path1.united(path2)# 交集intersected_path = path1.intersected(path2)# 差集subtracted_path = path1.subtracted(path2)# 绘制结果painter.drawPath(union_path)
8、坐标变换与视图
8.1 基本变换
# 平移painter.translate(100, 50) # 向右100,向下50# 旋转(以原点为中心)painter.rotate(45) # 顺时针45度# 缩放painter.scale(2.0, 0.5) # X轴放大2倍,Y轴缩小一半# 剪切painter.shear(0.2, 0.1) # X剪切0.2,Y剪切0.1# 重置变换painter.resetTransform()
8.2 矩阵变换
from PyQt6.QtGui import QTransform# 创建变换矩阵transform = QTransform()transform.translate(100, 50)transform.rotate(30)transform.scale(1.5, 1.5)# 应用矩阵变换painter.setTransform(transform)# 获取当前变换矩阵current_transform = painter.transform()# 组合变换transform1 = QTransform().translate(50, 50)transform2 = QTransform().rotate(45)combined_transform = transform1 * transform2 # 先平移后旋转painter.setTransform(combined_transform)
8.3 视图与窗口
# 设置视口(物理坐标)painter.setViewport(0, 0, 400, 300)# 设置窗口(逻辑坐标)painter.setWindow(-200, -150, 400, 300)# 设置世界矩阵painter.setWorldTransform(QTransform().rotate(30))# 启用世界变换painter.setWorldMatrixEnabled(True)# 坐标映射logical_point = painter.transform().map(QPoint(100, 100)) # 物理转逻辑physical_point = painter.transform().inverted().map(QPoint(100, 100)) # 逻辑转物理
9、高级绘制特性
9.1 渲染提示
# 开启抗锯齿(重要!)painter.setRenderHint(QPainter.RenderHint.Antialiasing)# 文本抗锯齿painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)# 平滑像素图变换painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)# 高质量图像缩放painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering)# 组合多个渲染提示painter.setRenderHints(QPainter.RenderHint.Antialiasing |QPainter.RenderHint.TextAntialiasing |QPainter.RenderHint.SmoothPixmapTransform)# 检查是否启用某个渲染提示if painter.renderHints() & QPainter.RenderHint.Antialiasing:print("抗锯齿已启用")
9.2 合成模式
from PyQt6.QtCore import Qt# 设置合成模式painter.setCompositionMode(QPainter.CompositionMode.SourceOver) # 默认:源覆盖目标painter.setCompositionMode(QPainter.CompositionMode.Source) # 只绘制源,忽略目标painter.setCompositionMode(QPainter.CompositionMode.DestinationOver) # 目标覆盖源painter.setCompositionMode(QPainter.CompositionMode.Clear) # 清除目标区域painter.setCompositionMode(QPainter.CompositionMode.Xor) # 异或模式# 常用合成模式示例# 1. 叠加模式(类似Photoshop的叠加)painter.setCompositionMode(QPainter.CompositionMode.Multiply) # 正片叠底painter.setCompositionMode(QPainter.CompositionMode.Screen) # 滤色painter.setCompositionMode(QPainter.CompositionMode.Overlay) # 叠加# 2. 混合模式painter.setCompositionMode(QPainter.CompositionMode.Darken) # 变暗painter.setCompositionMode(QPainter.CompositionMode.Lighten) # 变亮# 恢复默认合成模式painter.setCompositionMode(QPainter.CompositionMode.SourceOver)
9.3 裁剪区域
from PyQt6.QtCore import QRect, QRegion# 设置矩形裁剪区域painter.setClipRect(10, 10, 100, 100)# 设置路径裁剪区域path = QPainterPath()path.addEllipse(50, 50, 80, 80)painter.setClipPath(path)# 设置区域裁剪(多个矩形组合)region = QRegion(QRect(10, 10, 50, 50))region = region.united(QRegion(QRect(70, 70, 50, 50)))painter.setClipRegion(region)# 组合裁剪区域(交集)painter.setClipRect(0, 0, 200, 200)painter.setClipRect(50, 50, 100, 100, Qt.ClipOperation.IntersectClip)# 保存和恢复裁剪状态painter.save()painter.setClipRect(10, 10, 80, 80)# 在此裁剪区域内绘制painter.restore() # 恢复裁剪状态# 检查裁剪区域if painter.hasClipping():clip_rect = painter.clipBoundingRect()print(f"裁剪区域: {clip_rect}")# 禁用裁剪painter.setClipping(False)
9.4 不透明度与渐变
# 设置全局不透明度painter.setOpacity(0.5) # 50%不透明度# 使用透明颜色transparent_brush = QBrush(QColor(255, 0, 0, 128)) # 半透明红色painter.setBrush(transparent_brush)# 复杂渐变from PyQt6.QtGui import QConicalGradient, QRadialGradient, QLinearGradient# 锥形渐变conical_gradient = QConicalGradient(100, 100, 0) # 中心点,起始角度conical_gradient.setColorAt(0, QColor("#ff0000"))conical_gradient.setColorAt(0.5, QColor("#00ff00"))conical_gradient.setColorAt(1, QColor("#0000ff"))painter.setBrush(QBrush(conical_gradient))# 径向渐变(中心向外)radial_gradient = QRadialGradient(150, 150, 50, 150, 150) # 中心,半径,焦点radial_gradient.setColorAt(0, QColor("#ffffff"))radial_gradient.setColorAt(1, QColor("#000000"))painter.setBrush(QBrush(radial_gradient))# 线性渐变(带多个色标)linear_gradient = QLinearGradient(0, 0, 200, 0) # 起点,终点linear_gradient.setColorAt(0, QColor("#ff0000"))linear_gradient.setColorAt(0.3, QColor("#ffff00"))linear_gradient.setColorAt(0.7, QColor("#00ff00"))linear_gradient.setColorAt(1, QColor("#0000ff"))linear_gradient.setSpread(QGradient.Spread.ReflectSpread) # 反射扩展painter.setBrush(QBrush(linear_gradient))
9.5 性能优化技巧
# 1. 批量绘制(减少状态切换)painter.save()painter.setPen(QPen(Qt.GlobalColor.blue, 2))painter.setBrush(QBrush(Qt.GlobalColor.yellow))# 批量绘制多个图形painter.drawRect(10, 10, 50, 50)painter.drawRect(70, 10, 50, 50)painter.drawRect(130, 10, 50, 50)painter.restore()# 2. 使用 QPainterPath 预定义路径path = QPainterPath()path.moveTo(10, 10)path.lineTo(100, 10)path.lineTo(100, 100)path.lineTo(10, 100)path.closeSubpath()# 一次性绘制复杂路径painter.drawPath(path)# 3. 避免频繁创建 QPainter 对象# 错误做法:在循环中反复创建 QPainter# 正确做法:在 paintEvent 外创建一次,重复使用# 4. 使用 QPicture 记录和重放绘制命令from PyQt6.QtGui import QPicturepicture = QPicture()recorder = QPainter(picture)recorder.drawRect(10, 10, 100, 50)recorder.drawEllipse(50, 50, 80, 80)recorder.end()# 重放绘制命令painter.drawPicture(0, 0, picture)# 5. 禁用不需要的渲染提示painter.setRenderHint(QPainter.RenderHint.Antialiasing, False) # 关闭抗锯齿提升性能painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, False)# 6. 使用 QStaticText 绘制静态文本(性能更好)from PyQt6.QtGui import QStaticTextstatic_text = QStaticText("这是一个静态文本")painter.drawStaticText(10, 10, static_text)# 7. 合理使用 update() 和 repaint()# update():异步重绘,合并多个更新请求# repaint():立即重绘,用于需要即时反馈的场景
9.6 调试与问题排查
# 检查绘制设备if painter.device():print(f"绘制设备: {painter.device()}")else:print("警告:未绑定绘制设备")# 检查绘制状态if painter.isActive():print("QPainter 正在绘制中")else:print("QPainter 未激活,请先调用 begin() 或使用带设备的构造函数")# 检查变换矩阵transform = painter.transform()print(f"当前变换矩阵: {transform.m11()}, {transform.m12()}, {transform.m21()}, {transform.m22()}")# 保存绘制日志import logginglogging.basicConfig(level=logging.DEBUG)def paintEvent(self, event):painter = QPainter(self)logging.debug(f"开始绘制,设备: {painter.device()}")try:# 绘制操作...painter.drawRect(10, 10, 100, 50)logging.debug("绘制完成")except Exception as e:logging.error(f"绘制错误: {e}")finally:# QPainter 会自动结束pass# 常见问题:# 1. 忘记调用 begin() 或使用错误的设备# 2. 在 paintEvent 外使用 QPainter# 3. 未正确处理坐标变换# 4. 内存泄漏:确保 QPainter 在作用域结束时自动销毁

夜雨聆风