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

Wednesday, November 24, 2010

Qt Plugin and Signals

If is often required to emit signals from Plug-in created using Qt Plug-in Framework.

In such case most people, connect to signal from loaded plug-in which is derived from QObject and defined plug-in interface.

Like following sample plug-in,

Plug-in interface.
class Worker
{
public:
    virtual ~Worker() {}
    virtual void doWork() = 0;
};
Q_DECLARE_INTERFACE(Worker,"Worker");
Plug-in that implements required interface.
class SampleWorker: public QObject,public Worker
{
Q_OBJECT
public:
    void doWork();
signals:
    void workDone();
};
Plug-in loader code,
QPluginLoader pluginLoader(fileName);
QObject *plugin = pluginLoader.instance();
if (plugin) {
    Worker* worker = qobject_cast<Worker*>(plugin);
    if (worker) {        
        connect(worker,SIGNAL(workDone()),this,SLOT(workDone()));
        worker->doWork();
    }
}
In Plug-in loader code, We are creating plug-in object and if plug-in creation is successfully, we are connecting signal workDone() with some slot. Above code will work fine, because SampleWorker has defined workDone signal.

But problem here is, workDone signal is not part of Worker interface, it is defined in implemented plug-in. If plug-in defines required signal than all works well but we are not forcing plug-in to abide with interface.

Proper solution to this problem could be to create plug-in that return Factory, This factory then creates and returns proper object which is derived from Interface or base class that has defined required method and signals.

like shown in my following sample,
class Worker: public QObject
{
Q_OBJECT
public:
    virtual ~Worker() {}
    virtual void doWork() = 0;

signals:
    void workDone();
};

class WorkerFactory
{
public:
    virtual ~WorkerFactory() {}
    virtual Worker* newWorker() = 0;
};
Q_DECLARE_INTERFACE(WorkerFactory,"WorkerFactory");
Here Worker is actual interface that we need, It defines required method and signals so all subclass will have required method and signals.

Sample plug-in implementation.
class SampleWorker: public Worker
{
Q_OBJECT
public:
// it emits workDone signal, when its done
void doWork();
};

// Plug-in implementation
class SampleWorkerFactory: public QObject, public WorkerFactory
{
Q_OBJECT
Q_INTERFACES(WorkerFactory)
public:
    Worker* newWorker() {
        new SampleWorker();
    }
};
Plug-in loader implementation,
QPluginLoader pluginLoader(fileName);
QObject *plugin = pluginLoader.instance();
if (plugin) {
    WorkerFactory* workerFactory = qobject_cast(plugin);
    if (workerFactory) {
        Worker* worker = workerFactory->newWorker();
        connect(worker,SIGNAL(workDone()),this,SLOT(workDone()));
        worker->doWork();
    }
}
Here plug-in loader code is loading plug-in implementing WorkerFactory interface and asking it to create object that implements Worker interface which define all required method and signals. Now we can connect required signal slots with this worker object.

Sunday, November 21, 2010

Symbian development from Ubuntu machine

Like I have already told in my past few posts that currently I am working on porting my application to Symbian. Another problem while developing for Symbian is, there are different SDK for different version of Symbian and you need to setup all those SDK to support different symbian version and also Symbian SDK are currently supported only on windows platform.

But now, both problems are solved by new Nokia Qt SDK by adding remote compiler feature in its latest release.

Compiling is one part of development, but we also need to test our application on device and as individual developer its quite hard to get hold on different device. This problem is also solved by Forum Nokia from quite some time by Remote Device Access portal.

Remote Device Access allows you to use device remotely for testing purpose. Its has almost all latest device to test with. Thought it might not be possible to test all use case remotely but most use case can be tested remotely.

I am putting some snaps that show how to setup Nokia Qt SDK with remote compiler feature and how to access Remote Device Access portal.

So, you will see welcome screen after starting Nokia Qt SDK installation like below.




After agreeing license agreement, you need to select type of installation. As shown in figure you need to select Custom as type of installation.



After selecting custom for type of installation,select Experimental to enable remote compiler feature.



Now, wait till installation gets completed. After insallation gets completed you need accept Terms of Sevice to enalle Remote Complier in Qt Creator and login using Forum Nokia login id.

Go to Tools -> Options -> Projects to enable Remote Compiler.




Few snaps, how build is performed in remote compiler






After build gets done, it will produce sis file that you can use to test. Now to test this sis file on Remote device access.

Go to http://apu.ndhub.net/devices

You need to login using Forum Nokia login id, its free to all forum nokia member. After successfully login you can choose phone to test application.



Select phone and it will launch Remote Device Access application with interface to intreact with phone.



You can upload your sis file to device to install it.



Now you can test your application on device using Remote Device Access. Following is video from my application using Remote Device Access.

Thursday, November 18, 2010

Storing custom class in QVariant

One of my friend asked me, how we can store custom class or structure in QVariant.

I gave him following sample.Posting same sample in hope it will help others as well.

#include <QCoreApplication>
#include <QPoint>
#include <QVariant>

class MyStruct
{
public:
    QString name;
    QString id;
    QPoint point;
};
Q_DECLARE_METATYPE(MyStruct)

int main(int argc, char *argv[])
{    
    qRegisterMetaType<MyStruct>("MyStruct");

    MyStruct test1;
    test1.name="test";
    test1.id = "testid";
    test1.point=QPoint(100,100);
    QVariant v;
    v.setValue<MyStruct>(test1);

    MyStruct test2 =v.value<MyStructl>();

}

If you need to serialize this variant than you will need to provide serialization method for custom class and also need to register them with metatype system using qRegisterMetaTypeStreamOperators.

Tuesday, November 16, 2010

Mounting LVM Encrypted Volum to recover data

From past two hour I am trying figure out how to recover data from my crashed laptop. Problem was my laptop hard disk was encrypted and by using normal mount command its not possible to get data.

I found this useful link that shows how to recover data in such case. Finally, I booted my laptop using Ubuntu live cd and recovered my data using command provided above link.

Saturday, November 13, 2010

Sprite animation with SVG sprite sheet in Qt Graphics View

Recently I started to port my game to Symbian platform,
In my previous post I showed how to make graphics work with different resolution.But if you are using images in application, you need to use SVG image to make sure that images looks good in all resoltion.

When I started to replace my game images with SVG image, I need to make changes in my sprite class.With normal images I was drawing only certain portion of image from Sprite sheet like code in this post, but I could not make that thing work with SVG. Then to make my sprite class work I made following changes.

In following code, I am using SVG file which contain each frame with unique id and in code on next frame I am rendering SVG element with id for particular frame.

Code for animatedsvgitem.h
#ifndef ANIMATEDSVGITEM_H
#define ANIMATEDSVGITEM_H

#include <QGraphicsSvgItem>
#include <QTimer>

class AnimatedSvgItem : public QGraphicsSvgItem
{
    Q_OBJECT
public:
    AnimatedSvgItem();
    QRectF boundingRect() const;

private slots:
    void nextFrame();

private:
    QTimer *timer;
    QStringList frames;
    int currentFrame;
};
#endif // ANIMATEDSVGITEM_H

Code for animatedsvgitem.cpp
#include "animatedsvgitem.h"

AnimatedSvgItem::AnimatedSvgItem()
    :QGraphicsSvgItem( QString(":/egg.svg") )
{
    //svg element id of frame in svg file
    frames << "egg1" << "egg2" << "egg3" << "egg4" << "egg5" << "egg6" << "egg7" << "egg8";
    currentFrame = 0;
    this->setElementId( frames[currentFrame] );
    timer = new QTimer(this);
    QObject::connect(timer,SIGNAL(timeout()),this,SLOT(nextFrame()));
    timer->start(400);
}

QRectF AnimatedSvgItem::boundingRect() const
{
    return QRectF(0,0,20,20);
}

void AnimatedSvgItem::nextFrame()
{
    currentFrame = ++currentFrame % 8;
    this->setElementId( frames[currentFrame]);
    this->moveBy(5,0);
    update();
}
So this was all, Sprite sheet used for above code is like this but in SVG format.





Here video from sample code.