1

I am new to the Qt and I am confused how to use the QGraphicsScene. If I add for example 10 ellipses to the scene and animate them along the path, how would I do it? It gets even more complicated, because if I have 10 ellipses drawn along the ellipse and I want to animate those ellipses so they move away from the cente of the ellipse they are on. In the picture you can see the ellipses. Those are drawn with QPainter I haven't figured out how to add them to scene, but I would like the grey ellipses to move in between the inner and outter circle. I have gone through some examples, but can't really fit any of those to my situation.

Code used to draw ellipses with QPainter:

QPainter drElps;
drElps.setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
drElps.setPen(QPen(QColor(0xaf, 0xaf, 0xaa), 2));
double nrElps = 30;
int degree = (int)(360./nrElps);
painter.translate(QPointF(big_elps_circle_center));
while(--nrElps){
 painter.drawEllipse(0, -60, 3, 3);
 painter.rotate(degree);
}
painter.translate(QPOintF(back_to_origin));

Static image

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Croolman
  • 1,103
  • 13
  • 40

3 Answers3

1

If you draw them with QPainter then you are on your on. The point of QGraphivsScene is to inherit from QGraphicsItem, paint what you need to paint on the paintEvent, add them to the scene.

you are drawint more than 1 item on your paintEvent - so you are on your own - and QGraphivsScene cannot help you there.

Now, how to do that Correctly:

  • Inherit from QGraphicsScene or QGraphivsView
  • Inherit from QGraphicsEllipseItem and create a MovableEllipseItem
  • in the MovableEllipseItem constructor, create a QTimer, connect it's timeout() signal with a move slot that you will define

on your move slot, do the calculations on where the ellipse should be and move(x,y) it.

Tomaz Canabrava
  • 2,320
  • 15
  • 20
  • Do I understand correctly that then for every ellipse added to the scene, there would be a seperate timer? – Croolman Apr 24 '17 at 08:19
  • not if you add the QTimer as static inside of the Ellipse object, then you would only have one. the important thing is to Connect the timeout() to every Ellipse instance. – Tomaz Canabrava Apr 24 '17 at 09:58
1

You can use a custom Graphics Item, the QGraphicsScene::advance mechanism, together with a QTimeLine to control the animation.

The outline is like this:

QGraphicsScene *sc = new QGraphicsScene;
QTimeLine *tl = new QTimeLine(1000);
tl->setFrameRange(-20, 20);
connect(tl, &QTimeLine::frameChanged, sc, &QGraphicsScene::advance);
connect(tl, &QTimeLine::finished, tl, &QTimeLine::toggleDirection);
connect(tl, &QTimeLine::finished, tl, &QTimeLine::start);

for (int i = 0; i < 30; ++i) {
    sc->addItem(new AnimItem(360./30*i, tl));
}

In the custom AnimItem, you implement the drawing/animation logic. A good base would be QGraphicsEllipseItem. For example:

AnimItem::AnimItem(qreal angle, QTimeLine *timer, QGraphicsItem *parent)
    : QGraphicsEllipseItem(0, 0, 3, 3, parent),
      m_timer(timer)
{
    QTransform t;
    t.rotate(angle);
    t.translate(0, -120);
    setTransform(t);
    setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
}

void AnimItem::advance(int phase)
{
    if (phase == 1) {
        QTransform t = transform();
        t.translate(0, m_timer->currentFrame()/5);
        setTransform(t);
    }
}
king_nak
  • 11,313
  • 33
  • 58
  • This code will create dynamically the ellipses around the bigger ellipse, wont it? That would do something like a loading animation. I need all of them to move on timeout in the same time - in best, but not necessary scenario, in random directions, in between those two bigger ellipses. I do admit I might have expressed myself not quite clearly. – Croolman Apr 24 '17 at 09:51
1

Qt has a set of classes oriented to the animations, for this you must first create an object that inherits from QGraphicsObject, in this class you must implement the methods paint and boundingRect.

ellipseobject.h

#ifndef ELLIPSEOBJECT_H
#define ELLIPSEOBJECT_H

#include <QGraphicsObject>

class EllipseObject : public QGraphicsObject
{
    Q_OBJECT
public:
    EllipseObject(QGraphicsItem * parent = 0);

    void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
    QRectF boundingRect() const;
};

#endif // ELLIPSEOBJECT_H

ellipseobject.cpp

#include "ellipseobject.h"

#include <QPainter>

EllipseObject::EllipseObject(QGraphicsItem *parent):QGraphicsObject(parent)
{

}

void EllipseObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(QColor(0xaf, 0xaf, 0xaa));
    painter->setBrush(QBrush(QColor(0xaf, 0xaf, 0xaa)));
    QRectF rect = boundingRect();
    painter->drawEllipse(rect);
}

QRectF EllipseObject::boundingRect() const
{
    return QRectF(-4, -4, 8, 8);
}

Then we can use the QPropertyAnimation class, with this we will animate the position.

EllipseObject *item = new EllipseObject;
QPropertyAnimation *animation = new QPropertyAnimation(item, "pos");
for(int j = 0; j < p; j++){
    animation->setKeyValueAt( 1.0*j/(p-1),
                               (r+ delta*sin(2*M_PI*j/p) )*QPointF(qSin(2*M_PI*i/number_of_items), qCos(2*M_PI*i/number_of_items)));
}
animation->setDuration(2000);

Since we have several elements that are animated in parallel, we can use QParallelAnimationGroup to handle each of the animations.

group = new QParallelAnimationGroup(this);
[...]
group->addAnimation(animation);

If we want to be continuous we can make the following code.

group->start();
connect(group, &QParallelAnimationGroup::finished,[=](){
    group->start();
});

The complete code is here.

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • This one is the best so far, althought it is not exactly what I wanted it gives a pretty clear idea. Though I think I use it with combination with `advance()` method BTW the github link leads to empty Qt project. – Croolman Apr 24 '17 at 18:09
  • `advance()`, what do you mean by that? – eyllanesc Apr 24 '17 at 18:26
  • The `advance()` method of the `QGraphicsScene` which can be called on timeout, something similar to @king_nak answer below – Croolman Apr 24 '17 at 18:28
  • Within your question you do not say anything about it, I propose a solution that for me is the optimal for this type of tasks. – eyllanesc Apr 24 '17 at 18:32
  • I do understand the name of the question is a bit missleading. The question was, how to animate the ellipses so they move in between the inner and outer green circle - away from the centre of the ellipse. That is the tricky part. – Croolman Apr 24 '17 at 18:43
  • want to oscillate from the smallest circle to the outermost circle? – eyllanesc Apr 24 '17 at 18:45
  • Yes, indeed, exactly – Croolman Apr 24 '17 at 18:49
  • I have noticed that I have uploaded the incorrect files, and update my repository, in a few moments I will make the change you want. – eyllanesc Apr 24 '17 at 18:53
  • 1
    Already update the code for what you want, in addition to the project repository [code](https://github.com/eyllanesc/stackoverflow/tree/master/QtAnimate). – eyllanesc Apr 24 '17 at 19:12
  • Is it possible to change the set duration of the animation dynamically based on the event? For example change it from 2000 to 1500 then to 2300, etc. – Croolman Apr 24 '17 at 20:35
  • I would recommend that the code you draw is a function that you can call whenever you want, and then you connect a slot to the signal you want where the duration changes. – eyllanesc Apr 24 '17 at 20:39