13.2.3 派生新模型类(3)
该函数将新创建的QModelIndex对象内部的行号,列号,指针(或者内部ID)设置为参数中的row、column、ptr(或者id)。本例调用的是第2个createIndex()函数,内部ID表示一个数据项在成员数组中的下标。该下标显然能够确定一个数据项的存储位置,所创建的QModelIndex对象当然也就是一个数据项的完整索引。
对于本例,如果父节点为模型类的不可见根节点,子数据项一定是二叉树的根节点。它在成员数组中的下标为0,而其行号、列号由index()函数的参数row,column指定(应该都为0),所以我们在行①创建该子数据项的索引。
如果父节点不是模型类的不可见根节点,它就一定是二叉树的一个非叶节点。行②取得它在成员数组中的位置,做一些简单的计算,即可求出子数据项在成员数组中的位置,最后调用createIndex()创建该数据项的索引。
代码段13 3,类TreeModel的实现(续),取自z:\examples\mvc\binary_tree\treemodel.cpp
- QModelIndex TreeModel::index ( int row, int column, const QModelIndex & parent ) const
- {
- if ( ! parent.isValid() )
- return createIndex(row, column, 0); ①
- int parentparent_idx = parent.internalId(); ②
- int idx = parent_idx * 2 + ( row + 1 );
- return createIndex(row, column, idx );
- }
- QModelIndex TreeModel::parent ( const QModelIndex & index ) const
- {
- if (index.internalId() == 0) ③
- return QModelIndex(); ④
- int parent_idx = (index.internalId() - 1 )/2;
- return createIndex( (parent_idx+1) % 2, 0, parent_idx ); ⑤
- }
- QVariant TreeModel::data ( const QModelIndex & index, int role ) const
- {
- switch (role) {
- case Qt::DisplayRole:
- int value = numbers[ index.internalId() ];
- return QVariant( value );
- }
- return QVariant();
- }
成员函数parent()返回一个子数据项父节点的索引。参数index是这个子数据项的索引。如果这个索引的内部ID为0(行③),则这个子数据项是二叉树的根,其父节点是模型类的不可见根,行④调用QModelIndex的无参构造函数,创建并返回一个无效索引。如果子数据项不是二叉树的根,行④后的代码做一些简单的计算,求取父节点在成员数组中的位置,创建并返回其索引。这个子数据项的父节点可能是该子数据项的"爷"节点的左子节点或者右子节点,对应的行号可能会是0或者1。行⑤依据该子数据项的父节点在成员数组中的下标确定这个行号。
函数data()获取一个数据项中的数据。函数参数index是该数据项的索引,role是所要获取的数据子项的角色。本例只处理角色Qt::DisplayRole,该函数取得该数据项在成员数组中的下标,返回该数据项的数据,也就是二叉树节点中的整数,供视图类显示。对于其他角色,该函数调用QVariant的无参构造函数,构造一个表示"无效值"的QVariant对象,并返回给视图类,表明该模型类无法提供与角色role对应的数据子项,视图类自己应该为角色role指定一个默认值。比如,如果角色为Qt::BackgroundRole,视图类将会采用默认的白色作为显示数据项时的背景色。
重载了上述5个纯虚函数的类TreeModel就能够和Model/View框架中其他类协同工作了。由于这个模型类所表示的数据集是一棵树,我们采用类QTreeView来显示它的数据集,如代码段13 4所示。主函数只是简单地调用视图类的成员函数setModel(),建立起模型类和视图类两者之间的关联关系,两者就可以通过上述5个纯虚函数组成的最小接口进行信息交互。
代码段13 4,满二叉树例子的主函数,取自z:\examples\mvc\binary_tree\main.cpp
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- TreeModel treeModel;
- QTreeView treeView(0);
- treeView.setModel( & treeModel );
- treeView.show();
- return app.exec();
- }
本例的模型类TreeModel只是提供了最基本的功能:当一个视图类需要显示数据集中的数据时,该类向视图类报告数据集的构造,并向视图类返回指定数据项中的数据,以使视图类能够"显示"模型类中的数据集。Model/View框架所具有的功能远比这丰富。在下面几节中,我们将向类TreeModel中添加新的功能,以展示Model/View框架的能力。
3.处理更多的角色
数据集的每个数据项包含多个『角色,数据子项』对,其中一些数据子项存放应用程序的数据,比如二叉树例子中每个数节点中的整数,而另外一些数据子项和这个数据项的显示相关,比如背景色、文字字体。当视图类需要显示某个数据项时,会查询这个数据项所包含的所有『角色,数据子项』对。上一节的模型类TreeModel仅向视图类报告Qt::DisplayRole角色对应的数据子项。本节扩展TreeModel的功能,使其能够向视图类报告更多角色对应的数据子项,以控制数据项的外观。我们的目标是使用红色、26磅字体显示叶节点。对于其他非叶节点,采用黑色、20磅显示。
如代码段13 5所示,我们只需修改模型类的data()函数即可达到这个目标。函数参数index指向被查询的数据项,role是所要查询的角色。由于我们需要更改某些节点的显示颜色,TreeModel必须处理角色Qt::ForegroundRole(行①)。index的成员函数internalId()返回QModelIndex对象的内部ID。本例中,这个内部ID表示当前数据项在模型类的成员数组的位置。行②依据位置判断被查询数据项是否为二叉树的叶节点。如果是,则返回一个红色刷子。否则,表示当前数据项不是叶节点,行④构造并返回一个无效QVariant对象,视图类将采用默认的黑色显示当前数据项。类似地,如果当前数据项是叶节点,行③后的代码返回26磅字体,否则,行④返回20磅字体,视图类将使用返回的字体显示当前数据项。