Qt. Properly removing qGraphicItems

Hello, I am stuck with a problem regarding removal of items from a qGraphicsScene.
I have a scene with selectable and movable items in it. Each item have a lot of child items. I want to be able to select an item, press “delete” and remove that item along with it’s children from the scene.
The problem I have is that when I’ve been adding and removing items from the scene for a while, it randomly gets “stuck”. I can still navigate in the scene using my panning and zooming functionality, as well as add new items to the scene without any problem.
I can however not interact with anything in the scene any more, not select and therefore not delete anything.

I have been debugging this for a while and figured that maybe something is stuck as a mouseGrabberItem, preventing anything else from receiving mouse events. I can retrieve a mouseGrabberItem from the scene once it’s stuck, the item returned is most often a non visible item (however that is possible) but it once was a visible item that I could hide and show with the corresponding commands. I could NOT remove it from the scene using removeItem though. Trying to remove it didn’t return an error, but nothing got removed and I could still show/hide it.

Any suggestions to what may cause this strange lock-up would be very helpful :slight_smile:
Thanks in advance!

Ok, so I’ve managed to track down the interaction freeze bug to have something to do with mouse-grabbers, and specifically the following error:

QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene

The source code for QGraphicsItem leads me to belive that the QGraphicsItem may have lost it’s pointer to the scene? I’m not very experienced with c++, but that’s how I read it:

void QGraphicsItem::ungrabMouse()
	if (!d_ptr->scene) {
		qWarning(""QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene"");

Searching for the error on google only results in one un-answered question as well as links to source code and some project specific errors that makes little sense.
Some thoughts on the case would be much appreciated :slight_smile: Thanks!

The problems I’ve seen with Qt that could be related are:
1- Make sure no thread other than the main thread calls into Qt GUI stuff.
2- Sometimes, the ‘underlying C++ object’ will be deleted by python/Qt, even though python still has a reference to it somewhere. You can try sticking all your instances into some global list and see if that gets rid of the problem (which is better solved in another way!).

I doubt they’re related but I thought I’d post it anyway.

It’s probably an error on your end and unfortunately without any more details we can’t help you. I’ve had no problems with QGraphicsScene or removing items from it when building a node editor with alot of nodes and hierarchies.

What I can suggest is isolate parts of your code so you can have a reproducable bug with minimal code and work your way from there, and even upload that little isolated part here on the forums and let people check it. Though keep it very simple.

I managed to track it down to a certain part of my code and remove that functionality from the application. This functionality that got removed wasn’t essential, and I will not look in to it further at the moment because of it’s big scope. I will however post whatever I find out about it here if I solve it later, for further reference :slight_smile: Thanks for the help guys :slight_smile:

I am actually writing a node editor at the moment LoneWolf. I am having some trouble with performance though when I have more than lets say… 10 nodes in the scene. This is a bit problematic :stuck_out_tongue: Do you have any general “tips and tricks” regarding performance to share?

Yeah sure. The editor I wrote had more than 1000 nodes which had probably 10 or more connections to each other, and I had zero performance issues. It looked beautiful :smiley:

There are a few tricks you can do.

1. Enable caching on QGraphicsItems: self.setCacheMode(QtGui.QGraphicsItem.ItemCoordinateCache )
This allocates GPU memory, basically render targets, for your nodes to efficiently redraw it when needed and keeps it cached as long as the content has not changed.

2. Enable sort caching on QGraphicsScene: self.setSortCacheEnabled( True )
This will speed up parent-child related QGraphicsItems during interaction such as moving them around in the scene.

3. Enable background caching on QGraphicsView: self.setCacheMode( QtGui.QGraphicsView.CacheBackground )
This will allocate one render target for the entire background and reuse it on the GPU.

4. Set the cache size to be used by the pixmaps: QtGui.QPixmapCache.setCacheLimit(101200) # 100 mb GPU cache
This sets the maximum texture memory your software should use when caching on the GPU.

5. Set QGraphicsScene indexing to none: self.setItemIndexMethod( QtGui.QGraphicsScene.NoIndex )
This will speed up interactive scenes where stuff move around such as the user dragging nodes which in turn has children or bezier curve connections etc. Indexing is good for static scene that don’t change. This might give speed or might not. Try it.

6. Play around with ViewportUpdateMode on QGraphicsView: self.setViewportUpdateMode( … )
Here you can tell QGraphicsView how much to update the scene. Minimal, Smart, BoundingRect etc. Play around with these there is no magic choice.

7. Use as little QGraphicsItems as possible and handle the drawing yourself inside the paint method!
The GPU caching is very handy here since it allocates a render target per node which means it only needs to REDRAW the node if the CONTENT changes like within the paint method of QGraphicsItem. So you would draw the text, input knobs, output knobs, headers etc inside the paint method. :slight_smile:
This is also true for your bezier curve connections, play around with drawing them yourself inside drawBackground vs using actual QGraphicsItems. I went with using drawBackground on QGraphicsScene as far as I can remember for the bezier curves.

8. Don’t trigger something that would redraw the entire QGraphicsScene.
Don’t do stuff like calling update on the QGraphicsView or QGraphicsScene to redraw everything. You shouldn’t need to anyway when you use methods such as paint on your nodes. If you are going to use paint(…) on QGraphcisItem be sure to reimplement shape() which is used to determine what parts only need to be redrawn in the scene and boundingRect for selection and interaction.

These are some stuff that came to my mind that I used at my old job while working on a node editor!

Another thing you can do which might not sound like a trivial task is to use the OpenGL widgets that come with Qt. Alot of people had success with it. Basically you make your own node scene with node items :stuck_out_tongue:

But if you follow the advice above you shouldn’t have any problems really! OpenGL stuff should be a last resort thing where you really need every bit of performance.

Also not a performance related thing but more of a design choice. You should be able to connect your nodes, serialize, deserialize and stuff without a GUI being present. So design the connections, inputs, outputs, nodes with pure python or QObjects and then do the editor part.
Let the core API allow callback registration which the GUI uses to update itself (Observer Pattern).
This way adding a script editor to the GUI becomes a trivial task. Since the callbacks are already registered to the view (the GUI) any script editor executed stuff will automatically show up on the GUI. This way you can also record scripts and build templates and extend the GUI to automate common stuff and let users kinda build their own “lib” of actions. Also if you don’t like the GUI or rendering, you can easily change it without affecting the core.

Lycka till >_^

Excellent post LoneWolf, this should go into the docs for QGraphicsScene :slight_smile: Most of this info is out there, or in the docs themselves, but it’s a pain to put it all together… This is the perfect cheat sheet.

Haha ty! Yeah most of it is in the docs already but they are so scattered around that you have to carefully search for them. I remember I compiled down a list back then which I used as a guidence but I couldn’t find it so I just went and regathered them :slight_smile:

Tack så mycket! :wink:

LoneWolf, where were you when I started this project a couple of months ago? :stuck_out_tongue:
Thank you for one of the best answers I’ve ever received, I will look over the whole system immediately, remove all SVG’s, draw stuff myself, reduce the amount of QGraphicsItems to as few as possible per node, and make the whole system be independent of the GUI itself.
This will keep me busy for a while :stuck_out_tongue:

  1. Enable caching on QGraphicsItems: self.setCacheMode(QtGui.QGraphicsItem.ItemCoordina teCache )
    This allocates GPU memory, basically render targets, for your nodes to efficiently redraw it when needed and keeps it cached as long as the content has not changed.

I found that QtGui.QGraphicsItem.DeviceCoordinateCache worked better in my case. ItemCoordinateCache didn’t automatically re-cache when zooming in/out, preventing antialiasing from working on any zoom-level but the default one. Using DeviceCoordinateCache didn’t slow it down either :slight_smile:

Yeah my zooming was basically scaling down the scene itself. You could never scale up to blur the nodes. That’s the reason I went with ItemCoordinateCache. From the doc: “The cache is also reused as the item is transformed”, reuse draws as much as possible :slight_smile:

Was a big boost when I had alot of nodes and since we are scaling down aka zooming outwards the nodes got smaller and I didn’t have to issue a redraw.

I’ve seen the behavior described by Calon before.
Really not sure where it’s coming from but I’ve only encountered it where the QGraphicsScene has no predefined scene rectangle.

Might actually have to track it down if it shows up again!

ShadowM8, many thanks for your advice. It helps me a lot, I have the same problem of uncorrect removing items. The root of the problem was undefined scene rect, as you wrote.

Cool, good to know it’s confirmed :slight_smile:

I’ve always defined my rects so never had the problem…