Search code examples
qtkeypressqpropertyanimation

use of qpropretyanimation desirably


In this program I have built a QPropertyanimation and add to it my item and pos() property. I override KeyPressEvent. And with using of keys consist of j, f, z item go forward ,go back and jump.

According gravity when item jump should fall. For this purpose I call down function. But item just once jump don't fall. I also have another problem: when the first press j and f (forward and back) item animate desirably but for next times item go forward and go back all of scene.

I mean It should animated for example 40 pixel but It animated 800 pixel.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    QPoint start;
    QPoint end;
    ~MainWindow();
private:
    QGraphicsView* view;
    QGraphicsScene* scene;
    void keyPressEvent(QKeyEvent* k);
    MyQgraphicsObject* m;
    QPropertyAnimation* pr;
    QElapsedTimer* timer;
    int f;
    int u;
    int b;
    void forward();
    void up();
    void back();
    void down();
};

MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
{
    view=new QGraphicsView;
    scene=new QGraphicsScene;
    m=new MyQgraphicsObject;
    pr=new QPropertyAnimation(m,"pos");
    view->setScene(scene);
    view->resize(800,800);
    view->setFixedSize(800,800);
    setCentralWidget(view);
    scene->addItem(m);
    start= QPoint(0,0);
    f=30;
    u=-30;
    b=-30;
}

void MainWindow::keyPressEvent(QKeyEvent *k)
{
    switch (k->key()) {
    case Qt::Key_J: {
        forward();
        break;
        }
   case Qt::Key_Z: {
        up();
        down();
        break;
        }
   case Qt::Key_F: {
        back();
        break;
        }
   default:
        break;
   }
}

void MainWindow::forward()
{
    end.setX(f);
    pr->setEndValue(end);
    pr->setDuration(1000);
    pr->setEasingCurve(QEasingCurve::Linear);
    pr->start();
    f+=40;
}

void MainWindow::up()
{
    end.setY(u);
    pr->setEndValue(end);
    pr->setDuration(1000);
    pr->setEasingCurve(QEasingCurve::Linear);
    pr->start();
    u-=30;
    pr->pause();
}

void MainWindow::back()
{
    end.setX(b);
    pr->setEndValue(end);
    pr->setDuration(1000);
    pr->setEasingCurve(QEasingCurve::Linear);
    pr->start();
    b-=40;
}

void MainWindow::down()
{
    u+=30;
    end.setY(u);
    pr->setEndValue(end);
    pr->setDuration(1000);
    pr->setEasingCurve(QEasingCurve::Linear);
    pr->start();
}

Solution

  • You should not use resize and setFixedSize on view because you use it in setCentralWidget and its size will be managed by the layout. You should use setFixedSize on main window instead.

    Animations are asynchonous. For example, when you call up(); down(), these functions will be executed without 1-second pause. Also, starting animation when it's already started has no effect.

    Usually animations are used in such way when you know exactly where you need object to move in the next second. It's complicated to receive directives from user and change object's trajectory when the animation is already performing.

    Here is an example showing correct use of animations for this task. Object can receive one directive (forward, back or jump) per second, and according animation will be performed in the next second.

    Header:

    class MainWindow : public QMainWindow {
      Q_OBJECT      
    public:
      explicit MainWindow(QWidget *parent = 0);
      ~MainWindow();
    private:
      QGraphicsView* view;
      QGraphicsScene* scene;
      void keyPressEvent(QKeyEvent* k);
      QGraphicsObject* object;
      QPropertyAnimation* animation;
      QPointF pos;
      double speed;
    
      enum Command {
        command_none, command_jump, command_forward, command_back
      };
      Command next_command;
    
    private slots:
      void timeout();
    };
    

    Source:

    MainWindow::MainWindow(QWidget *parent) :
            QMainWindow(parent)
    {
      view = new QGraphicsView;
      scene = new QGraphicsScene;
      object = scene->addWidget(new QPushButton("test"));
      object->setPos(0, -object->boundingRect().height());
      animation = new QPropertyAnimation(object,"pos");
      animation->setDuration(1000);
      view->setScene(scene);
      setFixedSize(800,800);
      scene->addRect(-500, -200, 1000, 200);
      setCentralWidget(view);
      scene->addItem(object);
      next_command = command_none;
      speed = 100;
    
      QTimer* timer = new QTimer(this);
      connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
      timer->start(1000);
    }
    
    MainWindow::~MainWindow() {}
    
    void MainWindow::keyPressEvent(QKeyEvent *k)
    {
      switch (k->key()) {
      case Qt::Key_J: {
        next_command = command_forward;
        break;
      }
      case Qt::Key_Z: {
        next_command = command_jump;
        break;
      }
      case Qt::Key_F: {
        next_command = command_back;
        break;
      }
      default:
        break;
      }
    }
    
    void MainWindow::timeout() {
      //fall
      if (pos.y() < 0) {
        pos.setY(pos.y() + speed);
        if (pos.y() >= 0) {
          pos.setY(0);
        }
      }
      //action
      switch(next_command) {
      case command_forward:
        pos.setX(pos.x() + speed);
        break;
      case command_back:
        pos.setX(pos.x() - speed);
        break;
      case command_jump:
        if (pos.y() == 0) {
          pos.setY(pos.y() - speed);
        }
        break;
      default:
        break;
      }
      next_command = command_none;
      animation->stop();
      animation->setEndValue(pos - QPointF(0, object->boundingRect().height()));
      animation->start();
    }