用PyQt5设计汽车仪表盘——三色环表盘(一)(坐标转换,QPainter画环,画多边形)

一、绘图部分

  • 成品图
  • 仪表盘绘图部分
    • 坐标轴和坐标刻度的变换
    • 环和指针的绘制
      • 扇形环绘制要点
      • 指针绘制要点
    • 刻度线刻度值的绘制
      • 刻度线绘制要点
      • 刻度值绘制要点

成品图

先上成品图~

用PyQt5设计汽车仪表盘——三色环表盘(一)(坐标转换,QPainter画环,画多边形)_第1张图片
拖拉窗口下方滑动条,可令指针旋转指向不同速度值,该值会自动显示在表盘中下方的LCD显示屏上。速度范围为0~100Mbps。相比起滑动条的拖拉,指针的旋转动作会有些许延迟,更接近真实情景中汽车仪表盘的显示情况。

本系列博文将对该项目的三个主要部分:仪表盘绘图,LCD屏幕设置和动画设置 进行详述。

仪表盘绘图部分

绘图部分的重难点主要包括 1. 坐标轴和坐标刻度的变换2. 环和指针的绘制3. 刻度线刻度值的绘制

坐标轴和坐标刻度的变换

首先,先回顾一下Qt的坐标系统。
用PyQt5设计汽车仪表盘——三色环表盘(一)(坐标转换,QPainter画环,画多边形)_第2张图片
一般来讲,我们常用的几个获取控件尺寸的函数,诸如x(), y(), width(), 以及height()都是以 客户区(Client Area) 的左上角顶点为坐标轴原点来计量的。由于在本例中我们需要在窗口中心位置画图,因此为了方便起见,最好将坐标轴原点平移至客户区的中心点,坐标轴的正负方向保持不变,依然是x向右为正,y向下为正。同时,为保证窗口缩放不影响绘图时图线尺寸,故还要进行坐标刻度的“自适应”。代码如下:

def paintEvent(self, event):

    # 坐标轴变换
    width = self.width()
    height = self.height()
    painter = QtGui.QPainter(self) # 初始化painter
    painter.translate(width / 2, height / 2) # 坐标轴变换,调用translate()将坐标原点平移至窗口中心

    # 坐标刻度自适应
    side = min(width, height)
    painter.scale(side / 200.0, side / 200.0) # 本项目中将坐标缩小为side/200倍,即画出length=10的直线,其实际长度应为10*(side/200)。
    
    # 启用反锯齿,使画出的曲线更平滑
    painter.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing) 
    ...
    ...
    

环和指针的绘制

扇形环绘制要点

  1. 环为扇形环,其弧度为270° * 2pi *(3/4),范围为-(1/4)*pi~(5/4)*pi。
  2. Qt无直接绘制扇形环的draw函数,因此这个圆环需要我们自己来画。方法很多,比如可以用QRegion直接绘出该扇形环区域,再用QPainterPath添加path再fillPath;可以利用裁剪函数subtracted(),大扇形剪掉小扇形得到扇形环;也可以直接绘制三色饼图,在填充颜色时,靠近圆心区域填充为透明色setColorAt(ratio, QtCore.Qt.transparent),这样也能产生环的效果。
    具体代码如下:
    def drawColorPie(self, painter): # 绘制三色环
        painter.save() # save()保存当前坐标系
        
        # 设置扇形部分区域
        radius = 99 # 半径
        painter.setPen(QtCore.Qt.NoPen)
        rect = QtCore.QRectF(-radius, -radius, radius * 2, radius * 2) # 扇形所在圆区域
        
        # 计算三色圆环范围角度。green:blue:red = 1:2:1
        angleAll = 360.0 - self.startAngle - self.endAngle # self.startAngle = 45, self.endAngle = 45
        angleStart = angleAll * 0.25
        angleMid = angleAll * 0.5
        angleEnd = angleAll * 0.25
        
        # 圆的中心部分填充为透明色,形成环的样式
        rg = QtGui.QRadialGradient(0, 0, radius, 0, 0) # 起始圆心坐标,半径,焦点坐标
        ratio = 0.9 # 透明:实色 = 0.9 :1
        
        # 绘制绿色环
        rg.setColorAt(0, QtCore.Qt.transparent) # 透明色
        rg.setColorAt(ratio, QtCore.Qt.transparent)
        rg.setColorAt(ratio+0.01, self.pieColorStart)
        rg.setColorAt(1, self.pieColorStart)
        
        painter.setBrush(rg)
        painter.drawPie(rect, (270 - self.startAngle - angleStart) * 16 , angleStart * 16)

        # 绘制蓝色环
        rg.setColorAt(0, QtCore.Qt.transparent)
        rg.setColorAt(ratio, QtCore.Qt.transparent)
        rg.setColorAt(ratio+0.01, self.pieColorMid)
        rg.setColorAt(1, self.pieColorMid)
        
        painter.setBrush(rg)
        painter.drawPie(rect, (270 - self.startAngle - angleStart - angleMid) * 16 , angleMid * 16)

        # 绘制红色环
        rg.setColorAt(0, QtCore.Qt.transparent)
        rg.setColorAt(ratio, QtCore.Qt.transparent)
        rg.setColorAt(ratio + 0.01, self.pieColorEnd)
        rg.setColorAt(1, self.pieColorEnd)

        painter.setBrush(rg)
        painter.drawPie(rect, (270 - self.startAngle - angleStart - angleMid - angleEnd) * 16, angleEnd * 16)

        painter.restore() # restore()恢复坐标系

指针绘制要点

  1. drawConvexPolygon() 函数画出指针形状。
  2. rotate() 旋转指针,使指针的起始指向为0刻度处。
    具体代码如下:
    def drawPointerIndicator(self, painter):
        painter.save()
        # 绘制指针
        radius = 68 # 指针长度
        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(self.pointerColor)

        # (-5, 0), (0, -8), (5, 0)和(0, radius) 四个点绘出指针形状
        pts = QtGui.QPolygon()
        pts.setPoints(-5, 0, 0, -8, 5, 0, 0, radius)
        
        # 旋转指针,使得指针起始指向为0刻度处
        painter.rotate(self.startAngle)
        degRotate = (360.0 - self.startAngle - self.endAngle) / (self.maxValue - self.minValue)\
                    * (self.currentValue - self.minValue)
        painter.rotate(degRotate)
        painter.drawConvexPolygon(pts)
        painter.restore()

刻度线刻度值的绘制

刻度线绘制要点

刻度线的绘制比较简单,用drawLine() 函数即可。绘制时需注意计算主刻度之间的弧度,然后再画短竖线。同样,刻度线也需要进行rotate() 旋转。其关键代码如下:

        painter.rotate(self.startAngle) # self.startAngle = 45,旋转45度
        steps = 8 # 8个刻度
        angleStep = (360.0 - self.startAngle - self.endAngle) / steps  # 刻度角

以及每画完一条刻度线,记得旋转指针

        painter.setPen(pen)
        painter.drawLine(0, radius - 5, 0, radius)
        painter.rotate(angleStep)

刻度值绘制要点

绘制刻度值用drawText() 函数。和绘制刻度线相比,绘制刻度值时需给出速度值字符串所在的具体坐标。在此我们用python中的math.sin()math.cos() 来计算坐标位置。其关键代码如下:

        for i in range(self.scaleMajor + 1): # self.scaleMajor = 8, 8个主刻度
            # 正余弦计算
            sina = math.sin(startRad - i * deltaRad)
            cosa = math.cos(startRad - i * deltaRad)
            
            # 刻度值计算
            value = math.ceil((1.0 * i * ((self.maxValue - self.minValue) / self.scaleMajor) + self.minValue)) # math.ceil(x):返回不小于x的最小整数
            strValue = str(int(value))
            
            # 字符的宽度和高度
            textWidth = self.fontMetrics().width(strValue)
            textHeight = self.fontMetrics().height()

            # 字符串的起始位置。注意考虑到字符宽度和高度进行微调
            x = radius * cosa - textWidth / 2
            y = -radius * sina + textHeight / 4
            painter.drawText(x-offset, y, strValue + "M")

至此,仪表盘的绘制大致完成。每次调用paintEvent()时,都需要重绘上述所有内容。

你可能感兴趣的:(Qt,PyQt5,Python)