警告
本节包含自动从C++转换为Python的代码片段,可能包含错误。
动画框架#
动画框架的概述
动画框架提供了一种简单的方法来动画化您的GUI元素。它使您能够动画化小部件或QObject
的Qt属性值。该框架提供的大部分功能在Qt Quick中也可用,在Qt Quick中可以声明式地定义动画。
本概述解释了框架的架构,并使用示例来演示用于动画化QObject
和GUI元素的一些常用技术。
动画架构#
以下图表显示了框架提供的最重要的类
包括QAbstractAnimation
类,该类为动画提供必要的起点。该类定义了框架支持的所有动画的通用属性。例如,开始、停止和暂停动画的能力。该类还接收时间改变通知。
该框架进一步提供了QVariantAnimation
和QAnimationGroup
类,它们建立在它们的基类QAbstractAnimation
之上。在层次结构中下一个是QPropertyAnimation
,它源自QVariantAnimation,并允许您动画化小部件或QObject
的Qt属性。该类使用缓动曲线对属性值进行插值。这样,您只需要一个具有您要动画化的Qt属性值的QObject
类。
可以通过构建树形结构的方式来构建复杂动画,其中树形结构是由包含其他动画的QAnimationGroup
组成。这些动画组还可以包含代表不同组或动画的子组,例如QParallelAnimationGroup
和QSequentialAnimationGroup
。
在幕后,所有动画都由一个全局计时器控制,该计时器发送有关所有运行动画的更新
。
有关这些个别类及其在框架中角色的详细信息,请参阅其文档。
框架提供的类#
这些类为创建简单和复杂动画提供了必要的基础设施。
QAbstractAnimation类是所有动画的基础。
QAnimationGroup类是动画组的抽象基类。
QParallelAnimationGroup类提供了并行动画组。
QPauseAnimation类为QSequentialAnimationGroup提供了暂停功能。
QPropertyAnimation类用于动画Qt属性。
QSequentialAnimationGroup类提供了序列动画组。
QVariantAnimation类为动画提供基类。
QEasingCurve类为动画控制提供缓动曲线。
QTimeLine类为控制动画提供时间线。
动画Qt属性#
由于QPropertyAnimation
类可以在Qt属性上插值,因此它经常被使用。实际上,它的超类QVariantAnimation
提供了一个updateCurrentValue()
的抽象实现,除非你在valueChanged
信号处更改它,否则不会更改任何值。
此框架允许您对Qt中现有类的Qt属性进行动画处理。例如,QWidget类可以嵌入到QGraphicsView中,具有边界、颜色等属性。以下示例演示了如何对QPushButton小部件进行动画处理。
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QPushButton from PySide6.QtCore import QPropertyAnimation class MyButtonWidget(QWidget): # public MyButtonWidget(QWidget parent = None) def __init__(self, QWidget(parent): button = QPushButton(tr("Animated Button"), self) anim = QPropertyAnimation(button, "pos", self) anim.setDuration(10000) anim.setStartValue(QPoint(0, 0)) anim.setEndValue(QPoint(100, 250)) anim.start() if __name__ == "__main__": a = QApplication(argc, argv) buttonAnimWidget = MyButtonWidget() buttonAnimWidget.resize(QSize(800, 600)) buttonAnimWidget.show() return a.exec()
该示例对QPushButton的Qt属性pos进行动画处理,将其从屏幕的左上角移动到终点(250, 250),耗时10秒(10000毫秒)。
它使用线性插值法来控制动画在起始值和结束值之间的速度。尝试在起始值和结束值之间添加另一个值,以查看它们如何进行插值。这次使用QPropertyAnimation::setKeyValueAt函数添加这些值。
... anim->setDuration(10000); anim->setKeyValueAt(0, QPoint(0, 0)); anim->setKeyValueAt(0.8, QPoint(250, 250)); anim->setKeyValueAt(1, QPoint(0, 0)); ...
在这个例子中,动画在8秒内将按钮移动到(250, 250),然后在剩下的2秒内将其移回到原始位置。按钮的运动在这两点之间进行线性插值。
如果某个值未声明为Qt属性但有setter方法,您还可以对其动画处理。在这种情况下,您应从包含该值的类派生一个新类,并为该值添加一个带有setter的Qt属性。
注意
每个Qt属性也都需要一个getter,因此如果您没有定义它,您应该提供一个getter。
class MyGraphicsRectItem : public QObject, public QGraphicsRectItem
{
Q_OBJECT
Q_PROPERTY(QPointF pos READ pos WRITE setPos)
};
在这个例子中,MyGraphicsRectItem从QGraphicsRectItem和QObject派生出来,并定义了pos属性。即使QGraphicsRectItem没有提供pos属性,您也可以对该项的pos进行动画处理。
有关Qt属性系统的详细介绍,请参见Qt的属性系统 。
动画和图形视图框架Link to this heading
QPropertyAnimation
还可以用于对不是从QObject继承的自定义QGraphicsItem进行动画处理。在这种情况下,您应从您希望进行动画处理的图形项派生一个新类。该派生类还应继承QObject,以便可以在QGraphicsItem上使用QPropertyAnimation。以下示例展示了如何执行此操作。
class Pixmap : public QObject, public QGraphicsPixmapItem { Q_OBJECT Q_PROPERTY(QPointF pos READ pos WRITE setPos) ... }
注意
您还可以从QObject已经是从QObject派生的QGraphicsWidget进行派生。
如前所述,您需要定义要动画处理的属性。派生类必须首先继承QObject,因为元对象系统需要这样。
缓动曲线Link to this heading
QPropertyAnimation
进行起点和终点属性值之间的线性插值。除了向动画中添加更多关键值,你还可以选择一个缓动曲线来控制0和1之间插值的速度,而无需改变路径。
def __init__(self, QWidget(parent): button = QPushButton(tr("Animated Button"), self) anim = QPropertyAnimation(button, "pos", self) anim.setDuration(10000) anim.setStartValue(QPoint(0, 0)) anim.setEndValue(QPoint(100, 250)) anim.setEasingCurve(QEasingCurve.OutBounce) anim.start()
在这个例子中,动画遵循的曲线让按钮像球一样弹跳。 QEasingCurve
提供了许多曲线以供选择,这些曲线来自 Type
枚举。如果您想使用不可用的其他曲线,可以自己实现它并使用 QEasingCurve
进行注册。
组合动画
应用程序通常包含多个动画。例如,它希望同时移动多个图形项,或者在每个动画项之后按顺序移动。
QAnimationGroup
的子类 —— QSequentialAnimationGroup
和 QParallelAnimationGroup
— 是其他动画的容器,这样可以使得这些动画既可以顺序也可以并行地动画化。 QAnimationGroup
不直接对属性进行动画化,但它会定期收到时间变化的通知。这使得它可以将这些时间变化转移到动画组,并控制其动画何时播放。
以下两个示例分别演示了使用 QSequentialAnimationGroup
和 QParallelAnimationGroup
的用法。
def __init__(self, QWidget(parent): bonnie = QPushButton(tr("Bonnie"), self) clyde = QPushButton(tr("Clyde"), self) anim1 = QPropertyAnimation(bonnie, "pos", self) anim1.setDuration(3000) anim1.setStartValue(QPoint(0, 0)) anim1.setEndValue(QPoint(100, 250)) anim2 = QPropertyAnimation(clyde, "pos", self) anim2.setDuration(3000) anim2.setStartValue(QPoint(100, 250)) anim2.setEndValue(QPoint(500, 500)) parallelAnim = QParallelAnimationGroup() parallelAnim.addAnimation(anim1) parallelAnim.addAnimation(anim2) parallelAnim.start()
并行组同时播放多个动画。其 start()
函数开始播放组中所有的动画。
def __init__(self, QWidget(parent): bonnie = QPushButton(tr("Bonnie"), self) clyde = QPushButton(tr("Clyde"), self) anim1 = QPropertyAnimation(bonnie, "pos", self) anim1.setDuration(3000) anim1.setStartValue(QPoint(0, 0)) anim1.setEndValue(QPoint(100, 250)) anim2 = QPropertyAnimation(clyde, "pos", self) anim2.setDuration(3000) anim2.setStartValue(QPoint(0, 0)) anim2.setEndValue(QPoint(200, 250)) sequenceAnim = QSequentialAnimationGroup() sequenceAnim.addAnimation(anim1) sequenceAnim.addAnimation(anim2) sequenceAnim.start()
如名称所示,QSequentialAnimationGroup
顺序播放其动画。在之前的动画完成后,它将启动列表中的下一个动画。
组本身就是一个动画,因此您可以将它添加到另一个组中。这样就可以构建一个动画树,用于定义动画相对于彼此的播放时间。
对象所有权
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QPushButton from PySide6.QtCore import QPropertyAnimation class MyButtonWidget(QWidget): # public MyButtonWidget(QWidget parent = None) def __init__(self, QWidget(parent): button = QPushButton(tr("Animated Button"), self) anim = QPropertyAnimation(button, "pos", self) anim.setDuration(10000) anim.setStartValue(QPoint(0, 0)) anim.setEndValue(QPoint(100, 250)) anim.start() if __name__ == "__main__": a = QApplication(argc, argv) buttonAnimWidget = MyButtonWidget() buttonAnimWidget.resize(QSize(800, 600)) buttonAnimWidget.show() return a.exec()
注意
您还可以在启动动画时选择 delete policy
来控制动画的生命周期。