Responsive QGraphicsWidget

437 views Asked by At

My problem is a simple yet annoying user experience damaging one. I'm trying to display huge amounts (10 of thousands) of custom QGraphicsItems on a QGraphicsScene. I would also require a QGraphicsLayout, but to my understanding one could not simply put a layout on a scene, for that the layout have to be set on a QGraphicsWidget and then the widget added to the scene.

Now when I need to load a large number of items and prepare them to be displayed the scene just sits there empty, because I can't invalidate or repaint the aforementioned QGraphicsWidget. Only in the end it can be added to the scene otherwise any later change will not be displayed.

The goal: add some kind of layout management to scene -> {prepare an item -> add item to layout -> display -> prepare an item ->} repeat ...

But what I have: {prepare an item -> prepare another item ->} repeat ... -> add everything to layout -> set layout management on scene -> display

So the question: What way is there to create a responsive layout/container to the scene, one which repaints itself after a new child is added to it?

2

There are 2 answers

1
Scheff's Cat On

From Qt manual:

When the scene changes, (e.g., when an item moves or is transformed) QGraphicsScene emits the changed() signal.

It does not mention about addItem() and removeItem(). Hopefully, these are covered also.

If so your could apply the following:

A signal handler may "catch" the change and requests the repaint (e.g. by invalidate()) of the scene. (Un-)Fortunately, this does not immediately repaint the scene (with its parent view) but does queue a repaint event. Thus, the actual repaint is not done until the event loop is passed again.

(The event loop is part of the QApplication. If you stop your application inside a command initiated by the user interface you will find in the callstack something like this:

Qt5Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags)
Qt5Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags)
Qt5Cored.dll!QCoreApplication::exec()

)

You can force this by calling QApplication::processEvents(). (It's even static.) AFAIK, it is usually no problem if event loops are nested because they are prepared for this (as long as no non-terminated recursion is formed).

However, this probably needs some fine-tuning because too many repaints may slow down your application massively.

0
Tibor Czimbor On

Managed to find my error. It was in the custom layout I created for the QGraphicsWidget.

As the doc says on setting the layout:

All widgets that are currently managed by layout or all of its sublayouts, are automatically reparented to this item. The layout is then invalidated, and the child widget geometries are adjusted according to this item's geometry() and contentsMargins(). Children who are not explicitly managed by layout remain unaffected by the layout after it has been assigned to this widget.

After the layout has been set to the widget any new item added to it was not reparented to the widget, so they remained unaffected and not displayed.

Calling QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem* newItem) link on every item inserted to the layout solved my problem.