PDA

View Full Version : Draw a line animation



ZsCosa
27th July 2013, 14:51
http://i.stack.imgur.com/TQnIx.gif
Hi, I'm trying to achieve a drawing animation similar to the above. Can anyone suggest me a direction on this matter? Should I use a QTimer or should I use QPropertyAnimation and changing QLine properties. Thanks very much!

wysota
27th July 2013, 15:32
Either will work.

ZsCosa
27th July 2013, 20:27
Thanks alot for your reply. Do you think this could have been done using QGraphicsItemAnimation, I've been reading the documentation but can't find any properties that can support that. Or I've missed something, would really appreciate if you can give me a hint.

wysota
27th July 2013, 22:25
It could have but I don't see the point of looking for more solutions if you already have two to choose from.

ZsCosa
27th July 2013, 22:45
Actually I spent quite sometime to try the QGraphicsItemAnimation first, after failing I'm looking for more solutions. It still bugging me so it will be awesome if you can hint me with that so I can get it out of my head :D. Thanks anyway!

wysota
27th July 2013, 23:04
If you ask a specific question then I will answer it. All three of the solutions you found yourself have examples in the documentation on how to use them. QGraphicsItemAnimation is probably the worst approach you might have taken though since in your case it requires subclassing QGraphicsItemAnimation and reimplementing one of its virtual methods.

ZsCosa
28th July 2013, 13:49
Hi, I wanted to spent more time then come back with some questions so here I am.

I try with the QTimer approach so I draw a Line


void myitems::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPointF point1(0,0);
QPointF point2(100,100);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(Qt::red, 8, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin));
painter->setBrush(QBrush(Qt::blue, Qt::DiagCrossPattern));
painter->drawLine(point1,point2);
}

And create a timer:


timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
timer->start(100);


Connect timeout() with advance() slot, what should I put in this advance() to make the line draw, I've tried something but I was a mess.


void myitems::advance(int phase)
{
if(!phase) return;


}

Thank you.

wysota
28th July 2013, 16:01
If you wish to use advance(), put there some code that will change one of the end points of the line. Remember that you can only draw within the item's bounding rect so you'll need to update that as well. However the simplest approach would be to connect the timer to some slot and in the slot use QGraphicsLineItem::setLine() to update the line. There are more sophisticated solutions available however I suggest to avoid them if you don't feel comfortable with Qt yet.

ZsCosa
29th July 2013, 06:38
Thanks alot!! I'll try that and get back later.

ZsCosa
29th July 2013, 17:37
Hi, so here is what I got for myLine.h


#ifndef MYLINE_H
#define MYLINE_H

#include <QGraphicsLineItem>

class myLine : public QObject, public QGraphicsLineItem
{
Q_OBJECT

public:
myLine();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

public slots:
void mySlot();
private:
QLineF thisline;

};
#endif // MYLINE_H

myLine.cpp just to draw the line and try to implement the SLOT()


#include "myLine.h"

#include <QPainter>
#include <QApplication>

myLine::myLine()
{
thisline.setLine(0,0,50,50);
}

void myLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(Qt::red, 8, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin));
painter->setBrush(QBrush(Qt::blue, Qt::DiagCrossPattern));
painter->drawLine(thisline);
}


void myLine::mySlot()
{
for (int i = 1; i < 100; i++)
{
QLineF line = this->line();
line.setLine(0,0,50+i,50+i);
update();
}

}


There's a Dialog to display


#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTimer>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
Q_OBJECT

public:
explicit Dialog(QWidget *parent = 0);
~Dialog();

private:
Ui::Dialog *ui;
QGraphicsScene *scene;
QTimer *timer;
};

#endif // DIALOG_H

And the source for dialog.cpp where things happen!



#include "dialog.h"
#include "ui_dialog.h"
#include "myLine.h"

#include <QGraphicsView>

Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);

scene = new QGraphicsScene(this);

ui->graphicsView->setScene(scene);
ui->graphicsView->setSceneRect(0,0,700,700);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);

myLine *line = new myLine();
scene->addItem(line);

timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), line, SLOT(mySlot()));
timer->start(100);

}

Dialog::~Dialog()
{
delete ui;
}


- when compiles it only prints out the first line which is (0,0,50,50) I think and there's no animation. I've been trying to figure it out, can you please tell me what is wrong here? And thanks for being patience with me, I'm learning so... pardon my noobish!

wysota
29th July 2013, 18:03
The code looks fine (more or less). The problem has to be elsewhere (e.g. the event loop not working).

ZsCosa
29th July 2013, 18:23
I think its the loop too but it was only thing i can think of, any suggestion how I can fix them or do it differently ?

wysota
29th July 2013, 21:54
Sure. Declare a property (of type QPointF) in your dialog class that will control the end point of the line. Then use QPropertyAnimation to animate that property.


class Dialog : public QDialog {
Q_OBJECT
Q_PROPERTY(QPointF endPoint READ endPoint WRITE setEndPoint)
public:
// ...
QPointF endPoint() const { ... }
public slots:
void setEndPoint(const QPointF &pt) {
QLineF l = line->line();
l.setP2(pt);
line->setLine(l);
}
};

// ...

QPropertyAnimation *anim = new QPropertyAnimation(&dialog, "endPoint");
anim->setStartValue(...);
anim->setEndValue(...);
anim->setDuration(...);
anim->start();
Remember to run the event loop.

ZsCosa
30th July 2013, 16:09
I'VE DONE IT! I think what was not needed here is subclassing QGraphicsLineItem so I removed it then just add simple QGraphicsLineItem to the scene and using your above function to edit the endPoint and it worked. Much simpler, maybe I made it more complex then needed.

Thanks very much for you patience with me on this problem, I've learnt alot.

I've attached a working version in case someone ever need it.
dialog.h


#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTimer>
#include <QGraphicsLineItem>



namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(QPointF endPoint READ endPoint WRITE setEndPoint)

public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
QPointF endPoint() const;
public slots:
QPointF setEndPoint(const QPointF &pt);
private:
Ui::Dialog *ui;
QGraphicsScene *scene;
QTimer *timer;
QGraphicsLineItem *myLine;
};

#endif // DIALOG_H

dialog.cpp


#include "dialog.h"
#include "ui_dialog.h"


#include <QGraphicsView>
#include <QPropertyAnimation>

Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);

scene = new QGraphicsScene(this);

ui->graphicsView->setScene(scene);
ui->graphicsView->setSceneRect(0,0,300,300);
ui->graphicsView->setRenderHint(QPainter::Antialiasing);

myLine = new QGraphicsLineItem(0,0,10,10);
myLine->setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));


scene->addItem(myLine);

QPropertyAnimation *anim = new QPropertyAnimation(this, "endPoint");
anim->setStartValue(QPointF(50,50));
anim->setEndValue(QPointF(200,200));
anim->setDuration(1000);
anim->start();


}

Dialog::~Dialog()
{
delete ui;
}

QPointF Dialog::endPoint() const
{
QLineF l = myLine->line();
return l.p2();
}

QPointF Dialog::setEndPoint(const QPointF &pt)
{
QLineF l = myLine->line();
l.setP2(pt);
myLine->setLine(l);
}