PySide2:利用checkBox+QPropertyAnimation实现开关UI

使用QPropertyAnimation在PyQt中实现动态属性动画,
本文介绍了如何在PyQt中利用QPropertyAnimation对QObject的属性进行动画处理,包括动画的基础概念、设置方法以及一个实例,如制作一个可切换的圆形按钮动画。

Qt QPropertyAnimation

官方说明

QPropertyAnimation interpolates over Qt properties. As property values are stored in QVariants, the class inherits QVariantAnimation, and supports animation of the same meta types as its super class.
A class declaring properties must be a QObject. To make it possible to animate a property, it must provide a setter (so that QPropertyAnimation can set the property’s value). Note that this makes it possible to animate many of Qt’s widgets.
原文链接

QPropertyAnimation 对 Qt 属性进行插值。由于属性值存储在 QVariants(PySide 中不支持 QVariant 类型)中,因此该类继承了 QVariantAnimation,并支持与其超类相同的元类型动画。
声明属性的类必须是 QObject。为使属性动画成为可能,它必须提供一个设置器(以便 QPropertyAnimation 可以设置属性的值)。这使得许多 Qt 小工具的动画成为可能。
简单来说,利用 QPropertyAnimation 来制作一些小动画(类似前端),比如一些单点开关(在 Qt 中并没有对应支持,qtWidget 第三方库支持,本文章利用 QPropertyAnimation 动画实现)。
关闭状态
开启状态

参数

QPropertyAnimation(self, target: PySide2.QtCore.QObject, propertyName: PySide2.QtCore.QByteArray, parent: typing.Union[PySide2.QtCore.QObject, NoneType] = None) -> None

target:需要实现的动画对象
property:给该动画设置的属性名称(可利用该方法来设置动画位置)
parent:父类对象

其他关于 QPropertyAnimation 的相关参数可在官方文档中查阅

实例

这里实现的动画效果只是普通的直线效果,需要其他效果可以更改animation_curve属性

import PySide2
from PySide2.QtCore import Qt, QRect, QPropertyAnimation, QEasingCurve, Property
from PySide2.QtGui import QPainter, QColor
from PySide2.QtWidgets import  QCheckBox


class RToggleSwitch(QCheckBox):
    def __init__(
            self,
            width: int = 70,
            button_height: int = 28,
            bg_color: str = "#777",
            circle_color: str = "#000",
            active_color: str = "00BCff",

            # Change animations here: 按钮动画
            animation_curve=QEasingCurve.Custom
            # Change the Bouncing of Round Ball
    ):
        QCheckBox.__init__(self)

        # Set Detail Paramenter: 设置详细数据
        self.setFixedSize(width, button_height)
        self.setCursor(Qt.PointingHandCursor)

        # Color
        self._bg_color = bg_color  # Background Color: 背景颜色
        self._circle_color = circle_color  # Circle Color: 圆圈颜色
        self._active_color = active_color  # Active Color: 选中颜色

        # Create Animation: 创建动画事件
        self._circle_to_border = 4  # Circle Border: 圆圈默认距边框位置
        self._circle_position = 4  # Circle position: 圆圈目前所处位置,由 Qt.Property 控制
        self.animation = QPropertyAnimation(self, b"circle_position", self)
        self.animation.setEasingCurve(animation_curve)

        # Duration of Button Transition: 按钮转换的持续时间
        self.animation.setDuration(300)

        # Connect State Changed
        self.stateChanged.connect(self.start_transition)

    @Property(float)
    def circle_position(self):
        return self._circle_position

    @circle_position.setter
    def circle_position(self, pos_x: int):
        self._circle_position = pos_x
        self.update()

    @property
    def _circle_diameter(self):
        return self.height() - (self._circle_to_border * 2)

    def start_transition(self, state: bool):
        """
        利用按钮状态改变来定义动画滑动
        Args:
            state: bool

        Returns:

        """
        # Stop animation
        self.animation.stop()
        if state:
            # Circle animation left to right
            _circle_position = self.height() - self._circle_to_border
            self.animation.setEndValue(self.width() - _circle_position)
        else:
            # Circle animation right to left
            self.animation.setEndValue(self._circle_to_border)

        # Start animation
        self.animation.start()

    def hitButton(self, pos: PySide2.QtCore.QPoint) -> bool:
        """
        设置 Nes 的命中区域
        Args:
            pos:

        Returns:

        """
        return self.contentsRect().contains(pos)

    def paintEvent(self, event: PySide2.QtGui.QPaintEvent) -> None:
        rect = QRect(0, 0, self.width(), self.height())
        painter = QPainter(self)
        # setting anti-aliasing
        painter.setRenderHint(QPainter.Antialiasing)
        # Unchecked
        if not self.isChecked():

            # SET Background for night mode here
            painter.setBrush((QColor("#D9D9D9")))
            painter.setPen(Qt.NoPen)
            painter.drawRoundedRect(0, 0,
                                    rect.width(),
                                    self.height(),
                                    self.height() / 2,
                                    self.height() / 2)
            # Set Circle here
            painter.setBrush(QColor("#3B3B3B"))
            painter.drawEllipse(self._circle_position,
                                self._circle_to_border,
                                self._circle_diameter,
                                self._circle_diameter)

        else:

            # SET Background for Day mode here
            painter.setBrush(QColor("#188DB1"))
            painter.setPen(Qt.NoPen)
            painter.drawRoundedRect(0, 0,
                                    rect.width(),
                                    self.height(),
                                    self.height() / 2,
                                    self.height() / 2)
            # Set Circle here
            painter.setBrush(QColor("#FFFFFF"))
            painter.drawEllipse(self._circle_position,
                                self._circle_to_border,
                                self._circle_diameter,
                                self._circle_diameter)

:此处的函数装饰器一定要用 Qt 的 Property ,否则无法实现动画效果。
代码测试


import sys
from PySide2.QtWidgets import QApplication, QWidget, QHBoxLayout
class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setLayout(QHBoxLayout())
        switch = RToggleSwitch()
        self.layout().addWidget(switch)
        switch.stateChanged.connect(self.onStateChanged)

    def onStateChanged(self, state):
        if state:
            print("功能已启用")
        else:
            print("功能已关闭")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    ex.setGeometry(300, 300, 250, 150)
    ex.setWindowTitle('Toggle Switch')
    ex.show()
    sys.exit(app.exec_())

原文链接:https://github.com/Shumail-Abbasi/Pyqt-Animated-Toggle-Button/blob/main/py_toggle.py

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ctr+Alt+Del

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

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

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

打赏作者

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

抵扣说明:

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

余额充值