Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QAbstractProxyModel and childIndex != childIndex1 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:518)
QtWS25 Last Chance

QAbstractProxyModel and childIndex != childIndex1 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:518)

Scheduled Pinned Locked Moved Solved General and Desktop
qabstractproxym
4 Posts 2 Posters 124 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    Dariusz
    wrote on 26 Mar 2025, 13:32 last edited by
    #1

    Hey

    Oh dear I'm stuck! :D

    If any1 could help... I'm lost.
    I'm creating a "custom model on the fly" with my data, but I can't get these indexes to work :///

    The idea is to create multi group structure with virtual Groups for each column user select as group.

    Error > childIndex != childIndex1 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:518)

    GenericTreeModelGroup::~GenericTreeModelGroup() {
        }
    
        GenericTreeModelGroup::GenericTreeModelGroup(QObject *parent)
            : QAbstractProxyModel(parent), mRootNode(new VirtualNode{}) {
        }
    
        void GenericTreeModelGroup::setGroupingFilters(const QVector<int> &filters) {
            mFilters = filters;
            rebuildTree();
        }
    
        void GenericTreeModelGroup::setGroupingEnabled(bool enabled) {
            mGroupingEnabled = enabled;
            rebuildTree();
        }
    
        void GenericTreeModelGroup::rebuildTree() {
            beginResetModel();
            delete mRootNode;
            mRootNode = new VirtualNode{"Root"};
            if (sourceModel() && mGroupingEnabled) {
                buildFlatTree();
            }
            endResetModel();
        }
    
        void GenericTreeModelGroup::flattenAndFilter(const QModelIndex &parentIdx) {
            const int rows = sourceModel()->rowCount(parentIdx);
            for (int row = 0; row < rows; ++row) {
                QModelIndex idx = sourceModel()->index(row, 0, parentIdx);
                mGroupData.append(new VirtualNode{"", {}, idx, mRootNode});
                flattenAndFilter(idx);
            }
        }
    
        void GenericTreeModelGroup::buildFlatTree() {
            mSourceToProxyNodeMap.clear();
            qDeleteAll(mGroupData);
            mGroupData.clear();
            flattenAndFilter(QModelIndex());
            auto headersGroup = mFilters;
    
            GroupData *roots = new GroupData();
    
            for (auto row: mGroupData) {
                QStringList group_keys;
    
                for (auto &keyColumn: headersGroup) {
                    if (!row->sourceIndex.isValid()) { continue; }
                    auto workInx = row->sourceIndex.siblingAtColumn(keyColumn);
                    if (workInx.isValid()) {
                        const auto k = workInx.data(Qt::DisplayRole).toString();
                        if (k.size()) {
                            group_keys.append(k);
                        }
                    }
                }
                auto current_root = roots;
                for (auto &key: group_keys) {
                    if (!current_root->mChildren.contains(key)) {
                        current_root->mChildren[key] = new GroupData(key);
                    }
                    current_root = current_root->mChildren[key];
                }
                current_root->mItems.append(row);
            }
    
            auto buildRecursive = [this](GroupData *data, VirtualNode *parent, auto &&self) -> void {
                auto newNode = new VirtualNode{data->mText, {}, {}, parent};
                parent->children.append(newNode);
    
                for (auto &child: data->mChildren) {
                    self(child, newNode, self);
                }
                for (auto &item: data->mItems) {
                    item->parent = newNode;
                    newNode->children.append(item);
                    mSourceToProxyNodeMap[item->sourceIndex] = item;
                }
            };
    
            for (auto &child: roots->mChildren) {
                buildRecursive(child, mRootNode, buildRecursive);
            }
            delete roots;
        }
    
        QModelIndex GenericTreeModelGroup::parent(const QModelIndex &child) const {
            if (!child.isValid())
                return QModelIndex();
    
            VirtualNode *childNode = nodeFromIndex(child);
            if (!childNode || childNode == mRootNode || !childNode->parent || childNode->parent == mRootNode)
                return QModelIndex();
    
            VirtualNode *parentNode = childNode->parent;
            VirtualNode *grandParent = parentNode->parent;
            int row = grandParent ? grandParent->children.indexOf(parentNode) : mRootNode->children.indexOf(parentNode);
    
            // Use column 0 for the parent
            return createIndex(row, 0, parentNode);
        }
    
        QModelIndex GenericTreeModelGroup::index(int row, int column, const QModelIndex &parent) const {
            VirtualNode *parentNode = nodeFromIndex(parent);
            if (!parentNode || row < 0 || row >= parentNode->children.size() || column < 0 || column >= columnCount(parent))
                return QModelIndex();
    
            VirtualNode *childNode = parentNode->children.at(row);
            return createIndex(row, column, childNode); // perfect consistency here
        }
    
        int GenericTreeModelGroup::rowCount(const QModelIndex &parent) const {
            VirtualNode *node = nodeFromIndex(parent);
            return node ? node->children.size() : 0;
        }
    
        int GenericTreeModelGroup::columnCount(const QModelIndex &parent) const {
            VirtualNode *node = nodeFromIndex(parent);
            if (!node)
                return 0;
    
            if (node == mRootNode) {
                return sourceModel()->columnCount();
            }
            if (!node->sourceIndex.isValid()) {
                return sourceModel()->columnCount(); //1; we override so that we see all columns data...
            }
    
            return sourceModel()->columnCount(node->sourceIndex.parent());
        }
    
        QVariant GenericTreeModelGroup::data(const QModelIndex &index, int role) const {
            if (!mGroupingEnabled)
                return QAbstractProxyModel::data(index, role);
    
            VirtualNode *node = nodeFromIndex(index);
            if (!node) return {};
    
            if (node->sourceIndex.isValid()) {
                return node->sourceIndex.siblingAtColumn(index.column()).data(role);
            }
            if (role == Qt::DisplayRole && index.column() == 0)
                return node->value;
    
            return {};
        }
        bool GenericTreeModelGroup::hasChildren(const QModelIndex &parent) const {
            if (!mGroupingEnabled)
                return QAbstractProxyModel::hasChildren(parent);
            VirtualNode *node = nodeFromIndex(parent);
            if (!node) return false;
            return node->children.size();
        }
    
        QVariant GenericTreeModelGroup::headerData(int section, Qt::Orientation orientation, int role) const {
            if (!mGroupingEnabled) {
                return QAbstractProxyModel::headerData(section, orientation, role);
            }
            if (orientation == Qt::Horizontal) {
                return sourceModel()->headerData(section, orientation, role);
            }
            return {};
        }
    
        QModelIndex GenericTreeModelGroup::mapToSource(const QModelIndex &proxyIndex) const {
            auto node = nodeFromIndex(proxyIndex);
            if (node && node->sourceIndex.isValid())
                return node->sourceIndex.siblingAtColumn(proxyIndex.column());
    
            return QModelIndex();
        }
    
        QModelIndex GenericTreeModelGroup::mapFromSource(const QModelIndex &sourceIndex) const {
            auto it = mSourceToProxyNodeMap.find(sourceIndex.siblingAtColumn(0));
            if (it != mSourceToProxyNodeMap.end()) {
                VirtualNode *node = it.value();
                int row = node->parent->children.indexOf(node);
                return createIndex(row, sourceIndex.column(), node);
            }
            return QModelIndex();
        }
    
        Qt::ItemFlags GenericTreeModelGroup::flags(const QModelIndex &index) const {
            if (!mGroupingEnabled || !index.isValid())
                return QAbstractProxyModel::flags(index);
            VirtualNode *node = nodeFromIndex(index);
            if (!node) return {};
            if (node->sourceIndex.isValid()) {
                return sourceModel()->flags(node->sourceIndex);
            }
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
        }
    
        GenericTreeModelGroup::VirtualNode *GenericTreeModelGroup::nodeFromIndex(const QModelIndex &idx) const {
            return idx.isValid() ? static_cast<VirtualNode *>(idx.internalPointer()) : mRootNode;
        }
    

    h.

            GenericTreeModelGroup(QObject *parent = nullptr);
            ~GenericTreeModelGroup();
    
    
            void setGroupingFilters(const QVector<int> &filters);
            void setGroupingEnabled(bool enabled);
    
    
            QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
            QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
            QModelIndex index(int row, int column, const QModelIndex &parent) const override;
            QModelIndex parent(const QModelIndex &child) const override;
            int rowCount(const QModelIndex &parent) const override;
            int columnCount(const QModelIndex &parent) const override;
    
            Qt::ItemFlags flags(const QModelIndex &index) const override;
            QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
            bool hasChildren(const QModelIndex &parent) const override;
    
            QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    
        private:
            struct VirtualNode {
                QString value;
                QVector<VirtualNode *> children;
                QModelIndex sourceIndex; // valid for leaf nodes
                VirtualNode *parent = nullptr;
                ~VirtualNode() { qDeleteAll(children); }
            };
            struct GroupData {
                QString mText;
                QHash<QString, GroupData *> mChildren;
                QVector<VirtualNode *> mItems;
                ~GroupData() { qDeleteAll(mChildren); }
            };
            QVector<VirtualNode *> mGroupData;
            QHash<QModelIndex, VirtualNode *> mSourceToProxyNodeMap;
    
            void rebuildTree();
            void flattenAndFilter(const QModelIndex &parentIdx = {});
            void buildFlatTree();
    
            VirtualNode *nodeFromIndex(const QModelIndex &idx) const;
    
            QVector<int> mFilters;
            bool mGroupingEnabled = false;
            VirtualNode *mRootNode = nullptr;
    
    1 Reply Last reply
    0
    • D Offline
      D Offline
      Dariusz
      wrote on 27 Mar 2025, 09:37 last edited by
      #4

      Ok I've rewrite the test class as my own so I can debug.
      Here were the problems for any1 struggling with it too...

          int GenericTreeModelGroup::rowCount(const QModelIndex &parent) const {
              if (parent.column() > 0) {
                  return 0;
              }
              VirtualNode *node = nodeFromIndex(parent);
              return node ? node->children.size() : 0;
          }
      

      Needed to add

              if (parent.column() > 0) {
                  return 0;
              }
      

      So that I don't return row count XX for children at column 1/2/3/4/5.

      The second problem was with parent ()

          QModelIndex GenericTreeModelGroup::parent(const QModelIndex &child) const {
              if (!child.isValid())
                  return QModelIndex();
      
              VirtualNode *childNode = nodeFromIndex(child);
              if (!childNode) {
                  //|| childNode == mRootNode || !childNode->parent || childNode->parent == mRootNode)
                  return QModelIndex();
              }
              VirtualNode *parentNode = childNode->parent;
              VirtualNode *grandParent = parentNode->parent;
              int row = grandParent ? grandParent->children.indexOf(parentNode) : mRootNode->children.indexOf(parentNode);
              if (row == -1) {
                  return QModelIndex();
              }
              // Use column 0 for the parent
              return createIndex(row, 0, parentNode);
          }
      

      If you have a row -1, it means you have "ROOT" item, which should return QModelIndex() and not -1,-1, data QModelIndex of the model.

      After these 2 changes, all started to work. Yay!

      1 Reply Last reply
      2
      • Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on 26 Mar 2025, 17:00 last edited by
        #2

        Simplify the model - there is a lot of unneeded stuff in there for a reproducer. I'm pretty sure you will find the problem then.

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        1 Reply Last reply
        0
        • D Offline
          D Offline
          Dariusz
          wrote on 27 Mar 2025, 07:19 last edited by
          #3

          hmmm I though I had to replace all of these functions as they were =0 virtual? Hmmm maybe flags is not needed... but still its something to do with index/parent/columncount/rowcount/mapTo/mapFrom I recon

          1 Reply Last reply
          0
          • D Offline
            D Offline
            Dariusz
            wrote on 27 Mar 2025, 09:37 last edited by
            #4

            Ok I've rewrite the test class as my own so I can debug.
            Here were the problems for any1 struggling with it too...

                int GenericTreeModelGroup::rowCount(const QModelIndex &parent) const {
                    if (parent.column() > 0) {
                        return 0;
                    }
                    VirtualNode *node = nodeFromIndex(parent);
                    return node ? node->children.size() : 0;
                }
            

            Needed to add

                    if (parent.column() > 0) {
                        return 0;
                    }
            

            So that I don't return row count XX for children at column 1/2/3/4/5.

            The second problem was with parent ()

                QModelIndex GenericTreeModelGroup::parent(const QModelIndex &child) const {
                    if (!child.isValid())
                        return QModelIndex();
            
                    VirtualNode *childNode = nodeFromIndex(child);
                    if (!childNode) {
                        //|| childNode == mRootNode || !childNode->parent || childNode->parent == mRootNode)
                        return QModelIndex();
                    }
                    VirtualNode *parentNode = childNode->parent;
                    VirtualNode *grandParent = parentNode->parent;
                    int row = grandParent ? grandParent->children.indexOf(parentNode) : mRootNode->children.indexOf(parentNode);
                    if (row == -1) {
                        return QModelIndex();
                    }
                    // Use column 0 for the parent
                    return createIndex(row, 0, parentNode);
                }
            

            If you have a row -1, it means you have "ROOT" item, which should return QModelIndex() and not -1,-1, data QModelIndex of the model.

            After these 2 changes, all started to work. Yay!

            1 Reply Last reply
            2
            • D Dariusz has marked this topic as solved on 27 Mar 2025, 17:23

            1/4

            26 Mar 2025, 13:32

            • Login

            • Login or register to search.
            1 out of 4
            • First post
              1/4
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved