13.6.1 派生QAbstractProxyModel的子类(2)
如何定义代理模型对应QModelIndex对象的内部指针(或内部ID)是本例最重要的部分。由于代理模型只是在逻辑上包含了一些数据项,它根本不会在物理上存放这些虚拟数据项,所以我们无法令这个内部指针(或内部ID)指向某个虚拟数据项。但是,给定一个虚拟数据项,源模型中存在一个数据项与之对应,将其称为"源数据项"。我们可以令上述指针指向这个源数据项的索引。
例如,在图13 22中,设源模型父节点SP具有N行数据项(图中N为4)。代理模型第i行、第j列虚拟数据项对应的索引如图中右上角所示,QModelIndex对象的行号、列号就是i、j。这个虚拟数据项对应的源数据项在源模型中的行号为N-1-i、列号为j,它的索引如图中右下角灰色矩形所示。代理模型索引中的内部ID逻辑上指向源数据项的这个索引。本例中,我们使用一个类型为QVector的容器vector存放所有源数据项的索引,上述内部ID是源数据项索引在这个容器中的序号。本节将这个容器称为"索引池"。
显然,给定一个虚拟数据项,只有当索引池中存在着对应源数据项的索引时,我们才能够为这个虚拟数据项构建一个完整的索引。当视图对象需要访问代理模型中的某个虚拟数据项PC时,会首先调用代理模型的成员函数index(),请求代理模型为这个虚拟数据项构建一个索引,index()函数的原型如下:
- QModelIndex RevertProxyModel::index(int row, int column, const QModel
- Index & proxy_parent) const
其中proxy_parent是目标虚拟数据项的父节点PP的索引。这个父节点可能包含多个子节点,row和column是目标虚拟数据项的位置。
为了完成这个任务,我们首先需要求解代理模型中的这个父节点所对应的源模型中的那个节点SP。再求解源模型中SP所包含子节点的行数N,那么目标虚拟数据项PC就与源模型中SP节点的第N-1-row行、第column列的那个子节点SC对应。调用源模型的index()函数就可以轻易地求出子节点SC的索引。将这个索引添加到索引池,并令目标虚拟数据项的内部ID指向这个新索引,就可以建立目标虚拟数据项PC的完整索引。
这个求解过程看起来很容易,但是稍加分析就会发现一个问题:我们如何求解PP对应的SP。有的读者也许会回答:PP的索引(上述函数参数proxy_parent)的内部ID应该指向索引池中SP的索引,依靠SP的索引我们不就可以找到SP吗?可这里的问题是:此时SP的索引是否已被添加到了索引池?
一种可能的做法是:在代理模型响应视图对象的第一个请求之前,首先构造一个囊括源模型中所有数据项索引的索引池。这个方案虽然能够解决刚才的问题,但是却存在着性能问题:如果源模型是一个数据项很多、层次很深的树(比如一个庞大的文件系统),那么建立这个索引池既费时间又费空间。
实际上,我们所担心的问题根本不会发生。也就是说,我们可以确保上述SP的索引已经存在于索引池,原因如下:依照13.2.2节所述的协议,视图对象总会从代理模型的不可见根开始,以逐层深入的方式,请求代理模型构建虚拟数据项的索引。代理模型接收到这些请求后,也会以逐层深入的方式,请求源模型构建相关源数据项的索引,并将这些索引添加到索引池。按照这样的方式,当视图对象请求代理模型构建PC的索引时,SP的索引已被添加到索引池。
有了上述设计,我们就可以编写出代理模型RevertProxyModel的成员函数index(),如代码段13 21所示。该函数调用了其他两个成员函数mapToSource()以及register_index()。给定代理模型中一个虚拟数据项的索引,函数mapToSourcde()负责返回对应源数据项的索引。代理模型以及源模型都使用无效索引作为它们的不可见根节点的索引,因而如果给定虚拟数据项的索引是一个无效索引,该函数也返回一个无效索引,表示代理模型的不可见根被映射为源模型的不可见根。如果给定的索引是一个普通索引,行①求取QModelIndex对象的内部ID,并在索引池中查找并返回对应源数据项的索引。给定一个源数据项的索引,成员函数register_index()判断索引池是否已有这个索引。如果没有,则将其添加到索引池中。无论哪种情况,函数返回该索引在索引池中的位置。
代码段13 21,代理模型索引的创建,取自z:\examples\mvc\revertProxyModel\revertProxyModel.cpp
- QModelIndex RevertProxyModel::mapToSource ( const QModelIndex & proxy_ index ) const
- {
- if (!proxy_index.isValid())
- return QModelIndex();
- int pos = proxy_index.internalId(); ①
- return vector[ pos ];
- }
- int RevertProxyModel::register_index(const QModelIndex & source_index) const
- {
- int pos = vector.indexOf( source_index );
- if ( pos == -1 ){
- vector.push_back( source_index );
- pos = vector.size() - 1;
- }
- return pos;
- }
- QModelIndex RevertProxyModel::index(int row, int column, const QModel Index & proxy_parent) const
- {
- QModelIndex source_parent = mapToSource( proxy_parent );
- int rowCount = sourceModel()->rowCount( source_parent );
- QModelIndex source_index = sourceModel()->index( ②
- rowCount - 1 - row,column, source_parent );
- int pos = register_index( source_index );
- return createIndex( row, column, pos );
- }