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