QT—动画框架

x33g5p2x  于2022-07-26 转载在 其他  
字(7.7k)|赞(0)|评价(0)|浏览(226)

概念、数值类型

动画框架的目的是提供一种简单的方法来创建平滑的、具有动画效果的GUI界面。

该框架是通过控制Qt的属性来实现动画的,它可以应用在窗口部件和其他QObject对象上,也可以应用在图形视图框架中。

动画框架在Qt 4.6中被引入。该部分内容可以在帮助中通过TheAnimationFramework关键字查看。

动画框架中主要的类及其关系如图11-6所示。

基类QAbstractAnimation和它的两个子类QVariantAnimation以及QAnimationGroup构成了动画框架的基础。

这里的QAbstractAnimation是所有动画类的祖先,它定义了一些所有动画类都共享的功能函数,比如动画的开始、停止和暂停等;它也可以接收时间变化的通知,通过继承这个类可以创建自定义的动画类。

动画框架中提供了QPropertyAnimation 类,继承自QVariantAnimation, 用来执行Qt属性的动画
这个类使用缓和曲线(easing curve)来对属性进行插值。

如果要对一个值使用动画,则可以创建继承自QObject 的类,然后在类中将该值定义为一个属性。
属性动画为现有的窗口部件以及其他QObject子类提供了非常灵活的动画控制。

Qt现在支持的可以进行插值的QVariant类型有:Int、Uint、Double、Float、QLine、QLineF .QPoint, QPointF 、QSize、QSizeF、QRect、QRectF和QColor等

如果要实现复杂的动画,则可以通过动画组QAnimationGroup类实现,它的功能是作为其他动画类的容器,一个动画组中还可以包含另外的动画组

-动画框架也被设计作为状态机框架的一部分,将两者结合使用可以实现更为强大的功能。

实现属性动画

前面已经讲到QPropertyAnimation类可以对Qt属性进行插值,如果一个值要实现动画效果,则就要使用这个类,而它的父类QVariantAnimation是一个抽象类,无法直接使用。

之所以要使用Qt属性来进行动画的最主要原因是这样可以为已经存在的Qt API中的类提供灵活的动画设置

可以在QWidget类的帮助文档中查看它所有的属性,当然,并不是所有的属性都可以设置动画,必须是前面讲到的Qt支持的QVariant类型

下面来看一个例子:

新建空的Qt项目Empty qmake Project,名称为myanimation,完成后先在项目文件中添加“QT十= widgets"- -行代码并保存该文件,然后添加新文件main.cpp,并在其中添加如下代码:

#include <QApplication>
#include <QPushButton>
#include <QPropertyAnimation>

int main(int argc, char* argv[ ])
{
    QApplication app(argc, argv);
    QPushButton button("Animated Button");
    button.show();
    QPropertyAnimation animation(&button, "geometry");
    animation.setDuration(10000);
    animation.setStartValue(QRect(0, 0, 120, 30));   
    animation.setEndValue(QRect(250, 250, 200, 60));

    animation.start();
    
    return app.exec();
}

这里创建了一个按钮部件button并让其显示,然后为按钮部件的geometry属性创建了动画,并使用setDuration()函数指定了动画的持续时间为10000毫秒(即10秒)

然后使用函数setStartValue( )setEndValue()分别设置了动画开始和结束时geometry 属性的值

最后调用start()函数开始动画

这样就实现了按钮部件在10秒内从屏幕的(0, 0)点移动到(250,250)点,与此同时由宽120、高30的大小变为宽200、高60的大小的动画。

除了设置属性开始和结東的值以外,还可以调用setKeyValueAt(qreal step, const QVariant & value) 函数在动画中间为属性设置值

其中:

  • step 取值在0.0~1.0之间,0.0表示开始位置,1.0表示结束位置
  • value为属性的值。

将程序中调用setStartValue( )和setEndValue( )两个函数的代码更改为:

#include <QApplication>
#include <QPushButton>
#include <QPropertyAnimation>

int main(int argc, char* argv[ ])
{
    QApplication app(argc, argv);
    QPushButton button("Animated Button");
    button.show();
    QPropertyAnimation animation(&button, "geometry");
    animation.setDuration(10000);
//    animation.setStartValue(QRect(0, 0, 120, 30));
//    animation.setEndValue(QRect(250, 250, 200, 60));

    animation.setKeyValueAt(0, QRect(0, 0, 120, 30));
    animation.setKeyValueAt(0.8, QRect(250, 250, 200, 60));
    animation.setKeyValueAt(1, QRect(0, 0, 120, 30));

    animation.start();
    return app.exec();
}
总共10秒,0.8等于80%,等于8秒

这样就实现了在8秒内按钮部件由(0,0)点移动到(250,250)点,并变化大小,然后在后2秒内又回到原点并且恢复原来大小的动画。

现在可以运行程序查看效果:

使用缓和曲线(关键帧动画)

在前面程序的运行效果中可以看到,按钮部件的运动过程都是线性的,即匀速运动。除了在动画中添加更多的关键点,还可以使用缓和曲线,缓和曲线描述了怎样来控制0和1之间的插值速度的功能,这样就可以在不改变插值的情况下来控制动画的速度。

将前面程序中间部分设置值的代码更改如下:

animation.setDuration(2000);
    animation.setStartValue(QRect(250, 0, 120, 30));
    animation.setEndValue(QRect(250, 300, 120, 30));
    animation.setEasingCurve(QEasingCurve::OutBounce);

这里使用了QEasingCurve::OutBounce缓和曲线,此时运行程序会发现

它会使按钮部件就像从开始位置掉落到结束位置的皮球一样出现弹跳效果

QEasingCurve类中提供了四十多种缓和曲线,而且还可以自定义缓和曲线
详细可以查看一下QEasingCurve类的帮助文档。Qt中还提供了一个Easing Curves Example 的示例程序,可以演示所有缓和曲线的效果。

动画组

在一个应用中经常会包含多个动画,例如,要同时移动多个图形项或者让它们一个接一个地串行移动。

使用QAnimationGroup类可以实现复杂的动画,它的两个子类QSequentialAnimationGroupQParallelAnimationGroup分别提供了串行动画组并行动画组

下面先来看一个串行动画组的例子。

在前面程序的main. cpp文件中添加头文件# include < QSequentialAnimationGroup>,再将主函数的中间部分内容更改如下:

#include <QApplication>
#include <QPushButton>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>

int main(int argc, char* argv[ ])
{
    QApplication app(argc, argv);

    QPushButton button("Animated Button");
    button.show();
    // 按钮部件的动画1
    QPropertyAnimation *animation1 = new QPropertyAnimation(&button, "geometry");
    animation1->setDuration(2000);
    animation1->setStartValue(QRect(250, 0, 120, 30));
    animation1->setEndValue(QRect(250, 300, 120, 30));
    animation1->setEasingCurve(QEasingCurve::OutBounce);
    // 按钮部件的动画2
    QPropertyAnimation *animation2 = new QPropertyAnimation(&button, "geometry");
    animation2->setDuration(1000);
    animation2->setStartValue(QRect(250, 300, 120, 30));
    animation2->setEndValue(QRect(250, 300, 200, 60));
    // 串行动画组
    QSequentialAnimationGroup group;
    group.addAnimation(animation1);
    group.addAnimation(animation2);
    group.start();

    return app.exec();
}

这是串行动画组,此时运行程序就会先执行动画1,执行完成之后才执行动画2,动画的执行顺序与加入动画组的顺序是一致的。

动画1是2s内曲线运动从250,0到250,300,等于下落,但是是曲线(曲速)
动画2是1s内变大小,从120,30高宽变为200,60 等于高宽都增大了,1s内变大

下面再看一个并行动画组的例子:

先添加头文件# include < QParallelAnimationGroup> ,再将主函数的中间部分内容更改如下:

#include <QApplication>
#include <QPushButton>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>

int main(int argc, char* argv[ ])
{
    QApplication app(argc, argv);

    QPushButton button1("Animated Button");
    button1.show();
    QPushButton button2("Animated Button2");
    button2.show();
    // 按钮部件1的动画
    QPropertyAnimation *animation1 = new QPropertyAnimation(&button1, "geometry");
    animation1->setDuration(2000);
    animation1->setStartValue(QRect(250, 0, 120, 30));
    animation1->setEndValue(QRect(250, 300, 120, 30));
    animation1->setEasingCurve(QEasingCurve::OutBounce);
    // 按钮部件2的动画
    QPropertyAnimation *animation2 = new QPropertyAnimation(&button2, "geometry");
    animation2->setDuration(2000);
    animation2->setStartValue(QRect(400, 300, 120, 30));
    animation2->setEndValue(QRect(400, 300, 200, 60));
    // 并行动画组
    QParallelAnimationGroup group;
    group.addAnimation(animation1);
    group.addAnimation(animation2);
    group.start();

    return app.exec();
}

现在运行程序可以看到,两个按钮部件的动画是同时进行的。

另外,使用动画组还有一个好处,就是可以将它看作一个独立的动画,从而进行暂停、停止或者添加到其他动画组等操作。

图形视图框架中使用动画

要对QGraphicsItem使用动画,也可以使用QPropertyAnimation类。

但是, QGraphicsItem并不是继承自QObject类,所以直接继承自QGraphicsItem的图形项并不能直接使用QPropertyAnimation类来创建动画。

Qt 4.6中提供了一个QGraphicsItem的子类QGraphicsObject,它继承自QObject和QGraphicsItem,这个类为所有需要使用信号﹑槽以及属性的图形项提供了一个基类,通过创建这个QGraphicsObject类的子类就可以使用属性动画了。

QGraphicsObject还提供了多个常用的属性,比如位置pos ,透明度opacity ,旋转rotation和缩放scale等,这些都可以直接用来设置动画。

下面看个例子:

新建空的Qt项目,名称设置为myitemanimation。完成后先在项目文件中添加“QT+= widgets”一行代码并保存该文件,然后添加新的C++类,类名MyItem,基类设置为QGraphicsObject。完成后更改 myitem.h文件内容如下:

#ifndef MYITEM_H
#define MYITEM_H

#include <QGraphicsObject>

class MyItem : public QGraphicsObject
{
public:
    MyItem(QGraphicsItem *parent = 0);
    QRectF boundingRect() const;
    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option, QWidget *widget);
};

#endif // MYITEM_H

创建了一个MyItem 是QGraphicsItem 的子类,定义了二个函数

然后到myitem.cpp文件中,更改内容如下:

#include "myitem.h"
#include <QPainter>

MyItem::MyItem(QGraphicsItem *parent) :
    QGraphicsObject(parent)
{
}
QRectF MyItem::boundingRect() const
{
    return QRectF(-10 - 0.5, -10 - 0.5, 20 + 1, 20 + 1);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
                   QWidget *)
{
    painter->drawRect(-10, -10, 20, 20);
}

最后到main.cpp文件中,更改内容如下:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include "myitem.h"
#include <QPropertyAnimation>

int main(int argc, char* argv[ ])
{
    QApplication app(argc, argv);
    QGraphicsScene scene;
    scene.setSceneRect(-200, -150, 400, 300);
    MyItem *item = new MyItem;
    scene.addItem(item);
    QGraphicsView view;
    view.setScene(&scene);
    view.show();
    
    // 为图形项的rotation属性创建动画
    QPropertyAnimation *animation = new QPropertyAnimation(item, "rotation");
    animation->setDuration(2000);
    animation->setStartValue(0);
    animation->setEndValue(360);
    animation->start(QAbstractAnimation::DeleteWhenStopped);

    return app.exec();
}

这样就可以在图形视图框架中使用动画了

这里是2s,开始0.结束旋转360。

现在运行程序,图形项已经可以自动旋转了。

当然,这个动画效果也可以使用前面讲到的其他知识来实现,
不过可以看到,使用动画框架是非常简单的,而且如果要实现更加复杂的动画,
那么动画框架的优势就显而易见了。

在使用动画对象时,如果是创建对象的指针,而且执行—遍以后就不再使用,那么可以在 start ()函数中指定DeletcWhenStopped 删除策略,这样当动画执行结束后便会自动销毁该动画对象。

除了继承QGraphicsObject类以外,当然也可以同时继承Object和 QGraphicsItem类来实现自己的图形项。
注意,QObejct必须是第一个继承的类,这是元对象系统的要求。

另外,还可以继承自QGraphics Widget类,这个类已经是QObject的子类了。

如果要使用一个自定义的属性,那么就要先声明该属性,这个可以查看第7章的相关内容。

Qt还提供了一个Animated Tiles Example的示例程序,可以参考一下。

相关文章