PDA

View Full Version : Showing QGraphicsEllipseItems extremely slow



kalos80
12th January 2010, 13:24
Hi,

I have an application showing thousand of items (lines, points, polygons).
the user can click a button to show/hide all those items.

I'm porting that application from Qt3 to Qt4 (4.5.1).
The application was working greatily in Qt3, but after porting it to Qt4 I'm noticing it is extremely slower showing and hiding items. Debugging it deeply I found out that the bottleneck is the showing of points (QGraphicsEllipseItems) that is causing the application to get stuck. Actually to show 10000 points (QGraphicsEllipseItems w = 4 and h = 4) it took more that 5 minutes while in Qt3 it was almost instantaneous.
The same amount of Lines and Polygons is shown quite fast (just a few seconds).

I disabled the indexing in QGraphicsScene just to avoid it was causing the slowness, but it seems it didn't cause any improvement.

Am I missing something?
Here is part of the code I'm using to create and show the items. Attache is the whole Qt project.




MainWindow::MainWindow( QWidget * parent, Qt::WindowFlags flags)
: QMainWindow(parent, flags)
{
...
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 800, 600);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
scene->setBackgroundBrush(Qt::black);
gvView->setScene(scene);

// create the items
createNPoints(10000);
}

/*
* create some points
*/
void MainWindow::createNPoints(int num)
{
for( int i = 0; i < num; i++ )
createPoint();
}

void MainWindow::createPoint()
{
QGraphicsEllipseItem *p = scene->addEllipse(qrand()%int(scene->width()),qrand()%int(scene->height()), 4, 4);

QColor c(qrand()%32*8,qrand()%32*8,qrand()%32*8);
p->setPen(c);
p->setBrush(c);
p->setZValue(qrand()%256);

pointsList.append(p);
}

/*
This is the slot called when the user clicks the button to show the items
*/
void MainWindow::showPoints(bool yes)
{
for(int i = 0; i < pointsList.count(); i++)
{
if(yes)
pointsList[i]->show();
else
pointsList[i]->hide();
}
}



Thanks in advance for your help.

totem
12th January 2010, 14:02
here is how i did :

4115

certainly not elegant but sensibly faster

kalos80
12th January 2010, 14:52
Totem,

your solution works greatly! Now the show/hide of points is almost instantaneous.
Do you know what is causing the slowness using the default ellipse paint?

I think this is a very bad problem of QGraphicsScene, ellipses show/hide is unusable.

thanks again

kalos80
12th January 2010, 16:11
Totem,

I analysed better your solution.
What you are doing is when items are hidden by the user avoid to draw them (you disable the drawing in the paintEvent), but actually items are still there.
If you click the scene when items are hidden and you call items() on the scene, it will returns you some items under the mouse even if those items are hidden.

Unfortunately this is not a behavior admitted in my application, ;(

totem
12th January 2010, 17:08
Sorry i didn't know selection was important in your app.
Indeed what seems slow is to change items flags when there is a lot of items to affect, but I don't know why

If you want to keep my solution, you could take a look at QGraphicsItem::itemChange() method, with the QGraphicsItem::ItemSelectedChange flag and disable change it when s_show is false. But this becomes more and more ugly :


class MyEllipseItem : public QGraphicsEllipseItem
{
protected :

static bool s_show ;

public :
MyEllipseItem( qreal x, qreal y, qreal w, qreal h )
: QGraphicsEllipseItem(x,y,w,h)
{
setFlag( ItemIsSelectable, true ) ;
}

static void setEllipsesVisible( bool v ) { s_show=v ; }

protected :
virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if( s_show )
QGraphicsEllipseItem::paint(painter,option,widget) ;
}
virtual QVariant itemChange( GraphicsItemChange change, const QVariant & value )
{
if (change == QGraphicsItem::ItemSelectedChange )
{
// value is the new state.
bool newState = value.toBool() ;
return QVariant( newState&&s_show ) ;
}
return QGraphicsItem::itemChange(change, value);
}
} ;

kalos80
13th January 2010, 10:11
The Qt support replied to me:

"You're getting stuck in the code that manages which part of the screen should be repainted. When the items are showed/hidden explicitely they will request an update in the view, passing either their bounding box (I think its the bounding box, it could also be the shape(), but that would only be worse). This rectangle will be added to the update region, a QRegion, in the view. Since you have 10000 unique rectangles you get something equivalent to:

QRegion completeRegion;

for (int i=0; i<10000; ++i)
completeRegion += item[i].boundingRect();

where for each round in the loop, the region gets increasingly complex and the += is more and more heavy.

The fix is to update everything in one go, ignoring the update management. This can be done with:

gvView->setViewportUpdateMode(QGraphicsView::FullViewportU pdate);
"

I tried that solution and it worked greatly