Solved Make a sequence of QWidget moves
-
Hi everyone! I'm making a game. There're a game field made of 64 squares (like chess) and a QPushButton with icon on it. Button supposed to move on field randomly. Problem is that when i use move() several times in a row it only displays last move:
QPushButton->move(ax, ay); for(int i=0; i<10000; i++){} //for delay QPushButton->move(bx, by); for(int i=0; i<10000; i++){} QPushButton->move(cx, cy);
only
QPushButton->move(cx, cy);
will be executed with no delay before. Just button will be on position cx, cy when i start a program.
Same thing happens when i use QPropertyAnimation:QPropertyAnimation *animation = new QPropertyAnimation(ui->pushButton, "geometry"); animation->setDuration(10000); animation->setStartValue(QRect(20, 20, 80, 80)); animation->setEndValue(QRect(100, 20, 80, 80)); animation->start(); animation->setDuration(10000); animation->setStartValue(QRect(100, 20, 80, 80)); animation->setEndValue(QRect(100, 100, 80, 80)); animation->start(); animation->setDuration(10000); animation->setStartValue(QRect(100, 100, 80, 80)); animation->setEndValue(QRect(20, 100, 80, 80)); animation->start();
Only last animation will be executed.
How can i make as many moves as i need, so when i start the game all movements would be consistently executed with any needed delay? -
For Loop as Delay is not a good idea because it can freeze your interface. Instead, you could use a QTimer to perform certain operation at intervals.
For Example:
QTimer *timer = new QTimer(this); timer->setInterval(10000); connect (timer, &QTimer::timeout, [] () { // execute }); timer->start();
Considering your problem, you could use a data struture (FIFO - First In, First Out) to store and recover your movements.
struct Move{ int x, y; } QQueue<Move> moveQueue; moveQueue.enqueue({1,1}); moveQueue.enqueue({2,2}); moveQueue.enqueue({1,2}); moveQueue.enqueue({1,1}); while (!moveQueue.isEmpty()){ const Move& move = queue.dequeue(); qDebug() << "X: " << move.x << " - Y:" << move.y; }
Output:
X: 1 - Y: 1
X: 2 - Y: 2
X: 1 - Y: 2
X: 1 - Y: 1
Using the example shown above, this solution comes:
mainwindow.h file
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QQueue> namespace Ui { class MainWindow; } struct Move{ int x, y; }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; QQueue<Move> moveQueue; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QTimer> #include <QPropertyAnimation> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); moveQueue.enqueue({1,1}); moveQueue.enqueue({2,2}); moveQueue.enqueue({1,2}); moveQueue.enqueue({1,1}); QTimer *timer = new QTimer(this); timer->setInterval(10000); connect(timer, &QTimer::timeout, [this, timer](){ if(moveQueue.empty()){ timer->stop(); return; } const Move& move = moveQueue.dequeue(); QPropertyAnimation *animation = new QPropertyAnimation(ui->pushButton, "geometry"); connect(animation, &QPropertyAnimation::finished, animation, &QPropertyAnimation::deleteLater); // auto destroy animation->setDuration(1000); // 1s animation->setStartValue(ui->pushButton->geometry()); // actual geometry animation->setEndValue(QRect(80*move.x, 80*move.y, 80, 80)); // x = width * move.x | y = height * move.y animation->start(); }); timer->start(5000); // start after 5s } MainWindow::~MainWindow() { delete ui; }
-
For Loop as Delay is not a good idea because it can freeze your interface. Instead, you could use a QTimer to perform certain operation at intervals.
For Example:
QTimer *timer = new QTimer(this); timer->setInterval(10000); connect (timer, &QTimer::timeout, [] () { // execute }); timer->start();
Considering your problem, you could use a data struture (FIFO - First In, First Out) to store and recover your movements.
struct Move{ int x, y; } QQueue<Move> moveQueue; moveQueue.enqueue({1,1}); moveQueue.enqueue({2,2}); moveQueue.enqueue({1,2}); moveQueue.enqueue({1,1}); while (!moveQueue.isEmpty()){ const Move& move = queue.dequeue(); qDebug() << "X: " << move.x << " - Y:" << move.y; }
Output:
X: 1 - Y: 1
X: 2 - Y: 2
X: 1 - Y: 2
X: 1 - Y: 1
Using the example shown above, this solution comes:
mainwindow.h file
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QQueue> namespace Ui { class MainWindow; } struct Move{ int x, y; }; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; QQueue<Move> moveQueue; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QTimer> #include <QPropertyAnimation> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); moveQueue.enqueue({1,1}); moveQueue.enqueue({2,2}); moveQueue.enqueue({1,2}); moveQueue.enqueue({1,1}); QTimer *timer = new QTimer(this); timer->setInterval(10000); connect(timer, &QTimer::timeout, [this, timer](){ if(moveQueue.empty()){ timer->stop(); return; } const Move& move = moveQueue.dequeue(); QPropertyAnimation *animation = new QPropertyAnimation(ui->pushButton, "geometry"); connect(animation, &QPropertyAnimation::finished, animation, &QPropertyAnimation::deleteLater); // auto destroy animation->setDuration(1000); // 1s animation->setStartValue(ui->pushButton->geometry()); // actual geometry animation->setEndValue(QRect(80*move.x, 80*move.y, 80, 80)); // x = width * move.x | y = height * move.y animation->start(); }); timer->start(5000); // start after 5s } MainWindow::~MainWindow() { delete ui; }
-
@KillerSmath
Thanks a lot for your help!
Is there any reason behind this behavior, when only last move or animation will be displayed until i use stack? -
@Peter_Dev Loops and long lasting operation block event loop and as long as the event loop is blocked nothing will move. So, you move, then immediately block event loop, again move and block and so on - only last move will be actually visible.
-
@jsulm
Ok, i understand.
Thank you for explanation!