How to get notifications from QGraphicsScene on addItem or itemChange?
Asked Answered
Y

2

12

In my project I'm using a QGraphicsScene and adding / removing items all over the code.

Now I want to get notified whenever a QGraphicsItem gets added or removed. Many Qt classes have notification signals or at least virtual functions that get called on such changes. I'm trying to avoid adding many rows of code in many places which is not only cumbersome but also unsafe (forgetting an insert / removal now or in the future).

I need a solution that works with any QGraphicsItem.

This is a list of things that do not work:

  • connect to a signal of QGraphicsScene (as in QAbstractItemModel::rowsInserted()) -> there is none.
  • inherit from QGraphicsScene and overload a virtual notifier function (as in QTabWidget::tabInserted()) -> there is none.
  • inherit and overload addItem(), sending notifications myself (as in QMdiArea::addSubWindow()) -> addItem isn't virtual and gets called from the constructors of QGraphicsItems.
  • install event filters on newly added QGraphicsItems -> no idea how to get the newly added item AND it would be a sceneEventFilter which can only be installed on other QGraphicsItems.
  • connect to itemChange() of QGraphicsItem -> itemChange is not a signal AND overloading QGraphicsItem is not an option.
  • wrap QGraphicsScene (having the scene as a private member) and only expose functions addItem and removeItem -> but QGraphicsItems in that scene could still access it via scene() function, so this solution is not safe enough.

How can I get notifications on item changes?

If there is an easy way that I simply missed, please point me to it. Else, I'd appreciate ideas on this very much.

Yoshida answered 22/10, 2012 at 11:35 Comment(6)
I think the best way is just to wrap QGraphicsScene and provide your own addItem() function in the wrapper.Kassandrakassaraba
It's shame you can't subclass QGraphicsItem as all it's scene changes (including scene addition/removal) run through it's sceneEvent(QEvent* event) method - which provides a very clean and tunable solution to your problem.Deuce
@Kassandrakassaraba As I said, the addItem() function isn't virtual and gets called from the constructors of QGraphicsItems. So this would be not a complete solution.Yoshida
@Deuce I'd have to subclass QGraphicsObject as well (which is also in use), thus duplicating the code. Even worse, in order to send signals from QGraphicsItem I'd have to inherit from QObject, too, which breaks several inheritance hierarchies in my project.Yoshida
I think what pmr meant is you could write a class which contains a QGraphicsView rather than inheriting from it. That class would not grant public access to the view so creators of Items are forced to call your own addItem function. Obviously you could no longer supply the scene via constructor (because you cannot get() it from your wrapper) and you would have to change every line where an item is currently added to your scene.Luik
@TimMeyer This would still be unsafe, because the QGraphicsItems as well as the QGraphicsView know their scene() - which gets used e.g. for scene()->removeItem(this) - I'd still not be able to prevent that or at least get notified about it.Yoshida
P
2

I think the best you can do is to connect to QGraphicsScene::changed() signal.

It will not tell you what items are changed/added/removed since it's intended for QGraphicsView to update the display. But you should be able to find out using the supplied regions.

Peggy answered 22/10, 2012 at 13:49 Comment(0)
C
1

I realize one of the requirements of the OP is to not subclass the item, but I hope this still helps others who end up here. Qt is sending events to do this, it just requires extending the itemChange method to "get notified". You can then hook in a callback method like the example here, or provide signals, or whatever you need.

This is in Python but I'm sure it is the same pattern in C++:

def itemChange(self, change, value):
    """
    Runs if this item has the `ItemSendsGeometryChanges` flag set.

    This doesn't "accept" any of the changes, it is only to add hooks.
    """
    if change == QtWidgets.QGraphicsItem.ItemSceneChange:
        # The value for this event is the scene. None means the item was removed.
        if value:
            self.onAddedtoScene(value)
        else:
            self.onRemovedFromScene()

    return super(SceneNodeBase, self).itemChange(change, value)
Coenzyme answered 22/1, 2020 at 20:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.