c++ - 了解 Qt View 模型架构 : when to create and how to cleanup indexes in QAbstractItemModel implementation?

标签 c++ qt qtreeview qabstractitemmodel

我目前正在将我的项目从QTreeWidget 迁移到QtreeView,并且由于对Qt 模型- View 设计的理解不足而导致了很多问题。到目前为止,即使在 Qt 示例中我也找不到答案。

我已经实现了我的QAbstractItemModel。我正在通过 data 方法返回要在 QTreeView 中查看的字符串。现在,底层数据将在运行时发生变化。为了处理这个问题,我的模型订阅了一个通知,该通知确实 emit dataChanged(index(0,0), index(rowCount() - 1, LastColumn));。问题是:如何创建和清理QModelIndex 对象?其中一个 Qt 示例重新实现了 index 方法,所以我做了同样的事情:

QModelIndex CFileListModel::index(int row, int column, const QModelIndex &/*parent*/) const
{
    QModelIndex index = createIndex(row, column);
    return index;
}

但是,该示例中的数据是静态的,在我的例子中它会在运行时发生变化。我的 index 实现是否正确?如果为同一坐标多次调用 index 怎么办?在发出 dataChanged 之前,我是否需要以某种方式清理旧索引?

最佳答案

根据 C++ 的语义,您关于“删除”索引的问题毫无意义。根本没有办法销毁从函数内部按值返回的对象——至少在不诉诸有目的的肮脏黑客的情况下是这样。所以让我们忘记它吧。

dataChanged 信号和索引的生命周期并没有真正的关系。当您的 index() 方法返回一个索引时,您不是可以“删除”它的人;调用模型的 index() 方法的人负责破坏索引。不要介意你给出的索引无论如何都没有分配在免费存储中,所以删除的概念根本不适用。

QModelIndex 就是它在盒子上写的:一个索引。说到它的使用方式,它很像 C++ 迭代器。它带有一些与迭代器警告相同的警告:

  1. 它必须由模型使用工厂方法 index() 创建。在内部,您使用 createIndex() 工厂在模型中为您创建它。想想 C++ 容器的迭代器返回方法(begin()end() 等)。

  2. 必须立即使用,然后丢弃。如果您对模型进行更改,它将不再有效。相同的一般限制适用于 C++ 容器迭代器。

  3. 如果您需要随时间保留模型索引,请使用 QPersistentModelIndex。 C++ 标准库不提供此功能。

索引的生命周期不受您的控制。你创建它,你把它分发出去,期望它会根据这个协议(protocol)被使用。用户(例如 View )应该根据上面列出的限制使用它。例如,如果一个 View 保留索引的时间过长(通过干预修改),那么它会导致未定义的行为(例如,崩溃)是完全可以的。

当您发出(或接收,如果您是 View 或代理模型)dataChanged 时,您不应期望在该点之前发出的任何索引仍然可用。持久性索引当然应该仍然有效,但是如果指向的索引被删除(比如从电子表格中删除一个单元格,而不是单元格的数据被更改,则可以使它们无效!)。

如果您提供了一个索引,然后发出 dataChanged,并且您的模型的任何方法都被调用了那个旧索引,您就可以自由崩溃、断言、中止等等。

我们还要弄清楚您如何使用 dataChanged:您应该在给定索引处的数据项发生更改时发出它。您应该尽可能具体: 简单地告诉您一切都已改变的观点根本不是一个好主意,如果实际上并没有。如果一个索引已更改,则发出信号并将 topLeftbottomRight 设置为相同的索引。如果一个小矩形区域发生了变化,则发射这个矩形的角。如果多个不相关的项发生了更改,并且距离太远而无法有意义地捆绑在一个小的封闭索引矩形中,则您应该为每个更改的项分别指出此类更改。

你绝对应该使用 modeltest验证您的模型是否正常运行。

这可以通过将 modeltest.cppmodeltest.h 添加到您的项目,并为每个模型实例实例化测试器来完成。您可以直接在您的模型中执行此操作:

#include "modeltest.h"

MyModel(QObject * parent) : ... {
   new ModelTest(this, parent);
   ...
}

您还需要处理模型的持久索引,这是一个单独的问题。文档说:

Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:

  • An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and endInsertRows() immediately afterwards.
  • An insertColumns() implementation must call beginInsertColumns() before inserting new columns into the data structure, and endInsertColumns() immediately afterwards.
  • A removeRows() implementation must call beginRemoveRows() before the rows are removed from the data structure, and endRemoveRows() immediately afterwards.
  • A removeColumns() implementation must call beginRemoveColumns() before the columns are removed from the data structure, and endRemoveColumns() immediately afterwards.

The private signals that these functions emit give attached components the chance to take action before any data becomes unavailable. The encapsulation of the insert and remove operations with these begin and end functions also enables the model to manage persistent model indexes correctly. If you want selections to be handled properly, you must ensure that you call these functions. If you insert or remove an item with children, you do not need to call these functions for the child items. In other words, the parent item will take care of its child items.

关于c++ - 了解 Qt View 模型架构 : when to create and how to cleanup indexes in QAbstractItemModel implementation?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18941445/

相关文章:

python - PySide 在工作线程中等待来自主线程的信号

python - QTreeView:为 dropEvent() 上的项目设置图标

c++ - 如何捕获 QTreeView 取消选择事件?

python - 如何设置 QTreeView 的列宽?

c++ - 使用 libudev 在 Linux 中获取有关已安装驱动器的信息

c++ - 在使用 Qt 制作的 GUI 上按下按钮时启动 shell 脚本

qt - 带有 html 富文本委托(delegate)的 PyQt listview 将文本位移出位置(包括图片和代码)

c++ - QT QstandardItem问题

c++ - 在创建的 QThread 中运行所需的 Qt Socket 阻塞函数。有办法过去吗?

c++ - 成为模板模板参数的 friend