@QT状态机制--可实现动画界面

状态机顾名思义,应该有不同的状态在切换。上面状态机图中,我们提供了两种状态state1和state2。而状态的区分是由状态的属性来描述的,比如p1,p2…等等。从一个状态到另一个状态的转化,必须由触发条件来完成,上图state1到state2的状态转换由transition1来表示,state2到state1的状态转换由transition2来表示。如果希望在状态转换过程中有动画来展示,那么可以在transition1和transition2中加入动画效果animation1和animation2 。最后,状态机进入需要有一个初始状态,我们可以设定state1为我们这个状态机的初始态。
有了状态机的描述图,我们就可以看看用Qt-4.6的代码,如何实现以上功能。
创建状态机
QStateMachine *machine = new QStateMachine;
创建两个状态,状态的属性由一个QPushButton的位置大小决定。

QPushButton *button = new QPushButton(“Animated Button”);
QState *state1 = new QState(machine);
state1->assignProperty(button, “geometry”, QRect(0, 0, 150, 30));
QState *state2 = new QState(machine);
state2->assignProperty(button, “geometry”, QRect(250, 250, 150, 30));

将状态1设置为状态机的初始状态
machine->setInitialState(state1);
增加触发状态1到状态2的触发条件,QPushButton按钮被按下,动画效果由addAnimation()完成
QSignalTransition *transition1 = state1->addTransition(button,SIGNAL(clicked()), state2);
transition1->addAnimation(new QPropertyAnimation(button, “geometry”));
细心的读者可能发现QPropertyAnimation的setStartValue()和setEndValue()都没有被调用到。其实这里动画变化的初始态和结束态,由state1和state2决定的。另外,如果没有设置动画的持续时长,则默认是250毫秒。
同理,增加状态2到状态1的触发条件,也是按钮被按下
QSignalTransition *transition2 = state2->addTransition(button,SIGNAL(clicked()), state1);
transition2->addAnimation(new QPropertyAnimation(button, “geometry”));
最后一步,将状态机启动即可,是不是很简单?
machine->start();
完整代码在下头

 

#include <QApplication>
#include <QPushButton>
#include <QStateMachine>
#include <QState>
#include <QSignalTransition>
#include <QPropertyAnimation>
int main(int argc,char *argv[]){
QApplication app(argc,argv);
QPushButton *button = new QPushButton(“Animated Button”);
button->show();
QStateMachine *machine = new QStateMachine;
//QState *state1 = new QState(machine->rootState());
QState *state1 = new QState(machine);
state1->assignProperty(button, “geometry”, QRect(0, 0, 150, 30));
machine->setInitialState(state1);
//QState *state2 = new QState(machine->rootState());
QState *state2 = new QState(machine);
state2->assignProperty(button, “geometry”, QRect(250, 250, 150, 30));
QSignalTransition *transition1 = state1->addTransition(button,
SIGNAL(clicked()), state2);
transition1->addAnimation(new QPropertyAnimation(button, “geometry”));
QSignalTransition *transition2 = state2->addTransition(button,
SIGNAL(clicked()), state1);
transition2->addAnimation(new QPropertyAnimation(button, “geometry”));
machine->start();
app.exec();
}

//这个代码来自于Qt-4.6 Assistant帮助的Animation索引,原代码的rootState()函数是没有的,应该算是bug了吧:)

上边介绍了怎么使用Qt-4.6的动画类来实现位置的自动变化,有时候还会一些自定义属性变化需求,比如颜色的变化,以及某些非数值直接相关的属性,这时候可以用自定义的变量来灵活控制。下面我实现了一个非常简单的例子用于让Label上的文字自动做颜色变化,不过样子很丑 :( ,主要是演示功能

前文sanfanling提到QTextBrowser中选中文字的变色可以用类似方法来操作,不过我觉得更简便的方法是用timerEvent,呵呵(八卦一下,不知此sanfanling是否就是云帆论坛的KDE名人三翻领?)
//这一部分代码主要是利用继承给QLabel增加了一个颜色属性
#include <QApplication>
#include <QLabel>
#include <QPropertyAnimation>

 

class Label:public QLabel{
Q_OBJECT
Q_PROPERTY(int color READ color WRITE setColor)
public:
Label(const QString&text,QLabel *p=0):QLabel(text,p){icolor=0;};
inline const int& color() const{return icolor;};
void setColor(const int& );
private:
int icolor;
};

void Label::setColor(const int &rcolor) {
QPalette pal;
pal.setColor(QPalette::Foreground, rcolor);
setPalette(pal);
if(rcolor!=icolor){
icolor=rcolor;
//4 lines get “text”
QString color_str=QString("%1").arg(icolor,2,16);
color_str+=QString("00")+color_str;
QString str=QString("");
str+=QString("http://www.cuteqt.com/blog")+QString(">") + color_str;
setText(str);
}
repaint();
}

//标准的QPropertyAnimation动画接口,利用属性color进行自动变化
int main(int argc,char *argv[]){
QApplication app(argc,argv);
Label label("hello,www.cuteqt.com/blog");

QPropertyAnimation *anim=new QPropertyAnimation(&label, "color");
anim->setDuration(1800);
anim->setStartValue(16);
anim->setEndValue(0xffffff);
anim->start();

label.show();
return app.exec();
}

#include "main.moc"

 

]]> http://www.cuteqt.com/blog/?feed=rss2&p=1146 3 http://www.cuteqt.com/blog/?p=1142 http://www.cuteqt.com/blog/?p=1142#comments Sat, 05 Dec 2009 11:20:40
+0000 臭虫 http://www.cuteqt.com/blog/?p=1142 Qt-4.6新增了Animation Framework(动画框架),让我们能够方便的写一些生动的程序。不必像以前的版本一样,所有的控件都枯燥的呆在伟大光荣的QLayout里,也许它们可以唱个歌,跳个舞。

 

前面写过一篇文章Qt动画效果的幕后英雄:QTimeLine,介绍了怎么利用QTimeLine写一动画程序。今天又再重申前文里的一句话,所谓动画就是在一个时间段内的不同时间点有不同的状态,只要定义好这样状态,实现动画就是水到渠成的事情。当然做这件事情,最好用的就是状态机,没错Qt-4.6.0提供了QStateMachine类,不过今天我要讲的三字决要简单一些。

第一决:QPropertyAnimation

QPropertyAnimation用于和QObject中的属性properties进行通信,比如QWidget的大小,坐标等。来看代码

QPropertyAnimation *animation = new QPropertyAnimation(myWidget, “geometry”); 
animation->setDuration(10000); 
animation->setStartValue(QRect(0, 0, 100, 30)); 
animation->setEndValue(QRect(250, 250, 100, 30)); 
animation->start();

第一行创建的QPropertyAnimation对象关联了myWidget这个窗体的几何属性。后面的几句分别设置了这个动画的时长,起始坐标和结束坐标。剩下的事情就交改QProperAnimation去做就行了。然后调用start()启动它。没错,五行代码就完成了一个完成了一个自动从一个坐标点移动到另一个坐标点的窗体。下面我给出一个可以运行的代码,是一只小鸟从下角移到中间的一个小动画,当然你得自己准备这个同名的图片:)

#include <QApplication>
#include <QLabel>
#include <QPixmap>
#include <QPropertyAnimation>

int main(int argc,char *argv[]){
QApplication app(argc,argv);
QWidget *w=new QWidget();
w->resize(300,400);

QPixmap birdimg=QPixmap(“twitter-bird.png”).scaled(40,40);

QLabel *bird_1=new QLabel(w);
bird_1->setPixmap(birdimg);

QPropertyAnimation *anim1=new QPropertyAnimation(bird_1, “pos”);
anim1->setDuration(2000);
anim1->setStartValue(QPoint(0, 360));
anim1->setEndValue(QPoint(110, 180));
anim1->start();
bird_1->move(-40,-40);
w->show();
return app.exec();
}

上面的例子使用了label的位置属性pos。当然你可以在自己的类里增加其它property的,比如让颜色在变。

第二决:setEasingCurve

上面那个例子中小鸟的移动是线性的,未免太单调了点。QPropertyAnimation中的void setEasingCurve (const QEasingCurve & easing)函数正是用于实现不同的曲率变化的,QEasingCurve可用的参数列表(包括函数曲线图)可在文档中查到 。将上面动画相关的代码部分改成

QPropertyAnimation *anim1=new QPropertyAnimation(bird_1, “pos”);
anim1->setDuration(2000);
anim1->setStartValue(QPoint(0, 360));
anim1->setEndValue(QPoint(110, 180));
anim1->setEasingCurve(QEasingCurve::OutBounce);
anim1->start();

注意,新增的第四句。并且试试其它曲线参数,然后运行,看到的动态效果是不是不一样了。如果你对列表里已经有的曲线都不满意,你还可以继承QEasingCurve,实现你需要的效果。

第三决:QAnimationGroup

前面的例子是只有一个动画在运行,如果想多个动画一起运行的话,那就要用到动画组QAnimationGroup了。动画组分为两种分别为串行和并行,对应于QAnimationGroup的两个子类QSequentialAnimationGroup和QParallelAnimationGroup。其用法很简单

QSequentialAnimationGroup group;
//QParallelAnimationGroup group;
group.addAnimation(anim1);
group.addAnimation(anim2);
group.start();

上面的代码,如果是串行的话,那么动画anim1运行之后,才会运行anim2。如果是并行的话,两个动画是同时运行的。如果加了动画组,那么单个anim1->start()就没必要再单独调用了,由动画组来管理。 下面是一个可运行的代码,两只小鸟分别从窗体左上角和右下角移动到中间。

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPixmap>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include <QParallelAnimationGroup>

int main(int argc,char *argv[]){
QApplication app(argc,argv);
QWidget *w=new QWidget();
w->resize(300,400);

QPixmap birdimg=QPixmap(“twitter-bird.png”).scaled(40,40);

QLabel *bird_1=new QLabel(w);
bird_1->setPixmap(birdimg);

QPropertyAnimation *anim1=new QPropertyAnimation(bird_1, “pos”);
anim1->setDuration(2000);
anim1->setStartValue(QPoint(0, 360));
anim1->setEndValue(QPoint(110, 180));
//anim1->setEasingCurve(QEasingCurve::OutBounce);
//anim1->start();

QLabel *bird_2=new QLabel(w);
bird_2->setPixmap(birdimg);

QPropertyAnimation *anim2=new QPropertyAnimation(bird_2, “pos”);
anim2->setDuration(2000);
anim2->setStartValue(QPoint(0, 0));
anim2->setEndValue(QPoint(150, 180));
anim2->setEasingCurve(QEasingCurve::OutBounce);

QSequentialAnimationGroup group;
//QParallelAnimationGroup group;
group.addAnimation(anim1);
group.addAnimation(anim2);
group.start();

bird_1->move(-40,-40);
bird_2->move(-40,-40);
w->show();
return app.exec();
}

其实动画的本质就是在每一定时间间隔内显示一帧图像,当这个间隔较短的时候人眼就感觉不出来了,觉得看到的是连续的影像。Qt为开发动画效果的人员提供了一个很好的时间控制类QTimeLine.

 

QTimeLine的最简单用法是

1 QTimeLine timeline=new QTimeLine(1000);
2 timeLine->setFrameRange(0, 100);
3 connect(timeline,SIGNAL(frameChanged(int)),yourobj,SLOT(yourobjslot(int)));
4 timeline->start();

解释:

1. 创建的时间线持续时长,参数值是毫秒数,1000就是1秒

2. 在这段时间线内,创建的输出值范围。也就是第三行中frameChanged信号里传出的参数值范围

3. QTimeLine的默认时间间隔是40ms(也就是1秒25帧),每个间隔会发出一个frameChanged()的信号,此处将该信号连接到你能控制动画效果的对象和槽上。

4. 启动timeline后,每个时间间隔的frameChanged()信号才能正常发出。

当然还有一些复杂的参数设置可以更好的控制你的效果。

setLoopCount(int count)该函数控制了动画的重复次数。默认是1,如果设置成0则表示无限循环。

setUpdateInterval(int interval)该函数用于控制更新动画的时间间隔。

在QTimeLine所设置的持续时长过去后,相应的会发出一个finished()的信号,你可以在接收到这个信号以后做一些扫尾的工作。

另外还有一个别致的选项:

setCurveShape (CurveShape shape),该选项用于控制间隔输出数值的一个变化规律。CurveShage现有的选项是

QTimeLine::EaseInCurve 0 The value starts growing slowly, then increases in speed.

QTimeLine::EaseOutCurve 1 The value starts growing steadily, then ends slowly.

QTimeLine::EaseInOutCurve 2 The value starts growing slowly, then runs steadily, then grows slowly again.

QTimeLine::LinearCurve 3 The value grows linearly (e.g., if the duration is 1000 ms, the value at time 500 ms is 0.5).

QTimeLine::SineCurve 4 The value grows sinusoidally.

QTimeLine::CosineCurve 5 The value grows cosinusoidally.

不多加解释了,2是默认值。因为即使没有这个选项,如果只输出均匀数(选项3),我们还是可以自己对数据进行二次加工,生成我们想要的任意规律的数字。

利用QTimeLine我们很容易实现一些图像消隐的效果,只需要你在每个间隔结束后显示图像消隐过程中不同阶段的一个定格即可。这里就有一个很好的例子,短短300余行,想必用不了你多长时间即可读通

http://qt.gitorious.org/qt-labs/graphics-dojo/trees/master/genie

我们www.cuteqt.com/blog也奉献一个,互相交流:)

timeline-simple.tar

]]> http://www.cuteqt.com/blog/?feed=rss2&p=509 5 http://www.cuteqt.com/blog/?p=276 http://www.cuteqt.com/blog/?p=276#comments Mon, 01 Jun 2009 02:12:51
+0000 shiroki http://www.cuteqt.com/blog/?p=276 看到qtcn上有人在问怎么能动态的变换显示的图片(http://www.qtcn.org/bbs/read.php?tid=18835), 其实这是很容易实现的。 基本的是使用QTimer系列的类来控制时间, 另外从Qt4开始所有的绘制工作应该都放在窗体子类的paintEvent函数里调用, 所以一般情况下你需要把想定制绘图的类派生一下, 重写paintEvent虚函数,
把drawPixmap之类的函数放在这里调用。 笔者的例子一方面是有点取巧, 另一方面也为了和问的问题一致, 没有去派生子类, 而是用了个QLabel来显示图片, 各位看官在看代码的时候要注意了。

 

下面就简单的介绍一下程序的实现, 非常简单, 三言两语就清楚了。
程序的主窗体采用了QFrame, 其实随便一个类都可以, 比如QWidget也一样。
在主窗体中放了一个layout用来控制内部控件的布局, Label控件和Pushbutton都放在此布局中。 为了显示图片方便, 给Label设置了一个固定大小。
构造函数就是创建布局、子控件和定时器, 并连接信号和槽函数。

主类里定义了两个槽函数, 一个用来响应定时器并变换图片, 另一个用来响应用户点击按钮并启动或停止定时器。
代码看看附件就好了, 有任何不清楚可在本站bbs留言。

例子的打包格式是tar.gz, 由于blog上载的限制, 后缀名被篡改了, 大家下载之后自己改回去一下。 另外因为windows下的qt默认会给debug和release版本创建不同的目录存放binary, 所以代码里用的相对路径在windows下可能会找不到图片, 大家拿回去改改mainwin.cpp, 或者把图片放在程序启动的目录里。 如从debug目录运行,就要把图片放在debug目录中,或者若以debug\ani.exe这样运行的话,图片就要放在debug上级目录。
这样应该就可以显示图片了。 Windows就是这么麻烦!
例子程序下载: picanimation.tar.gz

]]> http://www.cuteqt.com/blog/?feed=rss2&p=276 5

posted @ 2014-08-25 10:52  流水~妖刀  阅读(1038)  评论(0编辑  收藏  举报