【问题标题】:Supporting multi-add/delete (and undo/redo) with a QAbstractItemModel (C++)使用 QAbstractItemModel (C++) 支持多添加/删除(和撤消/重做)
【发布时间】:2009-12-19 05:44:07
【问题描述】:

您好,

我一直在编写一些讨厌的代码来支持撤消/重做从我的模型中删除任意一组对象的操作。我觉得我做对了,因为所有其他的修改器(添加/复制粘贴)都是这个功能的子集。

代码比我需要的更糟糕,主要是因为改变模型的唯一方法是调用 beginInsertRows/beginRemoveRows 并删除一个范围内的行(一次只做 1 行,不需要优化“邻居”到一个电话呢)

beginInsertRows/beginRemoveRows 的问题是删除一行可能会影响另一个 QModelIndex(例如,缓存在列表中的一个)。例如:

ParentObj
   ->ChildObj1
   ->ChildObj2
   ->ChildObj3

假设我选择 ChildObj1 和 ChildObj3 并删除它们,如果我先删除 ChildObj1,我已经更改了 ChildObj3 的 QModelIndex(行现在不同)。如果我删除父对象,也会出现类似问题(但我已通过从对象列表中“修剪”子对象来解决此问题)。

以下是我想到的解决此界面限制的方法,但我认为在继续前进之前我会要求更好的方法:

  1. “向后”移动,假设提供的 QModelIndices 列表是从上到下排序的,只是从下往上。这确实需要排序是可靠的,并且排序可能是幼稚和缓慢的(也许有一种对 QModelIndexes 集合进行排序的聪明方法?或者 QItemSelectionModel 提供好的(有序)列表?)

  2. 每次删除/添加对象时更新其他 QModelIndeces(想不出非天真的解决方案,搜索列表,在需要的地方获取新的 QModelIndeces)

  3. 由于更新实际数据很容易,只需更新数据并重建模型即可。这看起来很奇怪,我可以想象它在处理大量数据时会变得很慢。

这些是我目前的想法。我现在正在处理选项 1。

问候, 丹奥

【问题讨论】:

    标签: c++ qt model-view-controller qabstractitemmodel


    【解决方案1】:

    将 beginRemoveRows/endRemoveRows 等视为要求 QAbstractItemModel 基类为您修复持久模型索引的方法,而不仅仅是一种更新视图的方法,并且尽量不要混淆 QAbstractItemModel 基类的工作那些索引。查看http://labs.trolltech.com/page/Projects/Itemview/Modeltest 来练习您的模型,看看您是否让 QAbstractItemModel 基类满意。

    如果您想将撤消/重做数据保留在模型之外,QPersistentModelIndex 无济于事。我构建了一个经过大量编辑的模型,我不想尝试将所有内容都保留在模型中。我将撤消/重做数据存储在撤消堆栈上。问题是,如果您编辑一列,将该列的持久索引存储在撤消堆栈中,然后删除包含该列的行,则该列的持久索引将变为无效。

    我所做的是同时保留一个持久模型索引和一个“历史”常规 QModelIndex。当需要撤消/重做时,我会检查持久索引是否无效。如果有,我将历史 QModelIndex 传递给我的模型的一个特殊方法,要求它根据行、列和 internalPointer 重新创建索引。由于我的所有编辑都在撤消堆栈上,当我备份到撤消堆栈上的该列编辑时,该行肯定在模型中。我在 internalPointer 中保留了足够的状态来重新创建原始索引。

    【讨论】:

    • 使用 QPersistantModelIndex 是唯一明智的方法吗? Qt MVC 类对我来说真的过时了……
    • 对于撤消,使用类似项目的“完整路径”是唯一合理的 xway。在他们较新的文档中,诺基亚明确表示,并警告 QPersistentModelIndex 不够好。在 Jim 的情况下它可以工作,但是因为他在 QModelIndex internalPointer 中存储了足够的状态。如果 internalPointer 只是一个指向例如可能被删除的树节点的指针,请使用路径。这很简单;只是 (row,col) 对的列表。
    【解决方案2】:

    我会考虑使用“全数据”模型和带有数据模型的过滤器代理模型。数据只会被添加到全数据模型中,永远不会被删除。这样,您可以使用对该模型的引用来存储您的撤消/重做信息。 (我可以建议QPersistentModelIndex吗?)。您的数据模型还可以以某种方式跟踪应该显示的内容。然后过滤器模型将只返回应在给定时间显示的项目的信息。

    【讨论】:

    • 我不是应用程序开发人员(这只是一个爱好项目),但从不释放模型中的对象似乎很邪恶。有效地索引这样一个(最终)大型且深度嵌套的数据集似乎也不太可能。 QPersistentModelIndex 看起来很有趣,我会研究如何使用这个类。
    • @Dan O:您将需要将数据存储在某个地方,无论是在您的撤消/重做树中还是在其他位置,例如数据模型。我应该澄清一下,其他模型不应该永远删除数据,而只能在特定的、明确指定的时间(然后可以与您的撤消/重做代码交互)。
    • 是的,我将数据存储在我的撤消/重做命令中。现在,命令生命周期的管理非常干净,因为它们已被撤消/重做命令堆栈释放。
    • 我同意丹的观点。从概念上讲,撤消堆栈保存数据是合适的。它需要一种将“真正持久”的索引存储到项目的机制,但这并不难。
    猜你喜欢
    • 2010-09-30
    • 2013-11-14
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 2012-04-05
    相关资源
    最近更新 更多