QAbstractProxyModel and childIndex != childIndex1 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:518)
-
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;
-
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!
-
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.
-
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!
-