PDA

View Full Version : Slow GraphicsItem->setVisible() with say 200 children



sheckey
9th May 2007, 03:17
I want to be able to support setting "layers" of items visible or not in a GraphicsScene, much like CAD programs do. I thought I would use the feature of GraphicsItem where you can set it's parent to another GraphicsItem. All items in a layer would have the same parent item. I would set the layer visible or not by toggling the parent's visibility using setVisible().

This works, but quickly becomes rather slow as the number of items in the layer increases. After around 30 or 40 items, whenever I toggle the visilibity it can take seconds to redraw. Something is going on here!

Extra data:

1. I can a have many, many more objects in the Scene and do things like zoom very quickly, so I know it's not my processor. I've seen GraphicsView do very much more complicated things very quickly on this same system.
2. For comparison I tried making the items in a layer be part of a GraphicsItemGroup instead and toggling the visibility of that and it is very fast. (I don't want to use Group though becasue I want items to be independently moveable.)

Any ideas? Besides fixing this situation, any ideas on a better approach for other reasons?

Thanks! Jim.

aamer4yu
9th May 2007, 08:20
how are u hiding the children ??
in a loop of parent ?? if yes, are u using update in the loop ??

marcel
9th May 2007, 08:26
No, probably he just hides the parent item.

sheckey
9th May 2007, 16:46
Yes. That was the idea behind making the children have a common parent: settign the visibility of the parent also sets the visibiltiy of the children. I'll post some code later today.

sheckey
9th May 2007, 21:50
Ok. Here is come code that illustrates the condition. (Forgive me if it is not formatted well. I am new to this forum and fora in general).

Make a form with graphics view on it. Make one of the following classes shown in the code section below and set your view's scene to it like this: graphicsView->setScene(new QMyTestScene);

Now run and click on the scene. It makes a new ellipse that is the child of the first one. It also toggles the visiblity of the parent and thus the children. Do this about 30 times and it gets pretty slow. At about 30 or so I end up waiting like a second for it to update.

Comment out the visiblity toggling to see that adding the ellipses does not take long at all up to large numbers of ellipses.

If anyone has the heart to try this it is very interesting. Thanks!



#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>

class QMyTestScene : public QGraphicsScene
{
public:
QMyTestScene(void)
{
// Make the ellipse that will be the parent of all others
theParentEllipse = addAnEllipse(0.0,0.0);
}
protected:
QGraphicsEllipseItem *theParentEllipse;

QGraphicsEllipseItem *addAnEllipse(qreal x, qreal y)
{
QGraphicsEllipseItem *item = new QGraphicsEllipseItem(QRectF(x-0.1,y-0.1,2.0*0.1,2.0*0.1));
addItem(item);
return item;
}

void mousePressEvent(QGraphicsSceneMouseEvent *e)
{
// Add another ellipse at the mouse scene position
QGraphicsEllipseItem *item = addAnEllipse(e->scenePos().x(),e->scenePos().y());
item->setParentItem(theParentEllipse);

// Toggle parent visiblity
theParentEllipse->setVisible(!theParentEllipse->isVisible());
}
};

marcel
9th May 2007, 22:08
I cannot yet give you a solution but I can tell you why it is taking so long:


if (d_ptr->visible == quint32(visible))
return;
if (!visible) {
if (d_ptr->scene && d_ptr->scene->mouseGrabberItem() == this)
d_ptr->scene->d_func()->mouseGrabberItem = 0;
if (hasFocus())
clearFocus();
if (isSelected())
setSelected(false);
}

if (!visible)
update();
d_ptr->visible = itemChange(ItemVisibleChange, quint32(visible)).toBool();
if (visible)
update();

foreach (QGraphicsItem *child, children())
child->setVisible(visible);


That is QGraphicsItem::setVisible().
As you can see, every time an item is hidden or shown, the corresponding scene rect is updated ( this is done in update() ). The update is the one that is taking so much time.

I am not sure if there is a solution to this.
Perhaps if it was possible to update the entire bounding rect of the items at once, maybe it would be faster. But now an update is triggered for every child item.

Lame solution: make your own QCustomItem class derived from QGraphicsItem and make your own setVisible method, in which you update only once, the entire bbox.
QGraphicsItem::setVisible is not virtual, so be careful to cast every time to your item class and call setVisible.

Regards

sheckey
10th May 2007, 01:57
Wow! I see what you mean. I would have thought that it would only update the items Rect. Good find!

I know these Qt guys have or will have a way to make layers in a graphics application. It seems like such a common thing to want to do. I think I'll just eat the delay for now and get my job done while I think about another way to do it or wait for a Qt employee response that goes something like this "we are just about to fix that in the forthcoming 4.3 release" :)

If I find out more or think of soemthign new I'll let you guys know. Thanks!

Jim