QGraphicsView is slow with alot of items

1,315 views
Skip to first unread message

illunara

unread,
Apr 2, 2013, 8:01:38 AM4/2/13
to python_in...@googlegroups.com
Hi everybody
I'm working with QGraphicsView for a while, and after had finished some basic stuff, i realized it gets slow down when there are a lot of items (around 20, and it will freeze).

When i set cacheMode in QGraphicsItem to ItemCoordinateCache which will block the paintEvent, the performance is improve. So i guess that is the cause. However, i don't know how to solve this yet :(

Thanks

Justin Israel

unread,
Apr 2, 2013, 2:07:53 PM4/2/13
to python_in...@googlegroups.com

20 items is by no means "a lot". I have had scenes with thousands of items. It sounds like you might be doing something heavy in your paint events.
Can you provide some more details about the items you are using? Snippet?

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To post to this group, send email to python_in...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Judah Baron

unread,
Apr 2, 2013, 7:04:12 PM4/2/13
to python_inside_maya
Yeah, that sounds suspicious. What's going on with those 20 items?

Tuan Nguyen

unread,
Apr 3, 2013, 1:33:17 AM4/3/13
to python_in...@googlegroups.com
The first thing i had found out is the svg image i use for GraphicsItem is too complicate, after i remove all the detail from it, it runs a little smoothly but thousands of items O.O, i can't even imagine it. Here is the class of GraphicsItem i used

https://gist.github.com/illunara/5298674

Thanks for reply


You received this message because you are subscribed to a topic in the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python_inside_maya/KO-lFG2GYMY/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to python_inside_m...@googlegroups.com.

Justin Israel

unread,
Apr 3, 2013, 2:06:45 AM4/3/13
to python_in...@googlegroups.com
The formatting and indents are all messed up on that gist. But it doesn't look like you are doing much of anything in the paint event. Is it slow to add them to the scene, or slow when you have 1000 of them and are just moving them around?

One thing I can see is that it may be related to the sheer number of individual svg images being loaded. What you might want to do is share a renderer for svgs that you constantly load. For instance, you have the common ":/Scene_ConnectNode.svg" image being loaded multiple times for every GSceneItem; once for the output and then N number of input keyItems. 
What you could do is create just one renderer for it and store it on the scene or whereever, and then set that renderer on every new Connectionitem:

##
self._connRenderer = QSvgRenderer(':/Scene_ConnectNode.svg')
...
inputNode = ConnectionItem(self)
inputNode.setSharedRenderer(self._connRenderer)
self.inputNode[key] = inputNode
...
outputNode = ConnectionItem(self)
outputNode.setSharedRenderer(self._connRenderer)
self.outputNode = outputNode
##

You could also do the same for your node.sceneImage paths if they are a common set. Keep a dictionary of renderers mapping to the path, and set the shared renderer on each one. This might greatly reduce the number of times you are loading this into memory. Though for all I know, Qt might be doing some smart caching under the hood. But if its anything similar to QPixmaps, this would work. I do the same with QPixmapCache, to cache the same path instead of loading a new one for each.

Also the docs say that by default caching is enabled so you shouldnt have to mess with any of that to get performance.


Tuan Nguyen

unread,
Apr 3, 2013, 2:51:32 AM4/3/13
to python_in...@googlegroups.com
Sorry, i had fixed the the indents. And yes, it slows when i try to moving them (the UI) around. not when i create them. This problem not relate to how we setup the GraphicsView and Scene, isn't it?

I will try what you told me first :3

Justin Israel

unread,
Apr 3, 2013, 3:30:52 AM4/3/13
to python_in...@googlegroups.com
There are optimizations for the QGraphicsView, but I wouldn't think you would need them right away with just a few items. Also the default ViewportUpdateMode should be the MinimalViewportUpdate which tries and draws only the smallest partial areas of the scene that change. Moving one item should only be drawing small portions at a time.
You would probably want to make sure you have the default DeviceCoordinateCache set for the CacheMode of the items. 

If you want to zip up a working minimal example that exhibits the slowness, I will take a look at it for you.

Tuan Nguyen

unread,
Apr 3, 2013, 3:45:19 AM4/3/13
to python_in...@googlegroups.com
Yes, please :3

Tuan Nguyen

unread,
Apr 3, 2013, 6:13:58 AM4/3/13
to python_in...@googlegroups.com
Sorry Justin, in previous post, you said that you will send me some example about how to minimize the slowness or i will send you the project that i currently working on? Look like i mess up with English :3

Justin Israel

unread,
Apr 3, 2013, 6:39:07 AM4/3/13
to python_in...@googlegroups.com
I was asking if you could zip up a short working example demonstrating the slowness, so that I could download it, test it out, and report back about why it might be running slowly.

Tuan Nguyen

unread,
Apr 3, 2013, 8:39:50 AM4/3/13
to python_in...@googlegroups.com
Here's my work that i had posted earlier . It's contain script for the GUI, ai file, icons...... You can find the setup for QGraphicsView and QGraphicsScene in GraphicsModule.py . Setting for QGraphicsItem inside NodeModule and skip all the rest :3

and use this to call it up

from Lib import GraphicsModule
GraphicsModule.showUi()

http://www.mediafire.com/download.php?l8giq5l992jj2bh

Group doesn't allow attach large file, so i upload it to some host :(

Thank Justin :3


On Wed, Apr 3, 2013 at 7:24 PM, Tuan Nguyen <cb.il...@gmail.com> wrote:
Here's my work that i had posted earlier . It's contain script for the GUI, ai file, icons...... You can find the setup for QGraphicsView and QGraphicsScene in GraphicsModule.py . Setting for QGraphicsItem inside NodeModule and skip all the rest :3

and use this to call it up

from Lib import GraphicsModule
GraphicsModule.showUi()

Thank Justin :3

Justin Israel

unread,
Apr 4, 2013, 5:36:48 AM4/4/13
to python_in...@googlegroups.com
I was kind of hoping you had a small, concise example, since it took a while to go through all of that code and figure out where possible slow downs could be coming from. But I went through and turned off as much as possible to narrow it down to simply the QGraphicsSvgItem subclass..

Here are two things you can play with to improve performance:

1. Using a shared renderer for the same svg. It was already a big improvement when I switched it to not load new svg items every time for every item. Unfortunately svg items are really slow to render when you have them all in a group near each other. So if they can all just come from one render source, the speed increases.

2. Caching a pixmap manually during a drag operation. I flag the mouse press event, and then create a cached pixmap of the item in the move event. Then when the paint event occurs, it sees the cached pixmap and paints that instead of asking the svg renderer to do anything. When the mouse is released, the flag is unset and it goes back to painting from svg again. 
I didn't have much time to spend on this so I was a bit hackish in determining a resolution. Just picked a multiplier for the size. Ideally it would be matched to the views scale a bit better so that you can render a really small pixmap when its zoomed out and then a high res only when its really zoomed in. 

Hope that helps. Mainly...the svgs are slow :-/ 
Maybe there are other tweaks to be made somewhere, but I would have to spend more time.


-- justin

Justin Israel

unread,
Apr 4, 2013, 5:40:10 AM4/4/13
to python_in...@googlegroups.com
I forgot to mention the 3rd point, which doesn't have a code example but is just a suggestion...
If you can manage to draw these nodes purely with QPainter, that would make it very fast. You might be able to still use an svg for the small icon or even a higher res pixmap. But the node itself is pretty simple and could be designed with QPainter.

Tuan Nguyen

unread,
Apr 4, 2013, 6:07:18 AM4/4/13
to python_in...@googlegroups.com
Sorry for trouble you, but since the script is relate to each other, i don't know where should i cut down.

So it takes more time to render down a Svg than paint itself?. I always think the opposite :3 Anyway, i get your ideas and will give it a try right away. Thank Justin

Justin Israel

unread,
Apr 4, 2013, 6:19:48 AM4/4/13
to python_in...@googlegroups.com

People have mentioned that they can be slow. You could also try replacing my pixmap approach with toggling the cache mode from device coordinate to item coordinate during drag, and back to device coordinate again. That does a similar thing where it caches it once in an internal pixmap cache.

Tuan Nguyen

unread,
Apr 6, 2013, 4:02:52 AM4/6/13
to python_in...@googlegroups.com
But if i use QPainter to paint custom Shape (like Ellipse, Cone, Pie) , how can i create an exact bounding box of them? that's the reason why i decided to use QSvgItems instead of QGraphicsItem :3

QGraphicsItem only allows us to use QRect to set the boundingRect of the item, isn't it?

Justin Israel

unread,
Apr 6, 2013, 4:18:05 AM4/6/13
to python_in...@googlegroups.com
I'm not sure I follow your concern.  The bounding rect of a custom QGraphicsItem is the calculated amount you decide and return. A QGraphicsRectItem returns the boundingRect to encompass its configured side. 
For a custom "node" item, you would just choose a node size, and draw various things within that rect. The paint event hands you the rect for which you should be painting inside, based on the boundingRect you are reporting for the item.

Create an item that returns a bounding rect of QRectF(0,0,60,30)
In the paint event, paint a rectangle matching the option.rect that is passed to you (don't calculate it again, just use the rect it gives you). Draw some more rects and rounded rects inside at relative positions. Draw a pixmap for the icon if you want, and use a cached one from QPixmapCache or just simply a shared pixmap that exists on all the same class instances. 

Tuan Nguyen

unread,
Apr 6, 2013, 5:12:27 AM4/6/13
to python_in...@googlegroups.com
Ah....i think i misunderstand it. Sorry for a vague question.

And as always, thank Justin :3

Justin Israel

unread,
Apr 6, 2013, 6:52:47 AM4/6/13
to python_in...@googlegroups.com

No problem!

Tuan Nguyen

unread,
Apr 7, 2013, 2:09:22 AM4/7/13
to python_in...@googlegroups.com
I try to paint those GraphicsItem by QPainter, but i only get it works when painting on a QWidget,
Here is the script

https://gist.github.com/illunara/5329248

painter = QtGui.QPainter(self)

I think i did something wrong with the PaintDevice?


--
You received this message because you are subscribed to a topic in the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python_inside_maya/KO-lFG2GYMY/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to python_inside_m...@googlegroups.com.

Justin Israel

unread,
Apr 7, 2013, 2:36:25 AM4/7/13
to python_in...@googlegroups.com

Don't create a painter. Look at the arguments. It passes you a shared painter.

Tuan Nguyen

unread,
Apr 7, 2013, 2:46:26 AM4/7/13
to python_in...@googlegroups.com
oh my..... T_T silly me.

Justin Israel

unread,
Apr 7, 2013, 2:50:23 AM4/7/13
to python_in...@googlegroups.com
1) Implement boundingRect() to return the appropriate size of your item at any given moment. You can return the same size if your widget doesn't change, and only the view does the scaling

2) Check out the method signature for the paint() event of a QGraphicsItem. It hands you a QPainter instance that is being passed down all of the children in the scene.  It also gives you a bunch of options about the current state, such as the bounding rect that should be drawn. At this point the bounding rect for your item has already been called so you shouldn't get it again


[snip]
    def boundingRect(self):
        return QtCore.QRectF(0,0,100,50)
...
    def paint(self, painter, option, widget):
        rect = option.rect
        painter.setRenderHint(painter.Antialiasing)
[/snip]


Tuan Nguyen

unread,
Apr 7, 2013, 3:39:42 AM4/7/13
to python_in...@googlegroups.com
Oh wow, thank Justin. It works XD

Justin Israel

unread,
Apr 7, 2013, 3:47:54 AM4/7/13
to python_in...@googlegroups.com
Once you design a replacement node, throw like 50 of them in your scene and drag them around, and report the results back. Interested to see if you find a significant performance increase.

Tuan Nguyen

unread,
Apr 7, 2013, 4:27:12 AM4/7/13
to python_in...@googlegroups.com
Actually. when you told me about how i render over and over those items,i remove SVG's unnecessary thing, shared render and the performance  had increased a lots (around hundreds ). But still, i want to go all the way down, and make it as much as i can :3

I will report back when i finished with it :3

Tuan Nguyen

unread,
Apr 7, 2013, 2:06:14 PM4/7/13
to python_in...@googlegroups.com
@Justin : I made it, after i re-draw all object using QPainter,  no more slow when i thrown in a ton of object into the scene. This is great, it still has a little lagging while pan, zooming through.

Is there anything else i can do to optimize the view?
Reply all
Reply to author
Forward
0 new messages