Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-95449

Slow item insertion/removal/move in QTreeView

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P2: Important
    • None
    • 5.6, 6.x
    • Widgets: Itemviews
    • None
    • All

    Description

      When using a QTreeView, inserting/removing/moving rows may cause major slowdowns when there are a lot of items in Model.

      See attached minimal example in Python (the bug is in C++ code).

      A quick Google search reveals that many developers are affected by this issue and they are suggested to mitigate it using 'QAbstractItemModel::fetchMore()' and 'TreeView.setUniformRowHeights(True)' when possible.

      Preable

      'QTreeView' stores all items in a flat mutable list called 'viewItems' (see 'src\widgets\itemviews\qtreeview_p.h').

      Each item in 'viewItems' is a 'QTreeViewItem' (also defined in 'src\widgets\itemviews\qtreeview_p.h') that caches row information like 'height'.

      Thanks to this cache, Qt avoids calling 'QAbstractItemDelegate.sizeHint()' millions of times.

      Bug

      When the 'QAbstractItemModel' changes and 'QTreeView::rowsInserted()' is called (see 'src\widgets\itemviews\qtreeview.cpp'), instead of updating the 'viewItems' cache, Qt calls 'doDelayedItemsLayout()'.

      In turn, when needed (e.g. a paint event is emitted), 'doDelayedItemsLayout()' will call 'QTreeView::doItemsLayout()' (also in 'src\widgets\itemviews\qtreeview.cpp') that will smash all 'viewItems' cache (calling 'd->viewItems.clear()') and rebuild it from scratch.

      The bad side effect is that the 'viewItems' will need to be rebuilt and refilled causing, in the worst case scenario, a call to 'QAbstractItemDelegate.sizeHint()' for every cell.

      The problem is exacerbated if:

      • TreeView.uniformRowHeights() is False;
      • QAbstractItemDelegate.sizeHint() is expensive to run;
      • QAbstractItemModel has many items;
      • TreeView.verticalScrollMode is QtWidgets.QAbstractItemView.ScrollPerPixel (in this case the scrollbar needs the height of all items when painted).

      The problem also affects items removal and movement (they all call 'doDelayedItemsLayout()').

      Fix

      Instead of smashing the 'viewItems' cache, 'QTreeView::rowsInserted()' should incremetally change 'viewItems' adding the new items. The code is already there: the 'QTreeViewPrivate::expand()' (also in 'src\widgets\itemviews\qtreeview.cpp') changes the cache incrementally.

      The same is true for items removal (code already in 'QTreeViewPrivate::collapse()') and items movement (which is basically a 'remove in source and insert in destination').

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            oni oni
            Votes:
            1 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes