概念

在model/view架构中,view从model中获得数据项然后显示给用户。数据显示的方式不必与model提供的表示方式相同,可以与底层存储数据项的数据结构完全不同。
内容与显式的分离是通过由QAbstractItemModel提供的标准模型接口,由QAsbstractItemview提供的标准视图接口共同实现的。普遍使用model index来表示数据项。view负责管理从model中读取的数据的外观布局。
它们自己可以去渲染每个数据项,也可以利用delegate来既处理渲染又进行编辑。
除了显示数据,views也处理数据项的导航,参与有关于数据项选择的部分功能。view也实现一些基本的用户接口特性,如上下文菜单与拖拽功能。view也为数据项提供了缺省的编程功能,也可搭配delegate实现更为特殊的定制编辑的需求。
一个view创建时必不需要model,但在它能显示一些真正有用的信息之前,必须提供一个model。view通过使用
selections来跟踪用户选择的数据项。每个view可以维护单独使用的selections,也可以在多个views之间共享。有些views,如QTableViewQTreeView,除数据项之外也可显示标题(Headers),标题部分通过一个view来实现,QHeaderView。标题与view一样总是从相同的model中获取数据。从 model中获取数据的函数是QabstractItemModel::headerDate(),一般总是以表单的形式中显示标题信息。可以从QHeaderView子类化,以实现更为复杂的定制化需求。

使用现成的view
Qt提供了三个现成的view 类,它们能够以用户熟悉的方式显示model中的数据。QListView把model中的数据项以一个简单的列表的形式显示,或是以经典的图标视图的形式显示。QTreeView把model中的数据项作为具有层次结构的列表的形式显示,它允许以紧凑的深度嵌套的结构进行显示。QTableView却是把model中的数据项以表格的形式展现,更像是一个电子表格应用程序的外观布局。

QT 中的模型视图 和代理_QT

以上这些标准view的行为足以应付大多数的应用程序,它们也提供了一些基本的编辑功能,也可以定制特殊的需求。

使用model
以前的例子中创建过一个string list model,可以给它设置一些数据,再创建一个view把model中的内容展示出来:
int main(int argc, char *argv[])
 {
   QApplication app(argc, argv);

 // Unindented for quoting purposes:
 QStringList numbers;
 numbers << "One" << "Two" << "Three" << "Four" << "Five";

 QAbstractItemModel *model = new StringListModel(numbers);
 //要注意的是,这里把StringListModel作为一个QAbstractItemModel来使用。这样我们就可以
 //使用model中的抽象接口,而且如果将来我们用别的model代替了当前这个model,这些代码也会照样工作。
 //QListView提供的列表视图足以满足当前这个model的需要了。
 QListView *view = new QListView;
 view->setModel(model);
  view->show();
  return app.exec();
}

QT 中的模型视图 和代理_QT_02

view会渲染model中的内容,通过model的接口来访问它的数据。当用户试图编辑数据项时,view会使用缺省的delegate来提供一个编辑构件。
一个model,多个views
为多个views提供相同的model是非常简单的事情,只要为每个view设置相同的model。
     QTableView *firstTableView = new QTableView;
     QTableView *secondTableView = new QTableView;

     firstTableView->setModel(model);
     secondTableView->setModel(model);
在model/view架构中信号、槽机制的使用意味着model中发生的改变会传递中联结的所有view中,这保证了
不管我们使用哪个view,访问的都是同样的一份数据。

QT 中的模型视图 和代理_QT_03

上面的图展示了一个model上的两个不同的views,尽管在不同的view中显示的model中的数据是一致的,每个
view都维护它们自己的内部选择模型,但有时候在某些情况下,共享一个选择模型也是合理的。

处理数据项的选择
view中数据项选择机制由QItemSelectionModel类提供。所有标准的view缺省都构建它们自己的选择模型,
以标准的方式与它们交互。选择模型可以用selectionModel()函数取得,替代的选择模型也可以通过
setSelectionModel()来设置。当我们想在一个model上提供多个一致的views时,这种对选择模型的控制能力非常有用。通常来讲,除非你子类化一个model或view,你不必直接操纵selections的内容。

多个views之间共享选择
接着上边的例子,我们可以这样:
secondTableView->setSelectionModel(firstTableView->selectionModel());
现在所有views都在同样的选择模型上操作,数据与选择项都保持同步。

QT 中的模型视图 和代理_QT_04

上面的例子中,两个view的类型是相同的,假如这两个view类型不同,那么所选择的数据项在每个view
中的表现形式会有很大的不同。例如,在一个table view中一个连续的选择,在一个tree view中表现出
来的可能会是几个高亮的数据项片断的组合。

 

 

Qt Model/View 学习笔记 (三)

清源游民 gameogre@gmail.com

Model类

基本概念
在model/view构架中,model为view和delegates使用数据提供了标准接口。在Qt中,标准接口QAbstractItemModel类中被定义。不管数据在底层以何种数据结构存储,QAabstractItemModel的子类会以层次结构的形式来表示数据,结构中包含了数据项表。我们按这种约定来访问model中的数据项,但这个约定不会对如何显示这些数据有任何限制。数据发生改变时,model通过信号槽机制来通知关联的views。
QT 中的模型视图 和代理_QT_05

Model Indexes

为了使数据存储与数据访问分开,引入了model index的概念。通过model index,可以引用model中的数据项,Views和delegates都使用indexes来访问数据项,然后再显示出来。因此,只有model需要了解如何获取数据,被model管理的数据类型可以非常广泛地被定义。Model indexes包含一个指向创建它们的model的指针,这会在配合多个model工作时避免混乱。
QAbstractItemModel *model = index.model();

model indexes提供了对一项数据信息的临时引用,通过它可以访问或是修改model中的数据。既然model有时会重新组织内部的数据结构,这时model indexes便会失效,因此不应该保存临时的model indexes。假如需要一个对数据信息的长期的引用,那么应该创建一个persistent model index。这个引用会保持更新。临时的model indexes由QModelIndex提供,而具有持久能力的model indexes则由QPersistentModelIndex提供。在获取对应一个数据项的model index时,需要考虑有关于model的三个属性:行数,列数,父项的model index。


行与列
在最基本的形式中,一个model可作为一个简单的表来访问,每个数据项由行,列数来定位。这必不意味着
底层的数据用数组结构来存储。行和列的使用仅仅是一种约定,它允许组件之间相互通讯。可以通过指定
model中的行列数来获取任一项数据,可以得到与数据项一一对应的那个index。
QModelIndex index = model->index(row, column, ...);
Model为简单的,单级的数据结构如list与tables提供了接口,它们如上面代码所显示的那样,不再需要别的信息被提供。当我们在获取一个model index时,我们需要提供另外的信息。
QT 中的模型视图 和代理_QT_06

上图代表一个基本的table model,它的每一项用一对行列数来定位。通过行列数,可以获取代表一个数据项的model index . 
QModelIndex indexA = model->index(0, 0, QModelIndex());
 QModelIndex indexB = model->index(1, 1, QModelIndex());
 QModelIndex indexC = model->index(2, 1, QModelIndex());
一个model的顶级项,由QModelIndex()取得,它们上式被用作父项。

父项
类似于表的接口在搭配使用table或list view时理想的,这种行列系统与view显示的方式是确切匹配的。
然则,像tree views这种结构需要model提供更为灵活的接口来访问数据项。每个数据项可能是别的项的
父项,上级的项可以获取下级项的列表。
当获取model中数据项的index时,我们必须指定关于数据项的父项的信息。在model外部,引用一个数据
项的唯一方法就是通过model index,因此需要在求取model index时指定父项的信息。
QModelIndex index = model->index(row, column, parent);
QT 中的模型视图 和代理_QT_07
上图中,A项和C项作为model中顶层的兄弟项:
 QModelIndex indexA = model->index(0, 0, QModelIndex());
 QModelIndex indexC = model->index(2, 1, QModelIndex());
A有许多孩子,它的一个孩子B用以下代码获取:
QModelIndex indexB = model->index(1, 0, indexA);

项角色
model中的项可以作为各种角色来使用,这允许为不同的环境提供不同的数据。举例来说,Qt::DisplayRole被用于访问一个字符串,它作为文本会在view中显示。典型地,每个数据项都可以为许多不同的角色提供数据,标准的角色在Qt::ItemDataRole中定义。我们可以通过指定model index与角色来获取我们需要的数据:
QVariant value = model->data(index, role);
QT 中的模型视图 和代理_QT_08

角色指出了从model中引用哪种类型的数据。views可以用不同的形式显示角色,因此为每个角色提供正确
的信息是非常重要的。通过为每个角色提供适当数据,model也为views和delegates提供了暗示,如何正确地
把这些数据项显给用户。不同的views可以自由地解析或忽略这些数据信息,对于特殊的场合,也可以定义
一些附加的角色。
概念总结:
1,Model indexes为views与delegages提供model中数据项定位的信息,它与底层的数据结构无关。
2,通过指定行,列数,父项的model index来引用数据项。
3,依照别的组件的要求,model indexes被model构建。
4,使用index()时,如果指定了有效的父项的model index,那么返回得到的model index对应于父项的某个孩子。
5,使用index()时,如果指定了无效的父项的model index,那么返回得到的model index对应于顶层项的某个孩子。
6, 角色对一个数据项包含的不同类型的数据给出了区分。

使用Model Indexes
QDirModel *model = new QDirModel;
     QModelIndex parentIndex = model->index(QDir::currentPath());
     int numRows = model->rowCount(parentIndex);
 for (int row = 0; row < numRows; ++row)
 {
         QModelIndex index = model->index(row, 0, parentIndex);
         tring text = model->data(index, Qt::DisplayRole).toString();
         // Display the text in a widget.

     }
以上的例子说明了从model中获取数据的基本原则:
1,model的尺寸可以从rowCount()与columnCount()中得出。这些函数通常都需要一个表示父项的model index。
2,model indexes用来从model中访问数据项,数据项用行,列,父项model index定位。
3, 为了访问model顶层项,可以使用QModelIndex()指定。
4, 数据项为不同的角色提供不同的数据。为了获取数据,除了model index之外,还要指定角色