目标:完全自定义一个开关按钮
涉及知识点:QPainter绘制、paintEvent事件、QPoint是否坐落在区域内、pyside的Property类、QPropertyAnimation动画。
代码
import PySide6
from PySide6.QtCore import QRect, QSize, QTimer, QPoint, Qt, QPropertyAnimation, Property, PyClassProperty, \
QMetaProperty, Signal, QEasingCurve
from PySide6.QtGui import QPixmap, QCursor, QIcon, QMouseEvent, QResizeEvent, QColor, QPainter, QPainterPath, QBrush, \
QGradient, QImage
from PySide6.QtWidgets import QApplication, QDialog, QPushButton, QHBoxLayout, QLabel, QSizePolicy, QWidget, \
QVBoxLayout, QSpacerItem, QGraphicsDropShadowEffect
# 自定义开关按钮
class SwitchButton(QWidget):
# 转换信号
change_signal = Signal(bool)
def set_status(self):
self.status_init = not self.status_init
# 立即重绘
self.repaint()
# 下次处理事件时重绘(推荐)
self.update()
# 动画(必须声明为QWidget中的某个属性)
self.animation_position = QPropertyAnimation(self, b'position')
self.animation_color = QPropertyAnimation(self, b'color')
# 持续时间
self.animation_position.setDuration(self.slip_time)
self.animation_color.setDuration(self.slip_time)
# 速度曲线(弹性效果)
# self.animation_position.setEasingCurve(QEasingCurve.InOutElastic)
# 重复次数
self.animation_position.setLoopCount(1)
self.animation_color.setLoopCount(1)
if self.status_init:
# 开始时属性值(此时的位置)
self.animation_position.setStartValue(self.position_init)
# 结束时属性值
self.animation_position.setEndValue(self.width() - self.height() / 2)
# 开始时属性值(此时的颜色)
self.animation_color.setStartValue(self.color_init)
# 结束时属性值
self.animation_color.setEndValue(self.open_color)
else:
# 开始时属性值(此时的位置)
self.animation_position.setStartValue(self.position_init)
# 结束时属性值
self.animation_position.setEndValue(self.height() / 2)
# 开始时属性值(此时的颜色)
self.animation_color.setStartValue(self.color_init)
# 结束时属性值
self.animation_color.setEndValue(self.close_color)
# 开始
self.animation_position.start()
self.animation_color.start()
self.change_signal.emit(self.status_init)
def get_status(self):
return self.status_init
def set_color(self, color: QColor):
self.color_init = color
def get_color(self):
return self.color_init
def set_position(self, position: int):
self.position_init = position
self.update()
def get_position(self):
return self.position_init
# 开关状态
status = Property(type=int, fset=set_status, fget=get_status, notify=change_signal)
# 组件颜色
color = Property(type=QColor, fset=set_color, fget=get_color)
# 开关位置
position = Property(type=int, fset=set_position, fget=get_position)
def __init__(self, parent=None,
open_color: QColor = QColor(64, 158, 255, 255),
close_color: QColor = QColor(220, 223, 230, 255),
btn_color: QColor = QColor(255, 255, 255, 255),
slip_time: int = 200):
"""
:param parent:
:param open_color: 打开时的颜色
:param close_color: 关闭时的颜色
:param btn_color: 按钮颜色
:param slip_time: 滑动时间(ms)
"""
super(SwitchButton, self).__init__(parent)
self.resize(200, 100)
# 记录鼠标按下时的位置
self.press_pos = QPoint(0, 0)
# 当前状态
self.status_init: bool = False
# 当前颜色
self.color_init: QColor = close_color
# 当前位置
self.position_init: int = int(self.height() / 2)
# 打开时的颜色
self.open_color = open_color
# 关闭时的颜色
self.close_color = close_color
# 圆形按钮的颜色
self.btn_color = btn_color
self.slip_time = slip_time
def mousePressEvent(self, event: PySide6.QtGui.QMouseEvent) -> None:
# 记录按下的坐标
self.press_pos = event.scenePosition().toPoint()
super().mousePressEvent(event)
def mouseReleaseEvent(self, event: PySide6.QtGui.QMouseEvent) -> None:
# 释放时的坐标
release_point = event.scenePosition().toPoint()
# 圆心坐标
if self.parent() is None:
point = QPoint(self.position_init, 50)
else:
point = QPoint(self.pos().x() + self.position_init, self.pos().y() + 50)
# 是否在圆内
inner_circle = (release_point.x() - point.x()) ** 2 + (release_point.y() - point.y()) ** 2 < 45 ** 2
# 鼠标按下和释放在同一位置,且点击位置在圆内
if release_point == self.press_pos and inner_circle:
self.set_status()
super().mousePressEvent(event)
# 绘图事件
def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
painter = QPainter(self)
# 反锯齿
painter.setRenderHint(QPainter.Antialiasing)
painter.begin(self)
# 绘制一个圆角矩形
painter.setBrush(self.color_init)
path = QPainterPath()
path.addRoundedRect(QRect(0, 0, self.width(), self.height()), self.height() / 2, self.height() / 2,
Qt.AbsoluteSize)
painter.drawPath(path)
# 绘制白色圆形
painter.setBrush(self.btn_color)
painter.drawEllipse(QPoint(self.position_init, 50), 45, 45)
painter.end()
super().paintEvent(event)
使用
class ExampleWidget(QWidget):
def __init__(self, parent=None):
super(ExampleWidget, self).__init__(parent)
switch_button = SwitchButton(self, open_color=QColor(19, 206, 102), close_color=QColor(255, 73, 73, 255), )
switch_button.change_signal.connect(lambda x: print('打开') if x else print("关闭"))
if __name__ == '__main__':
app = QApplication([])
main = ExampleWidget()
main.show()
app.exec()