PyQt5自定义组件之飞机水平仪
PyQt5自定义组件之飞机水平仪
效果如下图:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class GaugePlane(QWidget):
def __init__(self, parent=None):
super(GaugePlane, self).__init__(parent)
self.initUI()
def initUI(self):
self.borderOutColorStart = Qt.black
self.borderOutColorEnd = QColor(23, 149, 166)
self.borderInColorStart = QColor(50, 50, 50)
self.borderInColorEnd = QColor(220, 220, 220)
self.bgColor = QColor(41, 127, 184)
self.planeColor = QColor("#d68070")
self.glassColor = QColor(250, 250, 250)
self.scaleColor = QColor(255, 255, 255)
self.lineColor = QColor(255, 255, 255)
self.textColor = QColor(255, 255, 255)
self.pointerColor = QColor(255, 107, 107)
self.handleColor = QColor("grey")
self.degValue = 0 # 倾斜角
self.rollValue = 0 # 俯仰角
self.resize(400, 400)
self.time = QTimer()
self.time.start(1000)
self.time.timeout.connect(self.tsetDegValue)
def paintEvent(self, QPaintEvent):
width = self.width()
height = self.height()
side = min(width, height)
# 绘制准备工作, 启用反锯齿, 平移坐标轴中心, 等比例缩放
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
painter.translate(width / 2, height / 2)
painter.scale(side / 200.0, side / 200.0)
# 绘制外边框
self.drawBorderOut(painter)
# 绘制内边框
self.drawBorderIn(painter)
# 绘制背景
self.drawBg(painter)
# 绘制姿势仪背景
self.drawPlane(painter)
# 绘制玻璃遮罩层
self.drawGlass(painter)
# 绘制刻度尺
self.drawScale(painter)
# 绘制线条
self.drawLine(painter)
# 绘制指针
self.drawPointer(painter)
# 绘制手柄
self.drawHandle(painter)
def tsetDegValue(self):
import random
dev = random.randint(-30, 30)
roll = random.randint(-30, 30)
self.setRollValue(roll)
self.setDegValue(dev)
def drawBorderOut(self, painter):
radius = 99
painter.save()
painter.setPen(Qt.NoPen)
borderGradient = QLinearGradient(0, -radius, 0, radius)
borderGradient.setColorAt(0, self.borderOutColorStart)
borderGradient.setColorAt(1, self.borderOutColorEnd)
painter.setBrush(borderGradient)
painter.drawEllipse(-radius, -radius, radius * 2, radius * 2)
painter.restore()
def drawBorderIn(self, painter):
radius = 93
painter.save()
painter.setPen(Qt.NoPen)
borderGradient = QLinearGradient(0, -radius, 0, radius)
borderGradient.setColorAt(0, self.borderInColorStart)
borderGradient.setColorAt(1, self.borderInColorEnd)
painter.setBrush(borderGradient)
painter.drawEllipse(-radius, -radius, radius * 2, radius * 2)
painter.restore()
def drawBg(self, painter):
radius = 90
painter.save()
painter.setPen(Qt.NoPen)
borderGradient = QLinearGradient(0, -radius, 0, radius)
self.bgColor.setAlpha(255)
borderGradient.setColorAt(0, self.bgColor)
self.bgColor.setAlpha(150)
borderGradient.setColorAt(1, self.bgColor)
painter.setBrush(borderGradient)
painter.drawEllipse(-radius, -radius, radius * 2, radius * 2)
painter.restore()
def drawPlane(self, painter):
radius = 90
painter.save()
painter.rotate(self.degValue)
painter.setPen(Qt.NoPen)
painter.setBrush(self.planeColor)
rect = QRect(-radius, -radius, radius * 2, radius * 2)
if not painter:
painter.drawPie(rect, 0, -16 * 180)
else:
offset = -(self.rollValue * radius / 60)
startAngle = 180 + offset
endAngle = offset
span = endAngle + startAngle
painter.drawChord(rect, -16 * startAngle, 16 * span)
painter.restore()
def drawGlass(self, painter):
radius = 80
painter.save()
painter.setPen(Qt.NoPen)
# 饼圆1为整个圆形区域
pieRect1 = QRect(-radius, -radius, radius * 2, radius * 2)
# 饼圆2位饼圆1区域的中间部分的四分之一
pieRect2 = QRect(-radius, -radius / 4, radius * 2, radius / 4 * 2)
# 颜色线性渐变, 颜色透明度产生立体感
linearGradient = QLinearGradient(pieRect1.topLeft(), pieRect1.bottomRight())
self.glassColor.setAlpha(30)
linearGradient.setColorAt(0.1, self.glassColor)
self.glassColor.setAlpha(100)
linearGradient.setColorAt(0.5, self.glassColor)
# 绘制两个饼圆
painter.setBrush(linearGradient)
painter.drawPie(pieRect1, 0, 16 * 180)
painter.drawPie(pieRect2, 0, -16 * 180)
painter.restore()
def drawScale(self, painter):
radius = 88
painter.save()
# 设置画笔颜色
pen = QPen()
pen.setColor(self.scaleColor)
pen.setWidthF(4)
pen.setCapStyle(Qt.RoundCap)
painter.setPen(pen)
# 线条长度
len = 8
# 左侧平行线
painter.rotate(90)
painter.drawLine(0, radius - len, 0, radius)
# 逐个绘制大刻度
for i in range(6):
painter.rotate(30)
painter.drawLine(0, radius - len, 0, radius)
# 回到小刻度,重新设置画笔宽度
pen.setWidthF(2)
painter.setPen(pen)
painter.rotate(-120)
# 逐个绘制小刻度
for i in range(5):
painter.rotate(10)
painter.drawLine(0, radius - len, 0, radius)
painter.restore()
def drawLine(self, painter):
radius = 95
painter.save()
width = self.width()
height = self.height()
side = min(width, height)
# 重置坐标系
painter.resetTransform()
painter.translate(width / 2, height / 2 + self.rollValue)
painter.scale(side / 200.0, side / 200.0)
painter.rotate(self.degValue)
# 设置画笔颜色
pen = QPen()
pen.setColor(self.lineColor)
pen.setWidthF(2)
pen.setCapStyle(Qt.RoundCap)
painter.setPen(pen)
# 设置画笔字体
font = QFont()
font.setPixelSize(10)
font.setBold(True)
painter.setFont(font)
# 依次绘制水平距离线条及值
i = -30
while i <= 30:
if i == 0:
# 中心点不用绘制
i += 10
continue
value = qAbs(i)
# 绘制横向水平线条
pt1 = QPointF(-radius * value / 100, radius / 70 * i)
pt2 = QPointF(-pt1.x(), pt1.y())
painter.drawLine(pt1, pt2)
# 根据字体大小计算绘制文字的区域
# strValue = QString.number(value)
strValue = str(value).replace(".0", "")
fm = painter.fontMetrics()
textRect = QRectF(QPointF(QPoint(0, 0)), QSizeF(fm.size(Qt.AlignCenter, strValue)))
textRect.moveCenter(pt1 - QPointF(radius / 10, 0))
painter.drawText(textRect, Qt.AlignCenter, strValue)
textRect.moveCenter(pt2 + QPointF(radius / 10, 0))
painter.drawText(textRect, Qt.AlignCenter, strValue)
i += 10
painter.restore()
def drawPointer(self, painter):
radius = 70
painter.save()
painter.rotate(self.degValue + 90)
painter.setPen(Qt.NoPen)
# 指针长度
len = 10
# 三角形坐标
pts = QPolygon()
pts.setPoints(-radius, 0, -radius + len, -len / 2, -radius + len, len / 2)
painter.setBrush(self.pointerColor)
painter.drawConvexPolygon(pts)
painter.restore()
def drawHandle(self, painter):
painter.save()
pen = QPen()
pen.setColor(self.handleColor)
pen.setWidthF(4)
pen.setCapStyle(Qt.RoundCap)
pen.setJoinStyle(Qt.MiterJoin)
painter.setPen(pen)
painter.setBrush(self.handleColor)
# 绘制弧形
arcRadius = 15
rect = QRect(-arcRadius, -arcRadius, arcRadius * 2, arcRadius * 2)
painter.drawArc(rect, 0, -16 * 180)
# 绘制中心圆
centerRadius = 2
rect = QRect(-centerRadius, -centerRadius, centerRadius * 2, centerRadius * 2)
painter.drawEllipse(rect)
# 绘制左侧右侧横向水平线条
len = 30
pt1 = QPoint(-len, 0)
pt2 = QPoint(-arcRadius, 0)
painter.drawLine(pt1, pt2)
pt1 = QPoint(len, 0)
pt2 = QPoint(arcRadius, 0)
painter.drawLine(pt1, pt2)
# 绘制纵向垂直线条
pt1 = QPoint(0, len / 2)
pt2 = QPoint(0, 80)
painter.drawLine(pt1, pt2)
# 绘制底部梯形
p1 = QPoint(-30, 82)
p2 = QPoint(-20, 60)
p3 = QPoint(20, 60)
p4 = QPoint(30, 82)
pts = QPolygon([p1, p2, p3, p4])
# pts << p1 << p2 << p3 << p4
# pts.setPoints()
painter.drawConvexPolygon(pts)
# 绘制底部填充圆弧
radius = 32
rect = QRect(-radius, 77, radius * 2, 13)
painter.setPen(Qt.NoPen)
painter.drawPie(rect, 0, -16 * 180)
painter.restore()
def getBorderOutColorStart(self):
return self.borderOutColorStart
def getBorderOutColorEnd(self):
return self.borderOutColorEnd
def getBorderInColorStart(self):
return self.borderInColorStart
def getBorderInColorEnd(self):
return self.borderInColorEnd
def getBgColor(self):
return self.bgColor
def getPlaneColor(self):
return self.planeColor
def getGlassColor(self):
return self.glassColor
def getScaleColor(self):
return self.scaleColor
def getLineColor(self):
return self.lineColor
def getTextColor(self):
return self.textColor
def getPointerColor(self):
return self.pointerColor
def getHandleColor(self):
return self.handleColor
def getDegValue(self):
return self.degValue
def getRollValue(self):
return self.rollValue
def sizeHint(self):
return QSize(200, 200)
def minimumSizeHint(self):
return QSize(50, 50)
def setBorderOutColorStart(self, borderOutColorStart):
if (self.borderOutColorStart != borderOutColorStart):
self.borderOutColorStart = borderOutColorStart
self.update()
def setBorderOutColorEnd(self, borderOutColorEnd):
if (self.borderOutColorEnd != borderOutColorEnd):
self.borderOutColorEnd = borderOutColorEnd
self.update()
def setBorderInColorStart(self, borderInColorStart):
if (self.borderInColorStart != borderInColorStart):
self.borderInColorStart = borderInColorStart
self.update()
def setBorderInColorEnd(self, borderInColorEnd):
if (self.borderInColorEnd != borderInColorEnd):
self.borderInColorEnd = borderInColorEnd
self.update()
def setBgColor(self, bgColor):
if (self.bgColor != bgColor):
self.bgColor = bgColor
self.update()
def setPlaneColor(self, planeColor):
if (self.planeColor != planeColor):
self.planeColor = planeColor
self.update()
def setGlassColor(self, glassColor):
if (self.glassColor != glassColor):
self.glassColor = glassColor
self.update()
def setScaleColor(self, scaleColor):
if (self.scaleColor != scaleColor):
self.scaleColor = scaleColor
self.update()
def setLineColor(self, lineColor):
if (self.lineColor != lineColor):
self.lineColor = lineColor
self.update()
def setTextColor(self, textColor):
if (self.textColor != textColor):
self.textColor = textColor
self.update()
def setPointerColor(self, pointerColor):
if (self.pointerColor != pointerColor):
self.pointerColor = pointerColor
self.update()
def setHandleColor(self, handleColor):
if (self.handleColor != handleColor):
self.handleColor = handleColor
self.update()
def setDegValue(self, degValue):
if (self.degValue != degValue and degValue >= -180 and degValue <= 180):
self.degValue = degValue
self.update()
def setRollValue(self, rollValue):
if (self.rollValue != rollValue and rollValue >= -100 and rollValue <= 100):
self.rollValue = rollValue
self.update()
import sys
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = GaugePlane()
mainWindow.show()
sys.exit(app.exec_())