qt Model/View Programming LTS 收集
pyqt-model-view-framework-overview.pdf
https://doc.qt.io/qt-5/model-view-programming.html
Qt模型/视图(Model/View)指南
http://www.qingpingshan.com/rjbc/cyy/249134.html
中文
http://s0doc0qt0io.icopy.site/qt-5.12/model-view-programming.html
QT Basic 014 Model/View programming (模型、视图编程)
https://blog.csdn.net/yellow_hill/article/details/76691007
QT 模型视图编程概念
https://blog.csdn.net/chichixiaoqiang/article/details/6655894
Qt的模型/视图编程方法(model/view programming)
https://blog.csdn.net/doctorsc/article/details/6227550
Qt5.5文档翻译—Model/View programming
https://blog.csdn.net/u012160436/article/details/50083197
https://doc.qt.io/qt-5/model-view-programming.html
SmallTalk 语言使用的是 MVC(Model— View — Controlller)
QT语言 使用的是 MV(Model — View) 模式
QT语言的是 View 部件 集成了 Controller的功能
这样数据显示 和数据存储 就分离了.
https://doc.qt.io/qt-5/qtwidgets-index.html
Qt5气泡式聊天框——QListWidget+QPainter实现
https://blog.csdn.net/u014597198/article/details/81505832
pyqt 官方源码包中的例子
例子
Related Examples
Dir View
Spin Box Delegate
Pixelator
Simple Tree Model
Chart
See also Item Views Puzzle Example
翻译
The model/view architecture provides classes that manage the way data is presented to the user. Data-driven applications which use lists and tables are structured to separate the data and view using models, views, and delegates.
model/view 架构 提供一些类, 这些类负责管理 众多数据 是如何呈现给用户,如何向用户展示.
一个应用软件 包含list 列表和 tables 表格, 可以 使用 models ,views 和 delegates 来使 data数据 存储和 view 数据显示分离.
Model/View 编程简介
Qt contains a set of item view classes that use a model/view architecture to manage the relationship between data and the way it is presented to the user. The separation of functionality introduced by this architecture gives developers greater flexibility to customize the presentation of items, and provides a standard model interface to allow a wide range of data sources to be used with existing item views.
Qt 包含 一系列 item view 类,这些类使用 model/view 架构来管理 数据存放 和 数据展示 之间的关系.
这个架构 是使 数据存储 和 数据显示 分离的.
这样开发者可以自由的定制 items的显示方式
这个架构 同时提供了一个标准的 model 接口.
通过这个model接口,同一个 item views 展示的数据,可以有很多的来源,比如来自数据库.来自文本 等
就是 它可以兼容很多数据源,相当于一个发动机 即可以使用 柴油 ,有可以使用汽油,还可以使用食用油.
In this document, we give a brief introduction to the model/view paradigm, outline the concepts involved, and describe the architecture of the item view system. Each of the components in the architecture is explained, and examples are given that show how to use the classes provided.
这个文档中,我们给 model/view 架构一个简单的介绍,概述提到的内容,描述 item view 系统的架构.
体系结构中的每个部件,都会解释,还会提供例子 怎么使用这些类.
The model/view 架构
MVC 设计模式如下:
The Model is the application object,
the View is its screen presentation,
the Controller defines the way the user interface reacts to user input (用户互动,对用户输入行为的响应)
如果把MVC 设计模式的the view objects 和 the controller objects 合并在一起,那么就是model/view 架构.
model/view 架构 ,仍然 是 数据保存 和 数据显示分离的.
这个分离 使同样一个数据,可以通过几种不同的方式的显示.
实现新的显示方式,也不用修改底下的数据结构
为了实现 灵活的用户输入 响应,我们引入了delegrate的概念.
To allow flexible handling of user input, we introduce the concept of the delegate.
通过delegate 用户 可以 自定义 items中的数据 的渲染和编辑 方式
The advantage of having a delegate in this framework is that it allows the way items of data are rendered and edited to be customized.
也就是 items中的数据在 显示之前 会先经过 delegate 过滤一下,处理一下,美化一下,然后再呈现在 用户面前.
就像 手机上的美图秀秀等美颜软件, 前置摄像头 拍摄的图像是 data ,data 经过 美图秀秀 ,给美颜delegate一下,然后展示给用户一个美颜之后的图像view.用户 可以 设置 美颜的选项,变白,长腿 ,瘦脸等. data 不变,但是 展示的view变了.
The model communicates with a source of data, providing an interface for the other components in the architecture.
The model 和 数据源通信,给架构中的其他部件提供接口
The nature of the communication depends on the type of data source, and the way the model is implemented.
通信的方法 和 数据源 和 model的实现方式有关
The view obtains model indexes from the model;
view 从 model 中 获得 model indexes
these are references to items of data.
这些 model indexes 指向 items of data .
By supplying model indexes to the model, the view can retrieve items of data from the data source.
通过给 the model提供 model indexes,the view 可以 从 数据源 获得 items of data.
In standard views, a delegate renders the items of data.
在标准的 views中, delegate 负责渲染显示 items of data.
When an item is edited, the delegate communicates with the model directly using model indexes.
当 item 被编辑的时候,delegate 通过model indexes 直接和model通信
显示控件比如 tables表格的小格子,叫做item,item中的数据,叫做item data ,
item data 和 model 后面的数据,是通过 model indexes,作为桥梁通信的, model indexes 是一个指针或者映射关系
2
the model/view 类分为3组: models, views, and delegates.
每组定义成了一个抽象类 提供公共接口 和一些默认的功能实现
抽象类可以被子类化,来提供完整的功能
Models, views, and delegates 使用 signals and slots通信
Signals from the model inform the view about changes to the data held by the data source.
从model 来的信号,告诉view ,数据源的数据改变了
Signals from the view provide information about the user’s interaction with the items being displayed.
view会发出信号, 信号表示 用户的和 items之间的 互动 显示了,重绘了,渲染了
Signals from the delegate are used during editing to tell the model and view about the state of the editor.
在编辑的过程中 delegate 发出的信号,model 和view 会接收到,得知当前爱你的编辑状态
Models
All item models are 基于QAbstractItemModel 类.
views and delegates 用这个类定义的接口 to access data.
The data itself does not have to be stored in the model;
it can be held in a data structure or 仓库provided by a separate class, a file, a database, or some other application component.
The basic concepts surrounding models are presented in the section on Model Classes.
QAbstractItemModel 提供接口 to data ,that is 足够灵活 to handle views
that represent data in the form of tables, lists, and trees.
However, 当实现 new models for list and table-like data structures,
the QAbstractListModel and QAbstractTableModel 类 are better starting points
因为 他们 提供 合适的默认 实现 of common functions.
这3个函数 都是被 继承子类化 to provide models that 支持 特殊 类型的 lists and tables.
The process of subclassing models is discussed in the section on Creating New Models.
Qt provides some ready-made models that can be used to handle items of data:
QStringListModel is used to store a simple list of QString items.
QStandardItemModel manages more complex tree structures of items, each of which can contain arbitrary data.
QFileSystemModel provides information about files and directories in the local filing system.
QSqlQueryModel, QSqlTableModel, and QSqlRelationalTableModel are used to access databases using model/view conventions.
如果这些 standard models 不能满足你的需求, you can subclass QAbstractItemModel, QAbstractListModel, or QAbstractTableModel to create your own custom models.
Views
Complete implementations are provided for different kinds of views:
QListView displays a list of items,
QTableView displays data from a model in a table, and
QTreeView shows model items of data in a hierarchical list.
Each of these classes is based on the QAbstractItemView abstract base class.
Although these classes are ready-to-use implementations, they can also be subclassed to provide customized views.
The available views are examined in the section on View Classes.
Delegates
QAbstractItemDelegate is the 抽象基类 for delegates in the model/view framework.
The default delegate implementation is provided by QStyledItemDelegate,
and this is used as the default delegate by Qt’s standard views.
However, QStyledItemDelegate and QItemDelegate are independent alternatives to painting and providing editors for items in views.
The difference between them is that QStyledItemDelegate uses the current style to paint its items.
We therefore recommend using QStyledItemDelegate as the base class when implementing custom delegates or when working with Qt style sheets.
Delegates are described in the section on Delegate Classes.
Sorting
There are two ways of approaching sorting in the model/view architecture;
which approach to choose depends on your underlying model.
Sorting方法1
If your model is sortable, i.e, if it reimplements the QAbstractItemModel::sort() function, both QTableView and QTreeView provide an API that allows you to sort your model data programmatically.
通过编程的方式排序 重写 sort()方法
In addition, you can enable interactive sorting (i.e. allowing the users to sort the data by clicking the view’s headers),
by connecting the QHeaderView::sortIndicatorChanged() signal to the QTableView::sortByColumn() slot or the QTreeView::sortByColumn() slot, respectively.
Sorting方法2
The alternative approach, if your model does not have the required interface or if you want to use a list view to present your data, is to use a proxy model to transform the structure of your model before presenting the data in the view. This is covered in detail in the section on Proxy Models.
Convenience classes
A number of convenience classes are derived from the standard view classes for the benefit of applications that rely on
很多包装好的类,是从standard view classes中派生出来的. 在基于Qt’s item-baseditem view and table classes.的应用中,很好用
不推荐子类化他们
这些类 包括 QListWidget, QTreeWidget, and QTableWidget.
这些类 没有the view classes灵活,不能使用 arbitrary models.
我们推荐你使用 a model/view approach to handling data in item views 除非 you strongly need an item-based set of classes.
如果你希望 take advantage of the features provided by the model/view approach while still using an item-based interface,
可以考虑使用 view classes, such as QListView, QTableView, and QTreeView with QStandardItemModel.
Using Models and Views
The following sections explain how to use the model/view pattern in Qt. Each section includes an example and is followed by a section showing how to create new components.
Two models included in Qt
Qt提供的两个 standard models 是 QStandardItemModel 和 QFileSystemModel.
QStandardItemModel 是一个 multi-purpose model that can be used to represent various different data structures needed by list, table, and tree views.
This model also holds the items of data.
QFileSystemModel is a model that maintains information about the contents of a directory.
As a result, it does not hold any items of data itself, but simply represents files and directories on the local filing system.
QFileSystemModel 提供了一个 ready-to-use model to experiment with, and can be easily configured to use existing data. Using this model, we can show how to set up a model for use with ready-made views, and explore how to manipulate data using model indexes.
Using views with an existing model
The QListView and QTreeView classes are the most suitable views to use with QFileSystemModel.
The example presented below displays the contents of a directory in a tree view next to the same information in a list view.
The views share the user’s selection so that the selected items are highlighted in both views.
We set up a QFileSystemModel so that it is ready for use, and create some views to display the contents of a directory. This shows the simplest way to use a model. The construction and use of the model is performed from within a single main() function:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSplitter *splitter = new QSplitter;
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());
The model is set up to use data from a certain file system.
The call to setRootPath() tells the model which drive on the file system to expose to the views.
要通过 QFileSystemModel 获得本机的文件系统,需要用 setRootPath() 函数为 QFileSystemModel 设置一个根目录
静态函数 QDir::currentPath() 获取应用程序的当前路径。
QFileSystemModel 和视图组件 QTreeView 结合使用,可以用目录树的形式显示本机上的文件系统,如同 Widnows 的资源管理器一样。使用 QFileSystemModel 提供的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获得文件的详细信息。
We create two views so that we can examine the items held in the model in two different ways:
QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);
tree->setRootIndex(model->index(QDir::currentPath()));
QListView *list = new QListView(splitter);
list->setModel(model);
list->setRootIndex(model->index(QDir::currentPath()));
The views are constructed in the same way as other widgets.
Setting up a view to display the items in the model is simply a matter of calling its setModel() function
with the directory model as the argument.
We filter the data supplied by the model by calling the setRootIndex() function on each view,
passing a suitable model index from the file system model for the current directory.
The index() function used in this case is unique to QFileSystemModel;
we supply it with a directory and it returns a model index.
Model indexes are discussed in Model Classes.
The rest of the function just displays the views within a splitter widget, and runs the application’s event loop:
splitter->setWindowTitle("Two views onto the same file system model");
splitter->show();
return app.exec();
}
In the above example, we neglected to mention how to handle selections of items. This subject is covered in more detail in the section about Handling Selections in Item Views.
Model Classes
Before 检查 how selections are handled, you may find it useful to examine the concepts used in the model/view framework.
Basic concepts
在 model/view 架构中, the model 提供了一个 standard interface that views and delegates use to access data.
In Qt, the standard interface is defined by the QAbstractItemModel class.
No matter how the items of data are stored in any underlying data structure,
all subclasses of QAbstractItemModel represent the data as a hierarchical structure containing tables of items.
Views use this convention to access items of data in the model, but they are not restricted in the way that they present this information to the user.
Models also notify any attached views about changes to data through the signals and slots mechanism.
数据更新了 Models会 告诉 any attached views.
This section describes some basic concepts that are central to the way items of data are accessed by other components via a model class. More advanced concepts are discussed in later sections.
Model indexes
To ensure that the representation of the data is kept separate from the way it is accessed,
the concept of a model index is introduced.
Each piece of information that can be obtained via a model is represented by a model index.
Views and delegates use these indexes to request items of data to display.
As a result, only the model needs to know how to obtain data,
and the type of data managed by the model can be defined fairly generally.
Model indexes contain a pointer to the model that created them,
and this prevents confusion 混乱 when working with more than one model.
QAbstractItemModel *model = index.model();
Model indexes provide temporary references to pieces of information, and can be used to retrieve or modify data via the model. Since models may reorganize their internal structures from time to time, model indexes may become invalid, and should not be stored. If a long-term reference to a piece of information is required, a persistent model index must be created. This provides a reference to the information that the model keeps up-to-date. Temporary model indexes are provided by the QModelIndex class, and persistent model indexes are provided by the QPersistentModelIndex class.
To obtain a model index that corresponds to an item of data, three properties must be specified to the model: a row number, a column number, and the model index of a parent item. The following sections describe and explain these properties in detail.
Rows and columns
In its most basic form, a model can be accessed as a simple table in which items are located by their row and column numbers. This does not mean that the underlying pieces of data are stored in an array structure; the use of row and column numbers is only a convention to allow components to communicate with each other. We can retrieve information about any given item by specifying its row and column numbers to the model, and we receive an index that represents the item:
QModelIndex index = model->index(row, column, ...);
Models that provide interfaces to simple, single level data structures like lists and tables do not need any other information to be provided but, as the above code indicates, we need to supply more information when obtaining a model index.
Rows and columns
The diagram shows a representation of a basic table model in which each item is located by a pair of row and column numbers. We obtain a model index that refers to an item of data by passing the relevant row and column numbers to the model.
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
Top level items in a model are always referenced by specifying QModelIndex() as their parent item. This is discussed in the next section.
Parents of items
The table-like interface to item data provided by models is ideal when using data in a table or list view; the row and column number system maps exactly to the way the views display items. However, structures such as tree views require the model to expose a more flexible interface to the items within. As a result, each item can also be the parent of another table of items, in much the same way that a top-level item in a tree view can contain another list of items.
When requesting an index for a model item, we must provide some information about the item’s parent. Outside the model, the only way to refer to an item is through a model index, so a parent model index must also be given:
QModelIndex index = model->index(row, column, parent);
Parents, rows, and columns
The diagram shows a representation of a tree model in which each item is referred to by a parent, a row number, and a column number.
Items “A” and “C” are represented as top-level siblings in the model:
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());
Item “A” has a number of children. A model index for item “B” is obtained with the following code:
QModelIndex indexB = model->index(1, 0, indexA);
Item roles
Items in a model can perform various roles for other components, allowing different kinds of data to be supplied for different situations. For example, Qt::DisplayRole is used to access a string that can be displayed as text in a view. Typically, items contain data for a number of different roles, and the standard roles are defined by Qt::ItemDataRole.
We can ask the model for the item’s data by passing it the model index corresponding to the item, and by specifying a role to obtain the type of data we want:
QVariant value = model->data(index, role);
Item roles
The role indicates to the model which type of data is being referred to. Views can display the roles in different ways, so it is important to supply appropriate information for each role.
The Creating New Models section covers some specific uses of roles in more detail.
Most common uses for item data are covered by the standard roles defined in Qt::ItemDataRole. By supplying appropriate item data for each role, models can provide hints to views and delegates about how items should be presented to the user. Different kinds of views have the freedom to interpret or ignore this information as required. It is also possible to define additional roles for application-specific purposes.
Summary
Model indexes give views and delegates information about the location of items provided by models in a way that is independent of any underlying data structures.
Items are referred to by their row and column numbers, and by the model index of their parent items.
Model indexes are constructed by models at the request of other components, such as views and delegates.
If a valid model index is specified for the parent item when an index is requested using index(), the index returned refers to an item beneath that parent item in the model. The index obtained refers to a child of that item.
If an invalid model index is specified for the parent item when an index is requested using index(), the index returned refers to a top-level item in the model.
The role distinguishes between the different kinds of data associated with an item.
View Classes
在 model/view 架构中, the view obtains items of data from the model and presents them to the user.
The way that the data is presented need not resemble the representation of the data provided by the model,
and may be completely different from the underlying data structure used to store items of data.
The separation of content and presentation is achieved by
the use of a standard model interface provided by QAbstractItemModel,
a standard view interface provided by QAbstractItemView,
and the use of model indexes that represent items of data in a general way.
Views typically manage the overall layout of the data obtained from models.
They may render individual items of data themselves, or use delegates to handle both rendering and editing features.
As well as presenting data, views handle navigation 导航 between items, and some aspects of item selection.
The views also implement basic user interface features, such as context menus and drag and drop.
A view can provide default editing facilities 设备 for items, or it may work with a delegate to provide a custom editor.
A view can be constructed without a model, but a model must be provided before it can display useful information.
Views keep track of the items that the user has selected through the use of selections which can be maintained separately for each view, or shared between multiple views.
Some views, such as QTableView and QTreeView, display headers as well as items.
These are also implemented by a view class, QHeaderView.
Headers usually access the same model as the view that contains them.
They retrieve data from the model using the QAbstractItemModel::headerData() function, and usually display header information in the form of a label.
New headers can be subclassed from the QHeaderView class to provide more specialized labels for views.
Using an existing view
Qt provides three ready-to-use view classes that present data from models in ways that are familiar to most users.
QListView can display items from a model as a simple list, or in the form of a classic icon view.
QTreeView displays items from a model as a hierarchy of lists, allowing deeply nested structures to be represented in a compact way. QTableView presents items from a model in the form of a table, much like the layout of a spreadsheet application.
The default behavior of the standard views shown above should be sufficient满足 for most applications.
They provide basic editing facilities, and can be customized to suit the needs of more specialized user interfaces.
Using a model
We take the string list model that we created as an example model, set it up with some data, and construct a view to display the contents of the model. This can all be performed within a single function:
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);
Note that the StringListModel is declared as a QAbstractItemModel. This allows us to use the abstract interface to the model, and ensures that the code still works, even if we replace the string list model with a different model.
The list view provided by QListView is sufficient for presenting the items in the string list model. We construct the view, and set up the model using the following lines of code:
QListView *view = new QListView;
view->setModel(model);
The view is shown in the normal way:
view->show();
return app.exec();
}
The view renders the contents of a model, accessing data via the model’s interface. When the user tries to edit an item, the view uses a default delegate to provide an editor widget.
Using multiple views of a model
Providing multiple views onto the same model is simply a matter of setting the same model for each view. In the following code we create two table views, each using the same simple table model which we have created for this example:
QTableView *firstTableView = new QTableView;
QTableView *secondTableView = new QTableView;
firstTableView->setModel(model);
secondTableView->setModel(model);
The use of signals and slots in the model/view architecture means that changes to the model can be propagated to all the attached views, ensuring that we can always access the same data regardless of the view being used.
The above image shows two different views onto the same model, each containing a number of selected items. Although the data from the model is shown consistently across view, each view maintains its own internal selection model. This can be useful in certain situations but, for many applications, a shared selection model is desirable.
Handling selections of items
The mechanism for handling selections of items within views is provided by the QItemSelectionModel class.
All of the standard views construct their own selection models by default, and interact with them in the normal way. The selection model being used by a view can be obtained through the selectionModel() function, and a replacement selection model can be specified with setSelectionModel(). The ability to control the selection model used by a view is useful when we want to provide multiple consistent views onto the same model data.
Generally, unless you are subclassing a model or view, you don’t need to manipulate the contents of selections directly. However, the interface to the selection model can be accessed, if required, and this is explored in Handling Selections in Item Views.
Sharing selections among views
Although it is convenient that the view classes provide their own selection models by default,
when we use more than one view onto the same model it is often desirable that both the model’s data and the user’s selection are shown consistently in all views.
Since the view classes allow their internal selection models to be replaced,
we can achieve a unified selection between views with the following line:
secondTableView->setSelectionModel(firstTableView->selectionModel());
The second view is given the selection model for the first view.
Both views now operate on the same selection model, keeping both the data and the selected items synchronized.
In the example shown above, two views of the same type were used to display the same model’s data.
However, if two different types of view were used, the selected items may be represented very differently in each view;
for example, a contiguous selection in a table view can be represented as a fragmented set of highlighted items in a tree view.
Delegate Classes
Concepts
Unlike the Model-View-Controller pattern,
the model/view design does not include a completely separate component for managing interaction with the user.
Generally, the view is responsible for the presentation of model data to the user, and for processing user input.
To allow some flexibility in the way this input is obtained, the interaction is performed by delegates.
These components provide input capabilities and are also responsible for rendering individual items in some views.
The standard interface for controlling delegates is defined in the QAbstractItemDelegate class.
Delegates are expected to be able to render their contents themselves by implementing the paint() and sizeHint() functions.
However, simple widget-based delegates can subclass QStyledItemDelegate instead of QAbstractItemDelegate, and take advantage of the default implementations of these functions.
Editors for delegates can be implemented either by using widgets to manage the editing process or by handling events directly.
The first approach is covered later in this section, and it is also shown in the Spin Box Delegate example.
The Pixelator example shows how to create a custom delegate that performs specialized rendering for a table view.
Using an existing delegate
The standard views provided with Qt use instances of QStyledItemDelegate to provide editing facilities.
This default implementation of the delegate interface renders items in the usual style for each of the standard views: QListView, QTableView, and QTreeView.
All the standard roles are handled by the default delegate used by the standard views.
The way these are interpreted is described in the QStyledItemDelegate documentation.
The delegate used by a view is returned by the itemDelegate() function.
The setItemDelegate() function allows you to install a custom delegate for a standard view,
and it is necessary to use this function when setting the delegate for a custom view.
A simple delegate
The delegate implemented here uses a QSpinBox to provide editing facilities, and is mainly intended for use with models that display integers. Although we set up a custom integer-based table model for this purpose, we could easily have used QStandardItemModel instead, since the custom delegate controls data entry. We construct a table view to display the contents of the model, and this will use the custom delegate for editing.
We subclass the delegate from QStyledItemDelegate because we do not want to write custom display functions.
However, we must still provide functions to manage the editor widget:
class SpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
SpinBoxDelegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
Note that no editor widgets are set up when the delegate is constructed. We only construct an editor widget when it is needed.
Providing an editor
In this example, when the table view needs to provide an editor,
it asks the delegate to provide an editor widget that is appropriate for the item being modified.
The createEditor() function is supplied with everything that the delegate needs to be able to set up a suitable widget:
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
Note that we do not need to keep a pointer to the editor widget because the view takes responsibility for destroying it when it is no longer needed.
We install the delegate’s default event filter on the editor to ensure that it provides the standard editing shortcuts that users expect. Additional shortcuts can be added to the editor to allow more sophisticated behavior; these are discussed in the section on Editing Hints.
The view ensures that the editor’s data and geometry are set correctly by calling functions that we define later for these purposes. We can create different editors depending on the model index supplied by the view. For example, if we have a column of integers and a column of strings we could return either a QSpinBox or a QLineEdit, depending on which column is being edited.
The delegate must provide a function to copy model data into the editor. In this example, we read the data stored in the display role, and set the value in the spin box accordingly.
void SpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
In this example,
we know that the editor widget is a spin box,
but we could have provided different editors for different types of data in the model,
in which case we would need to cast the widget to the appropriate type before accessing its member functions.
Submitting data to the model
When the user has finished editing the value in the spin box, the view asks the delegate to store the edited value in the model by calling the setModelData() function.
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
Since the view manages the editor widgets for the delegate,
we only need to update the model with the contents of the editor supplied.
In this case, we ensure that the spin box is up-to-date, and update the model with the value it contains using the index specified.
The standard QStyledItemDelegate class informs the view when it has finished editing by emitting the closeEditor() signal.
The view ensures that the editor widget is closed and destroyed. In this example, we only provide simple editing facilities, so we need never emit this signal.
All the operations on data are performed through the interface provided by QAbstractItemModel. This makes the delegate mostly independent from the type of data it manipulates, but some assumptions must be made in order to use certain types of editor widgets. In this example, we have assumed that the model always contains integer values, but we can still use this delegate with different kinds of models because QVariant provides sensible default values for unexpected data.
Updating the editor’s geometry
It is the responsibility of the delegate to manage the editor’s geometry.
The geometry must be set when the editor is created, and when the item’s size or position in the view is changed.
Fortunately, the view provides all the necessary geometry information inside a view option object.
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
In this case, we just use the geometry information provided by the view option in the item rectangle.
A delegate that renders items with several elements would not use the item rectangle directly.
It would position the editor in relation to the other elements in the item.
Editing hints
After editing, delegates should provide hints to the other components about the result of the editing process, and provide hints that will assist any subsequent editing operations. This is achieved by emitting the closeEditor() signal with a suitable hint. This is taken care of by the default QStyledItemDelegate event filter which we installed on the spin box when it was constructed.
The behavior of the spin box could be adjusted to make it more user friendly. In the default event filter supplied by QStyledItemDelegate, if the user hits Return to confirm their choice in the spin box, the delegate commits the value to the model and closes the spin box. We can change this behavior by installing our own event filter on the spin box, and provide editing hints that suit our needs; for example, we might emit closeEditor() with the EditNextItem hint to automatically start editing the next item in the view.
Another approach that does not require the use of an event filter is to provide our own editor widget, perhaps subclassing QSpinBox for convenience. This alternative approach would give us more control over how the editor widget behaves at the cost of writing additional code. It is usually easier to install an event filter in the delegate if you need to customize the behavior of a standard Qt editor widget.
Delegates do not have to emit these hints, but those that do not will be less integrated into applications, and will be less usable than those that emit hints to support common editing actions.
Handling Selections in Item Views
先不看
Creating New Models
The separation of functionality between the model/view components allows models to be created that can take advantage of existing views. This approach lets us present data from a variety of sources using standard graphical user interface components, such as QListView, QTableView, and QTreeView.
The QAbstractItemModel class provides an interface that is flexible enough to support data sources that arrange information in hierarchical structures, allowing for the possibility that data will be inserted, removed, modified, or sorted in some way. It also provides support for drag and drop operations.
The QAbstractListModel and QAbstractTableModel classes provide support for interfaces to simpler non-hierarchical data structures, and are easier to use as a starting point for simple list and table models.
In this section, we create a simple read-only model to explore the basic principles of the model/view architecture. Later in this section, we adapt this simple model so that items can be modified by the user.
For an example of a more complex model, see the Simple Tree Model example.
The requirements of QAbstractItemModel subclasses is described in more detail in the Model Subclassing Reference document.
Designing a model
When creating a new model for an existing data structure, it is important to consider which type of model should be used to provide an interface onto the data. If the data structure can be represented as a list or table of items, you can subclass QAbstractListModel or QAbstractTableModel since these classes provide suitable default implementations for many functions.
However, if the underlying data structure can only be represented by a hierarchical tree structure, it is necessary to subclass QAbstractItemModel. This approach is taken in the Simple Tree Model example.
In this section, we implement a simple model based on a list of strings, so the QAbstractListModel provides an ideal base class on which to build.
Whatever form the underlying data structure takes,
it is usually a good idea to supplement the standard QAbstractItemModel API in specialized models with one that allows more natural access to the underlying data structure.
This makes it easier to populate the model with data,
yet still enables other general model/view components to interact with it using the standard API.
The model described below provides a custom constructor for just this purpose.
A read-only example model
The model implemented here is a simple, non-hierarchical, read-only data model based on the standard QStringListModel class.
It has a QStringList as its internal data source, and implements only what is needed to make a functioning model.
To make the implementation easier, we subclass QAbstractListModel because it defines sensible default behavior for list models, and it exposes a simpler interface than the QAbstractItemModel class.
When implementing a model it is important to remember that QAbstractItemModel does not store any data itself, it merely presents an interface that the views use to access the data. For a minimal read-only model it is only necessary to implement a few functions as there are default implementations for most of the interface. The class declaration is as follows:
class StringListModel : public QAbstractListModel
{
Q_OBJECT
public:
StringListModel(const QStringList &strings, QObject *parent = nullptr)
: QAbstractListModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
private:
QStringList stringList;
};
Apart from the model’s constructor, we only need to implement two functions: rowCount() returns the number of rows in the model and data() returns an item of data corresponding to a specified model index.
Well behaved models also implement headerData() to give tree and table views something to display in their headers.
Note that this is a non-hierarchical model, so we don’t have to worry about the parent-child relationships. If our model was hierarchical, we would also have to implement the index() and parent() functions.
The list of strings is stored internally in the stringList private member variable.
Dimensions of the model
We want the number of rows in the model to be the same as the number of strings in the string list.
We implement the rowCount() function with this in mind:
int StringListModel::rowCount(const QModelIndex &parent) const
{
return stringList.count();
}
Since the model is non-hierarchical,
we can safely ignore the model index corresponding to the parent item.
By default, models derived from QAbstractListModel only contain one column,
so we do not need to reimplement the columnCount() function.
Model headers and data
For items in the view, we want to return the strings in the string list.
The data() function is responsible for returning the item of data that corresponds to the index argument:
QVariant StringListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= stringList.size())
return QVariant();
if (role == Qt::DisplayRole)
return stringList.at(index.row());
else
return QVariant();
}
We only return a valid QVariant if the model index supplied is valid, the row number is within the range of items in the string list, and the requested role is one that we support.
Some views, such as QTreeView and QTableView, are able to display headers along with the item data. If our model is displayed in a view with headers, we want the headers to show the row and column numbers. We can provide information about the headers by subclassing the headerData() function:
QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return QStringLiteral("Column %1").arg(section);
else
return QStringLiteral("Row %1").arg(section);
}
Again, we return a valid QVariant only if the role is one that we support.
The orientation of the header is also taken into account when deciding the exact data to return.
Not all views display headers with the item data, and those that do may be configured to hide them.
Nonetheless, it is recommended that you implement the headerData() function to provide relevant information about the data provided by the model.
An item can have several roles, giving out different data depending on the role specified.
The items in our model only have one role, DisplayRole, so we return the data for items irrespective of the role specified.
However, we could reuse the data we provide for the DisplayRole in other roles,
such as the ToolTipRole that views can use to display information about items in a tooltip.
An editable model
The read-only model shows how simple choices could be presented to the user but, for many applications, an editable list model is much more useful. We can modify the read-only model to make the items editable by changing the data() function we implemented for read-only, and by implementing two extra functions: flags() and setData(). The following function declarations are added to the class definition:
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Making the model editable
A delegate checks whether an item is editable before creating an editor.
The model must let the delegate know that its items are editable.
We do this by returning the correct flags for each item in the model;
in this case, we enable all items and make them both selectable and editable:
Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
Note that we do not have to know how the delegate performs the actual editing process.
We only have to provide a way for the delegate to set the data in the model.
This is achieved through the setData() function:
bool StringListModel::setData(const QModelIndex &index,
const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
stringList.replace(index.row(), value.toString());
emit dataChanged(index, index, {role});
return true;
}
return false;
}
In this model, the item in the string list that corresponds to the model index is replaced by the value provided.
However, before we can modify the string list, we must make sure that the index is valid, the item is of the correct type,
and that the role is supported.
By convention, we insist that the role is the EditRole since this is the role used by the standard item delegate.
For boolean values, however, you can use Qt::CheckStateRole and set the Qt::ItemIsUserCheckable flag;
a checkbox is then used for editing the value.
The underlying data in this model is the same for all roles, so this detail just makes it easier to integrate the model with standard components.
When the data has been set, the model must let the views know that some data has changed.
This is done by emitting the dataChanged() signal.
Since only one item of data has changed, the range of items specified in the signal is limited to just one model index.
Also the data() function needs to be changed to add the Qt::EditRole test:
QVariant StringListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= stringList.size())
return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole)
return stringList.at(index.row());
else
return QVariant();
}
Inserting and removing rows
It is possible to change the number of rows and columns in a model.
In the string list model it only makes sense to change the number of rows, so we only reimplement the functions for inserting and removing rows. These are declared in the class definition:
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
Since rows in this model correspond to strings in a list, the insertRows() function inserts a number of empty strings into the string list before the specified position. The number of strings inserted is equivalent to the number of rows specified.
The parent index is normally used to determine where in the model the rows should be added.
In this case, we only have a single top-level list of strings, so we just insert empty strings into that list.
bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
beginInsertRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row) {
stringList.insert(position, "");
}
endInsertRows();
return true;
}
The model first calls the beginInsertRows() function to inform other components that the number of rows is about to change. The function specifies the row numbers of the first and last new rows to be inserted, and the model index for their parent item. After changing the string list, it calls endInsertRows() to complete the operation and inform other components that the dimensions of the model have changed, returning true to indicate success.
The function to remove rows from the model is also simple to write. The rows to be removed from the model are specified by the position and the number of rows given. We ignore the parent index to simplify our implementation, and just remove the corresponding items from the string list.
bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)
{
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row) {
stringList.removeAt(position);
}
endRemoveRows();
return true;
}
The beginRemoveRows() function is always called before any underlying data is removed,
and specifies the first and last rows to be removed.
This allows other components to access the data before it becomes unavailable.
After the rows have been removed, the model emits endRemoveRows() to finish the operation and let other components know that the dimensions of the model have changed.
Next steps
We can display the data provided by this model, or any other model, using the QListView class to present the model’s items in the form of a vertical list. For the string list model, this view also provides a default editor so that the items can be manipulated. We examine the possibilities made available by the standard view classes in View Classes.
The Model Subclassing Reference document discusses the requirements of QAbstractItemModel subclasses in more detail, and provides a guide to the virtual functions that must be implemented to enable various features in different types of models.