QGraphicsScene and event handling
-
I have an application with a QGraphicsScene.
In the event handler of the scene, I process mouse (and tablet) events. For example, I determine whether the pointer is on particular items and select or move them, or create new items that are added to the scene. I do not use the built-in functions like drag Events.
This kind of works, but I ran into a range of problems:
The first is that I noticed artifacts when quickly moving the mouse around (e.g. moving the end point of a line) Despite correct bounding boxes and calls to
prepareGeometryChange()
, sometimes artifacts remained at the periphery of the bounding box. The same applied when showing or hiding entire items.
This problem disappeared when I setsetCacheMode(QGraphicsView::CacheBackground);
andsetViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
for the view.The next problem was that when I called
removeItem(item)
to remove items from the scene, sometimes the items remained visible, although they were no longer "there" (not initems()
. And when I deleted the items, the applications sometimes segfaulted when painting the scene because it tried to re-draw the item that were removed. Apparently, the removed items were still in the BSPTree the scene maintains to find items in
given screen areas. This problem disappeared when I setsetItemIndexMethod(QGraphicsScene::ItemIndexMethod::NoIndex);
in the scene.Overall, this accumulation of problems seem to indicate that I do not use the
QGraphicsScene
as intended by its designers.I found several similar reports, and the way I understand them is that modifications to the scene should not be done in its event handler, but in the main GUI thread.
If this is true, then the problem emerges how to determine, in the event handler, whether an event should be accepted or passed on to the parent item's method.
For example, it seems possible that an item is scheduled to be removed, but that the removal has not yet taken place because the main Gui thread has not yet called the corresponding code. In this case, the event handler code would still "see" the item.So overall my questions are:
-
Is it true that scene modifications should not take place in the event handler?
-
How are what the event handler and the main GUI thread "see" of the scene kept synchronized?
-
-
I have now moved all event handling out of the event handler, with a
Qt::QueuedConnection
. I also found a case whereprepareGeometryChange()
was not called before a change in boundingRect. This has solved the issue. -
@QtUser467 The event handler runs in the main thread. So there is no special need to keep them synchronized.
However, deleting items inside an event handler is...tricky. Consider:CMyGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { // some condition { delete this; return; } }
Safe? I think not. This would be inside the item's event handler, but I see deleting an item inside the QGraphicsScene's event handler similar: The item's pointer might still exist in a list of items to deliver the event to. GraphicsView cannot check and filter those lists after every call to a customized event handler function. Therefore, I would recommend only deleting items between events. Hide them and put them into a "delete list" (or use deleteLater() if they are QObjects anyway).
Regarding drawing artifacts: Are you sure your boundingRect() still returns the previous value at the time where you call prepareGeometryChange? That's important: You need to call it before changing any values that will affect output of boundingRect()
-
I have now moved all event handling out of the event handler, with a
Qt::QueuedConnection
. I also found a case whereprepareGeometryChange()
was not called before a change in boundingRect. This has solved the issue. -
@QtUser467 said in QGraphicsScene and event handling:
I have now moved all event handling out of the event handler, with a Qt::QueuedConnection
Usually there's no need to specify this option yourself. Let the connect "decide".
You must useQt::QueuedConnection
when sending signals across different threads, but even then Qt will notice thatsender
andreceiver
don't live in the same thread.Regarding your artifacts:
Due to optimization, not the whole scene is redrawn after operation, so if somehow some painted path/region of a (re-)moved or resized item exceeds theboundingRect
, this area might not get updated. Therefore remains might stay visible. -
@Pl45m4 said in QGraphicsScene and event handling:
Due to optimization, not the whole scene is redrawn after operation, so if somehow some painted path/region of a (re-)moved or resized item exceeds the boundingRect, this area might not get updated. Therefore remains might stay visible.
This indeed was one problem. The other one was that despite the call to
removeItem()
, the item stayed in the BSPTree, because theboundingRect
changed without a previous call toprepareGeometryChange()
(which I had everywhere except in this hidden case). Clearly my fault! Overall, this is not very fault tolerant and also not easy to debug! In a way, it would be nice to have a function that could be called (during debugging) to do some consistency checking, rather than only finding out much later during a paint event. -