Results 1 to 11 of 11

Thread: QGraphicsView slow when adding new items

  1. #1
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default QGraphicsView slow when adding new items

    Hi everyone, new Qt user here.

    I am currently dealing with a QGraphicsScene / QGraphicsView animation with a small (<100) number of QGraphicsItems moving around. I am experiencing problems when trying to display their trajectories on the screen. As a first approach, for every new frame I'm creating some QGraphicsEllipseItem(s) with size 1 and adding them to the scene at the previous item positions.

    In the first steps of the animation, when the trajectories are short, the animation runs smooth. However, after some time (depending on how many items are present) the animation starts to get jerky. I disabled the scene indexing, no improvement. By hiding the trajectories (all trajectory points are grouped in a QGraphicsItemGroup) the animation becomes smooth again, so i think the issue is not with the point creation but with their rendering.

    Qt Code:
    1. //somewhere before the animation starts
    2. trajectory_points = new QGraphicsItemGroup;
    3.  
    4. scene->addItem(trajectory_points);
    5.  
    6. //...
    7.  
    8. //at each new frame
    9.  
    10. for(int i = 0; i < number_of_new_points; i++)
    11. {
    12.  
    13. //...
    14.  
    15. QGraphicsEllipseItem* point = new QGraphicsEllipseItem( temp_x, temp_y, 1, 1);
    16.  
    17. trajectory_points->addToGroup(point);
    18. }
    To copy to clipboard, switch view to plain text mode 

    By searching in the doc and some other questions on the internet, I thought the problem could be caused by the high number of calls to trajectory_points->addToGroup(point), one for each new point. So i tried to create a temporary QGraphicsItemGroup each frame, add the points to the temporary and then call scene->addItem(tempGroup) just once per frame, but it didn't solve the problem. I also tried setting the trajectory_points to not visible when adding the points and set it to visible again, but still no improvement.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Do you need interaction with the trajectory ellipses? If not, why not just create a custom QGraphicsItem to hold the ellipses, and draw them on demand using QPainter calls? (That is, don't make them proper QGraphicsItems, just make a custom QGraphicsItem and paint them on demand). Since you know their positions when you create them, you can simply store this information in your QGraphicsItem for drawing instead of creating child ellipses. Set the z-level of the ellipse class lower than the others so it appears "beneath" them.

  3. #3
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Thanks for your answer.
    By "draw them using QPainter calls" you mean to override its paint() method, right? I'll give it a try.
    By the way, with this method there will always be one item around, but it will have to redraw itself fully at each new frame only to add a few points every time, is it corect? Maybe this could be a problem with long trajectories. Is it possible to enable some sort of caching to avoid this?

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    I mean, derive a class from QGraphicsItem. In its paint method, you use QPainter calls to draw ellipses in the right places. By doing it this way, you have only one QGraphicsItem for all the ellipses, not dozens of QGraphicsEllipseItems. Of course it will redraw itself completely with each paint event, but I don't think your problem is with the drawing, it is the QGraphicsScene / QGraphicsView having to update so many items in the scene with each animation step. By eliminating all of the ones for the ellipses by pushing all of them into a single item, you minimize the number of things that must be updated.

    I doubt that the ellipse drawing using QPainter calls will be a bottleneck, but if it does turn out to be slower than you wish, then you could experiment with using a QPainterPath and adding successive ellipses to it. Then your paint method consists of a single call to QPainter::drawPath(). This would also mean that your custom graphics item doesn't need to keep track of the ellipses - your animation code simply adds a new ellipse to the path, and the path will "remember" it.

  5. #5
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Quote Originally Posted by d_stranz View Post
    I mean, derive a class from QGraphicsItem. In its paint method, you use QPainter calls to draw ellipses in the right places. By doing it this way, you have only one QGraphicsItem for all the ellipses, not dozens of QGraphicsEllipseItems. Of course it will redraw itself completely with each paint event, but I don't think your problem is with the drawing, it is the QGraphicsScene / QGraphicsView having to update so many items in the scene with each animation step. By eliminating all of the ones for the ellipses by pushing all of them into a single item, you minimize the number of things that must be updated.

    I doubt that the ellipse drawing using QPainter calls will be a bottleneck, but if it does turn out to be slower than you wish, then you could experiment with using a QPainterPath and adding successive ellipses to it. Then your paint method consists of a single call to QPainter::drawPath(). This would also mean that your custom graphics item doesn't need to keep track of the ellipses - your animation code simply adds a new ellipse to the path, and the path will "remember" it.
    I followed your advice and noticed a major improvement.
    By the way, after defining the "global" item to hold the trajectory points, i had an issue with its paint method:

    Qt Code:
    1. void TrajectoryItem::paint(QPainter *painter,
    2. {
    3. painter->setPen( QPen (QColor(235,145,0)));
    4. painter->setBrush(QBrush(QColor(235,145,0)));
    5.  
    6. painter->drawPath(path);
    7. }
    To copy to clipboard, switch view to plain text mode 

    I was suprised to realize that the setBrush line caused a bottleneck for long trajectories. I also realized it was useless, as the QPainterPath only stored 1-sized ellipses, so i just removed it.

    Aside from this case, I was wondering if there exists some way to specify default pen and brush options for an item without having to set expicitly set them at each paint() call.

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    By the way, after defining the "global" item to hold the trajectory points
    I hope you mean you defined a QPainterPath member variable of the class TrajectoryItem. If not, that is what you should be doing.

    I was wondering if there exists some way to specify default pen and brush options for an item
    Instead of deriving a custom class from QGraphicsItem, you could simply use a QGraphicsPathItem and set your QPainterPath on it. (QGraphicsPathItem::setPath()). Since this class is derived from QAbstractGraphicsShapeItem, you can set the pen and brush when you create the item and the QGraphicsPathItem::paint() method will use them.

    Sorry I didn't think of this earlier. What you have done is equivalent to QPainterPathItem, just that QPainterPathItem creates and stores the path for you.

  7. #7
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Quote Originally Posted by d_stranz View Post
    I hope you mean you defined a QPainterPath member variable of the class TrajectoryItem. If not, that is what you should be doing.
    Yes, that's what i did

    Quote Originally Posted by d_stranz View Post
    Instead of deriving a custom class from QGraphicsItem, you could simply use a QGraphicsPathItem and set your QPainterPath on it. (QGraphicsPathItem::setPath()). Since this class is derived from QAbstractGraphicsShapeItem, you can set the pen and brush when you create the item and the QGraphicsPathItem::paint() method will use them.

    Sorry I didn't think of this earlier. What you have done is equivalent to QPainterPathItem, just that QPainterPathItem creates and stores the path for you.
    This looks nice, i'll give it a try.

    I was also thinking about letting the user decide the length of the trajectories, so that for smaller settings the first points are not repainted. After a brief search it seems to me that this is not possible with QPainterPath, as there is no way to remove subpaths once their are added. So in this case I think I must stick to your first suggestion, that is drawing each ellipse separately in the paint() method. With this method i'll be able to choose which ellipse should drawn, depending of the user's setting. Is this the only way, or it could also be done with the QPainterPathItem?

  8. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Yes, one of the disadvantages of QPainterPath is that it is "write-only". However, you can still use the QGraphicsPathItem if it is convenient for the painting.

    If all of your ellipses are the same size, then you can use QList to keep track of their QPoint centers. At each animation step, you can use QList::push_front() to put the latest ellipse location on the top of the list, and if the list is larger than the maximum number of ellipses, you use QList::pop_back() to remove the last item on the list. With this convention, the most recent trajectory ellipse is always first on the list and the oldest one is last.

    However, if you want to make your animation a little bit more slick, go back to drawing each ellipse individually in the TrajectoryItem:: paint() method. Derive your class from QAbstractGraphicsShapeItem so you can take advantage of its built-in pen() and brush() methods.

    Qt Code:
    1. // Pseudocode, but you know what to do to make it work
    2.  
    3. void TrajectoryItem::paint( QPainter * painter, ... )
    4. {
    5. QPen pen = pen(); // use a new pen copied from the stored one
    6. QColor color( pen.color() );
    7.  
    8. int nMaxItems = maximum trajectory length;
    9. int nItems = ellipseList.size();
    10. for ( int nItem = 0; nItem < nItems; ++nItem )
    11. {
    12. const QPoint & point = ellipseList.at( nItem );
    13.  
    14. color.setAlphaF( float( nMaxItems - nItem ) / float( nMaxItems ) );
    15. pen.setColor( color );
    16. painter->setPen( pen );
    17. painter->drawEllipse( point, radius1, radius2 )
    18. }
    19. }
    To copy to clipboard, switch view to plain text mode 

    With this code, once the list has reached maximum length the alpha value of the color will decrease with each ellipse until the last one is barely visible. So if you keep a finite-length trajectory, then the trails will seem to fade out over each step of the animation. If the length of the list hasn't yet reached the maximum, the last ellipse will still have good visibility. Eventually nItems will equal nMaxItems.
    Last edited by d_stranz; 24th February 2016 at 17:12.

  9. #9
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Quote Originally Posted by d_stranz View Post
    Yes, one of the disadvantages of QPainterPath is that it is "write-only". However, you can still use the QGraphicsPathItem if it is convenient for the painting.

    If all of your ellipses are the same size, then you can use QList to keep track of their QPoint centers. At each animation step, you can use QList::push_front() to put the latest ellipse location on the top of the list, and if the list is larger than the maximum number of ellipses, you use QList::pop_back() to remove the last item on the list. With this convention, the most recent trajectory ellipse is always first on the list and the oldest one is last.
    I'm not sure I understand this. I basically used this strategy in the implementation without QPainterPath, I don't see how this could apply to the QPainterPath approach. In the QGraphicsPathItem case the ellipses are added to the path and there is no way to have the path reading what to draw from a list, isn't that true?

    Quote Originally Posted by d_stranz View Post
    However, if you want to make your animation a little bit more slick, go back to drawing each ellipse individually in the TrajectoryItem:: paint() method. Derive your class from QAbstractGraphicsShapeItem so you can take advantage of its built-in pen() and brush() methods.

    Qt Code:
    1. // Pseudocode, but you know what to do to make it work
    2.  
    3. void TrajectoryItem::paint( QPainter * painter, ... )
    4. {
    5. QPen pen = pen(); // use a new pen copied from the stored one
    6. QColor color( pen.color() );
    7.  
    8. int nMaxItems = maximum trajectory length;
    9. int nItems = ellipseList.size();
    10. for ( int nItem = 0; nItem < nItems; ++nItem )
    11. {
    12. const QPoint & point = ellipseList.at( nItem );
    13.  
    14. color.setAlphaF( float( nMaxItems - nItem ) / float( nMaxItems ) );
    15. pen.setColor( color );
    16. painter->setPen( pen );
    17. painter->drawEllipse( point, radius1, radius2 )
    18. }
    19. }
    To copy to clipboard, switch view to plain text mode 

    With this code, once the list has reached maximum length the alpha value of the color will decrease with each ellipse until the last one is barely visible. So if you keep a finite-length trajectory, then the trails will seem to fade out over each step of the animation. If the length of the list hasn't yet reached the maximum, the last ellipse will still have good visibility. Eventually nItems will equal nMaxItems.
    This is a nice hint, thanks.

  10. #10
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    I'm not sure I understand this. I basically used this strategy in the implementation without QPainterPath, I don't see how this could apply to the QPainterPath approach. In the QGraphicsPathItem case the ellipses are added to the path and there is no way to have the path reading what to draw from a list, isn't that true?
    If you use it with QPainterPath, then you need to re-create the path each time the list changes. And if you are going to do that, then using a QPainterPath may be less efficient than simply drawing the ellipses manually as you did in your first implementation. The only advantage using the QPainterPath buys you is that it is probably faster at painting between changes than the manual approach. The downside is that it doesn't let you implement the fade-out algorithm - everything in the path is painted using the same pen. So if you like the fade-out idea, then stick to keeping track of the ellipses yourself and painting them manually in the paint() method.

    Sorry for throwing so many different solutions at you. The more I think about it, the more ideas come to mind.

  11. #11
    Join Date
    Feb 2016
    Posts
    6
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsView slow when adding new items

    Ok now I understand. But couldn't this continous redefinition of the path make the overall method slower than the manual painting? Anyway, I decided to stick to the manual painting, after some other optimization here and there I'm satisfied of the result.

    No problem for the different solutions, it's great to have more than one hint, makes me dig it deeper

Similar Threads

  1. QComboBox extremely slow on adding a lot of items
    By Buldozer in forum Qt Programming
    Replies: 3
    Last Post: 16th May 2021, 13:46
  2. Adding items in QGraphicsScene is slow
    By Grade in forum Qt Programming
    Replies: 23
    Last Post: 27th July 2013, 16:09
  3. Adding millions of GraphicsItems is slow
    By rk123 in forum Qt Programming
    Replies: 1
    Last Post: 28th April 2009, 08:17
  4. Too slow adding & defining items to QTreeWidget
    By jnk5y in forum Qt Programming
    Replies: 16
    Last Post: 22nd April 2008, 21:26
  5. Adding QComboBox to QTableWidget very slow
    By munna in forum Qt Programming
    Replies: 2
    Last Post: 13th July 2006, 16:45

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.