Qt动画

转载自:  <http://jingyan.baidu.com/article/154b46315757b628ca8f4116.html> 和   <http://blog.csdn.net/syzobelix/article/details/9377863>

  • Qt动画架构中的主要类如下图所示:

动画框架由基类QAbstractAnimation和它的两个子类QVariantAnimation和QAnimationGroup组成。 QAbstractAnimation是所有动画类的祖宗。它包含了所有动画的基本属性。比如开始,停止和暂停一个动画的能力。它也可以接收时间改变通 知。

动画框架又进一步提供了QProertyAnimation类。它继承自QVariantAnimation并对某个Qt属性(它须是Qt的”元数据对象 系统”的一部分,见http://blog.csdn.net/nkmnkm/article/details/8225089)执行动画。此类对属性执 行一个宽松曲线插值。所以当你想去动画一个值时,你可以把它声明为一个属性,并且让你的类成为一个QObject。这给予我们极大的自由度来动画那些已存 在的widget和其它QObject。

复杂的动画可以通过建立一个QAbstractAnimation的树来构建。这个树通过使用QAnimationGroups来创 建,QAnimationGroups作为其它动画的容器。注意动画组也是从QAbstractAnimation派生的,所以动画组可以再包含其它动画 组。

动画框架可以单独使用,同时也被设计为状态机框架的一部分。状态机提供了一个特定的状态可以用来播放动画。在进入或退出某个状态时QState也可以设置 属性们,并且这个特定的动画状态将在指定QPropertyAnimation时给予的值之间做插值运算。后面我们要进一步介绍此问题。

在场景的背后,动画被一个全局定时器收集,这个定时器发送update到所有的正在播放的动画中。

  • 动画框架中的类们

QAbstractAnimation 所有动画类的基类

QAnimationGroup 动画组的基类

QEasingCurve 控制动画的宽松曲线类

QParallelAnimationGroup 并行动画组类

QPauseAnimation 串行动画组类的暂停类

QPropertyAnimation 动画Qt属性的类

QSequentialAnimationGroup 串行动画组类

QTimeLine 控制动画的时间线类

QVariantAnimation 各动画类的虚基类

  • 动画Qt属性们

如前面所讲,QPropertyAnimation类可以修改Qt属性们。要动画一个值,就需要使用此类。实际上,它的父类,QVariantAnimation,是一个虚拟类,不能被直接使用。

1、我们选择动画Qt属性的一个主要理由是Qt属性为我们提供了自己动画已存在的类的自由度。尤其是QWidget类(我们也可以把它嵌入到一个QGraphicsView中)具有很多属性表示其bounds,colors等等。让我们看一个小例子:

QPushButton button("Animated Button");

button.show();

QPropertyAnimation animation(&button, "geometry");

animation.setDuration(10000);

animation.setStartValue(QRect(0, 0, 0, 0));

animation.setEndValue(QRect(250, 250, 100, 30));

animation.start();

这段代码将把按钮在10秒种内从屏幕的左上角移动到(250,250)处,而且是逐渐变大。见下图效果:

2、上面的例子举在开始值和结束值之间做线性插值。还可以在开始和结束值之间设置值,插值运算就会经过这些点。

animation1 = new QPropertyAnimation(ui.pushButton, "geometry");

animation1->setDuration(10000);

animation1->setKeyValueAt(0, QRect(0, 0, 00, 00));

animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30));

animation1->setKeyValueAt(0.8, QRect(100, 250, 20, 30));

animation1->setKeyValueAt(1, QRect(250, 250, 100, 30));

animation1->setEndValue(QRect(250, 250, 100, 30));

在此例中,动画将按钮在8秒中内弄到(250,250)处,然后在2秒种内又弄回原位。移位是在这些点中间以线性插值进行的。

3、你也有可能动画一个QObject的值,虽然这些值并没有被声明为Qt属性。唯一的要求就是这个值具有一个setter。之后你可以从这个类派生 子类从而包含这些值并且声明一个使用这个setter的属性。注意每个Qt属性都需要有一个getter,所以你需要提供一个getter,如果它不存在 的话。

class MyGraphicsRectItem : public QObject, public QGraphicsRectItem

{

Q_OBJECT

Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)

};

在上例中,我们派生了QGraphicsRectItem并定义了一个geometry属性。我们现在可以动画这个widget的geometry了,即使QGraphicsRectItem没有提供geometry属性。

  • 动画和图形视图框架

当你想动画QGraphicsItems,你也要用QPropertyAnimation。然而,QGraphicsItem不是从QObject派生 的。一个好的解决方案是派生要动画的图形item。派生类也要从QObject派生。这样,QPropertyAnimation就可以被用于 QGraphicsItems了。

class Pixmap : public QObject, public QGraphicsPixmapItem

{

Q_OBJECT

Q_PROPERTY(QPointF pos READ pos WRITE setPos)

...

就如上一节中所讲的,我们需要定义希望去动画的属性。

注意:QObject必须是继承中的第一个,因为元数据对象系统需要这样做。

  • 宽松曲线

QPropertyAnimation在属性的开始值和结束值之间执行一个插值运算。除了向动画添加更多的关键值外,你还可以使用一个宽松曲线。宽松曲线描述了一个在0和1之间插值的速度变化的函数,如果你想控制一个动画的速度而不改变插值的路径时,就非常有用。

animation1 = new QPropertyAnimation(ui.pushButton, "geometry");

animation1->setDuration(10000);

animation1->setStartValue(QRect(0, 0, 0, 0));

animation1->setEndValue(QRect(250, 250, 100, 30));

animation1->setEasingCurve(QEasingCurve::OutBounce);

这里,动画将按照一个曲线进行,这个曲线使得动画像一个跳动的皮球从开始位置跳到结束位置。QEasingCurve具有一个大曲线集合,你可以从里面 选择一个。它们被定义为QEasingCurve::Type枚举。如果你需要不一样的曲线,你也可以自己实现一个,然后注册到 QEasingCurve。

  • 错误1:QPropertyAnimation: you're trying to animate a non-existing property 属性窗口

解释:

animation1 = new QPropertyAnimation(ui.pushButton, "geometry");

估计是你给的属性名字(propertyName)是错误的,必须是qt自带的属性名字,或者你通过自定义实现的名字。

我要是把代码写成:

animation1 = new QPropertyAnimation(ui.pushButton, "geometry23");

会提示如下错误,找不到属性geometry23

QPropertyAnimation: you're trying to animate a non-existing property setGeometry23 of your QObject

来源: <http://jingyan.baidu.com/article/154b46315757b628ca8f4116.html>

Qt坐标

QPainter的各种draw方法是基于窗口坐标系的。

窗口坐标为逻辑坐标,是基于视口坐标系的;视口坐标为物理坐标,是基于绘图设备坐标系的。没有做过改动的情况下,他们是一样的,都是以绘图设备(paint device,qwidget,qpixmap等都为绘图设备)大小为大小,左上角为原点(0,0)。

窗口:

窗口代表视口的区域,他始终以视口坐标为最终目标进行映射(这句话的意思到下面讲视口的时候会再讲),他的大小和逻辑位置可以通过QPainter::setWindow()设置,但是无论大小和逻辑位置设置为什么数值,他始终代表着整个视口。

例如你有一个实际大小为200×200像素的窗口,那么原始状态之下窗口大小也是200×200,视口大小也是200×200,,在0,0位置画一个大小为100×100的矩形的时候,他会占视口左上角的4分之一。
painter.drawRect(0,0,100,100);

如果这时候我们通过QPainter::setWindow修改了窗口位置和大小,例如setWindow(-50,-50,100,100)

函数原型:
void QPainter::setWindow(int x, int y, int width, int height)

参数:
x:窗口左上角x坐标
y:窗口左上角y坐标
width:窗口长度
height:窗口高度

窗口代表的还是整个视口,但是映射的数值有所不同,这时候窗口的逻辑坐标(-50,-50)成为了视口坐标的(0,0),而窗口的逻辑大小成为了 100×100的单位长度(这里用单位长度是因为窗口大小的长度并不固定,受视口大小影响),因为用100个单位长度代表原本物理大小的200像素,所 以,每一个单位长度就是实际的2像素。因为QPainter是以窗口坐标为基础的,所以这时候画一个位置为(-50,-50),大小为 50,50的矩形。
painter.drawRect(-50,-50,50,50);
矩形还是占窗口的左上角的4分之一(下图左),而
painter.drawRect(0,0,50,50);
矩形占窗口右下角的4分之一。

而视口对应的则是物理坐标,没有改动的情况下,视口大小与绘图区大小一样,上面的例子中,视口的属性一直没有改变过,所以视口的左上角还是在绘图区的物理位置(0,0),在窗口坐标的(-50,-50)。大小为物理大小的

200×200像素,而为窗口坐标系下的100×100单位长度。

视口:

现在我们看看设置视口对绘图的影响,为了简单起见,先把上面的setWindow()一句注释掉,即现在窗口,视口是一样的。

现在来改变视口的属性,先用painter.setViewPort(0,0,100,100);
函数原型:
void QPainter::setViewport ( int x, int y, int width, int height )

参数:
x:设置视口左上角x坐标
y::设置视口左上角y坐标
width:设置视口长度
height:设置视口宽度

所以上面语句的作业就是把视口的的原点位置设置为绘图设备(这里是QDialog)的原点,大小改变为100,100。

那么现在是个什么情况呢?
现在我们把视口的坐标设置为绘图区的左上角(0,0)位置,大小设置为绘图区的一半,因为绘图区是(200×200),而我们把视口设置为(100×100)。即现在实际的绘图区为绘图设备的左上角的4分之一。

那么这时候我们再用
painter.drawRect(0,0,100,100);画一个矩形,实际显示是怎么样的呢?看下图:

绘制出来的是dialog的16分之一了,为什么会这样呢?

前面我们讲过窗口坐标始终以视口坐标为最终目标进行映射, 而原来没有经过修改的窗口的属性为以左上角为原点,大小为200×200单位长度,我们修改视口大小为100×100像素后,窗口的200单位长度就映射 到100像素的视口长度上,即每一单位长度为0.5像素,所以绘制出来的结果就是100×0.5=50像素,所以长和高都是dialog的4分之一,面积 就是16分之一了。

这时候如果我们拖动dialog边界改变dialog的大小会怎么样呢?小狼原本的想法是画出来的矩形应该还是占总大小的16分之一,实际上这是错的。
void Lang::paintEvent(QPaintEvent *)
{
QPainter painter(this);

<code style="margin: 0px; padding: 0px; border: 0px; font-size: 0.857142857rem; vertical-align: baseline; font-family: Consolas, Monaco, 'Lucida Console', monospace; line-height: 2; display: block;">    qDebug()<<"after drag:"<<endl;qDebug()<<painter.viewport().width();qDebug()<<painter.viewport().height();painter.setViewport(0, 0, 100 ,100 );//painter.setWindow(-50,-50,100,100);painter.drawRect(0,0, 100, 100);
}
</code>

先通过painter.viewport().width()和heigth()获取当前实际视口大小(paintEvent之前,视口会被重置为绘图设备实际大小)。如下图,当我们把dialog拖动为400×400大小时,矩形框变得更小了。

其实这很简单。因为在paintEvent之前窗口值也会重置为dialog(绘图设备)大小,所以这时候窗口大小为400×400单位长度,而视口我们 还是再设定为100×100像素,所以这时候窗口大小的一单位长度为实际的100/400=0.25像素,所以画一个100×100单位长度的矩形时,实 际大小时25×25像素,所以变得更小。

上图中我是用qq的截图功能进行测量,qq截图会给出当前截取图形的大小,正是25×25(圈的时候有所偏差).

最后再来看一个窗口和视口一起设置的例子.
void Lang::paintEvent(QPaintEvent *)
{
QPainter painter(this);

<code style="margin: 0px; padding: 0px; border: 0px; font-size: 0.857142857rem; vertical-align: baseline; font-family: Consolas, Monaco, 'Lucida Console', monospace; line-height: 2; display: block;">    qDebug()<<"after drag:"<<endl;qDebug()<<painter.viewport().width();qDebug()<<painter.viewport().height();painter.setViewport(0, 0, 100 ,100 );//painter.setWindow(-50,-50,100,100);painter.drawRect(0,0, 100, 100);
}
</code>

这里设置窗口坐标(-50,-50)映射为视口的原点,把窗口100×100单位长度映射为视口的100×100像素大小
这时候窗口逻辑坐标-50,-50就是视口的坐标(0,0),也就是绘图设备的50,50坐标,所以窗口坐标(0,0)位置即绘图设备坐标的(100,100)
因为窗口坐标100单位长度映射到视口坐标的100像素,所以上图的painter.drawRect(0,0, 50, 50);一句绘制出了的结果就是在绘图设备的(100,100)位置绘制一个50×50像素的矩形。
 

总结:

要得到QPainter绘图的真正位置,要经过两步

第一步:
窗口坐标转换为视口坐标,转换公式为:
vp_width / win_width * (draw_x - win_x)
其中vp_width为视口长度,win_width为窗口长度,draw_x为实际要绘制的x左上坐标,win_x为窗口的x左上坐标,y坐标同理

窗口大小转换为视口大小,转换公式为:
draw_width * vp_width / win_width
draw_width为要绘制的窗口长度,vp_width为视口长度,win_width为窗口长度

height同理

第二步:
视口坐标转换为绘图设备坐标,这一步就简单的进行相加就好了
实际坐标x=上一步转换来的视口坐标+vp_x
vp_x为视口的左上x坐标,实际坐标y同理

来源: <http://blog.csdn.net/syzobelix/article/details/9377863>

Qt动画与Qt坐标小记相关推荐

  1. Qt动画框架Animation Framework

    Qt动画框架 Qt动画框架 动画架构 动画框架中的类 动画Qt属性 动画和图形视图框架 缓和曲线 将动画放在一起 Qt动画框架 动画框架旨在为创建动画和平滑的GUI提供一种简便的方法.通过对Qt属性进 ...

  2. Qt动画之鼠标水滴点击效果

    一.简述 前几天在群里看见有个小伙伴用的一款gif录屏软件有一个类似水滴的点击效果.于是想了想,便开始了Code.思路也很简单,就是借助Qt的动画类QVariantAnimation然后不断重绘达到点 ...

  3. Qt 动画飞舞的蝴蝶源码

    一.简介 GraphicsView框架结构主要包含三个主要的类QGraphicsScene(容器).QGraphicsView(视图).QGraphicsItem(图形项).QGraphicsScen ...

  4. Qt动画框架The Animation Framework

    一个网友翻译的,没有翻译完,我把剩下的那部分翻译出来贴出来 动画框架是Kinetic(运动)项目的一部分,它的目标是提供一中简单的方法创建动画的和流畅的GUI.借助Qt动画属性,可以提供非常自由的动画 ...

  5. 使用Qt动画框架设计角色的二维动画(二)

    使用Qt动画框架设计角色的二维动画(二) 接上次的 日志. 上次发布demo虽然使用了大量的动画框架.有限状态机框架,但是仍有瑕疵.比如说在用户一直按下按键的时候角色会被"冻"住, ...

  6. 使用Qt动画框架设计角色的二维动画

    使用Qt动画框架设计角色的二维动画 Qt的动画框架是Qt4.6新添加的一个重要的特性,有了它,开发人员可以制作激动人心的动画界面,而不必局限于单调的固定窗口了,可以说,Qt动画框架是其它界面库少见的功 ...

  7. Qt Creator使用Qt Quick工具栏

    Qt Creator使用Qt Quick工具栏 使用Qt Quick工具栏 预览影像 格式化文字 预览动画 编辑矩形 使用Qt Quick工具栏 当您在代码中选择QML类型并且工具栏可用时,将出现一个 ...

  8. Qt Creator开发Qt快速应用程序

    Qt Creator开发Qt快速应用程序 开发Qt快速应用程序 创建Qt快速项目 在设计模式下编辑QML文件 创建UI 添加动态 编辑3D场景 相关话题 浏览ISO 7000图标 将QML模块与插件一 ...

  9. Qt学习之Qt基础入门(下)

    1. 前言 前两篇博客简单的阐述了一下Qt的入门用法,这篇博客继续跟着视频学习. Qt入门系列: Qt学习之C++基础 Qt学习之Qt安装 Qt学习之Qt基础入门(上) Qt学习之Qt基础入门(中) ...

最新文章

  1. 三,ES6中需要注意的特性(重要)
  2. 拿下计网协议后,我就是公园里最靓的仔
  3. 每日一皮:资深程序员调试代码的样子...
  4. Redis高可用方案-RedisCluster-SpringBoot整合
  5. t oracle删除吗,Oracle 11g 手工建库与删库
  6. 筛法求素数c 语言,位筛法求素数,有段代码看不懂,有大佬可以来说一下
  7. 构建和实现单点登录解决方案
  8. 牛客练习赛52 C 烹饪(容斥+扩展欧几里得)
  9. MBR、主引导扇区,主分区、扩展分区、逻辑分区,活动分区、引导分区、系统分区、启动分区的区别详解
  10. ecshop百度收录插件,ECSHOP一键百度推送收录,ECSHOP一键百度收录
  11. Robot Framework+Autoit 安装教程
  12. 控制台报错: Another version of Vue Devtools seems to be installed. Please enable only one version at a ti
  13. 怎样判断计算机硬盘损坏,检测硬盘是否损坏的方法来了,这里有四种判断方法!...
  14. 基础开始——审计aduit
  15. 边缘计算概念以及应用
  16. ETCD数据库源码分析——Cluster membership changes日志
  17. RNA-seq——快速下载SRA数据、解决fq文件中测序质量全为 ‘?‘ 的问题
  18. 如何搭建企业邮箱服务器
  19. 一个预言家的命运:忽悠马云的“骗子”,风口上的先知
  20. 开箱即用的流媒体管理系统wvp-GB28181-pro 基于ZLMediaKit

热门文章

  1. mDNS实现之Bonjour与Avahi(二)——win/linux/arm交叉编译
  2. 2019个税,我们每年能省多少钱~
  3. MathType MTEF 数据分析
  4. Landsat 7 去条带
  5. HDLBits刷题_Verilog Language_Vector4
  6. 计算机软件删除了怎么恢复,不小心卸载的软件能恢复吗_软件卸载错了怎么恢复-win7之家...
  7. ping命令和arp命令、ping常见问题解决、TTL值判断操作系统
  8. Python还能这么玩?Turtle一个上帝的指纹!(斐波那契螺旋线)
  9. python字符串函数reversed_python reverse()方法和内置函数reversed()
  10. 网络架构,七层协议,三次握手四次挥手,socket套接字简单编程