了解Qt视图模型体系结构:何时创建以及如何在QAbstractItemModel实现中清理索引? [英] Understanding Qt view-model architecture: when to create and how to cleanup indexes in QAbstractItemModel implementation?

查看:137
本文介绍了了解Qt视图模型体系结构:何时创建以及如何在QAbstractItemModel实现中清理索引?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm currently migrating my project from QTreeWidget to QtreeView, and have a lot of problems caused by poor understanding of the Qt model-view design. So far I couldn't find answers even in Qt examples.

我已经实现了QAbstractItemModel.我正在返回要通过data方法在QTreeView中查看的字符串.现在,基础数据将在运行时更改.为了处理此问题,我的模型已订阅执行emit dataChanged(index(0,0), index(rowCount() - 1, LastColumn));的通知.问题是:如何创建和清除QModelIndex对象? Qt示例之一重新实现了index方法,所以我做了同样的事情:

I've implemented my QAbstractItemModel. I'm returning strings to be viewed in the QTreeView through data method. Now, the underlying data will change in runtime. To handle this my model is subscribed to a notification that does emit dataChanged(index(0,0), index(rowCount() - 1, LastColumn));. The question is: how to create and cleanup QModelIndex objects? One of the Qt examples reimplements index method, so I did the same:

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

但是,在该示例中,数据是静态的,在我的情况下,它在运行时会更改.我的index实现正确吗?如果对于同一坐标多次调用index怎么办?在发出dataChanged之前,我需要以某种方式清理旧索引吗?

However, data is static in that example and in my case it changes at runtime. Is my index implemetation correct? What if index is called more than once for the same coordinates? Do I need to somehow cleanup old indexes before emitting dataChanged?

推荐答案

根据C ++的语义,关于索引删除"的问题毫无意义.根本没有办法破坏从函数内部按值返回的对象-至少在没有采取有目的的恶意黑客的情况下如此.所以让我们忘记它吧.

Your question about "deletion" of indices makes no sense in the light of the semantics of C++. There is simply no way for you to destroy an object that you returned by value from inside of a function - at least not without resorting to purposeful dirty hacks. So let's forget about it.

dataChanged信号与索引的寿命并没有真正的关系.当您的index()方法返回索引时,您不是可以删除"该索引的人.调用模型的index()方法的人都负责破坏索引.没关系,您给出的索引无论如何都不会在免费存储区中分配,因此删除的概念根本不适用.

The dataChanged signal and the lifetime of indices are not really related. When your index() method returns an index, you are not the one who can "delete" it; whoever called your model's index() method is responsible for destructing the index. Never mind that the index you give out is not allocated in the free store anyway, so the notion of deletion doesn't apply at all.

QModelIndex的含义是:索引.关于如何使用它,它非常类似于C ++迭代器.它带有一些反映迭代器注意事项的警告:

The QModelIndex is what it says on the box: an index. When it comes to how it might be used, it's very much like a C++ iterator. It comes with a few caveats that mirror iterator caveats:

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

  1. It must be created by the model using a factory method index(). Internally you use the createIndex() factory to create it for you within the model. Think of iterator-returning methods of C++ containers do (begin(), end(), etc.).

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

It must be used at once and then discarded. It won't remain valid if you make changes to the model. The same general limitation applies to C++ container iterators.

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

If you need to keep a model index over time use a QPersistentModelIndex. The C++ standard library doesn't offer this.

索引的生命周期不受您的控制.创建它,给出它的期望是根据该协议使用它.用户(例如,视图)应按照上面列出的限制使用它.例如,如果视图对索引的保留时间过长(通过干预修改),则将导致不确定的行为(例如崩溃),这是完全可以的.

The lifetime of an index is out of your control. You create it, you give it out with an expectation that it will be used according to this protocol. The user (the view, for example) is supposed to use it subject to the limitations listed above. If a view, for example, holds on to an index for too long (through intervening modifications), it's entirely OK that it will result in undefined behavior (say, a crash).

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

When you emit (or receive, if you're a view or a proxy model) dataChanged, you shouldn't expect any indices given out prior to that point to remain usable. The persistent indices of course should still work, but it's ok to invalidate those if, say, the pointed-to index was removed (think of a cell being removed from a spreadsheet, not the cell's data being changed!).

如果您给出了索引,然后发出dataChanged,并且使用旧索引调用了模型方法的任何,则您可以随意崩溃,断言,中止,

If you gave out an index, then emit dataChanged, and any of your model's methods get called with that old index, you're free to crash, assert, abort, whatever.

让我们清楚地使用dataChanged的方式:只要给定索引处的数据项发生更改,就应该发出它.您应该尽可能地具体一点:简单地告诉您的观点,如果实际上一切都没有改变,那么不是是个好主意.如果一个索引已更改,请在topLeftbottomRight设置为相同索引的情况下发出信号.如果较小的矩形区域发生了变化,请发射该矩形的角.如果多个不相关的项已更改得太远而无法有效地捆绑在一个小的封闭索引矩形中,则应为每个更改的项分别指出此类更改.

Let's also be clear about how you use dataChanged: you're supposed to emit it whenever an item of data at a given index changes. You should be as specific as possible: it is not a good idea at all to simply tell your views that everything has changed if, in fact, it hasn't. If one index has changed, emit the signal with topLeft and bottomRight set to the same index. If a small rectangular area has changed, emit the corners of this rectangle. If multiple unrelated items have changed that are too far away to be meaningfully bundled in a small enclosing index rectangle, you should indicate such changes separately for each changed item.

您绝对应该使用 modeltest 来验证模型的行为正常.

You should definitely use modeltest to verify that your model behaves sanely.

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

This can be done by adding the modeltest.cpp and modeltest.h to your project, and instantiating the tester for each model instance. You can do it directly within your model:

#include "modeltest.h"

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

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

You also need to handle persistent indices for your model, and that's a separate issue. The documentation says:

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

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:

  • insertRows()实现必须在将新行插入数据结构之前先调用beginInsertRows(),然后立即调用endInsertRows().
  • insertColumns()实现必须在将新的列插入数据结构之前调用beginInsertColumns(),然后立即调用endInsertColumns().
  • 在从数据结构中删除行之前,removeRows()实现必须调用beginRemoveRows(),然后立即调用endRemoveRows().
  • 在将列从数据结构中删除之前,removeColumns()实现必须调用beginRemoveColumns(),然后立即调用endRemoveColumns().
  • 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.

这篇关于了解Qt视图模型体系结构:何时创建以及如何在QAbstractItemModel实现中清理索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆