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

Model/View Tutorial

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

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

image-20201209205743828

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

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

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

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

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

1、介绍

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

标准小部件使用的数据是小部件一部分。img
视图类对外部数据(模型)进行操作img

1.1 标准小部件

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

1.2 模型/视图的改善

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

1.3 模型/视图小部件概述

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

部件标准小部件 (基于项目的便利类)模型/视图视图类 (用于外部数据)
imgQListWidgetQlistView
imgQtableWidgetQtableview
imgQtreeWidgetQtreeview
imgQColumnView将树作为列表层次结构显示
imgQComboBox作为传统小部件工作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使用一个模型作为它的数据源。

img

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

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

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

2.1 一个只读的表

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

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

 // main.cpp
 #include <QApplication>
 #include <QTableView>
 #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 <QAbstractTableModel>

 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::ItemDataRoleMeaningType
Qt::DisplayRole文本QString
Qt::FontRole字体QFont
BackgroundRole单元格背景的画刷QBrush
Qt::TextAlignmentRole文本对齐方式enum Qt::AlignmentFlag
Qt::CheckStateRoleQVariant()强制使用复选框,
复选框设置Qt::Checked
或Qt::Unchecked
enum Qt::ItemDataRole

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

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

2.3 表格单元格内的时钟

img

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

 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 <QAbstractTableModel>
 #include <QString>

 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,这会产生一个读/写树。不需要对模型进行任何更改。树不会有任何层次结构,因为模型本身没有任何层次结构。

img

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

img

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

img

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

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

 #include <QTreeView>
 #include <QStandardItemModel>
 #include <QStandardItem>

 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处理选定项

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

1

让我们创建几个项目:

 #include "mainwindow.h"

 #include <QTreeView>
 #include <QStandardItemModel>
 #include <QItemSelectionModel>

 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 代理

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

视图有一个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 nameView class usedModel usedAspects covered
Team LeadersQListviewQStringListModelbook1,10.6
Directory ViewerQTreeViewQDirModelbook1,10.7
Color NamesQListViewQSortFilterProxyModel applied to QStringListModelbook1,10.8
CurrenciesQTableView基于 QAbstractTableModel只读book1,10.10
CitiesQTableView基于 QAbstractTableModel读/写book1,10.12
Boolean ParserQTreeView基于 QAbstractItemModel只读book1,10.14
Track EditorQTableWidget提供自定义编辑器的自定义委托book1,10.15
Four directory viewsQListView QTableView QTreeViewQDirModel演示多视图的使用Book2, 8.2
Address BookQListView QTableView QTreeView基于QAbstractTableModel读/写Book2, 8.4
Address Book with sortingQSortfilterProxyModel引入排序和筛选功能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
ViewsQListView QTableView QTreeViewQStandardItemModel只读book3,5-3
BardelegateQTableView基于QAbstractItemDelegate的表示的自定义委托book3,5-5
EditdelegateQTableView自定义委托,用于基于QAbstractItemDelegate进行编辑book3,5-6
SingleitemviewCustom view based on QAbstractItemView自定义视图book3,5-7
listmodelQTableView基于 QAbstractTableModel只读book3,5-8
treemodelQTreeView基于QAbstractItemModel只读book3,5-10
edit integersQListView基于 QAbstractListModel读/写book3,5-37,5-11
sortingQTableViewQSortFilterProxyModel applied to QStringListModel演示了排序book3,5-12

4.2 Qt Documentation

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

Example nameView class usedModel usedAspects covered
Address BookQTableViewQAbstractTableModel QSortFilterProxyModel使用QSortFilterProxyModel从一个数据池生成不同的子集
Basic Sort/Filter ModelQTreeViewQStandardItemModel QSortFilterProxyModel
ChartCustom viewQStandardItemModel设计与选择模型合作的自定义视图
Color Editor FactoryQTableWidget用一个新的自定义编辑器增强标准委托以选择颜色
Combo Widget MapperQDataWidgetMapper to map QLineEdit, QTextEdit and QComboBoxQStandardItemModel演示QComboBox如何充当视图类
Custom Sort/Filter ModelQTreeViewQStandardItemModel QSortFilterProxyModel子类QSortFilterProxyModel用于高级排序和过滤
Dir ViewQTreeViewQFileSystemModel演示如何将模型分配给视图的非常小的例子
Editable Tree ModelQTreeViewCustom tree model使用树的综合示例,演示了使用底层自定义模型编辑单元格和树结构
Fetch MoreQListViewCustom list model动态变化模型
Frozen ColumnQTableViewQStandardItemModel
InterviewMultipleCustom item model多个视图
PixelatorQTableViewCustom table model自定义委托的实现
PuzzleQListViewCustom list model模型/视图与拖放
Simple DOM ModelQTreeViewCustom tree model自定义树模型的只读示例
Simple Tree ModelQTreeViewCustom tree model自定义树模型的只读示例
Simple Widget MapperQDataWidgetMapper to map QLineEdit, QTextEdit and QSpinBoxQStandardItemModel基本的QDataWidgetMapper用法
Spin Box DelegateQTableViewQStandardItemModel使用自旋框作为单元格编辑器的自定义委托
SpreadsheetQTableView定制的代表
Star DelegateQTableWidget全面的自定义委托示例。

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值