2016-11-24 3 views
1

Meine Idee in diesem Projekt ist es, Swap-Animation auf Gegenstände durchzuführen. Problem ist jedoch, dass wenn ich die Objekte zum ersten Mal austausche, behalten sie ihre Position, aber wenn die andere Animation beginnt, die bereits getauschte Objekte beinhaltet, fallen diese Objekte auf ihre ursprünglichen Positionen zurück. Bitte sag mir was ich falsch mache. Animation wie folgt:Wie führe ich den Austausch von QGraphicsItems in QGraphicsView durch?

enter image description here

#include <QtCore> 
    #include <QtWidgets> 


    /** 
    * Element to be displayed in QGraphicsView 
    */ 
    class QGraphicsRectWidget : public QGraphicsWidget 
    { 
     Q_OBJECT 
     int m_number; 

    public: 

     void changePosition(QGraphicsRectWidget *other) 
     { 
      setPos(mapToParent(other->x() < x() ? -abs(x() - other->x()) 
                 : abs(x() - other->x()) ,0)); 
     } 

     static int NUMBER; 

     QGraphicsRectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent), m_number(NUMBER) 
     { NUMBER++;} 
     void paint(QPainter *painter, const QStyleOptionGraphicsItem *, 
        QWidget *) Q_DECL_OVERRIDE 
     { 
      painter->fillRect(rect(), QColor(127, 63, 63)); 
      painter->drawText(rect(), QString("%1").arg(m_number), QTextOption(Qt::AlignCenter)); 
     } 

    }; 

    int QGraphicsRectWidget::NUMBER = 1; 


    class MyAnim : public QPropertyAnimation 
    { 
     Q_OBJECT 

     QGraphicsView &pview; // View in which elements must be swapped 
     int i1, i2;   // Indices for elements to be swapped 

    public: 
     MyAnim(QGraphicsView &view, int index1 = 0, int index2 = 1, QObject *par = 0) 
      : QPropertyAnimation(par), pview(view), i1(index1), i2(index2) 
     { 
      QObject::connect(this, SIGNAL(finished()), SLOT(slotOnFinish())); 
     } 

    public slots: 
     /* !!!!!!!!!!!!!!!!!!!!!!! HERE IS THE PROBLEM (brobably) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ 
     // Triggered when animation is over and sets position of target element to position of its end value 
     void slotOnFinish() 
     { 
      auto list = pview.items(); 

      static_cast<QGraphicsRectWidget*>(list.at(i1)) 
        ->changePosition(static_cast<QGraphicsRectWidget*>(list.at(i2))); 
     } 
    }; 

    class GraphicsView : public QGraphicsView 
    { 
     Q_OBJECT 
    public: 
     GraphicsView(QGraphicsScene *scene, QWidget *parent = NULL) : QGraphicsView(scene, parent) 
     { 
     } 

    protected: 
     virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE 
     { 
      fitInView(scene()->sceneRect()); 
      QGraphicsView::resizeEvent(event); 
     } 
    }; 


    #define SWAP_HEIGHT 75 

    /** 
    * Creates swap animation for items in QGraphicsView 
    */ 
    QParallelAnimationGroup* getSwapAnimation(QGraphicsView &view, int noItem1, int noItem2) 
    { 
     auto list = view.items(); 
     QGraphicsRectWidget *wgt1 = static_cast<QGraphicsRectWidget*>(list.at(noItem1)); 
     QGraphicsRectWidget *wgt2 = static_cast<QGraphicsRectWidget*>(list.at(noItem2)); 

     MyAnim *pupperAnim, *plowerAnim; 
     QParallelAnimationGroup *par = new QParallelAnimationGroup; 

     plowerAnim = new MyAnim(view, noItem1, noItem2); 
     plowerAnim->setTargetObject(wgt2); 
     plowerAnim->setPropertyName("pos"); 
     plowerAnim->setDuration(5000); 
     plowerAnim->setKeyValueAt(1.0/3.0, QPoint(wgt2->x(), wgt1->y() - SWAP_HEIGHT)); 
     plowerAnim->setKeyValueAt(2.0/3.0, QPoint(wgt1->x(), wgt1->y() - SWAP_HEIGHT)); 
     plowerAnim->setEndValue(wgt1->pos()); 


     pupperAnim = new MyAnim(view, noItem2, noItem1); 
     pupperAnim->setTargetObject(wgt1); 
     pupperAnim->setPropertyName("pos"); 
     pupperAnim->setDuration(5000); 
     pupperAnim->setKeyValueAt(1.0/3.0, QPoint(wgt1->x(), wgt2->y() + SWAP_HEIGHT)); 
     pupperAnim->setKeyValueAt(2.0/3.0, QPoint(wgt2->x(), wgt2->y() + SWAP_HEIGHT)); 
     pupperAnim->setEndValue(wgt2->pos()); 

     par->addAnimation(pupperAnim); 
     par->addAnimation(plowerAnim); 
     return par; 
    } 

    int main(int argc, char **argv) 
    { 
     QApplication app(argc, argv); 

     QGraphicsRectWidget *button1 = new QGraphicsRectWidget; 
     QGraphicsRectWidget *button2 = new QGraphicsRectWidget; 
     QGraphicsRectWidget *button3 = new QGraphicsRectWidget; 
     QGraphicsRectWidget *button4 = new QGraphicsRectWidget; 
     button2->setZValue(1); 
     button3->setZValue(2); 
     button4->setZValue(3); 
     QGraphicsScene scene(0, 0, 300, 300); 
     scene.setBackgroundBrush(QColor(23, 0, 0)); 
     scene.addItem(button1); 
     scene.addItem(button2); 
     scene.addItem(button3); 
     scene.addItem(button4); 
     GraphicsView window(&scene); 
     window.setFrameStyle(0); 
     window.setAlignment(Qt::AlignLeft | Qt::AlignTop); 
     window.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
     window.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 


     QList<QGraphicsItem*> items = window.items(); 
     QPoint start(20, 125); 

     for (auto item : items) // Set items in initial position 
     { 
      QGraphicsWidget *wgt = static_cast<QGraphicsWidget*>(item); 
      wgt->resize(50,50); 
      wgt->moveBy(start.x(), start.y()); 
      start.setX(start.x() + 70); 
     } 


     QSequentialAnimationGroup gr; 

     gr.addAnimation(getSwapAnimation(window, 0, 1)); 
     gr.addAnimation(getSwapAnimation(window, 1, 2)); 
     gr.addAnimation(getSwapAnimation(window, 2, 3)); 
     gr.addAnimation(getSwapAnimation(window, 3, 1)); 
     gr.start(); 

     window.resize(300, 300); 
     window.show(); 

     return app.exec(); 
    } 

    #include "main.moc" 

UPD: Sie Animation nicht verwenden, um mit diesem Zweck

UPD *: Vergessen vorherigen UPD

+1

„UPD: Sie Animation nicht mit diesem Zweck verwenden“ Natürlich können Sie und eine Animation verwenden sollten, tun es gerade richtig :) –

+0

@KubaOber Ja, aber wenn man bedenkt, dass Ich sprach mit Max Schlee, der Autor von "Qt 5.3 Professionelles Programmieren mit C++" ist, und mir wurde geraten, keinen zu verwenden. Ich entschied mich, keinen zu verwenden. Ich behaupte nicht, dass du es nicht mit Animation machen kannst, aber es ist ein großer Schmerz im Gesäß. Und ich habe bereits eine Lösung gefunden – Vadixem

+1

Verwenden Sie, was funktioniert und was zu den meisten lesbaren Code führt. Ich bezweifle, dass die Performance eine große Rolle spielen wird, wenn Sie nicht viele Elemente animieren. Wenn Sie dies tun, können Sie mit einer 'QAbstractAnimation' beginnen und eine' QGraphicsItemPositionAnimation' implementieren, die den Overhead von 'QObject' -Elementen etc. vermeidet. –

Antwort

1

Ihre Animation behält die Position der Elemente beteiligt Zu der Zeit wird die Animation erstellt. Zu dem Zeitpunkt, zu dem die zweite Animation ausgeführt wird, sind diese Informationen ungültig.

Sie müssen die Animation neu gestalten, um ihre Schlüsselpunktwerte zum Zeitpunkt des Starts zu aktualisieren. Sie möchten vielleicht auch sicherstellen, dass die animierten Objekte mit einer konstanten Geschwindigkeit ausgeführt werden - oder zumindest mit einer Geschwindigkeit, über die Sie die volle Kontrolle haben.

Zum Beispiel:

// https://github.com/KubaO/stackoverflown/tree/master/questions/scene-anim-swap-40787655 
#include <QtWidgets> 
#include <cmath> 

class QGraphicsRectWidget : public QGraphicsWidget 
{ 
public: 
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override 
    { 
     painter->fillRect(rect(), Qt::blue); 
     painter->setPen(Qt::yellow); 
     painter->drawText(rect(), QString::number(zValue()), QTextOption(Qt::AlignCenter)); 
    } 
}; 

class SwapAnimation : public QPropertyAnimation 
{ 
    QPointer<QObject> other; 
    qreal offset; 
    QPoint propertyOf(QObject *obj) { 
     return obj->property(propertyName().constData()).toPoint(); 
    } 
    void updateState(State newState, State oldState) override { 
     if (newState == Running && oldState == Stopped) { 
     auto start = propertyOf(targetObject()); 
     auto end = propertyOf(other); 
     auto step1 = fabs(offset); 
     auto step2 = QLineF(start,end).length(); 
     auto steps = 2.0*step1 + step2; 
     setStartValue(start); 
     setKeyValueAt(step1/steps, QPoint(start.x(), start.y() + offset)); 
     setKeyValueAt((step1+step2)/steps, QPoint(end.x(), end.y() + offset)); 
     setEndValue(end); 
     setDuration(10.0 * steps); 
     } 
     QPropertyAnimation::updateState(newState, oldState); 
    } 
public: 
    SwapAnimation(QObject *first, QObject *second, qreal offset) : other(second), offset(offset) { 
     setTargetObject(first); 
     setPropertyName("pos"); 
    } 
}; 

QParallelAnimationGroup* getSwapAnimation(QObject *obj1, QObject *obj2) 
{ 
    auto const swapHeight = 75.0; 
    auto par = new QParallelAnimationGroup; 
    par->addAnimation(new SwapAnimation(obj2, obj1, -swapHeight)); 
    par->addAnimation(new SwapAnimation(obj1, obj2, swapHeight)); 
    return par; 
} 

int main(int argc, char **argv) 
{ 
    QApplication app(argc, argv); 

    QGraphicsScene scene(0, 0, 300, 300); 
    QGraphicsRectWidget buttons[4]; 
    int i = 0; 
    QPointF start(20, 125); 
    for (auto & button : buttons) { 
     button.setZValue(i++); 
     button.resize(50,50); 
     button.setPos(start); 
     start.setX(start.x() + 70); 
     scene.addItem(&button); 
    } 

    QSequentialAnimationGroup gr; 
    gr.addAnimation(getSwapAnimation(&buttons[0], &buttons[1])); 
    gr.addAnimation(getSwapAnimation(&buttons[1], &buttons[2])); 
    gr.addAnimation(getSwapAnimation(&buttons[2], &buttons[3])); 
    gr.addAnimation(getSwapAnimation(&buttons[3], &buttons[1])); 
    gr.start(); 

    QGraphicsView view(&scene); 
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
    view.resize(300, 300); 
    view.show(); 
    return app.exec(); 
} 
+0

Vielen Dank für Ihre Lösung! Ich habe allerdings schon eines ohne Animation gefunden. Hoffe man findet es nützlich! – Vadixem

Verwandte Themen