模型/视图教程
Qt 5.10.1
每个UI开发人员都应该了解ModelView编程,本教程的目标是为您提供一个易于理解本主题的介绍。
表,列表和树窗口小部件是GUI中经常使用的组件。 这些小部件可以通过两种不同的方式访问其数据。 传统方式涉及的小部件,其中包括用于存储数据的内部容器。 这种方法非常直观,但是,在许多应用程序中,它会导致数据同步问题。 第二种方法是模型/视图编程,其中小部件不维护内部数据容器。 它们通过标准化接口访问外部数据,因此避免了数据重复。 这一开始可能看起来很复杂,但是一旦仔细观察,它不仅容易掌握,而且模型/视图编程的许多好处也变得更加清晰。
在此过程中,我们将了解Qt提供的一些基本技术,例如:
- 标准和模型/视图小部件之间的区别
- 表格和模型之间的适配器
- 开发简单的模型/视图应用程序
- 预定义模型
中级主题,例如:
- 树视图
- 选择
- 代表们
- 使用模型测试进行调试
您还将了解是否可以使用模型/视图编程更轻松地编写新应用程序,或者是否也可以使用经典小部件。
本教程包含用于编辑和集成到项目中的示例代码。本教程的源代码位于Qt的examples/widgets/tutorials/modelview目录中。
有关更多详细信息,您可能还需要查看参考文档
1.介绍
模型/视图是一种技术,用于将数据与处理数据集的小部件中的视图分开。 标准窗口小部件不是为从视图中分离数据而设计的,这就是Qt有两种不同类型的窗口小部件的原因。 两种类型的小部件看起来都相同,但它们以不同的方式与数据交互。
项 | 描述 |
---|---|
Standard widgets use data that is part of the widget. | |
View classes operate on external data (the model) |
1.1标准小部件
让我们仔细看看标准表格小部件。表格小部件是用户可以更改的数据元素的2D数组。可以通过读取和写入表小部件提供的数据元素将表小部件集成到程序流中。此方法在许多应用程序中非常直观且有用,但使用标准表窗口小部件显示和编辑数据库表可能会有问题。必须协调两个数据副本:一个在小部件之外;小部件内部的一个。开发人员负责同步两个版本。除此之外,表示和数据的紧密耦合使得编写单元测试变得更加困难。
1.2模型/救援视图
模型/视图逐步提升,以提供使用更通用架构的解决方案。模型/视图消除了标准小部件可能出现的数据一致性问题。模型/视图还可以更轻松地使用同一数据的多个视图,因为一个模型可以传递给许多视图。最重要的区别是模型/视图小部件不会在表格单元格后面存储数据。实际上,它们直接从您的数据中运行。由于视图类不知道数据的结构,因此需要提供一个包装器以使数据符合QAbstractItemModel接口。视图使用此接口来读取和写入数据。实现QAbstractItemModel的类的任何实例都被称为模型。一旦视图收到指向模型的指针,它将读取并显示其内容并成为其编辑器。
1.3模型/视图小部件概述
以下是模型/视图小部件及其相应标准小部件的概述。
Widget | Standard Widget (an item based convenience class) | Model/View View Class (for use with external data) |
---|---|---|
QListWidget | QListView | |
QTableWidget | QTableView | |
QTreeWidget | QTreeView | |
QColumnView shows a tree as a hierarchy of lists | ||
QComboBox can work as both a view class and also as a traditional widget |
1.4在表单和模型之间使用适配器
在表单和模型之间使用适配器可以派上用场。
我们可以直接从表格中编辑存储在表格中的数据,但在文本字段中编辑数据会更加舒适。 没有直接的模型/视图来分隔一个值(QLineEdit
,QCheckBox
…),因此我们需要一个适配器来将表单连接到数据源。
QDataWidgetMapper
是一个很好的解决方案,因为它将表单小部件映射到表行,并且可以非常轻松地为数据库表构建表单。
适配器的另一个例子是QCompleter。 Qt有QCompleter
用于在Qt小部件中提供自动完成,例如QComboBox
,如下所示,QLineEdit
。 QCompleter
使用模型作为其数据源。
2.简单的模型/视图应用程序
如果要开发模型/视图应用程序,应该从哪里开始? 我们建议从一个简单的示例开始并逐步扩展它。 这使得理解架构变得更加容易。 在调用IDE之前,尝试详细了解模型/视图体系结构已被证明对许多开发人员来说不太方便。 从具有演示数据的简单模型/视图应用程序开始,实质上更容易。 试一试! 只需将您自己的数据替换为以下示例中的数据即可。
下面是7个非常简单和独立的应用程序,它们显示了模型/视图编程的不同方面。 源代码可以在examples/widgets/tutorials/modelview目录中找到。
2.1只读表
我们从使用QTableView
显示数据的应用程序开始。 我们稍后会添加编辑功能。
(文件来源:examples/widgets/tutorials/modelview/1_readonly/main.cpp)
// main.cpp
#include <QtWidgets/QApplication>
#include <QtWidgets/QTableView>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableView tableView;
MyModel myModel(0);
tableView.setModel( &myModel );
tableView.show();
return a.exec();
}
我们有通常的main()函数:
这是有趣的部分:我们创建一个MyModel实例并使用tableView.setModel(&myModel);
将它的指针传递给tableView。 tableView将调用它收到的指针的方法来找出两件事:
- 应显示多少行和列。
- 应在每个单元格中打印哪些内容。
该模型需要一些代码来响应这一点。
我们有一个表数据集,所以让我们从QAbstractTableModel
开始,因为它比更通用的QAbstractItemModel
更容易使用。
(文件来源: examples/widgets/tutorials/modelview/1_readonly/mymodel.h)
// mymodel.h
#include <QAbstractTableModel>
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent);
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;
};
QAbstractTableModel
需要实现三个抽象方法。
(文件来源: examples/widgets/tutorials/modelview/1_readonly/mymodel.cpp)
// mymodel.cpp
#include "mymodel.h"
MyModel::MyModel(QObject *parent)
:QAbstractTableModel(parent)
{
}
int MyModel::rowCount(const QModelIndex & /*parent*/) const
{
return 2;
}
int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
return 3;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
行和列的数量由MyModel::rowCount()
和MyModel::columnCount()
提供。当视图必须知道单元格的文本是什么时,它会调用方法MyModel::data()
。使用参数索引指定行和列信息,并将角色设置为Qt::DisplayRole
。其他角色将在下一节中介绍。在我们的示例中,生成了应显示的数据。在实际的应用程序中,MyModel将有一个名为MyData的成员,它作为所有读写操作的目标。
这个小例子展示了模型的被动性。该模型不知道何时使用或需要哪些数据。它只是在每次视图请求时提供数据。
当模型的数据需要更改时会发生什么?视图如何实现数据已更改并需要再次读取?模型必须发出一个信号,指示单元的变化范围。这将在2.3节中说明。
2.2扩展带有角色的只读示例
除了控制视图显示的文本外,模型还控制文本的外观。当我们稍微改变模型时,我们得到以下结果:
实际上,除了data()
方法之外,不需要更改字体,背景颜色,对齐和复选框。 下面是data()
方法,它产生上面显示的结果。 不同的是,这次我们使用参数int角色根据其值返回不同的信息。
(文件来源: 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);
break;
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)
{
QBrush redBackground(Qt::red);
return redBackground;
}
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;
}
}
return QVariant();
}
将从模型请求每个属性,并单独调用data()
方法。 role参数用于让模型知道正在请求哪个属性:
enum Qt::ItemDataRole | Meaning | Type |
---|---|---|
Qt::DisplayRole | text | QString |
Qt::FontRole | font | QFont |
BackgroundRole | brush for the background of the cell | QBrush |
Qt::TextAlignmentRole | text alignment | enum Qt::AlignmentFlag |
Qt::CheckStateRole | suppresses checkboxes with QVariant(), sets checkboxes with Qt::Checked or Qt::Unchecked | enum Qt::ItemDataRole |
请参阅Qt命名空间文档以了解有关Qt::ItemDataRole
枚举功能的更多信息。
现在我们需要确定使用分离模型如何影响应用程序的性能,让我们跟踪视图调用data()
方法的频率。 为了跟踪视图调用模型的频率,我们在data()
方法中放入了一个调试语句,该语句输出到错误输出流。 在我们的小例子中,data()
将被调用42次。 每次将光标悬停在字段上时,将再次调用data()
- 每个单元格调用7次。 这就是为什么在调用data()
并缓存耗时的查找操作时确保数据可用是很重要的。
2.3表格单元格内的时钟
我们仍然有一个只读表,但这次内容每秒都会改变,因为我们显示的是当前时间。
(文件来源: examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)
QVariant MyModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int col = index.column();
if (role == Qt::DisplayRole)
{
if (row == 0 && col == 0)
{
return QTime::currentTime().toString();
}
}
return QVariant();
}
为了让时钟走动起来。 我们需要每隔一段时间告诉视图时间已经改变,需要再次阅读。 我们用计时器做到这一点。 在构造函数中,我们将其间隔设置为1秒并连接其超时信号。
(文件来源: examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)
MyModel::MyModel(QObject *parent)
:QAbstractTableModel(parent)
{
// selectedCell = 0;
timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()) , this, SLOT(timerHit()));
timer->start();
}
这是相应的槽:
(文件来源: examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)
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);
}
我们要求视图通过发出dataChanged()
信号再次读取左上角单元格中的数据。 请注意,我们没有将dataChanged()
信号显式连接到视图。 当我们调用setModel()
时会自动发生这种情况。
2.4设置列和行的标题
可以通过视图方法隐藏标题:tableView->verticalHeader()->hide();
但是,标题内容是通过模型设置的,因此我们重新实现了headerData()
方法:
(文件来源: examples/widgets/tutorials/modelview/4_headers/mymodel.cpp)
QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal) {
switch (section)
{
case 0:
return QString("first");
case 1:
return QString("second");
case 2:
return QString("third");
}
}
}
return QVariant();
}
请注意,方法headerData()
也有一个参数角色,其含义与MyModel::data()
中的含义相同。
2.5最小编辑示例
在此示例中,我们将构建一个应用程序,通过重复输入表格单元格中的值,自动使用内容填充窗口标题。 为了能够轻松访问窗口标题,我们将QTableView
放在QMainWindow
中。
该模型决定编辑功能是否可用。 我们只需要修改模型,以便启用可用的编辑功能。 这是通过重新实现以下虚拟方法来完成的:setData()
和flags()
。
(文件来源: examples/widgets/tutorials/modelview/5_edit/mymodel.h)
// mymodel.h
#include <QAbstractTableModel>
#include <QString>
const int COLS= 3;
const int ROWS= 2;
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent);
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()
信号,可以将修改后的文本传输到窗口标题。
(文件来源: examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)
bool MyModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if (role == Qt::EditRole)
{
//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;
}
每次用户编辑单元格时都会调用setData()
。 index参数告诉我们哪个字段已被编辑,value提供编辑过程的结果。 该角色将始终设置为Qt::EditRole
,因为我们的单元格仅包含文本。 如果存在复选框并且用户权限设置为允许选中该复选框,则还将调用角色设置为Qt::CheckStateRole
。
(文件来源: examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)
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
您可以将上面的示例转换为具有树视图的应用程序。 只需将QTableView
替换为QTreeView
,即可生成读/写树。不需要对模型进行任何更改。树将没有任何层次结构,因为模型本身没有任何层次结构。
QListView
,QTableView
和QTreeView
都使用模型抽象,它是一个合并列表,表和树。 这使得可以使用来自同一模型的几种不同类型的视图类。
这是我们的示例模型到目前为止的样子:
我们想要呈现一棵真正的树。 我们已将上述示例中的数据包装起来以制作模型。 这次我们使用QStandardItemModel
,它是分层数据的容器,也实现了QAbstractItemModel
。 要显示树,必须使用QStandardItem
填充QStandardItemModel
,QStandardItem
能够保存文本,字体,复选框或画笔等项目的所有标准属性。
(文件来源: examples/widgets/tutorials/modelview/6_treeview/mainwindow.cpp)
// modelview.cpp
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include "mainwindow.h"
const int ROWS = 2;
const int COLUMNS = 3;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
treeView = new QTreeView(this);
setCentralWidget(treeView);
standardModel = new QStandardItemModel ;
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();
}
QList<QStandardItem *> MainWindow::prepareRow(const QString &first,const QString &second,const QString &third)
{
QList<QStandardItem *> rowItems;
rowItems << new QStandardItem(first);
rowItems << new QStandardItem(second);
rowItems << new QStandardItem(third);
return rowItems;
}
我们只是实例化一个QStandardItemModel
并将几个QStandardItem
添加到构造函数中。 然后我们可以创建分层数据结构,因为QStandardItem
可以容纳其他QStandardItem
。 节点在视图中折叠和展开。
3.2 使用选择
我们想要访问所选项目的内容,以便将其与层次结构级别一起输出到窗口标题中。
所以让我们创建几个项目:
(文件来源: examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp)
#include <QTreeView>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
treeView = new QTreeView(this);
setCentralWidget(treeView);
standardModel = new QStandardItemModel ;
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, SIGNAL(selectionChanged (const QItemSelection &, const QItemSelection &)),this, SLOT(selectionChangedSlot(const QItemSelection &, const QItemSelection &)));
}
视图管理选择模型,可以使用selectionModel()
方法检索。 我们检索选择模型以便将槽连接到其selectionChanged()
信号。
(文件来源:examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp)
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()
获得与选择对应的模型索引,并通过使用模型索引获取字段的字符串。然后我们只计算项目的hierarchyLevel。顶级项目没有父级,parent()
方法将返回默认构造的QModelIndex()
。这就是我们使用parent()
方法迭代到顶层同时计算迭代期间执行的步骤的原因。
可以检索选择模型(如上所示),但也可以使用QAbstractItemView::setSelectionModel
进行设置。这就是如何使用同步选择的3个视图类,因为只使用了一个选择模型的实例。 要在3个视图之间共享选择模型,请使用selectionModel()
并使用setSelectionModel()
将结果分配给第二个和第三个视图类。
3.3 预定义模型
使用模型/视图的典型方法是包装特定数据以使其可用于视图类。 但是,Qt还为常见的底层数据结构提供了预定义的模型。 如果其中一个可用的数据结构适合您的应用程序,则预定义的模型可能是一个不错的选择。
项 | 描述 |
---|---|
QStringListModel | Stores a list of strings |
QStandardItemModel | Stores arbitrary hierarchical items |
QFileSystemModel QDirModel | Encapsulate the local file system |
QSqlQueryModel | Encapsulate an SQL result set |
QSqlTableModel | Encapsulates an SQL table |
QSqlRelationalTableModel | Encapsulates an SQL table with foreign keys |
QSortFilterProxyModel | Sorts and/or filters another model |
3.4 代理
在目前为止的所有示例中,数据在单元格中显示为文本或复选框,并作为文本或复选框进行编辑。提供这些演示和编辑服务的组件称为委托。 我们才刚刚开始使用委托,因为视图使用默认委托。 但想象一下,我们想要一个不同的编辑器(例如,slider或下拉列表)或者想象我们想要将数据呈现为图形。 让我们来看一个名为Star Delegate的例子,其中星星用于显示评级:
该视图有一个setItemDelegate()
方法,它替换默认委托并安装自定义委托。可以通过创建继承自 QStyledItemDelegate
的类来编写新委托。 为了编写一个显示星号并且没有输入功能的委托,我们只需要覆盖2个方法。
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 Example
- QAbstractItemDelegate Class Reference
- QSqlRelationalDelegate Class Reference
- QStyledItemDelegate Class Reference
- QItemDelegate Class Reference
3.5 使用ModelTest进行调试
模型的被动性为程序员提供了新的挑战。 模型中的不一致可能导致应用程序崩溃。 由于该模型受到来自视图的多次调用的影响,因此很难找出哪个调用已经崩溃了应用程序以及哪个操作引入了该问题。
Qt Labs提供名为ModelTest的软件,可在编程运行时检查模型。 每次更改模型时,ModelTest都会扫描模型并使用断言报告错误。 这对于树模型尤其重要,因为它们的层次性质为微妙的不一致留下了许多可能性。
与视图类不同,ModelTest使用超出范围的索引来测试模型。 这意味着您的应用程序可能会因为没有它而在运行时崩溃。 因此,在使用ModelTest时,还需要处理超出范围的所有索引。
4.附加信息源
4.1书籍
模型/视图编程在Qt的文档中有相当广泛的介绍,但也包含在几本好书中。
- 使用Qt 4/Jasmin Blanchette进行C ++ GUI编程,Mark Summerfield,Prentice Hall,第2版,ISBN 0-13-235416-0。 也有德语版本:C ++ GUI Programmierung mit Qt 4:DieoffizielleEinführung,Addison-Wesley,ISBN 3-827327-29-6
- Qt4书,构建Qt应用的艺术/Daniel Molkentin,开源出版社,ISBN 1-59327-147-6。 翻译自Qt 4,Einführungindie Applikationsentwicklung,Open Source Press,ISBN 3-937514-12-0。
- Qt Development的基础/Johan Thelin,Apress,ISBN 1-59059-831-8。
- 高级Qt编程/Mark Summerfield,Prentice Hall,ISBN 0-321-63590-6。 本书涵盖超过150页的模型/视图编程。
以下列表概述了上面列出的前三本书中包含的示例程序。 其中一些是开发类似应用程序的非常好的模板。
Example name | View class used | Model used | Aspects covered | |
---|---|---|---|---|
Team Leaders | QListview | QStringListModel | Book 1, Chapter 10, Figure 10.6 | |
Directory Viewer | QTreeView | QDirModel | Book 1, Chapter 10, Figure 10.7 | |
Color Names | QListView | QSortFilterProxyModel applied to QStringListModel | Book 1, Chapter 10, Figure 10.8 | |
Currencies | QTableView | custom model based on QAbstractTableModel | Read only | Book 1, Chapter 10, Figure 10.10 |
Cities | QTableView | Custom model based on QAbstractTableModel | Read / write | Book 1, Chapter 10, Figure 10.12 |
Boolean Parser | QTreeView | Custom model based on QAbstractItemModel | Read only | Book 1, Chapter 10, Figure 10.14 |
Track Editor | QTableWidget | Custom delegate providing a custom editor | Book 1, Chapter 10, Figure 10.15 | |
Four directory views | QListView QTableView QTreeView | QDirModel | Demonstrates the use of multiple views | Book2, Chapter 8.2 |
Address Book | QListView QTableView QTreeView | Custom model based on QAbstractTableModel | Read / write | Book2, Chapter 8.4 |
Address Book with sorting | QSortfilterProxyModel | Introducing sort and filter capabilities | Book2, Chapter 8.5 | |
Address Book with checkboxes | Introducing checkboxes in model/view | Book2, Chapter 8.6 | ||
Address Book with transposed grid | Custom proxy Model based on QAbstractProxyModel | Introducing a custom model | Book2, Chapter 8.7 | |
Address Book with drag and drop | Introducing drag and drop support | Book2, Chapter 8.8 | ||
Address Book with custom editor | Introducing custom delegates | Book2, Chapter 8.9 | ||
Views | QListView QTableView QTreeView | QStandardItemModel | Read only | Book 3, Chapter 5, figure 5-3 |
Bardelegate | QTableView | Custom delegate for presentation based on QAbstractItemDelegate | Book 3, Chapter 5, figure 5-5 | |
Editdelegate | QTableView | Custom delegate for editing based on QAbstractItemDelegate | Book 3, Chapter 5, figure 5-6 | |
Singleitemview | Custom view based on QAbstractItemView | Custom view | Book 3, Chapter 5, figure 5-7 | |
listmodel | QTableView | Custom Model based on QAbstractTableModel | Read only | Book 3, Chapter 5, Figure 5-8 |
treemodel | QTreeView | Custom Model based on QAbstractItemModel | Read only | Book 3, Chapter 5, Figure 5-10 |
edit integers | QListView | Custom Model based on QAbstractListModel | Read / write | Book 3, Chapter 5, Listing 5-37, Figure 5-11 |
sorting | QTableView | QSortFilterProxyModel applied to QStringListModel | Demonstrates sorting | Book 3, Chapter 5, Figure 5-12 |
4.2 Qt文档
Qt 5.0附带19个模型/视图示例。 可以在“项目视图示例”页面上找到这些示例。
Example name | View class used | Model used | Aspects covered |
---|---|---|---|
Address Book | QTableView | QAbstractTableModel QSortFilterProxyModel | Usage of QSortFilterProxyModel to generate different subsets from one data pool |
Basic Sort/Filter Model | QTreeView | QStandardItemModel QSortFilterProxyModel | |
Chart | Custom view | QStandardItemModel | Designing custom views that cooperate with selection models |
Color Editor Factory | QTableWidget | Enhancing the standard delegate with a new custom editor to choose colours | |
Combo Widget Mapper | QDataWidgetMapper to map QLineEdit, QTextEdit and QComboBox | QStandardItemModel | Shows how a QComboBox can serve as a view class |
Custom Sort/Filter Model | QTreeView | QStandardItemModel QSortFilterProxyModel | Subclass QSortFilterProxyModel for advanced sorting and filtering |
Dir View | QTreeView | QFileSystemModel | Very small example to demonstrate how to assign a model to a view |
Editable Tree Model | QTreeView | Custom tree model | Comprehensive example for working with trees, demonstrates editing cells and tree structure with an underlying custom model |
Fetch More | QListView | Custom list model | Dynamically changing model |
Frozen Column | QTableView | QStandardItemModel | |
Interview | Multiple | Custom item model | Multiple views |
Pixelator | QTableView | Custom table model | Implementation of a custom delegate |
Puzzle | QListView | Custom list model | Model/view with drag and drop |
Simple DOM Model | QTreeView | Custom tree model | Read only example for a custom tree model |
Simple Tree Model | QTreeView | Custom tree model | Read only example for a custom tree model |
Simple Widget Mapper | QDataWidgetMapper to map QLineEdit, QTextEdit and QSpinBox | QStandardItemModel | Basic QDataWidgetMapper usage |
Spin Box Delegate | QTableView | QStandardItemModel | Custom delegate that uses a spin box as a cell editor |
Spreadsheet | QTableView | Custom delegates | |
Star Delegate | QTableWidget | Comprehensive custom delegate example. |
还提供了模型/视图技术的参考文档。