Qt 模型类 (转)
时间:2011-04-12 来源:93度的饼干
在模型/视图架构中,模型提供一个标准的接口供视图和委托(view/delegate)用来访问数据。在Qt中,这个标准的接口是由类QAbstractItemModel定义的。无论底层数据结构中的数据是如何存储的,类QAbstractItemModel的所有子类都将数据表示为一个包含由数据项组成的表格的层次型结构。视图按照这个约定来访问模型中的数据项,但是它们将信息展示给用户的时候不需要这样来做。
模型也使用信号和信号槽机制来向关联的视图通知关于数据的变化。
模型索引
为了确保数据的表示与数据被访问的方式是分开独立的,引入了模型索引(modelindex)的概念。每一个可以通过模型来获取的信息都被一个模型索引来表示。视图和委托使用这些模型索引来获取要显示的数据项(视图/委托操作的不是数据项,实际上操作的是保存了数据项内容的模型索引)。
结果是,只有模型需要知道如何获取数据,并且被该模型管理的数据的种类可以进行很广泛的定义。模型索引包含一个指向创建它们(模型索引)的模型的指针,这一点避免了在使用不止一个模型来工作时所产生的混乱。
QAbstractItemModel *model = index.model();
模型索引提供对信息片段的临时引用,可以被用来通过模型获取或者修改数据。由于模型可能会重新组织它们内部的数据结构,因此模型索引可能会失效,所 以不应当被保存。 如果你需要对一个信息片段的长期的引用,那么你必须创建一个持久模型索引。这样就提供了一个指向模型所维护的最新数据的引用。临时模型索引由 QModelIndex类提供,持久模型索引由QPersistentModelIndex类提供。
要获得对应于某一个数据项的模型索引,需要告诉模型3个属性:1个行号,1个列号,和父数据项的模型索引。接下来的小节描述及详细解释这些属性。
行与列
在最基本的形式下,一个模型可以被当作一个简单的表格来访问,在这个表格中数据项是通过行号和列号来定位的。这并不是说底层的数据片段存储在一个 矩阵结构中;行号和列号的使用只是一个用来让部件之间进行通信的约定。我们可以通过向模型提供任意指定的项目的行号和列号来获取它的信息,我们会获得一个 表示该数据项的模型索引:
QModelIndex index = model->index(row, column, …);
那些为简单、单级的数据结构,例如列表和表格提供接口的模型不需要任何其它的信息,但是,正如上面的代码所显示的,我们在获取一个模型索引时需要提供更多的信息。
这个示意图展示了一个基本的表格模型,其中每个数据项都是通过一对行号和列号来定位的。我们通过向模型传递一个数据项的对应的行号和列号来获取一个指向该项目的索引。
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
模型中的顶层数据项都是通过以QModelIndex()作为它们的父对象项目来引用的。这一点是在下一节描述的。
数据项的父对象
模型针对数据项提供的类似表格的接口完美适用于在表格或者列表视图中使用数据;行号和列号系统准确地与视图显示项目的方式对应。然而,像树视图这样的结构要求模型针对它管理的项目展现出一个更灵活的接口。结果是,每个数据项也可以作为另一个数据项组成的表格的父对象,就像树视图中的一个顶层数据项可以包含另一个由数据项组成的列表一样。
当我请求一个模型数据项的模型索引时,我位必须提供一些关于它的父对象的信息。在模型之外,指向一个项目的唯一的方法就是使用一个模型索引,所以一个父对象模型索引也必须被给出:
QModelIndex index = model->index(row, column, parent);
父对象,行和列
这个图显示的是一个树模型的表示方式,其中,每个数据项是使用一个父对象、一个行号和一个列号来索引的。
项目“A”和“C”在模型中是被以顶级的兄弟节点的方式来表示的:
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
项目“A”有一些子对象。项目“B”的模型索引是以下面的代码来获取的:
QModelIndex indexB = model->index(1, 0, indexA);
数据项角色
一个模型中的数据项可以为其它部件扮演各种各样的角色,以便为不同的情况来使用不同种类的数据。例如,Qt::DisplayRole用来在一个视图中获取一个可以被当成文本显示的字符串。一般地,数据项都包含了用于很多不同角色的数据,而标准的角色是被Qt::ItemDataRole定义的。
我们可以通过向模型传递对应于一个数据项的模型索引来获取它的数据,并且通过指定一个角色来获取我们想要的类型的数据:
QVariant value = model->data(index, role);
数据项角色
角色指示模型哪种类型的数据是我们想要的。视图可以以不同的方式来显示不同的角色,所以为每个角色提供合适的信息是很重要的。
创建新的模型一节更加具体地讲述了关于角色的一些特殊用法。
数据项数据的最常用的用法是被Qt::ItemDataRole中定义的标准角色描述的。通过为每个角色提供适当的数据项数据,模型可以为视图和委托提供关于数据项该如何显示给用户的提示。不同的视图可以自主选择是按照要求来忽略这个提示还是遵从这个提示。也可以定义附加的角色,以用于实现应用程序特定的目的。
概念的总结
- •.模型索引以一种不依赖于任何底层数据结构的方式来向视图和委托提供关于模型所提供的数据项的位置的信息。
- •.数据项是通过行号、列号和它们的父对象的模型索引来引用的。
- •.模型索引是模型在其它组件,例如视图和委托的请求下构造的。
- •.如果在使用index()来请求模型索引时,一个有效的模型索引被当作父项目的索引来指定,那么返回的索引将会指向模型中在该父数据项下方的一个子数据项。获取的模型索引指向那个父数据项的一个子数据项。
- •.如果在使用index()来请求索引时,一个无效的模型索引被当作父数据项的模型索引来指定,那么返回的索引将会指向模型中的一个顶层数据项。
- •.角色区分与一个数据项结合的不同类型的数据。
使用模型索引
为了演示如何使用模型索引来从模型中取得数据,我们设置一个没有视图的QFileSystemModel,并且在一个部件中显示文件和目录的名字。尽管这没有展示出一个使用模型的标准方式,但是它展示了使用模型索引时在模型中使用的一些约定。
我们以下面的方式来构造一个文件系统模型:
QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);
在这种情况下,我们建立一个默认的QFileSystemModel,使用该模型提供的特定的index()实现来获取一个父对象索引,再使用函数rowCount()来统计该模型中的行数。
简便起见,我们只关心该模型中第一列的项目。我们按顺序检查每一行,获取每一行中第一个项目的模型索引,再读取模型中为该项目储存的数据。
for (int row = 0; row < numRows; ++row) {
QModelIndex index = model->index(row, 0, parentIndex);
为了获得一个模型索引,我们指定行号、列号(为第一列指定为0),以及针对所有我们想要获取的项目的父项目的合适的模型索引。保存在每个项目中的文本是使用模型的data()函数来获取的。我们指定模型索引和DisplayRole以便以字符串的形式来获取该项目的数据。
QString text = model->data(index, Qt::DisplayRole).toString();
// Display the text in a widget.
}
上面的例子展示了从模型中获取数据时所用的基本原则:
- •.一个模型的维度可以使用rowCount()和columnCount()来获取。这些函数通常要求指定一个父模型索引。
- •.模型索引用来访问模型中的项目。要指定一个项目,需要行号、列号和父模型索引。
- •.要访问模型中的顶级项目,需要使用QModelIndex()来指定一个空的模型索引作为父索引。
- •.项目都包含针对不同角色的数据。为了获取一个指定角色的数据,必须同时向模型提供模型索引和角色。