PDA

View Full Version : Bad performance of QGraphicsScene



miraks
21st November 2008, 22:12
Hello,

I am creating a QGraphicsScene containing only 1416 rects with the following code (see next thread).

As you can see in traces, I have very bad performances:

## >SKGTableWithGraph::redrawGraph
## >SKGTableWithGraph::redrawGraph-remove scene
## <SKGTableWithGraph::redrawGraph-remove scene TIME=0.287117 ms
## >SKGTableWithGraph::redrawGraph-create scene
## Scene rect:-10,-354833,394415,394435
## Items:24x59=1416
## <SKGTableWithGraph::redrawGraph-create scene TIME=57306.6 ms
## >SKGTableWithGraph::redrawGraph-add scene to view
## <SKGTableWithGraph::redrawGraph-add scene to view TIME=1.1489 ms
## <SKGTableWithGraph::redrawGraph TIME=57312.6 ms

57s are needed just to build the scene :(

In my code, if I comment the addRect methods like this:

if (mode==0) {
//STACK
if (val>0) {
//graphItem=scene->addRect(width*x, -yPlus, width, -val, QPen(), QBrush(color));
yPlus+=val;
if (yPlus>ymax) ymax=yPlus;
} else if (val<0) {
//graphItem=scene->addRect(width*x, -yMoins, width, -val, QPen(), QBrush(color));
yMoins+=val;
if (yMoins<ymin) ymin=yMoins;
}
} else if (mode==1) {

then I have very good performances :) but my scene is empty (of course).

## >SKGTableWithGraph::redrawGraph
## >SKGTableWithGraph::redrawGraph-remove scene
## <SKGTableWithGraph::redrawGraph-remove scene TIME=0.179953 ms
## >SKGTableWithGraph::redrawGraph-create scene
## Scene rect:-10,-354833,394415,394435
## Items:24x59=1416
## <SKGTableWithGraph::redrawGraph-create scene TIME=9.6991 ms
## >SKGTableWithGraph::redrawGraph-add scene to view
## <SKGTableWithGraph::redrawGraph-add scene to view TIME=0.407059 ms
## <SKGTableWithGraph::redrawGraph TIME=13.9419 ms

Conclusion: Bad performances are due to addRect method.

Question: What is the solution to improve performances ?
I don't understand why "40000 Chips" example is very fast and not my "1416 rects" example ! :o

miraks
21st November 2008, 22:13
void SKGTableWithGraph::redrawGraph()
{
SKGTRACEIN(10, "SKGTableWithGraph::redrawGraph");
QApplication::setOverrideCursor(QCursor(Qt::WaitCu rsor));

ui.graphicView->hide();

//Recreate scene
if (scene) {
SKGTRACEIN(10, "SKGTableWithGraph::redrawGraph-remove scene");
scene->clear();
delete scene;
}


scene=new QGraphicsScene();
{
SKGTRACEIN(10, "SKGTableWithGraph::redrawGraph-create scene");
//Get current selection
int crow=ui.kTable->currentRow();
int ccolumn=ui.kTable->currentColumn();

int nbRows=ui.kTable->rowCount()-1; //Sum
int nbColumns=ui.kTable->columnCount();
int sumColumnIndex=nbColumns-1;
if (nbColumns>2) {
nbColumns=nbColumns-2; //Sum and average
--sumColumnIndex;
}
KLocale* locale=KGlobal::locale();

int mode=ui.kDisplayMode->currentIndex();

//Compute y limits
double minLimit=0;
double maxLimit=0;
if (mode==0) {
//STACK
for (int x=0; x<nbRows; ++x) {
QTableWidgetItem* tableItem=ui.kTable->item(x,sumColumnIndex);
if (tableItem) {
QString valstring=tableItem->text();
double val=locale->readMoney(valstring);
minLimit=qMin(minLimit, val);
maxLimit=qMax(maxLimit, val);
}
}
} else if (mode==1) {
//HISTOGRAM
for (int x=0; x<nbRows; ++x) {
for (int y=0; y<nbColumns; ++y) {
QTableWidgetItem* tableItem=ui.kTable->item(x,y);
if (tableItem) {
QString valstring=tableItem->text();
double val=locale->readMoney(valstring);
minLimit=qMin(minLimit, val);
maxLimit=qMax(maxLimit, val);
}
}
}
}

//Compute
double width=10;
double margin=width;
double maxX=0;
if (nbRows) {
if (mode==0) {
width=(maxLimit-minLimit)/nbRows;
maxX=width*nbRows+margin;
} else if (mode==1) {
width=(maxLimit-minLimit)/(nbRows*nbColumns);
maxX=width*(nbColumns-1)*nbRows+margin;
}
}
scene->setSceneRect(-margin, margin-maxLimit, maxX-margin, maxLimit-minLimit+2*margin);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);

SKGTRACE << "Scene rect:" <<scene->sceneRect().x() << "," << scene->sceneRect().y()<< "," << scene->sceneRect().width()<< "," << scene->sceneRect().height() << endl;
SKGTRACE << "Items:" <<nbRows << "x" << (nbColumns-1) << "=" << nbRows*(nbColumns-1) << endl;

//Redraw scene
double ymin=0;
double ymax=0;
double histoPos=0;
for (int x=0; x<nbRows; ++x) {
QString xname=ui.kTable->item(x, 0)->text();
double yPlus=0;
double yMoins=0;
for (int j=1; j<nbColumns; ++j) {
//Get column name
QString yname=ui.kTable->horizontalHeaderItem(j)->text();

//Get cell value
QTableWidgetItem* tableItem=ui.kTable->item(x,j);
if (tableItem) {
QString valstring=tableItem->text();
double val=locale->readMoney(valstring);

int color_h=359*x/nbRows;
int color_s=255-155*j/nbColumns;
int color_v=255-155*j/nbColumns;

QColor color;
if (x==crow && j==ccolumn) color=QApplication::palette().color(QPalette::High light);
else color=QColor::fromHsv(color_h,color_s,color_v);

QAbstractGraphicsShapeItem* graphItem=NULL;
if (mode==0) {
//STACK
if (val>0) {
graphItem=scene->addRect(width*x, -yPlus, width, -val, QPen(), QBrush(color));
yPlus+=val;
if (yPlus>ymax) ymax=yPlus;
} else if (val<0) {
graphItem=scene->addRect(width*x, -yMoins, width, -val, QPen(), QBrush(color));
yMoins+=val;
if (yMoins<ymin) ymin=yMoins;
}
} else if (mode==1) {
//HISTOGRAM
graphItem=scene->addRect(width*histoPos, 0, width, -val, QPen(), QBrush(color));
histoPos++;
if (val>ymax) ymax=val;
if (val<ymin) ymin=val;
}

if (graphItem) {
graphItem->setToolTip(xname+'\n'+yname+'\n'+valstring+'\n'+ta bleItem->toolTip());
graphItem->setFlags(QGraphicsItem::ItemIsSelectable);
graphItem->setData(1, x);
graphItem->setData(2, j);
graphItem->setData(11, color_h);
graphItem->setData(12, color_s);
graphItem->setData(13, color_v);

tableItem->setData(1, (qlonglong) graphItem);
}
}
}
}

//Draw axis
if (mode!=2) {
QGraphicsLineItem* item=scene->addLine(0, -ymin+margin, 0, -ymax-margin);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);

item=scene->addLine(0, -ymax-margin, margin/2, -ymax-margin/2);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);

item=scene->addLine(0, -ymax-margin, -margin/2, -ymax-margin/2);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);

item=scene->addLine(-margin, 0, maxX, 0);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);

item=scene->addLine(maxX, 0, maxX-margin/2, margin/2);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);

item=scene->addLine(maxX, 0, maxX-margin/2, -margin/2);
item->setFlag(QGraphicsItem::ItemIsSelectable, false);
}
}
{
SKGTRACEIN(10, "SKGTableWithGraph::redrawGraph-add scene to view");
ui.graphicView->setScene(scene);
ui.graphicView->onZoomOriginal();
ui.graphicView->show();
ui.graphicView->graphicsView()->setOptimizationFlag(QGraphicsView::DontClipPainter , true);
ui.graphicView->graphicsView()->setOptimizationFlag(QGraphicsView::DontSavePainter State, true);
ui.graphicView->graphicsView()->setOptimizationFlag(QGraphicsView::DontAdjustForAn tialiasing, true);

//Add selection event on scene
connect(scene, SIGNAL(selectionChanged()), this, SLOT(onSelectionChangedInGraph()), Qt::QueuedConnection);
}
QApplication::restoreOverrideCursor();
}

wysota
21st November 2008, 22:26
First of all add all items to the scene before setting it on the view. Otherwise you get a refresh of the view every time you add an item to the scene.

Second of all I don't what what your redrawGraph method does, but it recreates all the items instead of using the ones that are already there. Consider a situation where you are building a castle using some building blocks like LEGO. With your current approach if you want to add a new block to the construction, you first dismantle all that you have already constructed, then construct it back and add that new block. Not very efficient, is it? Maybe it's better to simply add that block to the ones that are already there? :)

miraks
21st November 2008, 22:52
Hi Wysota,


First of all add all items to the scene before setting it on the view. Otherwise you get a refresh of the view every time you add an item to the scene.

It's already the case. The scene is created and added to the view.



Second of all I don't what what your redrawGraph method does, but it recreates all the items instead of using the ones that are already there. Consider a situation where you are building a castle using some building blocks like LEGO. With your current approach if you want to add a new block to the construction, you first dismantle all that you have already constructed, then construct it back and add that new block. Not very efficient, is it? Maybe it's better to simply add that block to the ones that are already there?

This method creates the view representing the content of a QTableWidget. This method is called when the QTableWidget is populated. So the graph is never updated or modified.

My problem is that the initial creation is very long.
I don't understand why "40000 chips" example is able to create 40000 object in less than 1s and why I need 57s to create 1416 rects only.

What is wrong ?

PS: Sorry for the previous long message.

wysota
21st November 2008, 23:59
I'd suggest running this code via callgrind to see where the most time is spent. I wouldn't be surprised if it was in the table widget.

Why is your method called "redraw" if it doesn't redraw?

miraks
22nd November 2008, 10:05
Wysota,

Performances are very good (13.9419 ms) when the following line is commented:

graphItem=scene->addRect(width*x, -yMoins, width, -val, QPen(), QBrush(color));

and very bad (57312.6 ms) when this line is not commented.

There are nothing concerning QTableView in this line.
I will try callgrind...

Thank you for your help.

wysota
22nd November 2008, 14:01
Is there a chance you could provide a minimal compilable example reproducing the problem?

miraks
23rd November 2008, 21:41
Hi Wisota,

I found my error, in fact bad performance was due to the following line:

tableItem->setData(1, (qlonglong) graphItem);

So, you are right, it's due to the QTableView ! Good job !

I found a solution:
1-call QTableView::hide at the beginning of SKGTableWithGraph::redrawGraph
2-call QTableView::shod at the end of SKGTableWithGraph::redrawGraph

I have now good performances:

## >SKGTableWithGraph::redrawGraph
## >SKGTableWithGraph::redrawGraph-remove scene
## <SKGTableWithGraph::redrawGraph-remove scene TIME=0.206 ms
## >SKGTableWithGraph::redrawGraph-create scene
## Scene rect:-10,-350985,390567,390587
## Items:24x59=1416
## <SKGTableWithGraph::redrawGraph-create scene TIME=90.5479 ms
## >SKGTableWithGraph::redrawGraph-add scene to view
## <SKGTableWithGraph::redrawGraph-add scene to view TIME=454.512 ms
## <SKGTableWithGraph::redrawGraph TIME=551.457 ms

Thank you again for your help.