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. How to optimize QGraphicsView performance?
Forum Updated to NodeBB v4.3 + New Features

How to optimize QGraphicsView performance?

Scheduled Pinned Locked Moved Unsolved General and Desktop
7 Posts 4 Posters 1.6k Views 2 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.
  • S Offline
    S Offline
    sven1991
    wrote on 7 Nov 2022, 09:37 last edited by sven1991 11 Jul 2022, 09:38
    #1

    I want to make an application to simulate LED screen, which can display some text and scroll. So I came up with the idea of using QGraphView. I can draw a Ellipse to represent an LED lamp bead. I make the Grid class, which is used to layout these Ellipses. First, I will call the generate_grid method to generate each Ellipse. Then, call the update_pixel method to display the text. It can work perfectly. But when i want to scroll the text, the CPU usage will be high, and the graphics will not be smooth. My Cpu is Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz 2.30 GHz.
    ezgif.com-gif-maker (1).gif

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    class QTimer;
    class QGraphicsScene;
    class QGraphicsView;
    class QGraphicsEllipseItem;
    
    class Grid final : public QObject
    {
        Q_OBJECT
    public:
        explicit Grid(QGraphicsScene * scene, QObject * parent = Q_NULLPTR)
            : QObject(parent), m_scene(scene) {}
        virtual ~Grid() {}
    
        inline uint get_gap() const { return this->m_gap; }
        inline uint get_pixel_width() const { return m_grid_size.width(); }
        inline uint get_pixel_height() const { return m_grid_size.height(); }
    
        void generate_grid(const QColor & color);
        void update_pixel(uint x, uint y, const QColor & color);
    
    private:
        static constexpr uint default_size = 10;
        static constexpr uint default_gap = default_size + 2;
        static constexpr uint default_start_x = 10;
        static constexpr uint default_start_y = 10;
    
        uint m_size = default_size;
        uint m_gap = default_gap;
        uint m_startX = default_start_x;
        uint m_startY = default_start_y;
        QSize m_grid_size = {32, 150};
        QGraphicsScene * m_scene;
        QVector<QGraphicsEllipseItem *> m_items;
    };
    
    /***********************************************************/
    struct varSection final
    {
        uint offset = 0;
        uint width = 0;
        uint height = 0;
        uint real_width = 0;
        QString str_point;
    
        uint get_max_width() const { return std::max(this->real_width, this->width); }
    };
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
         virtual void resizeEvent(QResizeEvent *event) override;
    
    private slots:
        void handle_section_move();
    private:
        void init_widget();
    
    private:
        Grid * m_grid = Q_NULLPTR;
        QTimer * m_timer = Q_NULLPTR;
        QGraphicsView * m_view = Q_NULLPTR;
        varSection m_section;
    
    };
    #endif // WIDGET_H
    

    #include "widget.h"
    #include <QGraphicsEllipseItem>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QDebug>
    #include <QTimer>
    
    
    void Grid::generate_grid(const QColor & color)
    {
        for (int cur = m_items.size(); cur <= m_grid_size.height() * m_grid_size.width(); ++cur)
        {
            QGraphicsEllipseItem * pItem = this->m_scene->addEllipse(QRect());
            m_items.push_back(pItem);
        }
    
        for (int i = 0; i < m_grid_size.height(); i++)
        {
            for (int j = 0; j < m_grid_size.width(); j++)
            {
                int idx = i * m_grid_size.width() + j;
                if (idx >= m_items.size()) continue;
    
                QGraphicsEllipseItem * pItem = m_items[i * m_grid_size.width() + j];
                if (pItem)
                {
                    pItem->setRect(QRect(m_startX + m_gap * i, m_startY + m_gap * j, m_size, m_size));
                    pItem->setPen(color);
                    pItem->setBrush(color);
                    pItem->show();
                }
            }
        }
    }
    
    void Grid::update_pixel(uint x, uint y, const QColor & color)
    {
        int idx = x * m_grid_size.width() + y;
        if (idx < 0 || idx >= m_grid_size.height() * m_grid_size.width()) return;
    
        m_items[idx]->setPen(color);
        m_items[idx]->setBrush(color);
    }
    
    /******************************************************************************/
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        this->init_widget();
    }
    
    Widget::~Widget()
    {
    
    }
    
    void Widget::init_widget()
    {
        m_view = new QGraphicsView(this);
        m_view->setCacheMode(QGraphicsView::CacheBackground);
        m_view->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
        m_view->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
    //    m_view->setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
        m_view->resize(QSize(this->width(), this->height()));
    
        QGraphicsScene * scene = new QGraphicsScene(this);
        scene->setItemIndexMethod(QGraphicsScene::NoIndex);
        scene->addEllipse(QRect(0, 0, 1, 1));
    
        m_view->setScene(scene);
    
        m_grid = new Grid(scene, this);
        m_grid->generate_grid(Qt::gray);
    
        m_timer = new QTimer(this);
        connect(m_timer, &QTimer::timeout, this, &Widget::handle_section_move);
        m_timer->start(1000 / 30);
    
        m_section.str_point =
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000011110001100001100011000000001100011000000000000011001100110000001111001100000000000"
                "00000000000000011000000000000110110000000000000000000000000000000011001100110001100001100000000110011000000000000001100110110000"
                "00110011011000000000000000000000000001100000000000011011000000000000000000000000000000001100000011000000000000000000000000000000"
                "00000000011001100000000011000001100000000000000000000000000110000000000000001100000000000000000000000000000000111000011110011110"
                "01100111100110110011011011000001100110111000001100000111110001111001101100011111011111000111100110110000111100000000000000000000"
                "00000111000011000000110110110011011011001101110110000110011001100000110000011001101100110111011011001101100110000011011011000110"
                "01100000000000000000000000001110001100000011011011100001101100110110011000011001100110000011000001100110110011011001101100110110"
                "01100000110110110001100110000000000000000000000000011100110001111101100111100110110011011001100001100110011000001100000110011011"
                "00110110011011001101100110011111011011000111111000000000000000000000000000110011001100110110000111011011001101100110000110011001"
                "10000011000001100110110011011001101100110110011011001101101100011000000000000000000000000000000011001100110011011000001101101100"
                "11011001100001100110011000001100000110011011001101100110110011011001101100110110110001100000000000000000000000000011001100110011"
                "00110110110011011011001101100110000110011001100000110011011001101100110110011011001101100110110011011011100110000000000000000000"
                "00000000011110000110011111011001111001100111110110011000001111001111000001111001100110011110011001100111110110011001111101100111"
                "00111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
                "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        m_section.width = 150;
        m_section.height = 32;
        m_section.real_width = 131;
    
        this->resize(1500, 500);
    }
    
    void Widget::resizeEvent(QResizeEvent *event)
    {
        Q_UNUSED(event);
        m_view->resize(QSize(this->width(), this->height()));
    }
    
    void Widget::handle_section_move()
    {
        uint offset = m_section.offset % (m_section.real_width + m_section.width + 1);
    
        for (int i = 0; i < m_section.str_point.size(); ++i)
        {
            uint posx = (i % m_section.get_max_width()) + m_section.width - offset;
            uint posy = (i / m_section.get_max_width());
            if (posx < 0 || posy < 0 || posx >= 150 || posy >= 32) continue;
    
            m_grid->update_pixel(posx, posy, m_section.str_point[i] == '1' ? Qt::blue : Qt::gray);
        }
    
        m_section.offset++;
    }
    

    Upper codes can be copied to other projects for running. Please ignore the source of m_section, which comes from another project of mine. The code here is only used to demonstrate this problem.
    If I revise the update_pixel method, as follow.

    void Grid::update_pixel(uint x, uint y, const QColor & color)
    {
        int idx = x * m_grid_size.width() + y;
        if (idx < 0 || idx >= m_grid_size.height() * m_grid_size.width()) return;
    
       //m_items[idx]->setPen(color);
       //m_items[idx]->setBrush(color);
    }
    

    the CPU usage will be lower. I know that QPainter will eventually be called here to render graphics. I need to scroll at 30 steps per second or faster. Maybe it will be scrolling more text, depending on the user. How can I optimize this program or any other solution?

    1 Reply Last reply
    1
    • A Offline
      A Offline
      Asperamanca
      wrote on 8 Nov 2022, 07:15 last edited by
      #2

      I don't have a "do this, then it works" answer. However, there are a few things you could try to improve performance:

      • Set Qt::NoPen and just set the brush. Saves a lot on the painting, and also on painter state changes
      • Try setting ItemIgnoresParentOpacity flag on the LED item.
      • Do you need to scale your scene? If you can create the items in the correct size for screen display, you could also set ItemIgnoresTransformations, this should save some calculations
      • Try whether rectangles are a lot faster than ellipses. If they are, you can maybe approximate the small ellipses you need using 3-5 rectangles each
      • Alternatively, try drawing ALL your LEDs in a single custom QGraphicsItem. That way, you can first draw all gray LEDs, then all blue LEDs, which saves a lot of painter state changes.

      From my perspective, the first and last suggestions are the most promising.

      In any case, keep in mind that GraphicsView is a pure single-thread CPU thing. So it can't make proper use of modern computer architectures. For that, you'd need QML, ideally with custom SceneGraph classes that are more lightweight than normal QML items. But that's a tall order, given that documentation and examples in that area are thin.

      S 1 Reply Last reply 9 Nov 2022, 08:50
      2
      • A Asperamanca
        8 Nov 2022, 07:15

        I don't have a "do this, then it works" answer. However, there are a few things you could try to improve performance:

        • Set Qt::NoPen and just set the brush. Saves a lot on the painting, and also on painter state changes
        • Try setting ItemIgnoresParentOpacity flag on the LED item.
        • Do you need to scale your scene? If you can create the items in the correct size for screen display, you could also set ItemIgnoresTransformations, this should save some calculations
        • Try whether rectangles are a lot faster than ellipses. If they are, you can maybe approximate the small ellipses you need using 3-5 rectangles each
        • Alternatively, try drawing ALL your LEDs in a single custom QGraphicsItem. That way, you can first draw all gray LEDs, then all blue LEDs, which saves a lot of painter state changes.

        From my perspective, the first and last suggestions are the most promising.

        In any case, keep in mind that GraphicsView is a pure single-thread CPU thing. So it can't make proper use of modern computer architectures. For that, you'd need QML, ideally with custom SceneGraph classes that are more lightweight than normal QML items. But that's a tall order, given that documentation and examples in that area are thin.

        S Offline
        S Offline
        sven1991
        wrote on 9 Nov 2022, 08:50 last edited by
        #3

        @Asperamanca I really appreciate your suggestions. I will try it one by one.

        1 Reply Last reply
        0
        • R Offline
          R Offline
          reedhedges
          wrote on 9 Nov 2022, 20:21 last edited by
          #4

          I don't know as much about QGraphicsView, the above comments seem like good ideas. However, are you scrolling the text by clearing and painting pixels in each column at each time step? If so maybe instead generate an image bitmap wider than the view, use a set of update_pixel() calls to draw the text offscreen once, then to animate the scrolling effect, just translate the whole image bitmap at each step interval?

          A S 2 Replies Last reply 10 Nov 2022, 07:50
          1
          • R reedhedges
            9 Nov 2022, 20:21

            I don't know as much about QGraphicsView, the above comments seem like good ideas. However, are you scrolling the text by clearing and painting pixels in each column at each time step? If so maybe instead generate an image bitmap wider than the view, use a set of update_pixel() calls to draw the text offscreen once, then to animate the scrolling effect, just translate the whole image bitmap at each step interval?

            A Offline
            A Offline
            Asperamanca
            wrote on 10 Nov 2022, 07:50 last edited by
            #5

            @reedhedges said in How to optimize QGraphicsView performance?:

            I don't know as much about QGraphicsView, the above comments seem like good ideas. However, are you scrolling the text by clearing and painting pixels in each column at each time step? If so maybe instead generate an image bitmap wider than the view, use a set of update_pixel() calls to draw the text offscreen once, then to animate the scrolling effect, just translate the whole image bitmap at each step interval?

            Good idea! Let me expand on it:
            Why not paint the whole text into a large QPixmap at runtime (using QPainter), and then simply move it at the required speed?

            1 Reply Last reply
            0
            • R reedhedges
              9 Nov 2022, 20:21

              I don't know as much about QGraphicsView, the above comments seem like good ideas. However, are you scrolling the text by clearing and painting pixels in each column at each time step? If so maybe instead generate an image bitmap wider than the view, use a set of update_pixel() calls to draw the text offscreen once, then to animate the scrolling effect, just translate the whole image bitmap at each step interval?

              S Offline
              S Offline
              sven1991
              wrote on 11 Nov 2022, 09:37 last edited by sven1991 11 Nov 2022, 09:41
              #6

              @reedhedges Yep, it's really a good idea. but it doesn't suit to my situation. It's my demo that didn't demonstrate clearly. We will divide a screen into multiple areas, and then perform different operations on multiple areas. Thanks for your reply. Maybe I will use this idea in the future.
              Animation.gif

              1 Reply Last reply
              0
              • JoeCFDJ Offline
                JoeCFDJ Offline
                JoeCFD
                wrote on 11 Nov 2022, 20:10 last edited by
                #7

                you create a background image for grids. Then create a transparent text image and move it through the background.

                1 Reply Last reply
                0

                1/7

                7 Nov 2022, 09:37

                • Login

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