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. 30+ QGraphicsItems's update() failed, how to solve it?

30+ QGraphicsItems's update() failed, how to solve it?

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 3 Posters 1.1k Views
  • 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.
  • LimerL Offline
    LimerL Offline
    Limer
    wrote on last edited by Limer
    #1

    I have a qgraphicsscene, i will at least have 100+ items in it, and about 30+ items are signal lights. These signal lights will keep changing their signal states in every 50ms by QTimer.

    But, the actual result is as the below gif,

    (The forum's server maybe has some problems, the picture can't show, you can go to github to see it.https://github.com/Hapoa/_Repository/blob/master/images/2.gif)
    alt text

    I guess the problem is the function update() .

    void QGraphicsItem::update(const QRectF &rect = QRectF())
    Schedules a redraw of the area covered by rect in this item. You can call this function whenever your item needs to be redrawn, such as if it changes appearance or size.
    This function does not cause an immediate paint; instead it schedules a paint request that is processed by QGraphicsView after control reaches the event loop. The item will only be redrawn if it is visible in any associated view.
    As a side effect of the item being repainted, other items that overlap the area rect may also be repainted.
    If the item is invisible (i.e., isVisible() returns false), this function does nothing.

    Can someone give me some advice?

    Below are the most codes

    /* pl_item.h */
    
    #ifndef PL_ITEM_H
    #define PL_ITEM_H
    
    #include <QGraphicsItem>
    #include <QPainter>
    #include <QPixmap>
    #include <QTimer>
    #include <QDebug>
    
    class PLItem : public QObject, public QGraphicsItem
    {
        Q_OBJECT
    
    public:
        PLItem(QGraphicsItem* parent = 0) : QGraphicsItem(parent)
        {
            m_interval = 50;
    
            m_pl1Pixmap.load(":/pl1.png");
            m_pl1Pixmap = m_pl1Pixmap.scaled(60, 60);
            m_pl2Pixmap.load(":/pl2.png");
            m_pl2Pixmap = m_pl2Pixmap.scaled(60, 60);
    
            m_is = true;
    
            m_timer = new QTimer(this);
            m_timer->setInterval(m_interval);
            connect(m_timer, SIGNAL(timeout()), this, SLOT(stateChange()));
            startTimer();
        }
    
        virtual ~PLItem(){}
    
        void setInterval(int interval)
        {
            m_timer->stop();
            m_interval = interval;
            m_timer->setInterval(m_interval);
            m_timer->start();
        }
    
        void startTimer()
        {
            m_timer->start();
        }
    
        void stopTimer()
        {
            m_timer->stop();
        }
    
        virtual QRectF boundingRect() const
        {
            qreal adjust = 2;
            return QRectF(-m_pl1Pixmap.width() / 2 - adjust, -m_pl1Pixmap.height() / 2 - adjust,
                          m_pl1Pixmap.width() + adjust * 2, m_pl1Pixmap.height() + adjust * 2);
        }
    
    public slots:
        void stateChange()
        {
            update();
        }
    
    protected:
        virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
        {
            QPointF tl = boundingRect().topLeft();
    
            if (m_is)
                painter->drawPixmap(tl, m_pl1Pixmap);
            else
                painter->drawPixmap(tl, m_pl2Pixmap);
    
            //qDebug() << "pl paint";
    
            m_is = !m_is;
        }
    
    private:
        QPixmap m_pl1Pixmap;
        QPixmap m_pl2Pixmap;
        QTimer* m_timer;
        bool    m_is;
        int     m_interval;
    };
    
    #endif // PL_ITEM_H
    
    
    /* scene.h */
    
    #ifndef SCENE_H
    #define SCENE_H
    
    #include <QGraphicsScene>
    #include <QGraphicsSimpleTextItem>
    #include "pl_item.h"
    
    class Scene : public QGraphicsScene
    {
        Q_OBJECT
    
    public:
        Scene(QObject* parent = 0) : QGraphicsScene(parent)
        {
            this->setSceneRect(-400, -240, 800, 480);
    
            m_plItem = new PLItem[31];
            for (int i = 0; i < 5; i++)
            {
                this->addItem(m_plItem + i);
                m_plItem[i].setPos(125 + 60 * i, -140);
            }
            for (int i = 5; i < 18; i++)
            {
                this->addItem(m_plItem + i);
                m_plItem[i].setPos(-360 + 60 * (i - 5), -80);
            }
            for (int i = 18; i < 31; i++)
            {
                this->addItem(m_plItem + i);
                m_plItem[i].setPos(-360 + 60 * (i - 18), -20);
            }
        }
    
        virtual ~Scene(){}
    
    private:
        PLItem*                  m_plItem;
    };
    
    #endif // SCENE_H
    
    ```![0_1531125818720_2855448651635.gif](正在上传 100%)
    1 Reply Last reply
    0
    • A Offline
      A Offline
      Asperamanca
      wrote on last edited by
      #2

      Is the "problem" that the lights update in an irregular fashion, or that some of the lights don't blink at all?

      With a 50ms timer, I can easily imagine that the asynchronous update leads to undesired effects.
      I suggest trying a different approach: Instead having a separate timer in each item, control the blink frequency by reimplementing QGraphicsItem::advance(int) and calling QGraphicsScene::advance() from a single timer.

      1 Reply Last reply
      1
      • BuckwheatB Offline
        BuckwheatB Offline
        Buckwheat
        wrote on last edited by
        #3

        @Limer,

        Hi!
        @Asperamanca is correct. Use advance or manage the states of your items in the scene timer. I update 80K+ items very swiftly and each has their own state but the timing is managed at 20Hz and the flashing is managed at 5Hz. No advance is done, just a scene update whenever the state changes (the flash is also a state).

        By avoiding deriving each item from QObject, you also have lighter weight items. Use QGraphicsPixmapItem and have your state setting method call setPixmap. Then, all the bounding rects, drawing, etc. are managed for you.

        If you are still having trouble, please play with setViewportUpdateMode, setCacheMode in your QGraphicsView. Sometimes the smarter modes do not yield the desired results. For example, my settings are:
        FullViewportUpdate and CacheNone

        For my scene I use setItemIndexMethod to BspTreeIndex and I also set the ZValue for my items based on which layer order I would like to draw.

        Even using the best quality render hints I still use about 10% of my atom processor during full 20Hz all entity updates so don't be afraid to do full updates.

        Dave Fileccia

        LimerL 1 Reply Last reply
        2
        • BuckwheatB Buckwheat

          @Limer,

          Hi!
          @Asperamanca is correct. Use advance or manage the states of your items in the scene timer. I update 80K+ items very swiftly and each has their own state but the timing is managed at 20Hz and the flashing is managed at 5Hz. No advance is done, just a scene update whenever the state changes (the flash is also a state).

          By avoiding deriving each item from QObject, you also have lighter weight items. Use QGraphicsPixmapItem and have your state setting method call setPixmap. Then, all the bounding rects, drawing, etc. are managed for you.

          If you are still having trouble, please play with setViewportUpdateMode, setCacheMode in your QGraphicsView. Sometimes the smarter modes do not yield the desired results. For example, my settings are:
          FullViewportUpdate and CacheNone

          For my scene I use setItemIndexMethod to BspTreeIndex and I also set the ZValue for my items based on which layer order I would like to draw.

          Even using the best quality render hints I still use about 10% of my atom processor during full 20Hz all entity updates so don't be afraid to do full updates.

          LimerL Offline
          LimerL Offline
          Limer
          wrote on last edited by
          #4

          @Buckwheat @Asperamanca Thanks for your warm answers.

          But using one timer in scene to control all items is not suitable for me.

          Because each signal light will bind with a PLC, their frequency is controled by PLC.

          The frequency of signal lights range from 30ms to 1000ms, and the number is less than 100(at most 100).

          A 1 Reply Last reply
          0
          • LimerL Limer

            @Buckwheat @Asperamanca Thanks for your warm answers.

            But using one timer in scene to control all items is not suitable for me.

            Because each signal light will bind with a PLC, their frequency is controled by PLC.

            The frequency of signal lights range from 30ms to 1000ms, and the number is less than 100(at most 100).

            A Offline
            A Offline
            Asperamanca
            wrote on last edited by Asperamanca
            #5

            @Limer
            So each light would blink in a different but relatively high frequency? I would add an epilepsy warning ;-)

            This might be tricky to do. First, check whether your current solution already fully uses one CPU core. If it does, chances are you won't get fast enough update. The problem is, if you have 100 lights blinking at different intervals ranging from 30 to 100 ms, at worst the scene would need to redraw some parts over 1000 times a second. Because while each individual light has a minimum interval of about 30 ms, less than 1 ms later, another light might need to be redrawn. The asynchronous painting will lump these together, so you will have an irregular painting result.

            The best solution I can think of is to repaint the scene at a fixed rate that your CPU can manage (hopefully something like every 5 ms). Lumping together light changes which happen within 5 ms is no issue, because human eyes cannot recognize changes in such a short time span.

            EDIT: If you want to make sure the repaint gets done synchronously, you can experiment with

            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers)
            

            It's not pretty, and I normally discourage use, but it might be what you need in this case

            LimerL 1 Reply Last reply
            0
            • A Asperamanca

              @Limer
              So each light would blink in a different but relatively high frequency? I would add an epilepsy warning ;-)

              This might be tricky to do. First, check whether your current solution already fully uses one CPU core. If it does, chances are you won't get fast enough update. The problem is, if you have 100 lights blinking at different intervals ranging from 30 to 100 ms, at worst the scene would need to redraw some parts over 1000 times a second. Because while each individual light has a minimum interval of about 30 ms, less than 1 ms later, another light might need to be redrawn. The asynchronous painting will lump these together, so you will have an irregular painting result.

              The best solution I can think of is to repaint the scene at a fixed rate that your CPU can manage (hopefully something like every 5 ms). Lumping together light changes which happen within 5 ms is no issue, because human eyes cannot recognize changes in such a short time span.

              EDIT: If you want to make sure the repaint gets done synchronously, you can experiment with

              QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers)
              

              It's not pretty, and I normally discourage use, but it might be what you need in this case

              LimerL Offline
              LimerL Offline
              Limer
              wrote on last edited by
              #6

              @Asperamanca Thanks for your warm response.

              I found a solution, allocate a qtimer pointer array to control all items. One controls one ( or serveral).

              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