13.2.2 索引
在Qt的模型/视图框架中,模型类的使用者(比如视图类、委托类)每次只能访问数据集中的一个数据项。这些类总是通过一个索引来指定将要访问哪个数据项。为简单起见,本节仅以视图类为例,来说明如何使用该索引访问数据集中的数据项。Qt使用类QModelIndex表示这种索引,该类含有以下一些重要信息。
1.行号、列号。被索引数据项的父数据项可能含有多行、多列的子数据项,类Qmodel Index在其内部定义了一个行号、一个列号,表明被索引数据项在第几行、第几列。如果数据集是一个列表,这个行号可以唯一确定一个数据项在列表中的位置。如果数据集是一个表格,一个行号以及一个列号可以唯一确定一个数据项的位置。
2.一个指针。如果数据集是一个树或者其他更复杂的数据结构,仅仅靠行号、列号无法确定一个数据项的位置。类QModelIndex在其内部定义了一个指针,直接指向数据集中的一个数据项。有的情况下,模型类使用一个容器(比如数组或者QVector)存放数据项,通过指定一个整数类型的容器下标,也可以唯一确定一个数据项的位置,比使用指针更加方便。对于这种情形,QModelIndex将这个指针变量当作一个整数变量来使用,并没有另外定义一个整数变量,以节省内存空间。我们将这个整数称为"内部ID"(Internal ID)。为简单起见,本节只讨论该变量被用作指针的情形。
一个问题出现了:如果数据集是一个列表或者表格,视图类设置QModelIndex的行号、列号,即可通知模型类,它要访问哪个数据项。但是,如果数据集是一个树或者其他复杂的数据结构,仅仅设定行号、列号无法确定视图类要访问数据集中的哪个数据项。由于视图类根本不知道模型类采用了什么样子的底层数据结构存放这些数据项,它却又无法设置QModelIndex中的指针,使得视图类无法精确、直接地设定一个索引。
为了解决这个问题,视图类和模型类约定了一个合作协议。
(1)无论数据集是一个列表、表格、树或者其他数据结构,模型类在内部定义了一个不可见根节点(invisible root),作为列表、表格中所有数据项的父节点,或者树的根节点的父节点。这个不可见根节点并不存放数据集中的任何数据,但是对数据集任何数据项的访问都始于对这个不可见根节点的访问。Qt约定:一个无效索引(invalid index)指向这个不可见根节点。所谓无效索引,是指QModelIndex的一个特殊对象,它没有指向数据集中任何一个数据项。类QModelIndex的无参构造函数返回这样一个对象。
(2)当视图类需要访问列表、表格中的数据项,或者树的根节点时,视图类会将目标数据项的行号、列号、父节点的索引传递给模型类,要求模型类为目标数据项创建一个索引。由于这种情况下目标数据项的父节点是不可见根节点,所以父节点的索引一定是一个无效索引。
如果数据集是列表或者表格,由于视图类传递过来的行号、列号信息已经能够唯一确定目标数据项的位置,模型类只需创建一个QModelIndex对象,并将传来的行号、列号写入该对象即可,不用设置该对象的指针域。
如果数据集是一棵树,模型类创建一个QModelIndex对象,将传来的行号、列号写入该对象。树可能含有多个根节点,模型类依据该行号、列号确定目标数据项的位置,并设置QModelIndex的指针域,令其指向这个目标数据项。
如果数据集是列表或者表格,视图类利用模型类返回的索引,能够访问数据集中的任意一个数据项,两者通过QModelIndex可以进行畅通无阻的信息交换。如果数据集是树,本步骤只是为树的根节点建立了索引,还需要继续下面的步骤,为其他树节点建立索引。
(3)一棵树可能具有多个根节点,每个根节点可能含有多个子节点。为了求取某个子节点的索引,视图类把该子节点的父节点的索引,该子节点的行号、列号传递给模型类。例如,为了求取图13 9中节点B的索引,视图类会将A的索引,B的行号(等于1)、列号(由于A只有一列子节点,因而此值为0)传递给模型类,请求创建B的索引。模型类接收到这些信息后,会依据A的索引(图中的QModelIndex对象)中存放的指针,迅速找到节点A。再依据B的行号、列号确定B的位置,然后创建一个QModelIndex对象,将B的行号、列号、内存地址写入该对象。模型类将B的完整索引返回给视图类,视图类利用这个索引完成其他操作,比如获取B中的数据,或者继续求取B节点的子节点(C、D、E)的索引。沿着从父节点到子节点的方向,不断重复这个过程,可以求取每个树节点的索引。
|
| 图13 9 创建树节点的索引 |
(4)有些情况下,视图类将一个子节点的索引传递给模型类,请求模型类创建该子节点的父节点的索引。例如,视图类将图13 9中子节点D的索引传递给模型类,请求模型类创建B的索引。模型类依据D的索引可以轻易地找到D。由于是模型类负责整棵树的存放,因而它也可以轻易地找到D的父节点B,创建一个QModelIndex对象,令其指针域指向B。然而,工作尚未做完,模型类还需要求解B的行号、列号。它会找到B的父节点A,察看A含有哪些子节点,并计算B的行号、列号,然后将这些信息填入B的索引对象中。至此,B的完整索引才被创建完毕。