Qt动画与Qt坐标小记

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>

http://blog.csdn.net/sxpsxp12/article/details/50570941

时间: 2024-10-27 04:34:32

Qt动画与Qt坐标小记的相关文章

QT动画介绍(有例子可以下载)

所谓动画就是在一个时间段内的不同时间点有不同的状态,只要定义好这样状态,实现动画就是水到渠成的事情.当然做这件事情,最好用的就是状态机,点击这里查看Qt使用状态机实现动画效果实例. 不过,实现动画也有更简单的方法,Qt提供了QStateMachine类,应用该类可以快速的实现动画效果. Qt动画三字诀: 第一决:QPropertyAnimation QPropertyAnimation用于和QObject中的属性properties进行通信,比如QWidget的大小,位置坐标等.来看代码 1 2

Qt动画窗口,Qt之对话框消失动画

在讲解代码之前,先来认识几个概念 QPropertyAnimation:属性动画,可以参考qt 窗口动画 QGraphicsOpacityEffect:窗口透明度设置类,继承自QGraphicsEffect QGraphicsDropShadowEffect:窗口阴影,继承自QGraphicsEffect QGraphicsBlurEffect:滤镜,继承自QGraphicsEffect QGraphicsColorizeEffect:着色,继承自QGraphicsEffect http://w

详解Qt动画框架(2)--- 实现网易云音乐tag切换

在详解Qt的动画框架(一)介绍了关于Qt动画框架一些基础知识,在这一节中,将会实际的看到有关与动画框架的实现,该案例主要实现的具体是网易云音乐tag的切换,网易云音乐中的切换如图所示: 本文介绍的方法也可以达到这种切换的简易效果. 设计动画框架 首先我们需要设计对于动画框架,其需要的动画效果是什么?对于上图,我们需要的是三个tag可以不停的切换,可以抽象为左移以及右移,即一个tag从一个矩形区域,移动到另外的矩形区域,那么对于Tag的承载体选为什么较为合适呢?因为我们仅仅只需要图片的显示,因此Q

Qt动画架构中的主要类

Qt动画架构中的主要类如下图所示: 动画框架由基类QAbstractAnimation和它的两个儿子QVariantAnimation和QAnimationGroup组成.QAbstractAnimation是所有动画类的祖宗.它包含了所有动画的基本属性.比如开始,停止和暂停一个动画的能力.它也可以接收时间改变通知. 动画框架又进一步提供了QProertyAnimation类.它继承自QVariantAnimation并对某个Qt属性(它须是Qt的"元数据对象系统"的一部分,见http

Qt on Android: Qt Quick 简单教程

上一篇<Qt on Android: Qt Quick 之 Hello World 图文详解>我们已经分别在电脑和 Android 手机上运行了第一个 Qt Quick 示例-- HelloQtQuickApp ,这篇呢,我们就来介绍 Qt Quick 编程的一些基本概念,为创建复杂的 Qt Quick 应用奠定基础. 版权所有 foruok ,如需转载请注明来自博客 http://blog.csdn.net/foruok . 首先看一下<Qt on Android: Qt Quick

Qt 5.7 &gt; Qt Applications

本文翻译自Qt官方文档: http://doc.qt.io/qt-5/qmlapplications.html QML 应用 QML是声明式语言,它使得用户界面以及交互行为可以被"描述"出来.这是一种可读性非常高.动态互联其中的构件的语言,并且它允许构件可以非常容易的被重用和界面自定义.使用QtQuick模块,设计者和开发者可以使用QML开发流畅的动画界面,也可以选择将用户界面与后端的C++库连接起来. 什么是QML? QML是用户界面规范与编程语言.它使得开发者和设计者可以开发出高性

【Qt】关于Qt

什么是Qt Qt是一个针对桌面.嵌入式.移动设备的一个跨平台的应用程序开发框架,支持的平台包括Linux.OS X.Windows.VxWorks.QNX.Android.iOS.BlackBerry(黑莓).Sailfish OS(旗鱼操作系统)等. Qt 1991年由Trolltech-奇趣科技(成立于1994年)开发,2008年,Trolltech被Nokia收购,2012年,Qt被Digia收购. Qt是基于C++编写的框架,它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工

[QT][问题]关于QT语言家使用失败的原因之一

按照标准的qt使用教程 ( http://www.qter.org/forum.php?mod=viewthread&tid=781 ) 实践了下,发现除去主界面外,点开的窗口多语言没有问题.对次十分的不解,于是就查找了下问题. 原来加载与语言的时候 创建 MainWindow w; 需要在 加载语言之后.(动态加载除外). 类似问题: 1. qt中英文翻译: http://bbs.qter.org/forum.php?mod=viewthread&tid=288 qt问题搜索及学习推荐:

Qt on Android: Qt Quick事件处理之鼠标、键盘、定时器

在<Qt on Android: Qt Quick 事件处理之信号与槽>中介绍了 QML 中如何使用内建类型的信号以及如何自定义信号,这次我们来看看如何处理鼠标.键盘.定时器等事件.这些时间在处理时,通常是通过信号来完成的. 广而告之:我正在参加 CSDN 博文大赛,请给我的参赛文章<Qt on Android: Qt Quick 事件处理之信号与槽>投票,谢谢. 鼠标事件处理 桌面开发的话,难免要处理鼠标事件-- 变色矩形示例 看一个简单的处理鼠标事件的例子,先看代码(handl