13.6.1 派生QAbstractProxyModel的子类(3)
成员函数index()负责为一个虚拟数据项PC构建索引。该数据项在代理模型中的位置由该函数的3个参数确定:proxy_parent是其父节点PP的索引。这个父节点可能包含多个子节点,row和column表示PC的位置。
函数index()首先调用mapToSource(),求解PP所对应的源数据项SP的索引。再调用源模型的rowCount()函数求解SP所包含子节点的行数rowCount。与PC对应的那个源数据项SC就应该是SP的第rowCount-1-row行、第column列的那个子节点。行②调用源模型的index()函数,求取SC的索引,并调用register_index函数将其登记到索引池。最后,也是最重要的一步,调用基类QAbstractItemModel的成员函数createIndex(),构造一个QModelIndex对象,令其内部ID指向SC的索引。至此,一个指向目标虚拟数据项PC的完整索引构造完毕。
代理模型RevertProxyModel的成员函数rowCount(),columnCount()分别负责求取代理模型中某个虚拟数据项PP所含子节点的行数,列数。对于本例,PP和对应SP所含的行数、列数相等,因而这两个函数的实现比较简单,如代码段13 22所示。
代码段13 22,代理模型RevertProxyModel的其他2个接口函数,取自z:\examples\mvc\revertProxyModel\revertProxyModel.h
- int RevertProxyModel::rowCount(const QModelIndex & proxy_parent) const
- {
- QModelIndex source_parent = mapToSource(proxy_parent);
- int rowCount = sourceModel()->rowCount( source_parent );
- return rowCount;
- }
- int RevertProxyModel::columnCount(const QModelIndex & proxy_parent) const
- {
- QModelIndex source_parent = mapToSource(proxy_parent);
- return sourceModel()->columnCount( source_parent );
- }
RevertProxyModel的成员函数parent()负责求取代理模型中某个虚拟数据项PC的父节点PP的索引。该函数的实现如代码段13 23所示。代理模型只利用索引池存放虚拟数据项和源数据项的映射关系,它根本就没有存放各个虚拟数据项之间的父子关系信息。因此,我们必须将这个目标虚拟数据项PC映射到源模型中,求取其父节点SP,再将其映射回代理模型。代码段中的成员函数mapFromSource()完成最后这个映射操作。给定一个源数据项的索引,该函数将其添加到索引池中(如果此前尚未添加),创建一个属于代理模型的索引,令其内部ID指向新添加的索引,如行③所示。
代码段13 23,代理模型RevertProxyModel的接口函数parent(),取自z:\examples\mvc\revertProxyModel\revertProxyModel.cpp
- QModelIndex RevertProxyModel::mapFromSource ( const QModelIndex & source_index ) const
- {
- if (!source_index.isValid())
- return QModelIndex();
- int rowCount = sourceModel()->rowCount( source_index );
- int pos = register_index( source_index );
- return createIndex( rowCount -1 - source_index.row(), ③
- source_index.column(), pos );
- }
- QModelIndex RevertProxyModel::parent(const QModelIndex & proxy_child) const
- {
- QModelIndex source_child = mapToSource( proxy_child );
- QModelIndex source_parent = sourceModel()->parent( source_child );
- return mapFromSource( source_parent );
- }
由于所有的代理模型都应该定义成员函数mapToSource()以及mapFromSource(),负责虚拟数据项索引与源数据项索引之间的映射,所以QAbstractProxyModel将这两个函数定义为纯虚函数,要求所有派生类必须重载它们。
本例中的RevertProxyModel并没有重载QAbstractItemModel的接口函数data(),这是由于QAbstractProxyModel已经实现了该函数,如代码段13 24所示。Qt在实现类QAbstractProxyModel时用到了第8章中的d-pointer技术,该代码段中的"d->model"表示该类所指的源模型。行④将虚拟数据项的索引映射到源模型中,调用源模型的data()函数,读取并返回指定角色对应的数据子项。
代码段13 24,接口函数data( )的实现,取自q:\src\gui\itemviews\qabstractproxymodel.cpp
- QVariant QAbstractProxyModel::data(const QModelIndex &proxyIndex, int role) const
- {
- Q_D(const QAbstractProxyModel);
- return d->model->data(mapToSource(proxyIndex), role); ④
- }
类RevertProxyModel的数据成员vector表示索引池,该成员的声明为:- mutable QVector<QModelIndex> vector;
被声明为mutable的原因如下:类QAbstractItemModel中将5个接口函数定义为常量函数。总体上,这个做法是合理的,因为对于一般的模型,这5个接口函数index()、data()、parent()、rowCount()以及columnCount()只会查询模型中数据集的一些信息,即不会修改数据集中的数据,也不需要修改模型中的其他数据成员。但是,对于本例中的代理模型RevertProxyModel,部分接口函数比如index()会修改vector。由于这些接口函数在基类QAbstractItemModel中已经被定义为常量函数,RevertProxyModel也只能将它们定义为常量函数。为了使其中一些常量函数仍然能够修改vector,我们必须将vector定义为mutable。
另外,随书光盘中本例的源代码包含一些调试语句,能够向一个日志文件输出各个接口函数被调用的顺序以及各函数的执行过程。读者可以浏览生成的日志文件,印证本节所讲述的内容。