PyQt5 自定义开关按钮(2)

效果展示

在这里插入图片描述

代码展示

from PyQt5.QtCore import Qt, pyqtSignal, QTimer, QRectF, QRect
from PyQt5.QtGui import QFont, QColor, QPainter, QPainterPath
from PyQt5.QtWidgets import QWidget


class SwitchBtn(QWidget):
    clicked = pyqtSignal(bool)

    # 组件常量
    BORDER_WIDTH = 4
    COLOR_BORDER = (209, 207, 198)
    COLOR_SLIDER = (255, 255, 255)
    COLOR_TEXT = (255, 255, 255)
    COLOR_CLOSE = (112, 112, 112)
    COLOR_OPEN = (0, 77, 191)
    COLOR_ALPHA = 150  # 透明度(禁用时)
    SPEED = 0.008  # 每次移动整体宽度的 0.8%(单位时间根据定时器,默认1ms)

    TEXT_ON = 'ON'
    TEXT_OFF = 'OFF'
    FONT_FAMILY = '微软雅黑'
    FONT_SIZE = 9

    def __init__(self, parent=None):
        super().__init__(parent)
        # 开关状态,默认关闭
        self.__checked = False
        self.__enabled = True
        # 移动步值
        self.__step = self.width() * self.SPEED
        # 起始点
        self.__start = 0
        self.__end = 0
        # 颜色
        self.__color_border = QColor(*self.COLOR_BORDER)
        self.__color_slider = QColor(*self.COLOR_SLIDER)
        self.__color_back = QColor(*self.COLOR_CLOSE)
        self.__color_text = QColor(*self.COLOR_TEXT)
        # 滑块
        self.__timer_slider = QTimer()
        self.__timer_slider.setInterval(1)
        self.__timer_slider.timeout.connect(self.__to_slider)
        # 字体
        self.__font = QFont()
        self.__font.setFamily(self.FONT_FAMILY)
        self.__font.setPointSize(self.FONT_SIZE)
        self.__font.setBold(True)

    def __to_slider(self):
        """滑块"""
        if self.__checked:
            if self.__start < self.__end:
                self.__start = self.__start + self.__step
            else:
                self.__start = self.__end
                self.__timer_slider.stop()
        else:
            if self.__start > self.__end:
                self.__start = self.__start - self.__step
            else:
                self.__start = self.__end
                self.__timer_slider.stop()
        self.update()

    def isChecked(self):
        return self.__checked

    def setChecked(self, state: bool):
        if self.__checked == state:
            return
        self.__checked = state
        if self.__checked:
            self.__start = self.__end = self.width() - self.height()
        else:
            self.__start = self.__end = 0

    def mousePressEvent(self, event):
        """点击时"""
        self.__checked = not self.__checked
        self.clicked.emit(self.__checked)
        # 滑块操作
        if self.__checked:
            self.__end = self.width() - self.height()
        else:
            self.__end = 0
        self.__timer_slider.start()

    def resizeEvent(self, event):
        """形状变动时"""
        self.__step = self.width() * self.SPEED
        if self.__checked:
            self.__start = self.__end = self.width() - self.height()
        else:
            self.__start = self.__end = 0

    def paintEvent(self, event):
        """绘制"""
        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)  # 反锯齿
        if self.isChecked():
            color_back = self.COLOR_OPEN
        else:
            color_back = self.COLOR_CLOSE
        if self.isEnabled():
            self.__color_border = QColor(*self.COLOR_BORDER)
            self.__color_back = QColor(*color_back)
            self.__color_text = QColor(*self.COLOR_TEXT)
        else:
            self.__color_border = QColor(*self.COLOR_BORDER, self.COLOR_ALPHA)
            self.__color_back = QColor(*color_back, self.COLOR_ALPHA)
            self.__color_text = QColor(*self.COLOR_TEXT, self.COLOR_ALPHA)
        self.__color_slider = QColor(*self.COLOR_SLIDER)  # 滑块颜色固定
        self.paint_border(painter)
        self.paint_back(painter)
        self.paint_slider(painter)
        self.paint_text(painter)
        painter.end()

    def paint_border(self, painter):
        """绘制边框"""
        painter.save()
        painter.setPen(self.__color_border)
        painter.setBrush(self.__color_border)
        rect = QRect(0, 0, self.width(), self.height())
        # 半径为高度的一半
        radius = rect.height() / 2
        # 圆的宽度为高度
        circle_width = rect.height()
        path = QPainterPath()
        path.moveTo(radius, rect.top())
        path.arcTo(QRectF(rect.left(), rect.top(), circle_width, circle_width), 90, 180)
        path.lineTo(rect.width() - radius, rect.height())
        path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circle_width, circle_width), 270, 180)
        path.lineTo(radius, rect.top())
        painter.drawPath(path)
        painter.restore()

    def paint_back(self, painter):
        """绘制背景"""
        painter.save()
        painter.setPen(self.__color_border)
        painter.setBrush(self.__color_back)
        rect = QRect(self.BORDER_WIDTH, self.BORDER_WIDTH, self.width() - 2 * self.BORDER_WIDTH,
                     self.height() - 2 * self.BORDER_WIDTH)
        # 半径为高度的一半
        radius = rect.height() / 2
        # 圆的宽度为高度
        circle_width = rect.height()
        path = QPainterPath()
        path.moveTo(radius + self.BORDER_WIDTH, rect.top())
        path.arcTo(QRectF(rect.left(), rect.top(), circle_width, circle_width), 90, 180)
        path.lineTo(rect.left() + rect.width() - radius, rect.top() + rect.height())
        path.arcTo(QRectF(rect.left() + rect.width() - circle_width, rect.top(), circle_width, circle_width), 270, 180)
        path.lineTo(radius + self.BORDER_WIDTH, rect.top())
        painter.drawPath(path)
        painter.restore()

    def paint_slider(self, painter):
        """绘制滑块"""
        painter.save()
        painter.setPen(self.__color_slider)
        painter.setBrush(self.__color_slider)
        slider_width = self.height() - self.BORDER_WIDTH * 2 + 2
        slider_rect = QRect(self.__start + self.BORDER_WIDTH - 1, self.BORDER_WIDTH - 1, slider_width, slider_width)
        painter.drawEllipse(slider_rect)
        painter.restore()

    def paint_text(self, painter):
        """绘制文字"""
        painter.save()
        painter.setPen(self.__color_text)
        if self.isChecked():
            rect = QRect(0, 0, self.width() / 2 + self.BORDER_WIDTH, self.height())
            text = self.TEXT_ON
        else:
            rect = QRect(self.width() / 2, 0, self.width() / 2 - self.BORDER_WIDTH, self.height())
            text = self.TEXT_OFF
        painter.setFont(self.__font)
        painter.drawText(rect, Qt.AlignCenter, text)
        painter.restore()

    def set_font_size(self, font_size: float = None):
        if not font_size:
            font_size = self.FONT_SIZE
        self.__font.setPointSize(font_size)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值