模型视图(01):【纲】Model View Tutorial[官翻]

文章目录

  • 1、介绍
    • 1.1 标准小部件
    • 1.2 模型/视图的改善
    • 1.3 模型/视图小部件概述
    • 1.4 在表单和模型之间使用适配器
  • 2. 一个简单的模型/视图应用程序
    • 2.1 一个只读的表
    • 2.2 使用角色扩展只读示例
    • 2.3 表格单元格内的时钟
    • 2.4 设置行、列表头
    • 2.5 最小的编辑示例
  • 3.中间的话题
    • 3.1 TreeView
    • 3.2处理选定项
    • 3.3 预定义的模型
    • 3.4 代理
    • 3.5 通过模型测试进行调试
  • 4. 额外信息的良好来源
    • 4.1 Books
    • 4.2 Qt Documentation

Model/View Tutorial

每个UI开发人员都应该了解ModelView编程,而本教程的目标就是为您提供一个易于理解的关于这个主题的介绍。

表、列表和树小部件是 GUI 中经常使用的组件。这些小部件有两种不同的方式来访问它们的数据。传统的方法涉及到包含存储数据的内部容器的小部件。这种方法非常直观,但是,在许多重要的应用程序中,它会导致数据同步问题。第二种方法是模型/视图编程,其中小部件不维护内部的数据容器。它们通过标准化接口访问外部数据,从而避免了数据重复。乍一看,这似乎很复杂,但一旦深入了解,它不仅容易掌握,而且模型/视图编程的许多好处也会变得更加清晰。

模型视图(01):【纲】Model View Tutorial[官翻]_第1张图片

在这个过程中,我们会了解到Qt提供的一些基本技术,例如:

  • 标准部件和模型/视图部件之间的区别
  • 表单和模型之间的适配器
  • 开发简单的模型/视图应用程序
  • 预定义的模型
  • 中间主题,如:
    • Tree views
    • Selection
    • Delegates
    • Debugging with model test

您还将了解使用模型/视图编程是否可以更容易地编写新应用程序,或者经典的小部件是否也能同样工作。

本教程包含示例代码,供您编辑并集成到项目中。教程的源代码位于Qt的examples/widgets/tutorials/modelview目录中。

要获得更详细的信息,您还可以查看参考文档

1、介绍

模型/视图是一种用于在处理数据集的小部件中从视图中分离数据的技术。标准窗口小部件不是为将数据从视图中分离而设计的,这就是Qt有两种不同类型窗口小部件的原因。这两种类型的小部件外观相同,但它们与数据的交互方式不同。

标准小部件使用的数据是小部件一部分。 模型视图(01):【纲】Model View Tutorial[官翻]_第2张图片
视图类对外部数据(模型)进行操作 模型视图(01):【纲】Model View Tutorial[官翻]_第3张图片

1.1 标准小部件

让我们仔细看看一个标准的表小部件。表小部件是用户可以更改的数据元素的2D数组。通过读写表小部件提供的数据元素,可以将表小部件集成到程序流中。这种方法在许多应用程序中非常直观和有用,但是使用标准表小部件显示和编辑数据库表可能会有问题。必须协调数据的两个副本:一个在小部件外部;一个在小部件内部。开发人员负责同步两个版本。除此之外,表示和数据的紧密耦合使得编写单元测试更加困难。

1.2 模型/视图的改善

模型/视图进一步提供了一个更通用的架构的解决方案。模型/视图消除了标准小部件可能出现的数据一致性问题。模型/视图还使得使用同一数据的多个视图变得更容易,因为一个模型可以传递给多个视图。最重要的区别是模型/视图小部件不将数据存储在表格单元格后面。实际上,它们直接从您的数据进行操作。因为视图类不知道数据的结构,所以需要提供包装器来使数据符合QAbstractItemModel接口。视图使用这个接口来读取和写入数据。实现QAbstractItemModel的类的任何实例都被称为模型。一旦视图接收到模型的指针,它将读取和显示它的内容,并成为它的编辑器

1.3 模型/视图小部件概述

以下是模型/视图小部件及其相应的标准小部件的概述。

部件 标准小部件 (基于项目的便利类) 模型/视图视图类 (用于外部数据)
模型视图(01):【纲】Model View Tutorial[官翻]_第4张图片 QListWidget QlistView
模型视图(01):【纲】Model View Tutorial[官翻]_第5张图片 QtableWidget Qtableview
模型视图(01):【纲】Model View Tutorial[官翻]_第6张图片 QtreeWidget Qtreeview
模型视图(01):【纲】Model View Tutorial[官翻]_第7张图片 QColumnView将树作为列表层次结构显示
img QComboBox作为传统小部件工作 QComboBox也可以用作视图类

1.4 在表单和模型之间使用适配器

在表单和模型之间有适配器可以派上用场。

我们可以直接在表本身中编辑存储在表中的数据,但是在文本字段中编辑数据要舒服得多。没有直接对应于模型/视图的数据与视图分离的小部件来操作一个值而不是数据集(QLineEdit 、QCheckBox…),因此我们需要一个适配器来将表单连接到数据源。

QDataWidgetMapper是一个很好的解决方案,因为它将表单小部件映射到表行,并且使为数据库表构建表单变得非常容易。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRDOzZPr-1607992128752)(https://doc.qt.io/qt-5/images/widgetmapper.png)]

另一个适配器示例是QCompleter。Qt有QCompleter,用于在Qt小部件中提供自动补全功能,如QComboBox和(如下所示)QLineEdit。QCompleter使用一个模型作为它的数据源。

模型视图(01):【纲】Model View Tutorial[官翻]_第8张图片

2. 一个简单的模型/视图应用程序

如果你想开发一个模型/视图应用程序,你应该从哪里开始?我们建议从一个简单的示例开始,并逐步扩展它。这使得理解体系结构变得容易得多。在调用IDE之前,尝试理解模型/视图架构的细节对许多开发人员来说是不太方便的。从具有演示数据的简单模型/视图应用程序开始要容易得多。试试吧!只需用您自己的数据替换下面示例中的数据。

下面是7个非常简单和独立的应用程序,它们展示了模型/视图编程的不同方面。源代码可以在examples/widgets/tutorials/modelview目录中找到。

2.1 一个只读的表

我们从使用 QTableView 显示数据的应用程序开始。稍后我们将添加编辑功能。

模型视图(01):【纲】Model View Tutorial[官翻]_第9张图片

(file source: examples/widgets/tutorials/modelview/1_readonly/main.cpp)

 // main.cpp
 #include 
 #include 
 #include "mymodel.h"

 int main(int argc, char *argv[])
 {
     QApplication a(argc, argv);
     QTableView tableView;
     MyModel myModel;
     tableView.setModel(&myModel);
     tableView.show();
     return a.exec();
 }

我们有常用的main()函数:

下面是有趣的部分:我们创建一个MyModel的实例并使用tableView.setModel(&myModel);将它的指针传递给tableView。

tableView会调用接收到的指针的方法来找出两件事:

  • 应该显示多少行和列。
  • 应该在每个单元格中打印什么内容。

模型需要一些代码来响应这两点。

我们有一个表数据集,所以让我们从QAbstractTableModel开始,因为它比更通用的QAbstractItemModel更容易使用。

 // mymodel.h
 #include 

 class MyModel : public QAbstractTableModel
 {
     Q_OBJECT
 public:
     MyModel(QObject *parent = nullptr);
     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
 };

行数和列数由MyModel::rowCount()和MyModel::columnCount()提供。当视图必须知道单元格的文本是什么时,它调用方法MyModel::data()。用参数索引指定行和列信息,角色设置为Qt::DisplayRole。其他角色将在下一节中介绍。在我们的示例中,生成了应该显示的数据。在真实的应用程序中,MyModel将有一个名为MyData的成员,它作为所有读写操作的目标。

这个小示例演示了模型的被动性质。模型不知道何时使用它或需要哪些数据。它只是在视图每次请求数据时提供数据。

当模型的数据需要更改时,会发生什么? 视图如何意识到数据已更改并需要再次读取? 模型必须发出信号,表明单元格的范围发生了变化。这将在第2.3节中演示。

2.2 使用角色扩展只读示例

除了控制视图显示什么文本之外,模型还控制文本的外观。稍微改变模型,得到如下结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b9w8Mwxz-1607992128754)(https://doc.qt.io/qt-5/images/readonlytable_role.png)]

实际上,除了data()方法之外,不需要改变任何东西来设置字体、背景颜色、对齐方式和复选框。下面是产生上述结果的data()方法。不同之处在于,这次我们使用参数int role根据其值返回不同的信息。
(file source: examples/widgets/tutorials/modelview/2_formatting/mymodel.cpp)

 // mymodel.cpp
 QVariant MyModel::data(const QModelIndex &index, int role) const
 {
     int row = index.row();
     int col = index.column();
     // generate a log message when this method gets called
     qDebug() << QString("row %1, col%2, role %3")
             .arg(row).arg(col).arg(role);

     switch (role) {
     case Qt::DisplayRole:
         if (row == 0 && col == 1) return QString("<--left");
         if (row == 1 && col == 1) return QString("right-->");

         return QString("Row%1, Column%2")
                 .arg(row + 1)
                 .arg(col +1);
     case Qt::FontRole:
         if (row == 0 && col == 0) { //change font only for cell(0,0)
             QFont boldFont;
             boldFont.setBold(true);
             return boldFont;
         }
         break;
     case Qt::BackgroundRole:
         if (row == 1 && col == 2)  //change background only for cell(1,2)
             return QBrush(Qt::red);
         break;
     case Qt::TextAlignmentRole:
         if (row == 1 && col == 1) //change text alignment only for cell(1,1)
             return Qt::AlignRight + Qt::AlignVCenter;
         break;
     case Qt::CheckStateRole:
         if (row == 1 && col == 0) //add a checkbox to cell(1,0)
             return Qt::Checked;
         break;
     }
     return QVariant();
 }

每个格式化属性都将通过对data()方法的单独调用从模型中请求。role参数用于让模型知道被请求的属性:

enum Qt::ItemDataRole Meaning Type
Qt::DisplayRole 文本 QString
Qt::FontRole 字体 QFont
BackgroundRole 单元格背景的画刷 QBrush
Qt::TextAlignmentRole 文本对齐方式 enum Qt::AlignmentFlag
Qt::CheckStateRole QVariant()强制使用复选框,
复选框设置Qt::Checked
或Qt::Unchecked
enum Qt::ItemDataRole

请参阅Qt名称空间文档以了解关于Qt::ItemDataRole enum功能的更多信息。

现在我们需要确定使用独立模型如何影响应用程序的性能,因此让我们跟踪视图调用data()方法的频率。为了跟踪视图调用模型的频率,我们在data()方法中放置了一条调试语句,该语句将登录到错误输出流上。在我们的小示例中,data()将被调用42次。每次将光标悬停在字段上时,data()将再次被调用——每个单元格调用7次。这就是为什么在调用data()和缓存昂贵的查找操作时,确保数据可用非常重要。

2.3 表格单元格内的时钟

模型视图(01):【纲】Model View Tutorial[官翻]_第10张图片

我们仍然有一个只读表,但这次内容每秒都在变化,因为我们显示的是当前时间。

 QVariant MyModel::data(const QModelIndex &index, int role) const
 {
     int row = index.row();
     int col = index.column();

     if (role == Qt::DisplayRole && row == 0 && col == 0)
         return QTime::currentTime().toString();

     return QVariant();
 }

少了什么东西使时钟滴答作响。我们需要每秒钟告诉视图时间已经改变,需要再次读取它。我们用计时器来做这个。在构造函数中,我们将其间隔设置为1秒,并连接其超时信号。

MyModel::MyModel(QObject *parent)
    :QAbstractTableModel(parent)
    ,timer(new QTimer(this))
{
    timer->setInterval(1000);
    connect(timer, &QTimer::timeout , this, &MyModel::timerHit);
    timer->start();
}

对应的槽如下:

 void MyModel::timerHit()
 {
     //we identify the top left cell
     QModelIndex topLeft = createIndex(0,0);
     //emit a signal to make the view reread identified data
     emit dataChanged(topLeft, topLeft, {Qt::DisplayRole});
 }

我们通过发送dataChanged()信号请求视图再次读取左上角单元格中的数据。注意,我们没有显式地将dataChanged()信号连接到视图。当我们调用setModel()时,这将自动发生。

2.4 设置行、列表头

头部内容是通过模型设置的,因此我们要重新实现 headerData()方法

 QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
 {
     if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
         switch (section) {
         case 0:
             return QString("first");
         case 1:
             return QString("second");
         case 2:
             return QString("third");
         }
     }
     return QVariant();
 }

注意headerData()也有一个 role 参数,其含义和date() 方法中的role一样

2.5 最小的编辑示例

在本例中,我们将构建一个应用程序,通过重复在表格单元格中输入的值来自动填充窗口标题。为了方便地访问窗口标题,我们将QTableView放在QMainWindow中。

模型决定编辑功能是否可用。为了启用编辑功能,我们只需要修改模型。这是通过重新实现以下虚拟方法来实现的:setData()和flags()。

 // mymodel.h
 #include 
 #include 

 const int COLS= 3;
 const int ROWS= 2;

 class MyModel : public QAbstractTableModel
 {
     Q_OBJECT
 public:
     MyModel(QObject *parent = nullptr);
     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
     bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
     Qt::ItemFlags flags(const QModelIndex &index) const override;
 private:
     QString m_gridData[ROWS][COLS];  //holds text entered into QTableView
 signals:
     void editCompleted(const QString &);
 };

我们使用二维数组QString m_gridData来存储我们的数据。这使得m_gridData成为MyModel的核心。MyModel的其余部分充当一个包装器,并将m_gridData调整为QAbstractItemModel接口。我们还引入了editCompleted()信号,它可以将修改后的文本传输到窗口标题。

 bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
 {
     if (role == Qt::EditRole) {
         if (!checkIndex(index))
             return false;
         //save value from editor to member m_gridData
         m_gridData[index.row()][index.column()] = value.toString();
         //for presentation purposes only: build and emit a joined string
         QString result;
         for (int row = 0; row < ROWS; row++) {
             for (int col= 0; col < COLS; col++)
                 result += m_gridData[row][col] + ' ';
         }
         emit editCompleted(result);
         return true;
     }
     return false;
 }

每次用户编辑单元格时都会调用setData()。索引参数告诉我们哪个字段已经被编辑,value 提供编辑过程的结果。角色将始终设置为Qt::EditRole,因为单元格只包含文本。如果有一个复选框,并且用户权限设置为允许选中该复选框,那么还将使用角色设置为Qt::CheckStateRole进行调用。

 Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
 {
     return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
 }

单元格的各种属性可以使用flags()进行调整。

返回Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled就足以向编辑器显示可以选择单元格

如果编辑一个单元格所修改的数据多于特定单元格中的数据,则模型必须发出dataChanged()信号,以便读取已更改的数据。

3.中间的话题

3.1 TreeView

您可以将上面的示例转换为具有树视图的应用程序。简单地用QTreeView替换QTableView,这会产生一个读/写树。不需要对模型进行任何更改。树不会有任何层次结构,因为模型本身没有任何层次结构。

模型视图(01):【纲】Model View Tutorial[官翻]_第11张图片

QListView、QTableView和QTreeView都使用了一个模型抽象,即一个合并的列表、表和树。这使得在同一个模型中使用多个不同类型的视图类成为可能。

模型视图(01):【纲】Model View Tutorial[官翻]_第12张图片

这是我们的示例模型到目前为止的样子:

模型视图(01):【纲】Model View Tutorial[官翻]_第13张图片

我们想呈现一个真实的树。为了创建模型,我们已经将数据封装在上面的示例中。这次我们使用QStandardItemModel,它是分层数据的容器,也实现了QAbstractItemModel。要显示树,QStandardItemModel必须用QStandardItems填充,它能够保存项目的所有标准属性,如文本、字体、复选框或笔刷。

模型视图(01):【纲】Model View Tutorial[官翻]_第14张图片

![1](E:\Users\Desktop\1.png) // modelview.cpp
 #include "mainwindow.h"

 #include 
 #include 
 #include 

 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , treeView(new QTreeView(this))
     , standardModel(new QStandardItemModel(this))
 {
     setCentralWidget(treeView);

     QList<QStandardItem *> preparedRow = prepareRow("first", "second", "third");
     QStandardItem *item = standardModel->invisibleRootItem();
     // adding a row to the invisible root item produces a root element
     item->appendRow(preparedRow);

     QList<QStandardItem *> secondRow = prepareRow("111", "222", "333");
     // adding a row to an item starts a subtree
     preparedRow.first()->appendRow(secondRow);

     treeView->setModel(standardModel);
     treeView->expandAll();
 }

我们只需实例化一个QStandardItemModel,并向构造函数中添加两个QStandardItems。然后我们可以创建一个分层的数据结构,因为QStandardItem可以包含其他QStandardItems。节点在视图中被折叠和展开。

3.2处理选定项

我们希望访问所选项目的内容,以便将其输出到窗口标题和层次结构级别中。

模型视图(01):【纲】Model View Tutorial[官翻]_第15张图片

让我们创建几个项目:

 #include "mainwindow.h"

 #include 
 #include 
 #include 

 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , treeView(new QTreeView(this))
     , standardModel(new QStandardItemModel(this))
 {
     setCentralWidget(treeView);
     QStandardItem *rootNode = standardModel->invisibleRootItem();

     //defining a couple of items
     QStandardItem *americaItem = new QStandardItem("America");
     QStandardItem *mexicoItem =  new QStandardItem("Canada");
     QStandardItem *usaItem =     new QStandardItem("USA");
     QStandardItem *bostonItem =  new QStandardItem("Boston");
     QStandardItem *europeItem =  new QStandardItem("Europe");
     QStandardItem *italyItem =   new QStandardItem("Italy");
     QStandardItem *romeItem =    new QStandardItem("Rome");
     QStandardItem *veronaItem =  new QStandardItem("Verona");

     //building up the hierarchy
     rootNode->    appendRow(americaItem);
     rootNode->    appendRow(europeItem);
     americaItem-> appendRow(mexicoItem);
     americaItem-> appendRow(usaItem);
     usaItem->     appendRow(bostonItem);
     europeItem->  appendRow(italyItem);
     italyItem->   appendRow(romeItem);
     italyItem->   appendRow(veronaItem);

     //register the model
     treeView->setModel(standardModel);
     treeView->expandAll();

     //selection changes shall trigger a slot
     QItemSelectionModel *selectionModel = treeView->selectionModel();
     connect(selectionModel, &QItemSelectionModel::selectionChanged,
             this, &MainWindow::selectionChangedSlot);
 }

视图在单独的选择模型中管理选择,可以使用selectionModel()方法检索该模型。我们检索选择模型,以便将插槽连接到它的selectionChanged()信号。

 void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
 {
     //get the text of the selected item
     const QModelIndex index = treeView->selectionModel()->currentIndex();
     QString selectedText = index.data(Qt::DisplayRole).toString();
     //find out the hierarchy level of the selected item
     int hierarchyLevel = 1;
     QModelIndex seekRoot = index;
     while (seekRoot.parent() != QModelIndex()) {
         seekRoot = seekRoot.parent();
         hierarchyLevel++;
     }
     QString showString = QString("%1, Level %2").arg(selectedText)
                          .arg(hierarchyLevel);
     setWindowTitle(showString);
 }

我们通过调用treeView->selectionModel()->currentIndex()来获得与选择相对应的模型索引,并通过使用模型索引来获得字段的字符串。然后我们只计算项目的层级级别。顶级项没有父项,父()方法将返回一个默认构造的QModelIndex()。这就是为什么我们使用parent()方法迭代到顶层,同时计算迭代期间执行的步骤。

选择模型(如上所示)可以被检索,但是也可以用QAbstractItemView::setSelectionModel进行设置。这就是为什么有3个视图类与同步选择是可能的,因为只有一个选择模型的实例被使用。要在3个视图之间共享选择模型,请使用selectionModel()并使用setSelectionModel()将结果分配给第二个和第三个视图类。

3.3 预定义的模型

使用模型/视图的典型方法是包装特定数据,使其可用于视图类。然而,Qt还为公共底层数据结构提供了预定义的模型。如果可用的数据结构中有一种适合您的应用程序,那么预定义的模型可能是一个不错的选择。

说明
QStringListModel 存储字符串列表
QStandardItemModel 存储任意层次项
QFileSystemModel 封装本地文件系统
QDirModel 已过时,推荐使用QFileSystemModel
QSqlQueryModel 封装SQL结果集
QSqlTableModel 封装一个SQL表
QSqlRelationalTableModel 用外键封装SQL表
QSortFilterProxyModel 对另一个模型进行排序和/或筛选

3.4 代理

到目前为止的所有例子中,数据都以文本或单元格中的复选框的形式显示,并被编辑为文本或复选框。提供这些表示和编辑服务的组件称为代理。我们只是刚刚开始使用代理,因为视图使用了默认的代理。但是,假设我们想要有一个不同的编辑器(例如,滑动条或下拉列表),或者假设我们想要将数据表示为图形。让我们看一个叫做星型代理的例子,在这个例子中星型用来显示评级:

模型视图(01):【纲】Model View Tutorial[官翻]_第16张图片

视图有一个setItemDelegate()方法,该方法替换默认的代理并安装一个自定义代理。可以通过创建继承自QStyledItemDelegate的类来编写新的代理。为了编写一个显示星号且没有输入功能的代理,我们只需要覆盖2个方法。

// 这个代理不完整,还需相应的编辑器,现在只做了解用,完整用法可参考 Star Delegate Example 
class StarDelegate : public QStyledItemDelegate
 {
     Q_OBJECT
 public:
     StarDelegate(QWidget *parent = 0);
     void paint(QPainter *painter, const QStyleOptionViewItem &option,
                const QModelIndex &index) const;
     QSize sizeHint(const QStyleOptionViewItem &option,
                    const QModelIndex &index) const;
 };

paint()根据基础数据的内容绘制星星。可以通过调用index.data()来查找数据。代理的sizeHint()方法用于获取每个星星的尺寸,因此单元格将提供足够的高度和宽度来容纳星星。

如果您想在视图类的网格中使用自定义图形表示来显示数据,那么编写自定义代理是正确的选择。如果您想要留下网格,您不应该使用自定义代理,而应该使用自定义视图类。

Qt文档中关于代理的其他引用:

  • Spin Box Delegate 示例
  • Star Delegate Example
  • QAbstractItemDelegate类引用
  • QSqlRelationalDelegate类引用
  • QStyledItemDelegate类引用
  • QItemDelegate类引用

3.5 通过模型测试进行调试

模型的被动特性为程序员提供了新的挑战。模型中的不一致可能导致应用程序崩溃。由于模型受到来自视图的大量调用的影响,因此很难找出哪个调用导致应用程序崩溃,以及哪个操作引入了问题。

Qt Labs提供了一种叫做ModelTest的软件,它可以在程序运行时检查模型。每次更改模型时,ModelTest都会扫描模型并使用assert报告错误。这对于树模型尤其重要,因为它们的层次性为细微的不一致留下了许多可能性。

与视图类不同,ModelTest使用范围外的索引来测试模型。这意味着您的应用程序在使用ModelTest时可能会崩溃,即使它在没有ModelTest的情况下可以完美地运行。因此,在使用ModelTest时,您还需要处理超出范围的所有索引。

4. 额外信息的良好来源

4.1 Books

模型/视图编程在Qt的文档和一些好书中都有相当广泛的介绍。

  1. 《C++ GUI Programming with Qt 4》 / Jasmin Blanchette, Mark Summerfield, Prentice Hall,第二版,ISBN 0-13-235416-0。
  2. 《The Book of Qt4》,构建Qt应用程序的艺术/ Daniel Molkentin,开放源码出版社,ISBN 1-59327-147-6。翻译自Qt 4, Einfuhrung in die Applikationsentwicklung,开源出版社,ISBN 3-937514-12-0。
  3. 《Foundations of Qt Development 》/ Johan Thelin, Apress, ISBN 1-59059-831-8。
  4. 《Advanced Qt Programming》/ Mark Summerfield, Prentice Hall, ISBN 0-321-63590-6。这本书超过150页涵盖了模型/视图编程。

下面的列表提供了上面列出的前三本书中包含的示例程序的概述。其中一些可以作为开发类似应用程序的非常好的模板。

Example name View class used Model used Aspects covered
Team Leaders QListview QStringListModel book1,10.6
Directory Viewer QTreeView QDirModel book1,10.7
Color Names QListView QSortFilterProxyModel applied to QStringListModel book1,10.8
Currencies QTableView 基于 QAbstractTableModel 只读 book1,10.10
Cities QTableView 基于 QAbstractTableModel 读/写 book1,10.12
Boolean Parser QTreeView 基于 QAbstractItemModel 只读 book1,10.14
Track Editor QTableWidget 提供自定义编辑器的自定义委托 book1,10.15
Four directory views QListView QTableView QTreeView QDirModel 演示多视图的使用 Book2, 8.2
Address Book QListView QTableView QTreeView 基于QAbstractTableModel 读/写 Book2, 8.4
Address Book with sorting QSortfilterProxyModel 引入排序和筛选功能 Book2, 8.5
Address Book with checkboxes 在模型/视图中引入复选框 Book2, 8.6
Address Book with transposed grid 基于QAbstractProxyModel 引入自定义模型 Book2, 8.7
Address Book with drag and drop 引入拖放支持 Book2, 8.8
Address Book with custom editor 引入定制的代表 Book2, 8.9
Views QListView QTableView QTreeView QStandardItemModel 只读 book3,5-3
Bardelegate QTableView 基于QAbstractItemDelegate的表示的自定义委托 book3,5-5
Editdelegate QTableView 自定义委托,用于基于QAbstractItemDelegate进行编辑 book3,5-6
Singleitemview Custom view based on QAbstractItemView 自定义视图 book3,5-7
listmodel QTableView 基于 QAbstractTableModel 只读 book3,5-8
treemodel QTreeView 基于QAbstractItemModel 只读 book3,5-10
edit integers QListView 基于 QAbstractListModel 读/写 book3,5-37,5-11
sorting QTableView QSortFilterProxyModel applied to QStringListModel 演示了排序 book3,5-12

4.2 Qt Documentation

Qt 5.0提供了19个模型/视图示例。示例可以在项目视图示例页面上找到。

Example name View class used Model used Aspects covered
Address Book QTableView QAbstractTableModel QSortFilterProxyModel 使用QSortFilterProxyModel从一个数据池生成不同的子集
Basic Sort/Filter Model QTreeView QStandardItemModel QSortFilterProxyModel
Chart Custom view QStandardItemModel 设计与选择模型合作的自定义视图
Color Editor Factory QTableWidget 用一个新的自定义编辑器增强标准委托以选择颜色
Combo Widget Mapper QDataWidgetMapper to map QLineEdit, QTextEdit and QComboBox QStandardItemModel 演示QComboBox如何充当视图类
Custom Sort/Filter Model QTreeView QStandardItemModel QSortFilterProxyModel 子类QSortFilterProxyModel用于高级排序和过滤
Dir View QTreeView QFileSystemModel 演示如何将模型分配给视图的非常小的例子
Editable Tree Model QTreeView Custom tree model 使用树的综合示例,演示了使用底层自定义模型编辑单元格和树结构
Fetch More QListView Custom list model 动态变化模型
Frozen Column QTableView QStandardItemModel
Interview Multiple Custom item model 多个视图
Pixelator QTableView Custom table model 自定义委托的实现
Puzzle QListView Custom list model 模型/视图与拖放
Simple DOM Model QTreeView Custom tree model 自定义树模型的只读示例
Simple Tree Model QTreeView Custom tree model 自定义树模型的只读示例
Simple Widget Mapper QDataWidgetMapper to map QLineEdit, QTextEdit and QSpinBox QStandardItemModel 基本的QDataWidgetMapper用法
Spin Box Delegate QTableView QStandardItemModel 使用自旋框作为单元格编辑器的自定义委托
Spreadsheet QTableView 定制的代表
Star Delegate QTableWidget 全面的自定义委托示例。

还提供了模型/视图技术的参考文档。

你可能感兴趣的:(#,Qt,模型视图)