2016-11-24 3 views
1

이 프로젝트의 제 아이디어는 아이템에 스왑 애니메이션을 수행하는 것입니다. 그러나 항목을 처음으로 교환 할 때 여전히 위치를 유지하지만 이미 스왑 된 항목이 포함 된 다른 애니메이션이 시작되면 해당 항목은 원래 위치로 되돌아가는 문제가 있습니다. 내가 뭘 잘못하고 있는지 말해줘. 애니메이션은 다음과 같이QGraphicsView에서 QGraphicsItem의 교환을 수행하는 방법은 무엇입니까?

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 : 그 목적

UPD와 애니메이션을 사용하지 마십시오 것은 * :

+1

"UPD : 그 목적으로 애니메이션을 사용하지 마십시오"당신과 애니메이션을 사용해야 할 수 물론, 그냥 바로 :) –

+0

@KubaOber 그래,하지만 고려 사실 그렇게 나는 "Qt 5.3 Professional programming with C++"의 저자 인 Max Schlee에게 말했고, 이것을 사용하지 말라고 조언 받았고, 나는 그것을 사용하지 않기로 결정했다. 애니메이션으로 할 수는 없지만 엉덩이에는 큰 고통이 있습니다. 그리고 나는 이미 해결책을 찾았습니다. – Vadixem

+1

무엇이 가장 효과적이며 어떤 결과가 가장 가독성이 좋은 코드인지를 사용하십시오. 나는 당신이 많은 아이템을 애니메이트하지 않는 한 퍼포먼스가 많은 역할을 할 것이라는 것을 의심한다. 그렇다면'QAbstractAnimation'으로 시작해서'QObject' 아이템 등의 오버 헤드를 피할 수있는'QGraphicsItemPositionAnimation'을 구현할 수 있습니다 :) –

답변

1

귀하의 애니메이션이 항목의 위치를 ​​유지하고 이전 UPD를 잊어 참여 애니메이션이 생성 될 때. 두 번째 애니메이션이 실행될 때까지이 정보는 유효하지 않습니다.

시작시 키포인트 값을 업데이트하기 위해 애니메이션을 재 설계해야합니다. 또한 애니메이션 항목이 일정한 속도로 실행되도록하거나 적어도 전체 제어가 가능한 속도로 실행되도록 할 수 있습니다. 예를 들어

:

// 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

고맙습니다. 애니메이션을 사용하지 않고 이미 하나를 발견했습니다. 희망은 유용하다는 것을 희망합니다! – Vadixem