Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Custom QGraphicsItem not updating on QDialog form



  • I'm trying to learn how to do simple animation in Qt, but having a hard time wrapping my head around the whole QGraphicsView / QGraphicsScene / QGraphicsItem paradigm. I wrote a small program, which is supposed to plot a small dot on the screen and move it around in a random way. I have a QDialog containing a QGraphicsView. The QGraphicsView has an associated QGraphicsScene. I've subclassed QGraphicsItem to produce a custom graphic object class called MyShape. The Dialog class contains a function called advance(int iterations) which calls MyShape's advance() function, and is supposed to advance the scene by the given number of iterations.

    As per the Qt documentation I've reimplemented boundingrect(), paint() and advance(). I've based most of the code on VoidRealm's vid: Basic and Advanced Animation with the QGraphicsItem, but added a bunch of qDebug() statements so that I can see what the code does.

    The scene correctly updates after the Dialog constructor has executed. The problem is that the scene doesn't update while Dialog::update() is running. The funny thing is that MyItem::update() is called correctly, at least as far as I can see from the qDialog() statements.

    How do I get the scene to update after every iteration of the for loop in Dialog::update() ?

    Here's the code:

    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include <QDialog>
    #include <chrono>
    #include <thread>
    #include <QMainWindow>
    #include <QGraphicsItem>
    #include <QGraphicsScene>
    #include "myshape.h"
    
    using namespace std::this_thread;     // sleep_for, sleep_until
    using namespace std::chrono; // nanoseconds, system_clock, seconds
    
    namespace Ui {
    class Dialog;
    }
    class Dialog : public QDialog
    {
        Q_OBJECT
    public:
        explicit Dialog(QWidget *parent = nullptr);
        ~Dialog();
        void advance(int iterations);
    private:
        Ui::Dialog *ui;
        QGraphicsScene *_scene;
        MyShape *_shape; //Custom shape object
    };
    #endif // DIALOG_H
    
    #include "dialog.h"
    #include "ui_dialog.h"
    
    Dialog::Dialog(QWidget *parent) :
        QDialog(parent),
        ui(new Ui::Dialog)
    {
        qDebug() << "MainWindow::MainWindow(QWidget *parent) start on " << this;
        ui->setupUi(this);
        this->setModal(true); //modeless
        ui->graphicsView->setScene(_scene);
        ui->graphicsView->setRenderHint(QPainter::Antialiasing);
    
        int shapeCount = 1;
        for (int i = 0; i < shapeCount; i++)
        {
        _scene = new QGraphicsScene;
            _shape = new MyShape;
            _scene->addItem(_shape);
        }
        qDebug() << "MainWindow::MainWindow(QWidget *parent) end on " << this;
    }
    
    Dialog::~Dialog()
    {
        qDebug() << "MainWindow::~MainWindow() start on " << this;
        delete ui;
        qDebug() << "MainWindow::~MainWindow() end on " << this;
    }
    
    void Dialog::advance(int iterations)
    {
        qDebug() << "void MainWindow::advance(int iterations) start on " << this;
        for (int i = 0; i < iterations; i++)
        {
            _scene->advance();
            sleep_for(milliseconds(1000));
        }
        qDebug() << "void MainWindow::advance(int iterations) end on " << this;
    }
    
    
    #ifndef MYSHAPE_H
    #define MYSHAPE_H
    #include "QGraphicsItem"
    #include <QPainter>
    #include <QDebug>
    
    class MyShape : public QGraphicsItem
    {
    public:
        MyShape();
        QRectF boundingRect() const;
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                         QWidget *widget);
    protected:
        void advance(int phase);
    
    };
    
    #endif // MYSHAPE_H
    
    
    #include "myshape.h"
    
    MyShape::MyShape()
    {
        qDebug() << "MyShape::MyShape() start on " << this;
        setPos(0, 0);
        qDebug() << "MyShape::MyShape() end on " << this;
    }
    
    QRectF MyShape::boundingRect() const
    {
        qDebug() << "QRectF MyShape::boundingRect() const start on " << this;
        //qDebug() << "QRectF MyShape::boundingRect() const end on " << this;
        return QRectF(0, 0, 20, 20);
    }
    
    void MyShape::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        qDebug() << "void MyShape::paint(QPainter *painter... start on " << this;
        QPointF currentPosition = pos();
        QPointF bottomRight(currentPosition);
        bottomRight.setX(bottomRight.x()+20);
        bottomRight.setY(bottomRight.y()+20);
        painter->drawEllipse(QRectF(currentPosition, bottomRight));
        qDebug() << "void MyShape::paint(QPainter *painter... end on " << this;
    }
    
    void MyShape::advance(int phase)
    {
        qDebug() << "void MyShape::advance(int phase) start on " << this << "with phase" << phase;
        QPointF currentPosition = pos();
        if (phase == 0) return;
        setPos(currentPosition.x()+qrand()%50, currentPosition.y()+qrand()%50);
        qDebug() << "void MyShape::advance(int phase) end";
    }
    
    code_text#include "dialog.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        Dialog w;
        w.show();
        w.advance(10);
        return a.exec();
    }
    

  • Lifetime Qt Champion

    You're blocking the main thread with your sleep_for() call - what do you expect then?
    Don't block the main thread but use a QTimer.


  • Lifetime Qt Champion

    You're blocking the main thread with your sleep_for() call - what do you expect then?
    Don't block the main thread but use a QTimer.



  • @Christian-Ehrlicher, that solved the problem, thanks!


Log in to reply