Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

Friday, December 10, 2010

Creating custom QItemDelegate with QPushButton in Qt

Recently I was working on Audiobook Reader application for maemo. For this application I have created one custom item delegate for QListView which contains two clickable item and generate signal accordingly.

For application I was using some different mechanism to create button inside custom item delegate, but now I found better way to create custom item delegate with QPushButton. So thought to share code.

Following is code for custom item delegate derived from QItemDelegate.

Update: I have uploaded following code to gitorous, Please visit this link if you want a working sample code.

#include <QItemDelegate>
class CustomItemDelegate : public QItemDelegate
{
    Q_OBJECT
public:
    CustomItemDelegate(QObject *parent = 0);
    virtual void paint(QPainter *painter,
                       const QStyleOptionViewItem &option,
                       const QModelIndex &index) const ;

    virtual QSize sizeHint(const QStyleOptionViewItem &option,
                           const QModelIndex &index) const ;

    bool editorEvent(QEvent *event, QAbstractItemModel *model, 
                           const QStyleOptionViewItem &option, 
                           const QModelIndex &index);

signals:
    void buttonClicked(const QModelIndex &index);
private:
    QStyle::State  _state;
};

#include "customitemdelegate.h"
...

CustomItemDelegate::CustomItemDelegate(QObject *parent) :
    QItemDelegate(parent)
{
    _state =  QStyle::State_Enabled;
}

void CustomItemDelegate::paint(QPainter *painter,
                   const QStyleOptionViewItem &option,
                   const QModelIndex &index) const
{
   const QStandardItemModel* model = 
   static_cast<const QStandardItemModel*>(index.model());
   QStandardItem* item = model->item(index.row());

   QString text = item->text();
   QRect rect = option.rect;

    QRect textRect( rect);
    textRect.setHeight( 30);
    painter->drawText(textRect,text);

    QRect buttonRect( rect);
    buttonRect.setY(textRect.y()+ 35);
    buttonRect.setHeight( 30);
    QStyleOptionButton button;
    button.rect = buttonRect;
    button.text = text;
    button.state = _state | QStyle::State_Enabled;

    QApplication::style()->drawControl
        (QStyle::CE_PushButton, &button, painter);
}

QSize CustomItemDelegate::sizeHint(const QStyleOptionViewItem &/*option*/,
                       const QModelIndex &/*index*/) const
{
    //hard coding size for test purpose, 
    //actual size hint can be calculated from option param
    return QSize(800,70);
}

bool CustomItemDelegate::editorEvent(QEvent *event, 
    QAbstractItemModel *model, 
    const QStyleOptionViewItem &option, 
    const QModelIndex &index)
{
    if( event->type() == QEvent::MouseButtonPress ||
        event->type() == QEvent::MouseButtonRelease ) {
    } else {
         //ignoring other mouse event and reseting button's state
         _state = QStyle::State_Raised;
        return true;
    }

    QRect buttonRect( option.rect);
    buttonRect.setY(option.rect.y()+ 35);
    buttonRect.setHeight( 30);

    QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
    if( !buttonRect.contains( mouseEvent->pos()) ) {
        _state = QStyle::State_Raised;
        return true;
    }

    if( event->type() == QEvent::MouseButtonPress) {            
        _state = QStyle::State_Sunken;
    } else if( event->type() == QEvent::MouseButtonRelease) {
        _state = QStyle::State_Raised;
        emit buttonClicked( index);
    }    
    return true;
}
Basically in above code, I am calculating rect where I want to draw my button and drawing QPushButton on list item using QStyleOptionButton class.

And in editor event on mouse press and release event, I am checking if mouse position on click falls into my button's rect or not. If it falls inside my button's rect then I am emitting signal.

I am using item's signal as shown in below code.
CustomList::CustomList(QWidget *parent) :
    QWidget(parent),_view(0)
{
    _view = new QListView();
    _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    _view->setEditTriggers(QAbstractItemView::NoEditTriggers);

    //creating custom item delegate and setting  it to view
    CustomItemDelegate* itemDelegate = new CustomItemDelegate(_view);
    _view->setItemDelegate( itemDelegate );

    _view->setModel(&_model); 
   
    //connecting delegate's signal to this class's slot
    connect(itemDelegate,SIGNAL(buttonClicked(QModelIndex)),
    this,SLOT(listButtonClicked(QModelIndex)));

    QHBoxLayout* mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget( _view);

    //creating and adding data to model
    QStandardItem* item = new QStandardItem;
    item->setText("testing");

    QStandardItem* item1 = new QStandardItem;
    item1->setText("testing1");

    _model.appendRow(item);
    _model.appendRow(item1);
}

// following slot will be invoked when delegate's button is clicked
void CustomList::listButtonClicked(const QModelIndex &index)
{
    qDebug() << "######### listbutton clicked ######### " << index.row();
}
Following is snap of how custom item delegate looks.

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.

Saturday, October 23, 2010

Creating Qt game for mobile device with differnt screen size

Till now I have created few Qt games for Maemo 5 (N900) device, and now I wanted to run same games on Symbian device which support Qt.

First problem I encounter doing so is different screen size from N900, in my current game implementation I have hard coded screen size and coordination of game object.

After playing with graphics framework from some time, I created a small prototype application that shows two rectangle one on top left other on bottom right corner and can run on different Symbian device and N900 as well. So first thing I did is to remove all hard coded coordinate and implemented resize event, then scaled all graphics item which are added to graphics scene.

Following is my prototype code.

In below code, I have added QGraphicsView as QMainWindow's central widget and also reimplemented resizeEvent of QMainWindow.
GraphicsWidget::GraphicsWidget(QWidget *parent)
    : QMainWindow(parent)
{    
    _scene = new MyScene();
    _scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    _view = new QGraphicsView(_scene,this);
    _view->setDragMode(QGraphicsView::NoDrag);
    _view->setCacheMode(QGraphicsView::CacheBackground);
    _view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    setCentralWidget(_view);

    // Disable scrollbars
    _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    _view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}

void GraphicsWidget::resizeEvent(QResizeEvent* event)
{
    _scene->setSceneRect(_view->rect());
    QWidget::resizeEvent(event);
}

And then in MyScene class, derived from QGraphicsScene, on sceneRectChanged signal i am scaling all item to scale factor according to my reference screen size, which is of n900 device's screen size.
MyScene::MyScene()
    :QGraphicsScene()
{
    item1 = new MyGraphicsItem();
    addItem( item1 );

    item2 = new MyGraphicsItem();
    addItem( item2 );
    QObject::connect(this,SIGNAL(sceneRectChanged(QRectF)),
                    this,SLOT(resize(QRectF)));
}

void MyScene::resize(const QRectF& newRect)
{
    qreal w = (qreal)newRect.width()/800 ;
    qreal h =  (qreal)newRect.height()/480;

    item1->resetMatrix();
    item1->scale(w,h);
    item1->setPos(newRect.topLeft());

    item2->resetMatrix();
    item2->scale(w,h);
    item2->setPos(newRect.width()-(item2->boundingRect().width()*w),
            newRect.height() - (item2->boundingRect().height()*h));
}
Finally in main function, I am showing QMainWindow in full screen mode.
int main(int argc, char* argv[] )
{
    QApplication app(argc,argv);
    GraphicsWidget widget;
    widget.showFullScreen();
    return app.exec();
}
Following are snaps from prototype running on emulator for different device.



Thursday, October 21, 2010

Using QStateMachine with sprite animation

In 4.6 Qt introduced new State machine framework. Framework looks quite easy to use and extensible for complex use-case.
To explore Qt's State machine framework I thought to use it with sprite animation, as a complex sprite also holds many state and maintaining sprite state and transition from one state to another can become pain if not done properly.

So following is my sample code for prince sprite which use QStateMachine to maintain sprite's state and transition from one state to another. From somewhere in Internet I found sprite sheet for Prince of Persia, to make it easy for sample code I created another simple version of original sprite sheet.
PrinceSprite::PrinceSprite(QGraphicsItem * parent)
    :QGraphicsObject(parent)
{
    mSpriteImage = new QPixmap(":/prince.png");
    changeDir(this);

    //creating different state object , 
    //which are derived from QState and l
    //istening to timer's timeout signal 
    //and also modify frame according to animation
    StandingState* standing = new StandingState(this);
    RunningState* running = new RunningState(this);
    PrepareForAttack* prepareForAttack = 
                        new PrepareForAttack(this);
    Attack* attack = new Attack(this);
    InAttackMode* inAttackMode = new InAttackMode(this);

    //adding transition to state
    //goto running state from stating state at left click
    standing->addTransition(this,SIGNAL(leftClick()),running);
    running->addTransition(this,SIGNAL(leftClick()),standing);
    standing->addTransition(this,SIGNAL(rightClick())
                                    ,prepareForAttack);
    prepareForAttack->addTransition(prepareForAttack,
                        SIGNAL(exited()),inAttackMode);
    inAttackMode->addTransition(this,SIGNAL(leftClick()),attack);
    attack->addTransition(attack,SIGNAL(exited()),inAttackMode);
    inAttackMode->addTransition(this,
                     SIGNAL(rightClick()),standing);

    //adding all state to QStateMachine
    _stateMachine.addState(standing);
    _stateMachine.addState(running);
    _stateMachine.addState(prepareForAttack);
    _stateMachine.addState(attack);
    _stateMachine.addState(inAttackMode);

    //setting initial state and then starting state machine
    _stateMachine.setInitialState( standing);
    _stateMachine.start();
}

In above code I have create different state object for different sprite state, like for StandingState or Attack state.
This state classes are derived from QState class and listen to QTimer's timeout signal to change frame according to animation.

For example following code for Attack state.
#include <QState>

class Attack : public QState
{
    Q_OBJECT
public:
    Attack(PrinceSprite* prince);
    void onEntry ( QEvent * event );
    void onExit ( QEvent * event );

signals:
    void exited();

private slots:
    void nextFrame();
};

#include "attack.h"

Attack::Attack(PrinceSprite* prince)
    :QState()
{}

void Attack::nextFrame()
{    
    _prince->_x += 1;
    _prince->setX(_prince->x() + 7);
    if (_prince->_x >= 4 ) {
        emit exited();
    }
}

void Attack::onEntry ( QEvent * event )
{
    QObject::connect(_prince,SIGNAL(tick()),
                     this,SLOT(nextFrame()));
    _prince->_x = 0;
    _prince->_y = 2;
}

void Attack::onExit ( QEvent * event )
{
    QObject::disconnect(_prince,SIGNAL(tick()),
                        this,SLOT(nextFrame()));
}

So finally, I am not sure if this is correct approach to implement sprite or not, but using Qt's State machine framework is helping a lot to make it simpler.

Follwing is demo of my implementation.

Sunday, October 3, 2010

Detecting Swipe gesture in Qt

In continuation of my previous post of long press gesture in Qt, I created another simple gesture class to detect swipe gesture.

In my implementation, I am storing initial coordinate on mouse press event and comparing initial coordinate with coordinate on mouse release event.In this code I have not considered speed of swipe but we can easily measure speed of swipe by measuring time difference between two event.

myswipegesture.h file
#ifndef MYSWIPEGESTURE_H
#define MYSWIPEGESTURE_H

#include <QObject>
#include <QPoint>

class MySwipeGesture : public QObject
{
    Q_OBJECT
public:
    explicit MySwipeGesture(QObject *parent = 0);
    void handleEvent( QEvent *event);
public:
    enum SwipeDirection {
        Left = 0,
        Right,
        Up,
        Down
    };
signals:
    void handleSwipe( MySwipeGesture::SwipeDirection direction );
private:
    QPoint _startPoint;
    QPoint _endPoint;
};
#endif // MYSWIPEGESTURE_H
myswipegesture.cpp file
#include "myswipegesture.h"
#include <QEvent>
#include <QMouseEvent>

MySwipeGesture::MySwipeGesture(QObject *parent)
    :QObject(parent),_startPoint(0,0),_endPoint(0,0)
{}

void MySwipeGesture::handleEvent( QEvent *event)
{
    if( event->type() == QEvent::MouseButtonPress ) {
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
        _startPoint = mouseEvent->pos();
    } else if( event->type() == QEvent::MouseButtonRelease ) {
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
        _endPoint = mouseEvent->pos();

        //process distance and direction
        int xDiff = _startPoint.x() - _endPoint.x();
        int yDiff = _startPoint.y() - _endPoint.y();
        if( qAbs(xDiff) > qAbs(yDiff) ) {
            // horizontal swipe detected, now find direction
            if( _startPoint.x() > _endPoint.x() ) {
                emit handleSwipe( Left);
            } else {
                emit handleSwipe( Right);
            }
        } else {
            // vertical swipe detected, now find direction
            if( _startPoint.y() > _endPoint.y() ) {
                emit handleSwipe( Up);
            } else {
                emit handleSwipe( Down);
            }
        }
    } else if( event->type() == QEvent::MouseMove ) {
        //ignore event
    }
}
Some test code.
#include "myswipegesture.h"

TestWidget::TestWidget(QWidget *parent) : QWidget(parent)
{
    _swipeGesture = new MySwipeGesture(this);
    connect(_swipeGesture,SIGNAL(handleSwipe(MySwipeGesture::SwipeDirection)),this,SLOT(swipe(MySwipeGesture::SwipeDirection)));
}

bool TestWidget::event(QEvent *event)
{
    _swipeGesture->handleEvent(event);
    return QWidget::event(event);
}

void TestWidget::swipe(MySwipeGesture::SwipeDirection direction)
{
    qDebug() << "swipe" << direction;
}
Hope this helps.

Sunday, September 26, 2010

Detecting Tap And Hold (Long Press) event in Qt

Recently while working on Qt project I required to recognize Tap and Hold (Long Press) gesture. Qt introduced new gesture handler library in 4.6 but some how it was not working on my machine and I needed to create my own solution. I already have created one custom solution for detecting Tap and Hold for iOS and I tried to port same thing to Qt as well.

Following is my code to detect Tap and Hold for Qt. In code I am starting timer on mouse press event, if timer expire then we have Tap and Hold event. If user release mouse while timer is still active then Tap and Hold is canceled.
#ifndef MYTAPANDHOLDGESTURE_H
#define MYTAPANDHOLDGESTURE_H

#include <QObject>

class QTimer;

class MyTapAndHoldGesture: public QObject
{
    Q_OBJECT
public:
    MyTapAndHoldGesture( QObject *parent = 0 );
    void handleEvent( QEvent *event);

signals:
    void handleTapAndHold();

private slots:
    void timeout();

private:
    QTimer* _timer;
};

#endif // MYTAPANDHOLDGESTURE_H

#include "mytapandholdgesture.h"

#include <QMouseEvent>
#include <QTimer>

MyTapAndHoldGesture::MyTapAndHoldGesture( QObject *parent )
{
    _timer = new QTimer(this);
    connect(_timer,SIGNAL(timeout()),this,SLOT(timeout()));
}

void MyTapAndHoldGesture::handleEvent( QEvent *event)
{
    if( event->type() == QEvent::MouseButtonPress ) {
        _timer->start( 1000 );
    } else if( event->type() == QEvent::MouseButtonRelease ) {
        if( _timer->isActive() ) {
            // tap and hold canceled
            _timer->stop();
        }
    } else if( event->type() == QEvent::MouseMove ) {
        // tap and hold canceled
        _timer->stop();
    }
}

void MyTapAndHoldGesture::timeout()
{
    emit handleTapAndHold();
    _timer->stop();
}

Widget code for testing.
#ifndef TESTWIDGET_H
#define TESTWIDGET_H

#include <QWidget>

class MyTapAndHoldGesture;

class TestWidget : public QWidget
{
    Q_OBJECT
public:
    TestWidget(QWidget *parent = 0);

private:
    bool event(QEvent *event);

private slots:
    void tapAndHold();

private:
    MyTapAndHoldGesture* _gestureHandler;
};

#endif // TESTWIDGET_H

#include "testwidget.h"
#include <QDebug>
#include "mytapandholdgesture.h"

TestWidget::TestWidget(QWidget *parent) :
    QWidget(parent)
{
    _gestureHandler = new MyTapAndHoldGesture(this);
    connect(_gestureHandler,SIGNAL(handleTapAndHold()),
    this,SLOT(tapAndHold()));
}

bool TestWidget::event(QEvent *event)
{
    _gestureHandler->handleEvent( event );
    return QWidget::event(event);
}

void TestWidget::tapAndHold()
{
    qDebug() << "tapAndHold";
}

Wednesday, September 15, 2010

Simple priority queue with Qt

Recently in one of my Qt project I required to use priority queue and found that Qt framework dose not provide such container. Then I decided to write one my self.

Following is simple version of my actual implementation.

In code Queue class implement simple priority queue. Internally I am using QQueue as storage and created one helper structure which holds data and its priority. Actual work is done inside enqueue method, which check priority of item being inserted with other stored items and insert item at appropriate index.
#ifndef QUEUE_H
#define QUEUE_H

#include <QQueue>
#include <QDebug>

enum Priority {
    Normal = 0,
    High = 1
};

template<class T>
class Queue
{
public:

    Queue()
    {}

    void enqueue(Priority priority,T value)
    {
        Item<T> item(priority,value);
        for(int i = 0 ; i < _queue.count() ; ++i ) {
            const Item<T>& otherItem = _queue[i];
            if( priority > otherItem._priority )  {
                _queue.insert(i,item);
                return;
            }
        }
        _queue.append(item);
    }

    T dequeue()
    {
        const Item<T>& item = _queue.dequeue();
        return item._value;
    }

    int count()
    {
        return _queue.count();
    }

private:

    template<class C>
    struct Item
    {
        Priority _priority;
        C _value;

        Item(Priority priority, C value)
        {
            _priority = priority;
            _value = value;
        }
    };

    QQueue< Item<T > > _queue;

};

#endif // QUEUE_H

Some basic test code.
{
    //testing with int as value
    Queue<int> q;
    q.enqueue(High,1);
    q.enqueue(Normal,2);
    q.enqueue(High,3);
    q.enqueue(Normal,5);
    q.enqueue(Normal,6);
    q.enqueue(High,4);

    //output should be 1,3,4,2,5,6
    while( q.count() > 0 )
    {
        qDebug() << q.dequeue();
    }
}