-
Notifications
You must be signed in to change notification settings - Fork 72
fix: many issues with RoleCombineModel #1189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,42 +24,133 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel | |
| } | ||
|
|
||
| connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) { | ||
| beginInsertRows(index(parent.row(), parent.column()), first, last); | ||
| // 对于QAbstractListModel,parent通常是无效的,我们直接使用QModelIndex() | ||
| beginInsertRows(QModelIndex(), first, last); | ||
|
|
||
| // 先调整现有映射中后续行的索引 | ||
| QMap<QPair<int, int>, QPair<int, int>> newIndexMap; | ||
| for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) { | ||
| int row = it.key().first; | ||
| int col = it.key().second; | ||
|
|
||
| if (row >= first) { | ||
| // 将后续行的索引向后移动 | ||
| int newRow = row + (last - first + 1); | ||
| newIndexMap[qMakePair(newRow, col)] = it.value(); | ||
| } else { | ||
| // 保持前面行的映射不变 | ||
| newIndexMap[it.key()] = it.value(); | ||
| } | ||
| } | ||
| m_indexMap = newIndexMap; | ||
|
|
||
| // 为新插入的行创建映射 | ||
| int columnCount = sourceModel()->columnCount(); | ||
| for (int i = first; i <= last; i++) { | ||
| QModelIndex majorIndex = sourceModel()->index(i, 0); | ||
| QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor); | ||
| if (majorIndex.isValid() && minorIndex.isValid()) | ||
| m_indexMap[qMakePair(i, 0)] = qMakePair(minorIndex.row(), minorIndex.column()); | ||
| for (int j = 0; j < columnCount; j++) { | ||
| QModelIndex majorIndex = sourceModel()->index(i, j); | ||
| QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor); | ||
| if (majorIndex.isValid() && minorIndex.isValid()) | ||
| m_indexMap[qMakePair(i, j)] = qMakePair(minorIndex.row(), minorIndex.column()); | ||
| } | ||
| } | ||
| endInsertRows(); | ||
| }); | ||
|
|
||
| connect(sourceModel(), &QAbstractItemModel::columnsInserted, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) { | ||
| beginInsertColumns(index(parent.row(), parent.column()), first, last); | ||
| beginInsertColumns(QModelIndex(), first, last); | ||
|
|
||
| // 先调整现有映射中后续列的索引 | ||
| QMap<QPair<int, int>, QPair<int, int>> newIndexMap; | ||
| for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) { | ||
| int row = it.key().first; | ||
| int col = it.key().second; | ||
|
|
||
| if (col >= first) { | ||
| // 将后续列的索引向后移动 | ||
| int newCol = col + (last - first + 1); | ||
| newIndexMap[qMakePair(row, newCol)] = it.value(); | ||
| } else { | ||
| // 保持前面列的映射不变 | ||
| newIndexMap[it.key()] = it.value(); | ||
| } | ||
| } | ||
| m_indexMap = newIndexMap; | ||
|
|
||
| // 为新插入的列创建映射 | ||
| int rowCount = sourceModel()->rowCount(); | ||
| for (int j = first; j <= last; j++) { | ||
| QModelIndex majorIndex = sourceModel()->index(0, j); | ||
| QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor); | ||
| if (majorIndex.isValid() && minorIndex.isValid()) | ||
| m_indexMap[qMakePair(0, j)] = qMakePair(minorIndex.row(), minorIndex.column()); | ||
| for (int i = 0; i < rowCount; i++) { | ||
| QModelIndex majorIndex = sourceModel()->index(i, j); | ||
| QModelIndex minorIndex = func(majorIndex.data(majorRoles), m_minor); | ||
| if (majorIndex.isValid() && minorIndex.isValid()) | ||
| m_indexMap[qMakePair(i, j)] = qMakePair(minorIndex.row(), minorIndex.column()); | ||
| } | ||
| } | ||
| endInsertColumns(); | ||
| }); | ||
|
|
||
| connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) { | ||
| beginRemoveRows(index(parent.row(), parent.column()), first, last); | ||
| beginRemoveRows(QModelIndex(), first, last); | ||
|
|
||
| // 删除被移除行的映射 | ||
| int columnCount = sourceModel()->columnCount(); | ||
| for (int i = first; i <= last; i++) { | ||
| if (m_indexMap.contains(qMakePair(i, 0))) { | ||
| m_indexMap.remove(qMakePair(i, 0)); | ||
| for (int j = 0; j < columnCount; j++) { | ||
| m_indexMap.remove(qMakePair(i, j)); | ||
| } | ||
| } | ||
|
|
||
| // 调整后续行的索引映射 | ||
| QMap<QPair<int, int>, QPair<int, int>> newIndexMap; | ||
| for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) { | ||
| int row = it.key().first; | ||
| int col = it.key().second; | ||
|
|
||
| if (row > last) { | ||
| // 将后续行的索引向前移动 | ||
| int newRow = row - (last - first + 1); | ||
| newIndexMap[qMakePair(newRow, col)] = it.value(); | ||
| } else if (row < first) { | ||
| // 保持前面行的映射不变 | ||
| newIndexMap[it.key()] = it.value(); | ||
| } | ||
| // 被删除行的映射已经在上面移除了 | ||
| } | ||
| m_indexMap = newIndexMap; | ||
|
|
||
| endRemoveRows(); | ||
| }); | ||
|
|
||
| connect(sourceModel(), &QAbstractItemModel::columnsRemoved, this, [this](const QModelIndex &parent, int first, int last) { | ||
| beginRemoveColumns(index(parent.row(), parent.column()), first, last); | ||
| beginRemoveColumns(QModelIndex(), first, last); | ||
|
|
||
| // 删除被移除列的映射 | ||
| int rowCount = sourceModel()->rowCount(); | ||
| for (int j = first; j <= last; j++) { | ||
| if (m_indexMap.contains(qMakePair(0, j))) { | ||
| m_indexMap.remove(qMakePair(0, j)); | ||
| for (int i = 0; i < rowCount; i++) { | ||
| m_indexMap.remove(qMakePair(i, j)); | ||
| } | ||
| } | ||
|
|
||
| // 调整后续列的索引映射 | ||
| QMap<QPair<int, int>, QPair<int, int>> newIndexMap; | ||
| for (auto it = m_indexMap.constBegin(); it != m_indexMap.constEnd(); ++it) { | ||
| int row = it.key().first; | ||
| int col = it.key().second; | ||
|
|
||
| if (col > last) { | ||
| // 将后续列的索引向前移动 | ||
| int newCol = col - (last - first + 1); | ||
| newIndexMap[qMakePair(row, newCol)] = it.value(); | ||
| } else if (col < first) { | ||
| // 保持前面列的映射不变 | ||
| newIndexMap[it.key()] = it.value(); | ||
| } | ||
| // 被删除列的映射已经在上面移除了 | ||
| } | ||
| m_indexMap = newIndexMap; | ||
|
|
||
| endRemoveColumns(); | ||
| }); | ||
|
|
||
|
|
@@ -104,13 +195,73 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel | |
| } | ||
| }); | ||
|
|
||
| // 添加对minor模型删除操作的处理 | ||
| connect(m_minor, &QAbstractItemModel::rowsRemoved, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) { | ||
| // 当minor模型删除行时,需要更新映射并可能触发数据变化信号 | ||
| QList<QModelIndex> affectedMajorIndexes; | ||
|
|
||
| // 找到受影响的major索引 | ||
| for (auto it = m_indexMap.begin(); it != m_indexMap.end();) { | ||
| int minorRow = it.value().first; | ||
| int minorCol = it.value().second; | ||
|
|
||
| if (minorRow >= first && minorRow <= last) { | ||
| // 这个映射指向的minor行被删除了,需要重新建立映射 | ||
| int majorRow = it.key().first; | ||
| int majorCol = it.key().second; | ||
| auto majorIndex = sourceModel()->index(majorRow, majorCol); | ||
|
|
||
| if (majorIndex.isValid()) { | ||
| affectedMajorIndexes.append(majorIndex); | ||
| // 尝试重新建立映射 | ||
| QModelIndex newMinorIndex = func(majorIndex.data(majorRoles), m_minor); | ||
| if (newMinorIndex.isValid()) { | ||
| it.value() = qMakePair(newMinorIndex.row(), newMinorIndex.column()); | ||
| ++it; | ||
| } else { | ||
| // 无法建立新映射,删除这个映射 | ||
| it = m_indexMap.erase(it); | ||
| } | ||
| } else { | ||
| it = m_indexMap.erase(it); | ||
| } | ||
| } else if (minorRow > last) { | ||
| // 调整后续行的索引 | ||
| int newMinorRow = minorRow - (last - first + 1); | ||
| it.value().first = newMinorRow; | ||
| ++it; | ||
| } else { | ||
| ++it; | ||
| } | ||
| } | ||
|
|
||
| // 对受影响的major索引发送数据变化信号 | ||
| for (const auto &majorIndex : affectedMajorIndexes) { | ||
| Q_EMIT dataChanged(majorIndex, majorIndex, m_minorRolesMap.values()); | ||
| } | ||
| }); | ||
|
|
||
| connect(m_minor, &QAbstractItemModel::columnsRemoved, this, [this, majorRoles, func](const QModelIndex &parent, int first, int last) { | ||
| // 当minor模型删除列时,需要更新映射 | ||
| for (auto it = m_indexMap.begin(); it != m_indexMap.end(); ++it) { | ||
| int minorRow = it.value().first; | ||
| int minorCol = it.value().second; | ||
|
|
||
| if (minorCol > last) { | ||
| // 调整后续列的索引 | ||
| int newMinorCol = minorCol - (last - first + 1); | ||
| it.value().second = newMinorCol; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| connect(m_minor, &QAbstractItemModel::rowsInserted, this, | ||
| [this, majorRoles, func](const QModelIndex &parent, int first, int last){ | ||
| auto rowCount = sourceModel()->rowCount(); | ||
| auto columnCount = sourceModel()->columnCount(); | ||
| for (int i = 0; i < rowCount; i++) { | ||
| for (int j = 0; j < columnCount; j++) { | ||
| // alreay bind, pass this | ||
| // already bind, pass this | ||
| if (m_indexMap.contains(qMakePair(i ,j))) | ||
| continue; | ||
|
|
||
|
|
@@ -127,8 +278,25 @@ RoleCombineModel::RoleCombineModel(QAbstractItemModel* major, QAbstractItemModel | |
| // create minor role map | ||
| auto minorRolenames = m_minor->roleNames(); | ||
| m_roleNames = createRoleNames(); | ||
| std::for_each(minorRolenames.constBegin(), minorRolenames.constEnd(), [&minorRolenames, this](auto &roleName) { | ||
| m_minorRolesMap.insert(m_roleNames.key(roleName), minorRolenames.key(roleName)); | ||
|
|
||
| // 修复角色映射逻辑:应该映射到新创建的minor角色,而不是major角色 | ||
| auto majorRoleNames = sourceModel()->roleNames(); | ||
|
|
||
| std::for_each(minorRolenames.constBegin(), minorRolenames.constEnd(), [&minorRolenames, &majorRoleNames, this](auto &roleName) { | ||
| int minorRoleKey = minorRolenames.key(roleName); | ||
|
|
||
| // 在组合角色中找到对应的key,但排除major模型已有的key | ||
| int combinedRoleKey = -1; | ||
| for (auto it = m_roleNames.constBegin(); it != m_roleNames.constEnd(); ++it) { | ||
| if (it.value() == roleName && !majorRoleNames.contains(it.key())) { | ||
| combinedRoleKey = it.key(); | ||
| break; | ||
| } | ||
|
Comment on lines
+285
to
+294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Role mapping logic may not handle duplicate role names across major and minor models. If major and minor models share a role name but have different keys, the logic skips mapping for keys already in the major model, potentially omitting roles from the combined model. Please confirm if this is intentional. |
||
| } | ||
|
|
||
| if (combinedRoleKey != -1) { | ||
| m_minorRolesMap.insert(combinedRoleKey, minorRoleKey); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -152,19 +320,31 @@ QHash<int, QByteArray> RoleCombineModel::roleNames() const | |
|
|
||
| int RoleCombineModel::rowCount(const QModelIndex &parent) const | ||
| { | ||
| if (parent.isValid()) | ||
| return 0; // 平坦列表模型:有效的parent表示某个项目,项目没有子项 | ||
| return sourceModel()->rowCount(); | ||
| } | ||
|
|
||
| int RoleCombineModel::columnCount(const QModelIndex &parent) const | ||
| { | ||
| if (parent.isValid()) | ||
| return 0; // 平坦列表模型:有效的parent表示某个项目,项目没有子项 | ||
| return sourceModel()->columnCount(); | ||
| } | ||
|
|
||
| QVariant RoleCombineModel::data(const QModelIndex &index, int role) const | ||
| { | ||
| if (m_minorRolesMap.contains(role)) { | ||
| int row, column; | ||
| std::tie(row, column) = m_indexMap.value(qMakePair(index.row(), index.column()), qMakePair(-1, -1)); | ||
| auto majorKey = qMakePair(index.row(), index.column()); | ||
| auto mapping = m_indexMap.value(majorKey, qMakePair(-1, -1)); | ||
| int row = mapping.first; | ||
| int column = mapping.second; | ||
|
|
||
| // 检查映射是否有效 | ||
| if (row == -1 || column == -1) { | ||
| return QVariant(); // 返回空值而不是用无效索引访问 | ||
| } | ||
|
|
||
| return m_minor->data(m_minor->index(row, column), m_minorRolesMap[role]); | ||
| } else { | ||
| return sourceModel()->data(sourceModel()->index(index.row(), index.column()), role); | ||
|
|
@@ -176,6 +356,12 @@ bool RoleCombineModel::hasIndex(int row, int column, const QModelIndex &parent) | |
| return sourceModel()->hasIndex(row, column, parent); | ||
| } | ||
|
|
||
| bool RoleCombineModel::hasChildren(const QModelIndex &parent) const | ||
| { | ||
| // 平坦列表模型:只有根节点有子项,其他项目都没有子项 | ||
| return !parent.isValid() && rowCount(parent) > 0; | ||
| } | ||
|
|
||
| QModelIndex RoleCombineModel::index(int row, int column, const QModelIndex &parent) const | ||
| { | ||
| if (!hasIndex(row, column, parent)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): No handling for mappings that become invalid after minor column removal.
Mappings referencing removed columns are not updated, which may leave invalid entries. Please ensure such mappings are removed or invalidated.