Qt第四十二章:自定义按钮

目标:完全自定义一个开关按钮


涉及知识点: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()

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值