了解Qt视图模型架构:何时创建以及如何在QAbstractItemModel实现中清除索引

Understanding Qt view-model architecture: when to create and how to cleanup indexes in QAbstractItemModel implementation?

本文关键字:QAbstractItemModel 实现 索引 清除 模型 视图 Qt 创建 何时 了解      更新时间:2023-10-16

我目前正在将我的项目从QTreeWidget迁移到QtreeView,由于对Qt模型视图设计的理解不足,我遇到了很多问题。到目前为止,我甚至在Qt的例子中都找不到答案。

我已经实现了我的QAbstractItemModel。我将返回要在QTreeViewdata方法中查看的字符串。现在,底层数据将在运行时发生更改。为了处理这个问题,我的模型订阅了一个执行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++的语义来看,您关于"删除"索引的问题毫无意义。你根本没有办法销毁从函数内部按值返回的对象——至少在不诉诸有目的的肮脏黑客的情况下是这样。所以让我们忘掉它吧。

CCD_ 12信号和指数的寿命实际上并不相关。当您的index()方法返回一个索引时,您不是可以"删除"它的人;调用模型的index()方法的人负责破坏索引。不要介意你给出的索引没有在免费存储中分配,所以删除的概念根本不适用。

QModelIndex就是它在盒子上写的:一个索引。当谈到如何使用它时,它非常像C++迭代器。它附带了一些镜像迭代器注意事项的注意事项:

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

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

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

索引的生存期超出了您的控制范围。你创建了它,你把它分发出去,期望它能按照这个协议使用。用户(例如视图)应该根据上面列出的限制来使用它。例如,如果一个视图保留索引的时间过长(通过中间的修改),那么它会导致未定义的行为(比如崩溃),这是完全可以的。

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

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

让我们明确一下如何使用dataChanged:只要给定索引中的数据项发生变化,就应该发出它。你应该尽可能具体:如果事实上一切都没有改变,那么简单地告诉你的观点就不是一个好主意。如果一个索引发生了变化,则发出topLeftbottomRight设置为相同索引的信号。如果一个小矩形区域发生了更改,则发射该矩形的角。如果多个不相关的项目发生了更改,但距离太远,无法有意义地捆绑在一个小的封闭索引矩形中,则应为每个更改的项目分别指示这些更改。

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

这可以通过将modeltest.cppmodeltest.h添加到项目中,并为每个模型实例实例化测试人员来完成。你可以直接在你的模型中完成:

#include "modeltest.h"
MyModel(QObject * parent) : ... {
new ModelTest(this, parent);
...
}

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

为可调整大小的数据结构提供接口的模型可以提供insertRows()、removeRows(),insertColumns()和removeColumns()的实现。在实现这些功能时,重要的是在模型尺寸发生变化之前和之后通知任何连接的视图:

  • insertRows()实现必须在向数据结构中插入新行之前调用beginInsertRows(
  • insertColumns()实现必须在向数据结构中插入新列之前调用beginInsertColumns(
  • removeRows()实现必须在从数据结构中删除行之前调用beginRemoveRows(),然后立即调用endRemoveRows
  • removeColumns()实现必须在从数据结构中删除列之前调用beginRemoveColumns(),然后立即调用endRemoveColumns

这些函数发出的专用信号使附加组件有机会在任何数据不可用之前采取行动。插入和删除操作与这些begin和end函数的封装也使模型能够正确管理持久模型索引。如果希望正确处理选择,则必须确保调用这些函数。如果插入或删除带有子项的项,则不需要为子项调用这些函数。换句话说,父项将负责其子项。

相关文章:
  • 没有找到相关文章