效果展示
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5952ec68269982cb2bcda53d3111a090.gif#pic_center)
代码展示
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
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)