From 71cf207b5473aa2d87d0e1133feca3c2991e135a Mon Sep 17 00:00:00 2001 From: Alexander Neundorf Date: Tue, 28 Jan 2020 14:07:51 +0100 Subject: QGraphicsItem: optimize setCursor() for large number of graphic items This patch optimizes setCursor() by invoking view->items(position) only if the current graphics item is under the mouse cursor. If it is not, setting the cursor for this item should not have any effect on the actually currently visible mouse cursor, so there should be no reason to call _q_setViewportCursor() at all in this case, so it is not necessary to query for all the items under the cursor position. In my use case this gives a significant performance improvement from slow behavior to almost immediately (Linux with X11). My scenario is that I have many rectangular graphic items next to each other in a graphics view, and there is the functionality to add one more graphics item (a "handle") to one of those rectangular graphics items, and then for this new "handle" a new graphics item will be added on each of the other existing rectangular graphics items. The ctor of the "handle" graphics item calls setCursor(Qt::OpenHandCursor). QGraphicsItem::setCursor() calls view->item(cursorPosition), and then updates the visible cursor according to the top most graphics item under the cursor. So assuming that there are e.g. 1000 rectangular graphics items next to each other, each showing e.g. 5 "handle" graphic items, adding one more "handle" graphics item will add one graphics item on each of the 1000 rectangular items, so 1000 times view->items(cursorPos) will be called, at the beginning with 6000 graphic items (1000 rectangles and 5000 handles) already in the scene, for the last one with 6999 items in the scene (999 "handles" have been already added), from which the ones under the cursor have to be found. This is basically O^2 complexity. With the patch it changes to linear regarding the number of rectangular graphics items. Change-Id: I9836fc710a8f11d01a94930ea64c6c946e0db282 Reviewed-by: Volker Hilsheimer --- src/widgets/graphicsview/qgraphicsitem.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/widgets/graphicsview/qgraphicsitem.cpp') diff --git a/src/widgets/graphicsview/qgraphicsitem.cpp b/src/widgets/graphicsview/qgraphicsitem.cpp index a2120e53fe..38cab60fc2 100644 --- a/src/widgets/graphicsview/qgraphicsitem.cpp +++ b/src/widgets/graphicsview/qgraphicsitem.cpp @@ -2331,12 +2331,17 @@ void QGraphicsItem::setCursor(const QCursor &cursor) view->viewport()->setMouseTracking(true); // Note: Some of this logic is duplicated in QGraphicsView's mouse events. if (view->underMouse()) { - const auto itemsUnderCursor = view->items(view->mapFromGlobal(QCursor::pos())); - for (QGraphicsItem *itemUnderCursor : itemsUnderCursor) { - if (itemUnderCursor->hasCursor()) { - QMetaObject::invokeMethod(view, "_q_setViewportCursor", - Q_ARG(QCursor, itemUnderCursor->cursor())); - break; + const QPoint viewPoint = view->mapFromGlobal(QCursor::pos()); + const QPointF cursorPos = mapFromScene(view->mapToScene(viewPoint)); + // the cursor can only change if the current item is under the mouse + if (boundingRect().contains(cursorPos)) { + const auto itemsUnderCursor = view->items(viewPoint); + for (QGraphicsItem *itemUnderCursor : itemsUnderCursor) { + if (itemUnderCursor->hasCursor()) { + QMetaObject::invokeMethod(view, "_q_setViewportCursor", + Q_ARG(QCursor, itemUnderCursor->cursor())); + break; + } } } break; -- cgit v1.2.3