1. 概念

在MVC架构中,视图通过与模型交互,将数据项进行显示。在此还需要再一次声明,数据的呈现方式可能与底层存储数据项的数据结构完全不同。 数据和显示能够分离,是因为使用了QAbstractItemModel提供了统一接口,和QAbstarctItemView提供了一个标准视图接口,以及使用模型索引提供了一个通用方法表示数据。 视图从模型获取数据,在界面显示,可以自己渲染数据项,也可以使用委托进行渲染。 在使用视图,需要创建一个视图,然后设置模型,如果不设置模型,也可以使用视图,但没内容。 除了呈现数据,视图还处理项之间的导航和部分项目选择。视图还实现基本的用户界面功能,如上下文菜单和拖放。视图可以为项目提供默认的编辑功能,也可以与委托合作提供自定义编辑器。 视图是可以对数据项进行选择的,以下是视图的选择行为和选择模式: 选择行为: image.png 选择模式:

常量 描述
QAbstractItemView::SingleSelection 1 当用户选择一个项目时,任何已经选择的项目都变为未选择。用户可以取消选择所选项目。
QAbstractItemView::ContiguousSelection 4 当用户以通常的方式选择一个项目时,选中的项目将被清除,新项目将被选中。但是,如果用户在单击项时按Shift键,则当前项和所单击项之间的所有项都将被选中或未选中,具体取决于所单击项的状态。
QAbstractItemView::ExtendedSelection 3 当用户以通常的方式选择一个项目时,选中的项目将被清除,新项目将被选中。但是,如果用户在单击项时按下Ctrl键,则被单击的项被切换,而所有其他项保持不变。如果用户在单击项时按Shift键,则当前项和所单击项之间的所有项都被选中或未选中,具体取决于所单击项的状态。可以通过将鼠标拖动到多个项目上来选择它们。
QAbstractItemView::MultiSelection 2 当用户以通常的方式选择一个项目时,该项目的选择状态被切换,而其他项目保持不变。可以通过将鼠标拖动到多个项目上来切换它们。
QAbstractItemView::NoSelection 无法选择项目。

像QTableView和QTreeView,即可以显示数据,还可以显示表头。表头通过QHeaderView类实现,通过QAbstractItemModel::headerData()函数从模型中获取表头数据,之后使用一个标签显示表头信息。可以通过子类化QHeaderView设置标签的显示。

以下是QAbstractItemView的类图: image.png 但以上都是只能使用规范的格式显示数据,如果需要实现比如条形图或者点图显示数据,需要重新实现视图类。

2. Qt提供的视图类

Qt提供了三个现成可用的视图类,以常见于大多数用户的方式来呈现模型中的数据。QListView可以将模型中的项以简单的列表形式或经典的图标视图形式显示。QTreeView将模型中的项显示为列表的层次结构,可以以紧凑的方式表示深层嵌套的结构。QTableView以类似于电子表格应用程序的布局形式将模型中的项呈现为表格形式。 显示如下: image.png 以上标准视图的默认行为对于大多数应用程序来说足够了使用。它们提供基本的编辑功能,并且可以定制以适应更专业的用户界面的需要。

3. 视图使用模型

视图如果要显示数据,需要给视图设置一个模型,然后将数据设置到模型中,这样就可以显示。 如下:

    QStringList numbers;
    numbers << "好运" << "健康" << "友情" << "理想" << "财富";

    QAbstractItemModel *model = new QStringListModel(numbers);


    QListView *view = new QListView;
    view->setModel(model);

image.png

4. 给多个视图设置同一模型

可以给不同视图设置同一个模型,比如:

    QStringList numbers;
    numbers << "好运" << "健康" << "友情" << "理想" << "财富" << "爱情" << "知识" << "才华" << "修养" << "善良";

    QTableView* pView1 = new QTableView();
    QTableView* pView2 = new QTableView();
    QStandardItemModel* pModel = new QStandardItemModel();
    int n = 0;
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 5; ++j) {
            pModel->setItem(i, j, new QStandardItem(numbers.at(n++)));
        }
    }

    pView1->setModel(pModel);
    pView2->setModel(pModel);

而且,对于许多应用程序来说,共享选择模型是可取的。 显示如下: image.png

5. 项目选择

在视图中处理选择项的机制是由QItemSelectionModel类提供的。默认情况下,所有的标准视图都构造自己的选择模型,并以正常的方式与它们交互。视图使用的选择模型可以通过selectionModel()函数获得,替换的选择模型可以通过setSelectionModel()指定。当我们希望为相同的模型数据提供多个一致的视图时,控制视图所使用的选择模型的能力非常有用。 一般来说,除非要对模型或视图进行子类化,否则不需要直接操作选择的内容。但是,如果需要,可以访问选择模型的接口,这在Item视图中的处理选择中进行了探讨。 视图中选定项的信息存储在QItemSelectionModel类的实例中。这个类维护了一个单个模型中项的模型索引,并且与任何视图无关。由于可以有多个视图与一个模型关联,因此可以在视图之间共享选择,允许应用程序以一致的方式显示多个视图。 选择由选择范围指定。通过仅记录所选项的每个范围的起始和结束模型索引,这些范围可以高效地维护有关大量项的信息。通过使用多个选择范围来描述选择,可以构建非连续的项选择。 选择应用于由选择模型保存的模型索引集合。应用的最新项选择被称为当前选择。即使在应用选择之后,通过使用某些类型的选择命令,仍然可以修改选择的效果。

5.1 当前项和选择项

在视图中,总是有一个当前项和一个选定项——两个独立的状态。一个项目可以同时是当前项目和被选中的项目。视图负责确保总是有一个当前项,例如键盘导航需要一个当前项。 以下截图于文档,显示了当前项和选定项的不同: image.png 翻译过来就是: 当前项:

  • 只能有一个当前项目。
  • 通过键导航或单击鼠标按钮可以更改当前项目。
  • 如果按下编辑键F2或双击该项目(如果启用了编辑功能),将编辑当前项目。
  • 当前项目由焦点矩形指示。

选择项:

  • 可以有多个选择项。
  • 当用户与项目交互时,项目的选择状态是设置或不设置的,这取决于几个预定义的模式-例如,单选择,多选择等。
  • 当前项可以与锚点一起使用,以指定应该选择或取消选择的范围(或两者的组合)。
  • 选定的项目用选择矩形表示。

在操作选择时,可以将QItemSelectionModel视为项目模型中所有项目的选择状态的记录。一旦设置了选择模型,就可以选择、取消选择项目集合,或者切换它们的选择状态,而不需要知道哪些项目已经被选中。可以随时检索所有选定项的索引,并且可以通过信号和槽机制通知其他组件选择模型的更改。

5.2 使用选择模型

标准视图类提供了默认的选择模型,可用于大多数应用程序。可以使用视图的selectionModel()函数获取属于该视图的选择模型,并使用setSelectionModel()在多个视图之间共享,因此通常不需要构建新的选择模型。 如下:

    QStandardItemModel *model = new QStandardItemModel(8, 4, this);

    QTableView *table = new QTableView(0);
    table->setModel(model);

    QItemSelectionModel *selectionModel = table->selectionModel();

    QModelIndex topLeft;
    QModelIndex bottomRight;

    topLeft = model->index(0, 0, QModelIndex());
    bottomRight = model->index(5, 2, QModelIndex());

    QItemSelection selection(topLeft, bottomRight);
    selectionModel->select(selection, QItemSelectionModel::Select);

    setCentralWidget(table);

首先获取视图的选择模型selectionModel,要使用选择模型来选择视图中的项,需要指定QItemSelection 和 QItemSelectionModel::SelectionFlag. QItemSelection 是一个选择块,需要指定其左上角和右下角模型索引。要将选择应用于模型中的项,需要将选择提交给选择模型。 image.png

以下是选择标志: QItemSelectionModel::NoUpdate : 不会进行选择。 QItemSelectionModel::Clear : 完整选择将被清除。 QItemSelectionModel::Select : 将选择所有指定的索引。 QItemSelectionModel::Deselect : 将取消选择所有指定的索引。 QItemSelectionModel::Toggle : 根据当前状态选择或取消选择(相反)所有指定的索引。 QItemSelectionModel::Current : 将更新当前选择。 QItemSelectionModel::Rows : 选择指定项所在行的所有项。 QItemSelectionModel::Columns : 选择指定项所在列的所有项。 QItemSelectionModel::SelectCurrent : 为方便起见提供的选择和当前的组合。 QItemSelectionModel::ToggleCurrent : 为方便起见提供的切换和当前的组合。 QItemSelectionModel::ClearAndSelect : 为方便起见提供的清除和选择的组合。

下面代码将显示选择为第三列所有项,如下:

    QStandardItemModel *model = new QStandardItemModel(8, 4, this);

    QTableView *table = new QTableView(0);
    table->setModel(model);

    QItemSelectionModel *selectionModel = table->selectionModel();


    QModelIndex topLeft;
    QModelIndex bottomRight;

    topLeft = model->index(1, 2, QModelIndex());
    bottomRight = model->index(5, 2, QModelIndex());

    QItemSelection selection(topLeft, bottomRight);
    selectionModel->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Columns);

image.png

5.3 遍历选择模型

可以使用selectedIndexes()函数读取存储在选择模型中的模型索引。这将返回一个未排序的模型索引列表,但只要知道它们是为哪个模型而使用,就可以对其进行迭代:

      QModelIndexList indexes = selectionModel->selectedIndexes();
      QModelIndex index;

      foreach(index, indexes) {
          QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
          model->setData(index, text);
      }