Monday, November 29, 2010

Animating object along with curve in Qt using QPainterPath

Recently I was playing with Qt and found QPainterPath class. While reading documentation, I thought how can I use QPainterPath class and I created following prototype application in result.

QPainterPath can contain many different kind of shapes and we can draw all those shape using drawPath API of QPainter. But I used it to animate an object on curve path instead of just drawing, QPainterPath has some intresting API using which curve animation is quite easy.

In my sample app, I want to animate dot on curve as shown in below pic.


To achieve this, I first created a curved QPainterPath like following,
QPainterPath path;
path.moveTo(100,100);
path.quadTo(100,600,400,400);
Above code will draw curve as shown in above pic.

Now I created a timer and on timeout event, I am updating progress of dot on curve painter path. Like below.
void timeout()
{
    progress += 0.01;
    if( progress > 1 ) {
        progress  = 0;
    }
    update();
}
Now that, I have progress of dot on curve path at certain point in terms of percentage.QPainterPath has API pointAtPercent(), that can be used to get point on curve path at progress value and can draw Dot object at that point like below.
void Testwidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPath( path );

    painter.setBrush(Qt::SolidPattern);
    painter.drawEllipse(path.pointAtPercent(progress),20,20);
}
Output of above code will look like following.


If you don't like creating animation using QTimer and wants same output using Qt's animation framework then following code shows how that can be done.

In following code, I have created a custom widget named DotWidget and I am animating it on curve path using QPropertyAnimation.

Following code creates a painter path and a widget then applies QPropertyAnimation on it.
createPath();
DotWidget* dotWidget = new DotWidget(this);

QPropertyAnimation* animation = new QPropertyAnimation(dotWidget,
                               "geometry",this);
animation->setDuration(20000);
animation->setEasingCurve(QEasingCurve::Linear);
animation->setLoopCount(-1); //loop forever

//setting value for animation on different position using QPainterPath
for( double i = 0 ; i < 1; i = i+0.1) {
    animation->setKeyValueAt(i,
               QRect(path.pointAtPercent(i).toPoint(),QSize(30,30)));
}
animation->start(); 
Following is code for DotWidget custom widget class. Its very simple widget that draws circle.
#include 
#include 
class DotWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DotWidget(QWidget *parent = 0)
        :QWidget(parent)
    {}

    void paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setBrush(Qt::SolidPattern);
        painter.drawEllipse(0,0,25,25);
    }
};
I have created custom widget just to show how it can be done with custom widget, but you can apply same animation of any widget. I have uploaded video that shows output with QPushButton at end of post.

Thats all,following is video for custom animation with timer.



Following is video for animation using QPropertyAnimation with QPushButton

2 comments:

  1. Great !!
    Can u please show how this path can be linked with any QML element ?

    ReplyDelete
  2. Ok I will create another post for QML

    ReplyDelete