PyQt5实现虚拟摇杆

PyQt5实现虚拟摇杆

效果如下:

代码如下:

from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import (
    QColor, QPainterPath, QFont, QPainter, QLinearGradient, QBrush, QPen, QFontMetrics,
    QRadialGradient, QPixmap, QTransform
)
from PyQt5.QtCore import QPoint, QRectF
from PyQt5.Qt import Qt
import math


class CustomButton(QWidget):
    def __init__(self, parent=None):
        super(CustomButton, self).__init__(parent)
        self.m_pressIndex = 0
        self.m_enterIndex = 0
        self.m_isMouseEntered = False
        self.m_isMousePressed = False
        self.m_radius = 80
        self.m_arcLength = 55
        self.mCenterRound = QPoint(0, 0)
        self.m_bTextModeEn = False
        self.setMouseTracking(True)
        self.mSectorColor = QColor(38, 38, 38)
        self.m_arcPathList = []
        self.m_colorList = []
        self.m_textPathList = []
        self.mCurWorkRegion = []
        self.QUADRANT_UP = 1
        self.QUADRANT_LEFT = 2
        self.QUADRANT_DOWN = 3
        self.QUADRANT_RIGHT = 4
        self.M_PI = 3.14159265358979323846

        self.initButton()
        self.setWidgetStyle("Dark")
        self.setAxesVertical(False)

    def setWidgetStyle(self, style):
        if (style == "Bright"):
            self.mSectorColor = QColor(238, 241, 240)
            self.colorSPL = QColor(63, 155, 178)
            self.colorBKG = QColor(193, 199, 209)

            self.colorSectorUp2 = QColor(240, 243, 208)
            self.colorSectorUp = QColor(239, 242, 247)
            self.colorSectorDown = QColor(221, 225, 231)

            self.colorbgGradient0 = QColor(175, 180, 191)
            self.colorbgGradient1 = QColor(239, 242, 247)

            self.colorExcircle0 = QColor(211, 215, 223)
            self.colorExcircle5 = QColor(231, 235, 240)
            self.colorExcircle9 = QColor(182, 187, 197)
            self.colorInnerCircle0 = QColor(45, 48, 56)
            self.colorInnerCircle9 = QColor(30, 32, 37)
        else:
            self.mSectorColor = QColor(38, 38, 38)
            self.colorSPL = QColor(32, 149, 216)
            self.colorBKG = QColor(41, 44, 50)

            self.colorSectorUp2 = QColor(68, 68, 68)
            self.colorSectorUp = QColor(60, 60, 60)
            self.colorSectorDown = QColor(22, 22, 22)

            self.colorbgGradient0 = QColor(24, 24, 24)
            self.colorbgGradient1 = QColor(53, 57, 63)

            self.colorExcircle0 = QColor(68, 68, 68)
            self.colorExcircle5 = QColor(37, 40, 46)
            self.colorExcircle9 = QColor(22, 22, 22)
            self.colorInnerCircle0 = QColor(45, 48, 56)
            self.colorInnerCircle9 = QColor(30, 32, 37)
        self.update()

    def setRadiusValue(self, radius):
        self.m_radius = radius

    def setArcLength(self, arcLength):
        self.m_arcLength = arcLength

    def drawRotatedText(self, painter, degrees, x, y, text):
        painter.save()  # 保存原来坐标系统
        painter.translate(x, y)  # 平移坐标原点到x, y
        painter.rotate(degrees)  # 坐标旋转degrees度
        painter.drawText(0, 0, text)  # 在原点绘制文本
        painter.restore()  # 回复原来的坐标系统

    def setAxesVertical(self, axesVertical):
        self.mAxesVertical = axesVertical
        if (self.mAxesVertical == False):
            self.addArc(1, 0, 45, 90, self.mSectorColor)
            self.addArc(0, 1, 135, 90, self.mSectorColor)
            self.addArc(-1, 0, 225, 90, self.mSectorColor)
            self.addArc(0, -1, 315, 90, self.mSectorColor)
        else:
            self.addArc(1, 0, 0, 90, self.mSectorColor)
            self.addArc(0, 1, 90, 90, self.mSectorColor)
            self.addArc(-1, 0, 180, 90, self.mSectorColor)
            self.addArc(0, -1, 270, 90, self.mSectorColor)

        # 绘制中心圆
        self.centerRoundPath = QPainterPath()
        self.centerRoundPath.addEllipse(QPoint(0, 0), self.m_radius - self.m_arcLength + 2,
                                        self.m_radius - self.m_arcLength + 2)
        self.m_arcPathList.append(self.centerRoundPath)
        self.m_colorList.append(QColor(255, 255, 255))

        # 添加文字
        self.font = QFont()
        self.font.setFamily("Microsoft YaHei")
        self.font.setPointSize(14)

        for i in range(len(self.m_arcPathList)):
            painterPath = QPainterPath()
            self.m_textPathList.append(painterPath)

        self.mStrUp = "前"
        self.mStrLeft = "左"
        self.mStrDown = "后"
        self.mStrRight = "右"
        self.update()

    def initButton(self):
        self.addArc(1, 0, 45, 90, self.mSectorColor)
        self.addArc(0, 1, 135, 90, self.mSectorColor)
        self.addArc(-1, 0, 225, 90, self.mSectorColor)
        self.addArc(0, -1, 315, 90, self.mSectorColor)

        # 绘制中心圆
        self.centerRoundPath = QPainterPath()
        self.centerRoundPath.addEllipse(QPoint(0, 0), self.m_radius - self.m_arcLength + 2,
                                        self.m_radius - self.m_arcLength + 2)
        self.m_arcPathList.append(self.centerRoundPath)
        self.m_colorList.append(QColor(255, 255, 255))

        # 添加文字
        font = QFont()
        font.setFamily("Microsoft YaHei")
        font.setPointSize(14)

        for i in range(len(self.m_arcPathList)):
            painterPath = QPainterPath()
            self.m_textPathList.append(painterPath)

        self.mStrUp = "前"
        self.mStrLeft = "左"
        self.mStrDown = "后"
        self.mStrRight = "右"

    def paintEvent(self, QPaintEvent):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)

        painter.setPen(Qt.NoPen)
        painter.translate(self.width() >> 1, self.height() >> 1)

        # 背景色,分割线颜色
        painter.setBrush(self.colorBKG)
        painter.drawEllipse(QPoint(0, 0), self.m_radius + 8, self.m_radius + 8)

        linearGradient = QLinearGradient(0, -self.m_radius - 2, 0, self.m_radius + 2)
        linearGradient.setColorAt(0.0, self.colorSectorUp2)
        linearGradient.setColorAt(0.9, self.colorSectorDown)
        painter.setBrush(QBrush(linearGradient))
        painter.drawEllipse(QPoint(0, 0), self.m_radius + 2, self.m_radius + 2)

        linearGradient = QLinearGradient(0, -self.m_radius, 0, self.m_radius)
        linearGradient.setColorAt(0.0, self.colorSectorUp)
        linearGradient.setColorAt(0.9, self.colorSectorDown)
        painter.setBrush(QBrush(linearGradient))
        painter.drawEllipse(QPoint(0, 0), self.m_radius, self.m_radius)

        bgGradient = QLinearGradient(0, -37, 0, 37)
        bgGradient.setColorAt(0.0, self.colorbgGradient0)
        bgGradient.setColorAt(1.0, self.colorbgGradient1)
        painter.setBrush(bgGradient)
        painter.drawEllipse(QPoint(0, 0), self.m_radius - self.m_arcLength + 4,
                            self.m_radius - self.m_arcLength + 4)

        count = 4
        for i in range(count):
            painter.save()
            if (self.mAxesVertical == False):
                painter.rotate(45 + 90 * i)

            else:
                painter.rotate(0 + 90 * i)
            painter.setPen(QPen(self.colorBKG, 3, Qt.SolidLine))
            painter.drawLine(0, self.m_radius - self.m_arcLength + 5, 0, self.m_radius + 5)
            painter.setPen(QPen(self.colorSPL, 3, Qt.SolidLine, Qt.RoundCap))
            painter.drawLine(0, self.m_radius - self.m_arcLength + 6, 0, 40)
            painter.setPen(QPen(self.colorSPL, 3, Qt.SolidLine))
            painter.drawLine(0, 40, 0, self.m_radius - 5)
            painter.restore()

        linearGradient = QLinearGradient(0, self.mCenterRound.y() - self.m_radius + self.m_arcLength - 1, 0,
                                         self.mCenterRound.y() + self.m_radius - self.m_arcLength + 1)
        linearGradient.setColorAt(0.0, self.colorExcircle0)
        linearGradient.setColorAt(0.0, self.colorExcircle5)
        linearGradient.setColorAt(0.9, self.colorExcircle9)

        painter.setBrush(QBrush(linearGradient))
        painter.drawEllipse(self.mCenterRound, self.m_radius - self.m_arcLength, self.m_radius - self.m_arcLength)

        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

        if self.mAxesVertical == False:
            leftmatrix = QTransform()
            if (self.QUADRANT_UP in self.mCurWorkRegion):
                painter.drawPixmap(-30, -60, 60, 30, self.mDegreePixmap_y)
                leftmatrix.reset()
            if (self.QUADRANT_LEFT in self.mCurWorkRegion):
                leftmatrix.rotate(270)
                mDegreePixmap = self.mDegreePixmap_x.transformed(leftmatrix, Qt.SmoothTransformation)
                painter.drawPixmap(-60, -30, 30, 60, mDegreePixmap)
                leftmatrix.reset()
            if (self.QUADRANT_DOWN in self.mCurWorkRegion):
                leftmatrix.rotate(180)
                mDegreePixmap = self.mDegreePixmap_y.transformed(leftmatrix, Qt.SmoothTransformation)
                painter.drawPixmap(-30, 30, 60, 30, mDegreePixmap)
                leftmatrix.reset()
            if (self.QUADRANT_RIGHT in self.mCurWorkRegion):
                leftmatrix.rotate(90)
                mDegreePixmap = self.mDegreePixmap_x.transformed(leftmatrix, Qt.SmoothTransformation)
                painter.drawPixmap(30, -30, 30, 60, mDegreePixmap)
                leftmatrix.reset()

        font = QFont()
        font.setFamily("Microsoft YaHei")
        font.setPointSize(12)
        painter.setFont(font)
        painter.setPen(QColor(170, 170, 170))
        if self.mAxesVertical == True:
            painter.drawText(QPoint(-60, -40), self.mStrUp)
            painter.drawText(QPoint(30, -40), self.mStrLeft)

            painter.drawText(QPoint(-80, 40), self.mStrDown)
            painter.drawText(QPoint(20, 40), self.mStrRight)
        else:

            fm = QFontMetrics(font)
            rText = 65
            nHeight = fm.height() - 4
            iTotalWidth = fm.width(self.mStrUp)
            painter.save()
            painter.rotate(-90 * iTotalWidth / (rText * self.M_PI))
            for i in range(len(self.mStrUp)):
                self.nWidth = fm.width(self.mStrUp[i])
                painter.rotate(90 * self.nWidth / (rText * self.M_PI))
                painter.drawText(-self.nWidth / 2, -1 * rText, self.mStrUp[i])
                painter.rotate(90 * self.nWidth / (rText * self.M_PI))
            painter.restore()
            painter.save()
            if (self.m_bTextModeEn):

                iTotalWidth = fm.width(self.mStrRight)
                painter.rotate(90 - 90 * iTotalWidth / (rText * self.M_PI))
                for i in range(len(self.mStrRight)):
                    nWidth = fm.width(self.mStrRight[i])
                    painter.rotate(90 * nWidth / (rText * self.M_PI))
                    painter.drawText(-nWidth / 2, -1 * rText, self.mStrRight[i])
                    painter.rotate(90 * nWidth / (rText * self.M_PI))
            else:
                iTotalWidth = nHeight * len(self.mStrRight)
                painter.rotate(-90 * iTotalWidth / (rText * self.M_PI))
                for i in range(len(self.mStrRight)):
                    painter.rotate(90 * nHeight / (rText * self.M_PI))
                    painter.drawText(rText - 5, nHeight / 2, self.mStrRight[i])
                    painter.rotate(90 * nHeight / (rText * self.M_PI))
            painter.restore()

            painter.save()
            iTotalWidth = fm.width(self.mStrDown)
            painter.rotate(90 * iTotalWidth / (rText * self.M_PI))
            for i in range(len(self.mStrDown)):
                nWidth = fm.width(self.mStrDown[i])
                painter.rotate(-90 * nWidth / (rText * self.M_PI))
                painter.drawText(-nWidth / 2, rText + nHeight - 8, self.mStrDown[i])
                painter.rotate(-90 * nWidth / (rText * self.M_PI))
            painter.restore()

            painter.save()
            if (self.m_bTextModeEn):
                iTotalWidth = fm.width(self.mStrLeft)
                painter.rotate(90 + 90 * iTotalWidth / (rText * self.M_PI))
                for i in range(len(self.mStrLeft)):
                    nWidth = fm.width(self.mStrLeft[i])
                    painter.rotate(-90 * nWidth / (rText * self.M_PI))
                    painter.drawText(-nWidth / 2, rText + nHeight - 8, self.mStrLeft[i])
                    painter.rotate(-90 * nWidth / (rText * self.M_PI))

            else:
                iTotalWidth = nHeight * len(self.mStrLeft)
                painter.rotate(90 * iTotalWidth / (rText * self.M_PI))
                for i in range(len(self.mStrLeft)):
                    painter.rotate(-90 * nHeight / (rText * self.M_PI))
                    painter.drawText(-rText - 10, nHeight / 2, self.mStrLeft[i])
                    painter.rotate(-90 * nHeight / (rText * self.M_PI))
            painter.restore()

    def addArc(self, x, y, startAngle, angleLength, color):
        rect = QRectF(-self.m_radius + x, -self.m_radius + y, self.m_radius * 2, self.m_radius * 2)
        path = QPainterPath()
        path.arcTo(rect, startAngle, angleLength)
        subPath = QPainterPath()
        subPath.addEllipse(rect.adjusted(self.m_arcLength, self.m_arcLength, -self.m_arcLength, -self.m_arcLength))
        path -= subPath
        self.m_arcPathList.append(path)
        radialGradient = QRadialGradient()
        radialGradient.setCenter(0, 0)
        radialGradient.setRadius(self.m_radius)
        radialGradient.setColorAt(0, color)
        radialGradient.setColorAt(1.0, color)
        self.m_colorList.append(radialGradient)

    def mousePressEvent(self, event):
        mousePressPoint = event.pos()
        translatePoint = mousePressPoint - QPoint(self.width() >> 1, self.height() >> 1)
        for i in range(len(self.m_arcPathList)):
            if (self.m_arcPathList[i].contains(translatePoint) or self.m_textPathList[i].contains(translatePoint)):
                self.m_pressIndex = i
                self.m_isMousePressed = True
                self.update()
                break

    def mouseReleaseEvent(self, event):
        if (self.m_isMousePressed):
            self.m_isMousePressed = False
            self.mCenterRound = QPoint(0, 0)
            self.mDegreePixmap_x = QPixmap(0, 0)
            self.mDegreePixmap_y = QPixmap(0, 0)
            self.update()

    def mouseMoveEvent(self, event):
        if (self.m_isMousePressed):
            point = event.pos() - QPoint(self.width() >> 1, self.height() >> 1)
            x = point.x()
            y = -point.y()
            if y >= 85:
                y = 85
            elif y <= -85:
                y = -85
            if x >= 85:
                x = 85
            elif x <= -85:
                x = -85
            if x * x + y * y > 85 * 85:
                x_y = abs(x) / abs(y)
                _y = 85 / math.sqrt(x_y * x_y + 1.0)
                _x = x_y * _y
                if y >= 0:
                    y = abs(_y)
                else:
                    y = -abs(_y)
                if x >= 0:
                    x = abs(_x)
                else:
                    x = -abs(_x)
            self.mCenterRound = QPoint(x, -y)
            angle = self.analysisAngle(x, y)
            if angle >= 90 and angle <= 180:
                self.mCurWorkRegion = [self.QUADRANT_UP, self.QUADRANT_LEFT]
            elif angle > 0 and angle <= 90:
                self.mCurWorkRegion = [self.QUADRANT_UP, self.QUADRANT_RIGHT]
            elif angle > 180 and angle <= 270:
                self.mCurWorkRegion = [self.QUADRANT_DOWN, self.QUADRANT_LEFT]
            elif angle > 270 and angle < 360:
                self.mCurWorkRegion = [self.QUADRANT_DOWN, self.QUADRANT_RIGHT]
            self.mDegreePixmap_x = self.getPixmap(abs(x))
            self.mDegreePixmap_y = self.getPixmap(abs(y))
            self.update()

    def analysisAngle(self, x, y):
        angle = math.atan2(abs(y), abs(x)) / (2 * math.acos(-1)) * 360
        if (x < 0 and y > 0):
            angle = 180 - angle
        if (x < 0 and y < 0):
            angle += 180
        if (x > 0 and y < 0):
            angle = 360 - angle
        return angle

    def getPixmap(self, ping):
        return self.getSignalPixmap(QColor(0x5, 0x00c7, 0xc7), self.getLineNum(ping))

    def getColor(self, ping):
        if (ping <= 10):
            return QColor(0xea, 0x00, 0x00)
        elif (ping <= 20):
            return QColor(0xff, 0x00, 0x80)
        elif (ping <= 30):
            return QColor(0xe8, 0x00, 0xe8)
        elif (ping <= 40):
            return QColor(0xea, 0xc1, 0x00)
        elif (ping <= 50):
            return QColor(0xe1, 0xe1, 0x00)
        elif (ping <= 60):
            return QColor(0x9a, 0xff, 0x02)
        elif (ping <= 70):
            return QColor(0x00, 0xff, 0xff)
        elif (ping <= 80):
            return QColor(0x28, 0x94, 0xff)
        else:
            return QColor(0x6a, 0x6a, 0xff)

    def getLineNum(self, ping):
        if (ping <= 20):
            return 1
        elif (ping <= 40):
            return 2
        elif (ping <= 60):
            return 3
        elif (ping <= 80):
            return 4
        else:
            return 5

    def getSignalPixmap(self, color, linenum):

        pixmap = QPixmap(60, 30)
        pixmap.fill(QColor(255, 255, 255, 0))

        painter = QPainter(pixmap)
        painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
        painter.setPen(QPen(color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        i = 1
        xpos = 0
        while i <= linenum:
            painter.drawArc(30 - i * 6, 30 - i * 5, i * 12, i * 10, 53 * 16, 37 * 2 * 16)
            i += 1
            xpos += 1
        return pixmap


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    mainWindow = CustomButton()
    mainWindow.show()
    sys.exit(app.exec_())

有用的话请点赞.

原文地址:https://www.cnblogs.com/zyyhxbs/p/12834433.html