QT 状态机详解 statemachine
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));
transition1->addAnimation(new QPropertyAnimation(button, “geometry”));
transition2->addAnimation(new QPropertyAnimation(button, “geometry”));
#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了吧:)
前文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"
前面写过一篇文章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();
}
文章的源在:http://docs.google.com/View?id=dhhvrcmh_100m5xs7wf3,如有更新可能会反映在那边
最后罗唆一下,这代码要用在qt-4.6环境下运行:)
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也奉献一个,互相交流:)
]]> 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上级目录。
Qt4.6引入了动画框架,摆脱了以往控件只能安静的呆在布局里的时代,利用Qt提供的动画框架,我们可以让控件跳起舞来,呵呵,很有趣啊… 在Qt4.7中又引入了Qt quick技术,其中的QML语言也是专门来定制GUI动画效果的,这是一种新的GUI动画机制,我刚也接触了些,语法类似CSS,实现预定义的动画很方便,所见即所得,路还得一步步走,先学习一下传统的Qt动画方式——状态机
这里先直接给出例子(见图):
下面是源码:
#include <QApplication>
#include <QStateMachine>
#include <QPushButton>
#include <QSignalTransition>
#include <QPropertyAnimation>
int main(int argc,char **argv)
{
QApplication app(argc,argv);
QWidget *w=new QWidget;
w->resize(240,320);
QPushButton *button = new QPushButton("hicjiajia",w);
QStateMachine *machine = new QStateMachine; //新建状态机
QState *state1 = new QState(machine); //状态1
state1->assignProperty(button,"geometry",QRect(0,0,80,30)); //绑定button的geomertry属性
machine->setInitialState(state1); //state1设为初始化状态
QState *state2 = new QState(machine);
state2->assignProperty(button,"geometry",QRect(w->width()-30,w->height()-80,30,80));
QFont font = QFont("Airl", 12);
state2->assignProperty(button,"font",font); //状态2字体
QPropertyAnimation *ani=new QPropertyAnimation(button,"geometry");
ani->setDuration(2000);
ani->setEasingCurve(QEasingCurve::OutBounce); //动画效果—弹跳
QSignalTransition *transition1=state1->addTransition(button,SIGNAL(clicked()),state2); //动画触发信号
transition1->addAnimation(ani);
QSignalTransition *transition2=state2->addTransition(button,SIGNAL(clicked()),state1);
transition2->addAnimation(ani);
machine->start(); //开启状态机
w->show();
return app.exec();
}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。