Soy nuevo en Qt y estoy confundido sobre cómo usar QGraphicsScene. Si agrego, por ejemplo, 10 puntos suspensivos a la escena y los animo a lo largo del camino, ¿cómo lo haría? Se vuelve aún más complicado, porque si tengo 10 elipses dibujadas a lo largo de la elipse y quiero animar esas elipses para que se alejen del centro de la elipse en la que están. En la imagen puedes ver las elipses. Esos están dibujados con QPainter No he descubierto cómo agregarlos a la escena, pero me gustaría que las elipses grises se muevan entre el círculo interno y externo. He visto algunos ejemplos, pero realmente no puedo adaptar ninguno de ellos a mi situación.

Código utilizado para dibujar elipses con 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

1
Croolman 23 abr. 2017 a las 23:19

3 respuestas

La mejor respuesta

Qt tiene un conjunto de clases orientadas a las animaciones, para esto primero debe crear un objeto que herede de QGraphicsObject, en esta clase debe implementar los métodos paint y 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);
}

Entonces podemos usar la clase QPropertyAnimation, con esto animaremos la posición.

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);

Como tenemos varios elementos que están animados en paralelo, podemos usar QParallelAnimationGroup para manejar cada una de las animaciones.

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

Si queremos ser continuos podemos hacer el siguiente código.

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

El código completo es aquí. .

enter image description here

1
eyllanesc 24 abr. 2017 a las 19:11

Si los dibuja con QPainter, entonces está listo. El objetivo de QGraphivsScene es heredar de QGraphicsItem, pintar lo que necesita pintar en el paintEvent, agregarlos a la escena.

Estás dibujando más de 1 elemento en tu paintEvent, por lo que estás solo, y QGraphicsScene no puede ayudarte allí.

Ahora, cómo hacer eso correctamente:

  • Heredar de QGraphicsScene o QGraphivsView
  • Herede de QGraphicsEllipseItem y cree un MovableEllipseItem
  • en el constructor MovableEllipseItem, cree un QTimer, conecte su señal de tiempo de espera () con una ranura de movimiento que definirá

En su ranura de movimiento, haga los cálculos sobre dónde debería estar la elipse y muévala (x, y).

1
Tomaz Canabrava 24 abr. 2017 a las 07:54

Puede usar un elemento gráfico personalizado, el mecanismo QGraphicsScene::advance, junto con un QTimeLine para controlar la animación.

El esquema es así:

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));
}

En el AnimItem personalizado, implementa la lógica de dibujo / animación. Una buena base sería QGraphicsEllipseItem. Por ejemplo:

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);
    }
}
1
king_nak 24 abr. 2017 a las 08:45