Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Drawing rectangles every 50 ms
Qt 6.11 is out! See what's new in the release blog

Drawing rectangles every 50 ms

Scheduled Pinned Locked Moved Solved General and Desktop
19 Posts 4 Posters 2.0k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    HappyVisualizer
    wrote on last edited by HappyVisualizer
    #1

    Dear all,

    Currently I'm developing an application that draws a set of QGraphicsitems every 50 ms. These QGraphicsitems are 20 rectangles in vertical order, each rectangle having the same height and width. Their x-coordinate is based on the current time and their y-coordinate is based on their position in the stack of 20. Then, after 50 ms, the scenerect is changed and another set of rectangles are drawn right after. What I find, is that the first rectangles are redrawn as well, even after the scenerect changes. The scene does not seem to make a jump over the x-axis, but needs to redraw all rectangles as well. This means that running the program for a longer time, a lot of paint() calls are being made.

    It is the goal to show up to 100,000 rectangles in the screen and redrawing all these rectangles every 50 ms is infeasible.

    I found the following link, explaining that grouping the rectangles into one QGraphicsItem is faster and that setting the brush once is a bottleneck:

    https://stackoverflow.com/questions/18397603/how-to-move-around-1000-items-in-a-qgraphicsscene-without-blocking-the-ui/18400665#18400665

    But, for my application, I also would like to give each individual rectangle its own tooltip and its own color. Therefore, it seems not to be possible to use the brush only once per paint, as the brush changes when you want to use a different color. Further, I could not find that it is possible to set multiple tooltips for one QGraphicsItem at different positions.

    To further clarify what I have done, I add some sample code:

    main.cpp:

    #include "sample_graphics_window.h"
    
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
    
        QApplication app(argc, argv);
    
        SampleGraphicsWindow window;
        window.resize(1200, 800);
    
        window.show();
        
        return app.exec();
    }
    

    sample_rectangle.h

    #ifndef SAMPLERECTANGLEITEM_H
    #define SAMPLERECTANGLEITEM_H
    
    #include <QGraphicsRectItem>
    #include <QPainter>
    #include <QGraphicsSceneHoverEvent>
    
    class SampleRectangle : public QGraphicsItem
    {
        public:
            SampleRectangle(long start_x, long start_y, long width, long height, QColor col, long current_time);
    
            enum { Type = UserType + 2 };
    
            int type() const override
            {
                // Enable the use of qgraphicsitem_cast with this item.
                return Type;
            }
    
            long get_time() {return current_time;}
    
        protected:
            void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
            void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override;
            QRectF boundingRect() const override;
    
        private:
            long start_x;
            long start_y;
            long width;
            long height;
            QColor col;
            long current_time;
    
    
    };
    
    #endif //SAMPLERECTANGLEITEM_H
    

    sample_rectangle.cpp

    #include "sample_rectangle.h"
    #include <QDebug>
    #include <iostream>
    #include <QToolTip>
    
    SampleRectangle::SampleRectangle(long start_x, long start_y, long width, long height, QColor col, long current_time) 
    : start_x(start_x), start_y(start_y), width(width), height(height), col(col), current_time(current_time)
    {
        setToolTip("Hello");
        setAcceptHoverEvents(true);
        setCacheMode(ItemCoordinateCache);
        // setCacheMode(DeviceCoordinateCache);
    }
    
    void SampleRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                     QWidget *widget) 
    {
      QPen pen(col);
     
      painter->setPen(pen);
      painter->setBrush(col);
      
      painter->drawRect(start_x, start_y, width, height);
    }
    
    void SampleRectangle::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
    {  
        col = QColor(0, 255, 0, 255);
        QToolTip::showText(event->screenPos(), "Tooltip");
    }
    
    
    QRectF SampleRectangle::boundingRect() const
    {
        return QRectF(start_x,start_y,width,height);
    }
    

    sample_rectangle_view.h

    #ifndef SAMPLE_RECTANGLE_VIEW_H
    #define SAMPLE_RECTANGLE_VIEW_H
    
    #include <QGraphicsView>
    
    class SampleRectangleView : public QGraphicsView
    {
        Q_OBJECT
    
        public:
            SampleRectangleView(QWidget *parent = nullptr);
    
        public slots:
            void timeout();
    
        private:
            int red, green, blue;
    
            long time0;
            long min_time;
            long max_time;
            long previous_time;
            long current_time;
    
            bool got_first_message;
    
    
    };
    
    #endif // SAMPLE_RECTANGLE_VIEW_H
    

    sample_rectangle_view.cpp

    #include "sample_rectangle_view.h"
    #include "sample_rectangle.h"
    
    #include <iostream>
    #include <chrono>
    #include <QDebug>
    
    SampleRectangleView::SampleRectangleView(QWidget *parent)
        : QGraphicsView(parent), got_first_message(false), red(0), blue(100), green(200), current_time(0)
    {
        setMouseTracking(true);
    
        setAlignment(Qt::AlignTop | Qt::AlignLeft);
    
    }
    
    void SampleRectangleView::timeout()
    { 
        auto now = std::chrono::system_clock::now();
        auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
        auto epoch = now_ms.time_since_epoch();
        auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
        long duration = value.count();
    
        max_time = duration + 500;
    
        min_time = max_time - 250 * 1000;
        current_time = duration;
    
    
        if (!got_first_message)
        {
    
            previous_time = min_time;
            got_first_message = true;
    
        }
    
        scene()->setSceneRect(min_time * width() / (max_time - min_time), 0, width(), height());
    
    
        for (int i = 0; i < 20; i++)
        {
            SampleRectangle *item = new SampleRectangle(0, 0, (max_time - previous_time) * width() / (max_time - min_time), height() / 20, 
                QColor(red, green, blue, 255 ), current_time);
    
            item->setPos(previous_time * width() / (max_time - min_time), i * height() / 20);
    
    
            scene()->addItem(item);  
    
            red++;
            green++;
            blue++;
    
    
            if (red == 256) 
                red = 0;
    
            if (green == 256)
                green = 0;
    
            if (blue == 256)
                blue = 0;
        }
    
        previous_time = current_time;
    
        // scene()->update();
    
    }
    

    sample_graphics_window.h

    #include <QWidget>
    #include <QGraphicsScene>
    #include <QTimer>
    
    class SampleGraphicsWindow : public QWidget
    {
        Q_OBJECT
    
    public:
        SampleGraphicsWindow(QWidget *parent = nullptr);
    
    private:
       
        QGraphicsScene *scene;
    
        QTimer* m_myTimer;
    };
    

    sample_graphics_window.cpp

    #include "sample_graphics_window.h"
    
    #include "sample_rectangle_view.h"
    
    #include <QHBoxLayout>
    #include <QOpenGLWidget>
    #include <QSurfaceFormat>
    #include <QDebug>
    
    
    
    SampleGraphicsWindow::SampleGraphicsWindow(QWidget *parent)
        : QWidget(parent), scene(new QGraphicsScene(this))
    {
        setMouseTracking(true);
      
        SampleRectangleView *view = new SampleRectangleView();
    
        m_myTimer = new QTimer(this);
        connect(m_myTimer, SIGNAL(timeout()), view, SLOT(timeout()));
    
        m_myTimer->start(50);
        
        view->setScene(scene);
    
        QOpenGLWidget *gl = new QOpenGLWidget();
        QSurfaceFormat format;
        format.setSamples(4);
        gl->setFormat(format);
        view->setViewport(gl);
    
        QHBoxLayout *layout = new QHBoxLayout;
        layout->addWidget(view);
        
        setLayout(layout);
    }
    
    

    Further, I could perhaps implement the same code into OpenGL and use Qt as the wrapper. I could then have faster painting. However, implementing a tooltip seems to be complex compared to the QGraphicsItem. Further, I would like to draw some text on the screen and OpenGL is not the best suited option for that.

    Do you know what solutions I could try to increase the painting speed and reduce the overhead?

    Looking forwards to your reply :)

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #15

      Do you really need to show that many times at the same time ?

      One thing you should take into account is how much data makes sense to be shown. Take for example video games, they do not render absolutely everything in every frame because that fine detail that is supposed to be 1000m away does not make sense to render in full details because you can't see them anyway.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      H 1 Reply Last reply
      0
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #2

        Hi,

        Why create new items rather than shuffling those you have around ?

        As for multiple tooltips, you will have to roll your own handling for them.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        1
        • H Offline
          H Offline
          HappyVisualizer
          wrote on last edited by HappyVisualizer
          #3

          @SGaist Thanks for your help.

          What do you exactly mean with shuffling? I can imagine that you can draw them all at the beginning and only change the color for all rectangles. However, then you still would need to iterate over all the rectangles, changing their colors. Is that what you mean?

          SGaistS 1 Reply Last reply
          0
          • C Offline
            C Offline
            ChrisW67
            wrote on last edited by
            #4

            Is there a reason for not inheriting from QGraphicsRectItem?

            H 1 Reply Last reply
            0
            • C ChrisW67

              Is there a reason for not inheriting from QGraphicsRectItem?

              H Offline
              H Offline
              HappyVisualizer
              wrote on last edited by
              #5

              @ChrisW67 I tested it before, but had some problems back then with drawing. Using a QGraphicsRectItem, I still get that paint is called multiple times for the same rectangle. I'm not sure if this will resolve the problem.

              1 Reply Last reply
              0
              • H HappyVisualizer

                @SGaist Thanks for your help.

                What do you exactly mean with shuffling? I can imagine that you can draw them all at the beginning and only change the color for all rectangles. However, then you still would need to iterate over all the rectangles, changing their colors. Is that what you mean?

                SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #6

                @HappyVisualizer i meant shuffling their positions.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                H 1 Reply Last reply
                0
                • SGaistS SGaist

                  @HappyVisualizer i meant shuffling their positions.

                  H Offline
                  H Offline
                  HappyVisualizer
                  wrote on last edited by
                  #7

                  @SGaist Thanks for clarifying. So using a simple setpos() for each rectangle? Or do you suggest other functions / functionality? I'm really stuck at the moment..

                  1 Reply Last reply
                  0
                  • SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #8

                    No no, setPos is fine for that.

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    H 1 Reply Last reply
                    0
                    • SGaistS SGaist

                      No no, setPos is fine for that.

                      H Offline
                      H Offline
                      HappyVisualizer
                      wrote on last edited by HappyVisualizer
                      #9

                      @SGaist Thanks a lot for your reply. It's been a while, but I did some testing and it seems that every time when calling setSceneRect(...), the paint function for all QGraphicsRectItems are being called. Is this indeed the case and instead of changing the coordinates of the scene every time (by going from [0, x] to [x, 2x] using setSceneRect(...)), is setPos(x,y) for each QGraphicsRectItem then faster and should I just change the positions of all these items in the scene, relative to the scene (and making sure 'old' rectangles are removed from the scene)?

                      And in that case, would QT be able to do this setPos() for 100,000 rectangles within 50 milliseconds? The idea behind the 100,000 rectangles is that we roughly 1.5h of data shown. Each rectangle has a width of 50 ms and each 50 ms, a new rectangle of data arrives. This pushes out the oldest rectangle, and would insert the newest rectangle. Thus, ((1.5 * 60 * 60 * 1000) / 50) = 108,000 objects where we need to apply setPos every 50 ms.

                      JonBJ 1 Reply Last reply
                      0
                      • H HappyVisualizer

                        @SGaist Thanks a lot for your reply. It's been a while, but I did some testing and it seems that every time when calling setSceneRect(...), the paint function for all QGraphicsRectItems are being called. Is this indeed the case and instead of changing the coordinates of the scene every time (by going from [0, x] to [x, 2x] using setSceneRect(...)), is setPos(x,y) for each QGraphicsRectItem then faster and should I just change the positions of all these items in the scene, relative to the scene (and making sure 'old' rectangles are removed from the scene)?

                        And in that case, would QT be able to do this setPos() for 100,000 rectangles within 50 milliseconds? The idea behind the 100,000 rectangles is that we roughly 1.5h of data shown. Each rectangle has a width of 50 ms and each 50 ms, a new rectangle of data arrives. This pushes out the oldest rectangle, and would insert the newest rectangle. Thus, ((1.5 * 60 * 60 * 1000) / 50) = 108,000 objects where we need to apply setPos every 50 ms.

                        JonBJ Online
                        JonBJ Online
                        JonB
                        wrote on last edited by
                        #10

                        @HappyVisualizer
                        Yes to all that. Changing the whole scene rectangle will presumably repaint the whole scene, changing individual items redraws just them (and whatever they uncover).

                        H 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @HappyVisualizer
                          Yes to all that. Changing the whole scene rectangle will presumably repaint the whole scene, changing individual items redraws just them (and whatever they uncover).

                          H Offline
                          H Offline
                          HappyVisualizer
                          wrote on last edited by
                          #11

                          @JonB Thanks for your reply. I just tested this as follows:

                          for(auto& scene_item : scene()->items())
                              {
                                  if (scene_item->type() == SampleRectangle::Type)
                                  {
                                      SampleRectangle *rect_item = dynamic_cast<SampleRectangle*>( scene_item );
                          
                          
                                      if (rect_item->get_time() < min_time)
                                      {
                                          scene()->removeItem(scene_item);
                                      }
                          
                                      else 
                                      {
                                          scene_item->setPos( width() * ((double)(rect_item->get_time() - min_time) / (double)(max_time - min_time)), 0);
                                      }
                                      
                                  }
                              }
                          

                          The code checks if the rectangle item is outside the screen (by determining whether its time is lower than the current displayed minimum time) and else, we update the position of the rectangle in the scene. However, printing a counter in the paint() method of the QRectItem, I still see the counter incrementing, indicating the the paint() method is called everytime the setPos() function is called. I'm using Qt5 by the way. Do you know why paint() is still called for every setPos()?

                          JonBJ 1 Reply Last reply
                          0
                          • H HappyVisualizer

                            @JonB Thanks for your reply. I just tested this as follows:

                            for(auto& scene_item : scene()->items())
                                {
                                    if (scene_item->type() == SampleRectangle::Type)
                                    {
                                        SampleRectangle *rect_item = dynamic_cast<SampleRectangle*>( scene_item );
                            
                            
                                        if (rect_item->get_time() < min_time)
                                        {
                                            scene()->removeItem(scene_item);
                                        }
                            
                                        else 
                                        {
                                            scene_item->setPos( width() * ((double)(rect_item->get_time() - min_time) / (double)(max_time - min_time)), 0);
                                        }
                                        
                                    }
                                }
                            

                            The code checks if the rectangle item is outside the screen (by determining whether its time is lower than the current displayed minimum time) and else, we update the position of the rectangle in the scene. However, printing a counter in the paint() method of the QRectItem, I still see the counter incrementing, indicating the the paint() method is called everytime the setPos() function is called. I'm using Qt5 by the way. Do you know why paint() is still called for every setPos()?

                            JonBJ Online
                            JonBJ Online
                            JonB
                            wrote on last edited by
                            #12

                            @HappyVisualizer
                            Whose paint() is being called? Presumably you mean that of a QGraphicsItem, like QGraphicsRectItem? So wouldn't you expect that to be called if you move the rectangle object? Something has to draw it at its new position, together with removing it from its old position....

                            H 1 Reply Last reply
                            0
                            • JonBJ JonB

                              @HappyVisualizer
                              Whose paint() is being called? Presumably you mean that of a QGraphicsItem, like QGraphicsRectItem? So wouldn't you expect that to be called if you move the rectangle object? Something has to draw it at its new position, together with removing it from its old position....

                              H Offline
                              H Offline
                              HappyVisualizer
                              wrote on last edited by
                              #13

                              @JonB Yes, that makes a lot of sense to me. But then I may have to rethink whether using a QGraphicsView and painting 100,000 custom QGraphicsRectItems every 50 ms is feasible. What do you think, would this be possible, or should I use something else like do the drawing with OpenGL?

                              The reason I went for custom QGraphicsRectItems is their HoverEvent and the capability of displaying tooltips. With OpenGL, I have to implement this myself (which seems to be a bit harder, but not impossible).

                              JonBJ 1 Reply Last reply
                              0
                              • H HappyVisualizer

                                @JonB Yes, that makes a lot of sense to me. But then I may have to rethink whether using a QGraphicsView and painting 100,000 custom QGraphicsRectItems every 50 ms is feasible. What do you think, would this be possible, or should I use something else like do the drawing with OpenGL?

                                The reason I went for custom QGraphicsRectItems is their HoverEvent and the capability of displaying tooltips. With OpenGL, I have to implement this myself (which seems to be a bit harder, but not impossible).

                                JonBJ Online
                                JonBJ Online
                                JonB
                                wrote on last edited by
                                #14

                                @HappyVisualizer
                                Not my area I'm afraid. 100k per 50ms does sound like a lot to me. Don't forget that --- unless you explicitly tell scene/view to redraw each time you update a rectangle, which you should not do --- Qt will "buffer" all the updates. My understanding is that when it's that many it will probably end up redrawing the whole scene/view rather than each rectangle individually,

                                It would not surprise me if OpenGL is better at speed, but you will have to await a more knowledgeable expert in that area than I.

                                1 Reply Last reply
                                0
                                • SGaistS Offline
                                  SGaistS Offline
                                  SGaist
                                  Lifetime Qt Champion
                                  wrote on last edited by
                                  #15

                                  Do you really need to show that many times at the same time ?

                                  One thing you should take into account is how much data makes sense to be shown. Take for example video games, they do not render absolutely everything in every frame because that fine detail that is supposed to be 1000m away does not make sense to render in full details because you can't see them anyway.

                                  Interested in AI ? www.idiap.ch
                                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                  H 1 Reply Last reply
                                  0
                                  • SGaistS SGaist

                                    Do you really need to show that many times at the same time ?

                                    One thing you should take into account is how much data makes sense to be shown. Take for example video games, they do not render absolutely everything in every frame because that fine detail that is supposed to be 1000m away does not make sense to render in full details because you can't see them anyway.

                                    H Offline
                                    H Offline
                                    HappyVisualizer
                                    wrote on last edited by
                                    #16

                                    @SGaist It's a good question. Another possibility would be to aggregate data. The idea is that it is possible to zoom in and out over timeframes, so for instance when we look at a timespan of 1 minute, we need 60 * 1000 / 50 = 1200 objects to be repainted every 50 ms, which seems to be attainable. Then, when we go to a larger timespan (say 1 hour), it would be possible to aggregate and let every block consist of (for instance) 1 second of data instead of 50 ms of data. Then, to draw the whole scene, we need to draw 60 * 60 * 1000 / (20 * 50) = 3600 objects, which is a lot less than previously proposed. Would you support this strategy?

                                    1 Reply Last reply
                                    0
                                    • SGaistS Offline
                                      SGaistS Offline
                                      SGaist
                                      Lifetime Qt Champion
                                      wrote on last edited by
                                      #17

                                      Yes, that's the idea. Clustering data for more meaningful representation.

                                      Interested in AI ? www.idiap.ch
                                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                      1 Reply Last reply
                                      0
                                      • H Offline
                                        H Offline
                                        HappyVisualizer
                                        wrote on last edited by
                                        #18

                                        @JonB, @SGaist It has been a while, but I have integrated the aggregation of data and it works like a charm. Thanks a lot guys!

                                        1 Reply Last reply
                                        1
                                        • SGaistS Offline
                                          SGaistS Offline
                                          SGaist
                                          Lifetime Qt Champion
                                          wrote on last edited by
                                          #19

                                          Great !

                                          Then please mark the thread as solved using the "Topic Tools" button or the three dotted menu beside the answer you deem correct so that other forum users may know a solution has Ben found :-)

                                          Interested in AI ? www.idiap.ch
                                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                          1 Reply Last reply
                                          0

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved