Faster way to find a QRectItem in a QGraphicsScene
-
Hi,
I have a method which I'm trying to get a rectItem from a scene so I can set its color. Essentially it is a grid of squares which could be thousands of them and I have to set certain cells to a specific color. The problem is when I iterate through all the cells the main event loop hangs trying to do the processing. I can't color the cells in another thread as that is not the proper way to do it plus it can cause a crash.
My code looks like this, is there a way I can optimize the search so it doesn't take so long? 10,000 cells can take about a minute or so and I can have many more cells. The grid is always symmetrical like a chess board. The number of rows always equals the number of columns. Any help would be appreciated.colorCells(int column, int row, QString failureColor)
{
QString text = NULL;QList<QGraphicsItem*> itemList = scene_->items(); for (int i = 0; i < itemList.size(); i++) { // find the global (screen) position of the item if (auto lineItem{ dynamic_cast<CustomRectItem*>(itemList[i]) }) { //this takes a long time!! QPointF sceneP = itemList.at(i)->mapToScene(itemList[i]->boundingRect().topLeft()); int globalColumn = ((static_cast<int>(sceneP.x() - sceneStartCornerX_) / CELL_SIZE)) + 2; int globalRow = ((static_cast<int>(sceneP.y() - sceneStartCornerY_) / CELL_SIZE)) + 2; if (column == globalColumn && row == globalRow) { if (failureColor.compare("red") == 0) { lineItem->setBrush(Qt::red); //turn the cell red } else if(failureColor.compare("white") == 0)//no failure { if(globalColumn == globalRow) lineItem->setBrush(Qt::lightGray); //no failure so light grey if diagnal cell else lineItem->setBrush(Qt::white); //no failure so no color } else //yellow lineItem->setBrush(Qt::yellow); //turn the cell red return; } } }
}
-
@leinad
Trying to understand your code.....You have some constant
globalColumn
&globalRow
variables? You want to find which of yourCustomRectItem
items in the scene intersect that point? Map your coordinates to scene coordinates and useQGraphicsScene::itemAt()
or quite possibly one of theQGraphicsScene::items()
overloads which takes aQRectF
to find any gfx items intersecting a rectangular area, whatever is appropriate to your requirement. The docs includeQGraphicsScene uses an indexing algorithm to manage the location of items efficiently.
One of QGraphicsScene's greatest strengths is its ability to efficiently determine the location of items. Even with millions of items on the scene, the items() functions can determine the location of an item within a few milliseconds.Basically, you seem to be iterating every gfx item to see if it's in the desired place. Change that to ask
QGraphicsScene
what item(s) are at the desired place, as above, it can do that very fast without looking through all the items. -
@leinad said in Faster way to find a QRectItem in a QGraphicsScene:
Thanks but how can I find a specific rect corresponding to it column/row location?
Only you know that. Row/column location in what sense? If it's a "grid of squares", multiply them to find the corresponding rectangle in scene coordinates then search for the items intersecting that rectangle?
-
Sorry but I'm not sure what you mean multiply them?
I pass in (column, row)
I now have QList<QGraphicsItem*> itemList = scene_->items();
how to I find itemList.at(i) using column and row? Column and row is 2 dimensional. QList is one dimensional.
Do you mean itemList.at(column *row)? -
@leinad
I explained. Multiply your row, column as necessary to change it intoGraphicsScene
coordinates (a pair of values, likerow,column
, not one single number). You now have the top-left of the square in gfx coords, and you know the size of a square in gfx coordinates too. Depending on your requirements, if you only need to look at the top-left coord to locate aQGraphicsItem
(i.e. you always place them at the top-left of a square), you can useitemAt()
to determine which gfx item, if any, is there. If you need to look at the whole gfx coords square to see which gfx item(s) it intersects, use the appropriateQGraphicsScene::items()
overload which looks for aQRectF
area. -
@leinad said in Faster way to find a QRectItem in a QGraphicsScene:
how to I find itemList.at(i) using column and row? Column and row is 2 dimensional. QList is one dimensional.
As @JonB mentioned, use one of the QGraphicsScene::items() overloads that takes a scene position or scene Rect.