该控件是参考了feiyangqingyun的广告占比图(Qt作品),更改了配色,重用PyQt5编写而成。原版在此:
https://blog.csdn.net/feiyangqingyun/article/details/98472081
先上成品图
当鼠标停在饼圆对应区域,该区域面积会扩大弹出,同时,对应下方的图例字体会变成黄色。
该项目的重难点主要有两个:1. 绘制饼圆; 2. 鼠标进入事件。
当用户对窗口进行拉伸,移动,或者鼠标悬停在某个扇形饼圆区域,则触发paintEvent 对窗口元素进行重绘,其核心代码如下:
def paintEvent(self, event):
# 绘制准备工作, 启用反锯齿
painter = QtGui.QPainter(self)
painter.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing)
# 平移坐标中心,等比例缩放
width = self.width()
height = self.height()
side = min(width, height)
painter.translate(width / 2, height / 2) # 坐标中心移至窗口中心位置
painter.scale(side / 200.0, side / 200.0) # 坐标刻度缩放为原来的(side/200)倍
# 画圆
self.drawOuterCircle(painter) # 绘制外侧圆
self.drawMidCircle(painter) # 绘制内侧圆
self.drawOuterPie(painter) # 绘制外侧大的三色扇形饼圆
self.drawInnerPie(painter) # 绘制内侧小的三色扇形饼圆
self.drawLegend(painter) # 绘制图例
self.drawTitle(painter) # 绘制左上角文字
扇形饼圆的绘制是由大扇形减去小扇形得来的。以绘制外侧香槟色饼圆为例,其核心代码如下所示:
def drawOuterPie(self, painter):
painter.save() # 以下绘图保存至painter的坐标系统
# 设置标志位,判断鼠标是否进入该区域。如果进入该区域,则半径扩大
if self.mouseOuterChampagne:
radius = self.outerPieRadius + 3
else:
radius = self.outerPieRadius
# 绘制大扇形
rect = QtCore.QRectF(-radius, -radius - 10, radius * 2, radius * 2) # 该扇形饼圆所在的region
pathOuterChampagnePie = QtGui.QPainterPath() # 新建QPainterPath对象
pathOuterChampagnePie.arcMoveTo(rect, 0) # 画弧线的起点角度,从0°开始
pathOuterChampagnePie.arcTo(rect, 0, self.outerChampagnePercent * 360) # 扇形饼圆弧度为self.outerChampagnePercent * 360
pathOuterChampagnePie.lineTo(0, -10) # 画直线
pathOuterChampagnePie.lineTo(radius, -10) # 另一条直线
pathOuterChampagnePie.closeSubpath() # 使该路径闭合
# 以下同理绘制小扇形
radius = self.midCircleRadius
rect1 = QtCore.QRectF(-radius, -radius - 10, radius * 2, radius * 2)
pathMidPie = QtGui.QPainterPath()
pathMidPie.arcMoveTo(rect1, 0)
pathMidPie.arcTo(rect1, 0, self.outerChampagnePercent * 360)
pathMidPie.lineTo(0, -10)
pathMidPie.lineTo(radius, -10)
pathMidPie.closeSubpath()
# 大扇形减去小扇形,得到扇形饼圆
self.pathOuterChampagne = pathOuterChampagnePie.subtracted(pathMidPie)
painter.setPen(self.outerPieChampagne)
painter.setBrush(self.outerPieChampagne)
painter.drawPath(self.pathOuterChampagne)
......
painter.restore() # 恢复坐标系
圆角矩形的绘制用painter.drawRoundedRect() 函数来实现,其核心代码如下:
# 穿衣
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(self.outerPiePurple)
painter.drawRoundedRect(-120, 64, 10, 10, 2, 2) # 绘制圆角矩形
if self.mouseOuterPurple == True:
painter.setPen(self.emphasizedTextColor)
else:
painter.setPen(QtCore.Qt.white)
painter.drawText(-106, 72, str("穿衣 : 30%"))
在鼠标相应事件中,所参考的坐标系是以窗口左上角为零点的坐标系统。因此在判断鼠标是否落入扇形饼圆时,应将鼠标位置坐标转换为坐标原点为窗口中心 的坐标系下的坐标。若鼠标悬停在某区域,则改变标志位,使得在调用painterEvent 重绘时突出该区域。核心代码如下:
def mouseMoveEvent(self, event):
# 坐标系转换,和之前painter转换坐标系相对应
width = self.width()
height = self.height()
side = min(width, height)
enterPoint = QtCore.QPoint((event.pos().x() - width/2) /side * 200.0,
(event.pos().y() - height/2)/side * 200.0)
# 判断鼠标是否进入,并置标志位
if self.pathOuterChampagne.contains(enterPoint): self.mouseOuterChampagne = True
else: self.mouseOuterChampagne = False
if self.pathOuterCream.contains(enterPoint): self.mouseOuterCream = True
else: self.mouseOuterCream = False
......
self.update() # 重绘
以上,就是该项目的几个关键点。在此写博客记录一下~
最后,上海的Qter们,欢迎关注公众号“Qter欢聚在上海”,让我们共同学习,共同成长?