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

(Crash/Assert) QSortFilterProxyModel: Proxy Mappings become corrupted when recursiveFilteringEnabled is set to true

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P1: Critical
    • Resolution: Invalid
    • Affects Version/s: 5.10.1, 5.11.0 Beta 2
    • Fix Version/s: None
    • Component/s: Widgets: Itemviews
    • Labels:
      None
    • Environment:
      Visual Studio 2017

      Description

      We've recently migrated one of our older models to use the recently introduced recursiveFiltering functionality. Unfortunately we're seeing an assert

      ASSERT failure in QVector<T>::remove: "index out of range"

      On the line:

          source_to_proxy.remove(start, delta_item_count);
      

      where delta_item_count = -1, because source_to_proxy.size() == 0

          if (end >= source_to_proxy.size())
              end = source_to_proxy.size() - 1;
          int delta_item_count = end - start + 1;
      

      We've actually seen this crash where no filtering takes place. i.e.

      bool RecursiveFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
      {
      return true;
      }
      

      However it requires a lot more model operations to trigger it. I've managed to create a small test case which exhibits the problem and attached it. It's not the cleanest / minimal example I was after, but it's fairly close. It has some interesting requirements, the mappings must be created before it will crash, in the example I've instatantiated the mappings by creating a tree view on the filter model. It then requires a a number of add / removes from the model before it'll crash (enough to corrupt the proxy mappings I believe). I've connected up a timer in main to cause the crash once the tree views have instantiated. In this particular test case both of the following are required, but in our main application we've only required the recursivefiltering enabled I believe.

         setDynamicSortFilter(false);
         setRecursiveFilteringEnabled(true);
      

      I believe there's some extra checking that recalculates the mappings if the sort filter is on that avoids the crash (or perhaps just delays it). The tree model has been pulled directly from Examples\Qt-5.11.0\widgets\itemviews\simpletreemodel, though it has two extra functions (void TreeItem::removeChild(int row) and TreeModel::CrashFilterModel()).

      The key functions are however:

      Crashing Code

      void TreeModel::CrashFilterModel()
      {
         // Choose the first child of the root
         // In the dummy data this is "Getting Started"
         // It currently has two children.
         auto firstChildOfRoot = index(0, 0, QModelIndex());
      
         // Make sure the model has some data in it
         if (firstChildOfRoot.isValid()) {
            auto* treeItemParent = static_cast<TreeItem*>(firstChildOfRoot.internalPointer());
      
            QList<QVariant> dummyColumnData;
            dummyColumnData.append("First Column");
            dummyColumnData.append("second column");
      
            // Begin by inserting an element at position 2 (after the current children)
            // And then removing the element at position 0
            beginInsertRows(firstChildOfRoot, 2, 2);
            treeItemParent->appendChild(new TreeItem(dummyColumnData, treeItemParent));
            endInsertRows();
      
            Q_ASSERT(treeItemParent->childCount() == 3);
      
            beginRemoveRows(firstChildOfRoot, 0, 0);
            treeItemParent->removeChild(0);
            endRemoveRows();
      
            Q_ASSERT(treeItemParent->childCount() == 2);
      
            beginInsertRows(firstChildOfRoot, 2, 2);
            treeItemParent->appendChild(new TreeItem(dummyColumnData, treeItemParent));
            endInsertRows();
      
            Q_ASSERT(treeItemParent->childCount() == 3);
      
            beginRemoveRows(firstChildOfRoot, 0, 0);
            treeItemParent->removeChild(0);
            endRemoveRows();
      
            Q_ASSERT(treeItemParent->childCount() == 2);
      
            beginInsertRows(firstChildOfRoot, 2, 2);
            treeItemParent->appendChild(new TreeItem(dummyColumnData, treeItemParent));
            endInsertRows();
      
            Q_ASSERT(treeItemParent->childCount() == 3);
      
            beginRemoveRows(firstChildOfRoot, 0, 0);
            treeItemParent->removeChild(0);
            endRemoveRows();
      
            Q_ASSERT(treeItemParent->childCount() == 2);
      
            // Remove twice in a row, but remove the second item
            beginRemoveRows(firstChildOfRoot, 1, 1);
            treeItemParent->removeChild(1);
            // CRASH HAPPENS HERE
            endRemoveRows();
      
            Q_ASSERT(treeItemParent->childCount() == 1);
      
         }
      }
      

      Filtering

        bool RecursiveFilterModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
      {
         bool accepts = true;
         auto childIndex = sourceModel()->index(source_row, 0, source_parent);
      
         // Filter all items which have no children
         if ((childIndex.isValid()) && (sourceModel()->rowCount(childIndex) == 0)) {
            accepts = false;
         }
      
         // Filter all elements at row 0
         if ((childIndex.isValid()) && ((childIndex.row() == 0))) {
            accepts = false;
         }
      
         return accepts;
      }
      

      Initial Tree

      Getting Started				How to familiarize yourself with Qt Designer
          Launching Designer			Running the Qt Designer application
              A Dialog Without Auto-Connect	How to connect widgets without a naming scheme
          The User Interface			How to interact with Qt Designer
              The User Interface			How to interact with Qt Designer
      

        Attachments

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

          Activity

            People

            Assignee:
            dfaure_kdab David Faure
            Reporter:
            lmv Luke
            Votes:
            2 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Gerrit Reviews

                There are no open Gerrit changes