当前位置:网站首页>QT MV\MVC结构
QT MV\MVC结构
2022-08-05 02:32:00 【rainbow_lucky0106】
文章目录
简介、作用
Model/View 结构将数据模型和用户界面分离开来,分别用不同的实现,是一种显示和编辑数据的有效结构,在处理大型数据时尤其明显。
- 在少量数据的情形下,我们不需要动用 model 这样重量级的组件。Qt 为了方便起见也提供了 item view 类,分别是 QListWidget,QTableWidget 和 QTreeWidget,使用这些类可以直接对 item 进行操作。这种实现很像 Qt 早期版本,组件中包含了相应的 item,例如 QTableWidget 中包含有QTableWidgetItem 等。
- 对于很大的数据,我们则需要使用 Qt 的 view 类,比如 QListView,QTabelView 和 QTreeView,同时需要提供一个 model,可以是自定义 model,也可以是 Qt 预置的model。例如,如果数据来自数据库,那么你可以使用 QTabelView 和 QSqlTableModel 这两个类。
模型对外提供标准接口存取数据,不关心数据如何显示。视图自定义数据的显示方式,不关心数据如何组织存储。可以将一个数据模型在不同的视图中显示,也可以在不修改数据模型的情况下,设计特殊的视图组件。
- 把一个 model 注册给多个 view,为同一个数据提供不同的显示方式。Qt 会自动地对这些 view 保持同步,自动刷新所有的 view 以显示最新的数据。这样,我们就可以只对 model 进行修改,view 会自动更新。
为了处理用户输入,引入了委托(delegate)。引入委托的好处是可以自定义数据项的渲染和编辑。
模型必须为每一个数据提供独一无二的索引ModelIndex,视图通过索引访问模型中的数据。
MVC
MVC由三种对象组成:
- 模型负责获取需要显示的数据,并且能够存储这些数据的修改。每种数据类型都有它自己对应的模型,但是这些模型提供一个相同的 API,用于隐藏内部实现。
- 视图用于将模型数据显示给用户。对于很大的数据,或许只显示一小部分,这样就能很好的提高性能。
- 控制器(QT中使用delegate代理)是模型和视图之间的媒介,将用户的动作解析成对数据的操作,比如查找数据或者修改数据,然后转发给模型执行,最后再将模型中需要被显示的数据直接转发给视图进行显示。
流程
源数据由模型 (Model) 读取,然后在视图 (View) 组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。
视图从模型获取模型索引,通过将模型索引反向传给模型,视图又可以从数据源获取数据。在标准视图中,委托渲染数据项;在需要编辑数据时,委托使用直接模型索引直接与模型进行交互。
- Data(源数据)是原始数据,如数据库的一个数据表或SQL查询结果、内存中的一个字符串列表或磁盘文件结构等。
- Model(模型/数据模型)与源数据通信,并为视图组件提供数据接口。它从源数据提取需要的数据,用于视图组件进行显示和编辑。
- View(视图/视图组件)是界面控件,视图从数据模型中根据一定条件(如行号、列号等)获得模型索引(一个指向数据项的引用),然后显示在界面上。
- Delegate(代理)在视图与模型之间交互操作时提供临时编辑组件的功能。
通信机制
模型、视图和委托使用信号槽进行交互:
- 当源数据发生变化时,数据模型发射信号通知视图组件。
- 当用户在界面上操作数据时,视图组件发射信号表示这些操作信息。
- 在编辑数据时,代理会发射信号告知数据模型和视图组件编辑器的状态。
实现
实现自定义模型可以通过**QAbstractItemModel(复杂数据)类继承,也可以通过QAbstractListModel(一维)和QAbstractTableModel(二维)**类继承实现列表模型或者表格模型。
模型索引
通过数据模型存取的每个数据都有一个模型索引,视图组件和代理都通过索引来获取数据。
通过行号、列号、父项的模型索引三个参数来获得需要的模型索引。
数据模型的基本形式是用行和列定义的表格数据,但这并不意味着底层的数据是用二维数组存储的。
临时索引QModelIndex
模型内部组织数据的结构可能随时改变,所以模型索引是临时的。例如,对于一个QTreeView组件,如果获取一个节点的模型索引后又修改了模型数据,则先前获得的模型索引或不再指向原节点。
模型索引提供了所需要的信息的临时索引,用于通过模型取回或者修改数据。由于模型随时可能重新组织其内部的结构,因此模型索引很可能变成不可用的,此时,就不应该保存这些数据。如果你需要长期有效的数据片段,必须创建持久索引。
QModelIndex index = model->index(row, column, parent);
QModelIndex indexA = model->index(0, 0, QModelIndex());//如果是顶层节点传入空的父节点索引即可
QModelIndex indexC = model->index(2, 1, QModelIndex());
QModelIndex indexB = model->index(1, 0, indexA);
持久索引QPersistentModelIndex
item role 数据角色
为数据模型的一个项设置数据时,可以为项设置不同角色的数据。
一个项可以有不同角色的数据,对应不同的用途。
实际上是Qt的一个enum定义的,比较常见的有Qt::DisplayRole
和Qt::EditRole
,另外还有Qt::ToolTipRole
, Qt::StatusTipRole
, 和Qt::WhatsThisRole
等。并且,还有一些属性是用来描述基本的展现属性的,比如Qt::FontRole
, Qt::TextAlignmentRole
, Qt::TextColorRole
, Qt::BackgroundColorRole
等。
通过为每一个角色提供恰当的数据,模型可以告诉视图和委托如何向用户显示内容。不同类型的视图可以选择忽略自己不需要的数据,也可以添加所需要的额外数据。
View
视图不仅用于显示数据,还用于在数据项之间的导航以及数据项的选择。
视图也需要支持很多基本的用户界面的特性,例如右键菜单以及拖放。
视图可以提供数据编辑功能,也可以将编辑功能交由某个委托完成。
视图可以脱离模型创建,但是在其进行显示之前,必须存在一个模型。
对于用户的选择,多个视图可以相互独立,也可以进行共享。
Model
Model提供一种标准接口,供视图和委托访问数据。 QT中,Model接口由QAbstractItemModel类进行定义。不管底层数据是如何存储的,只要是QAbstractItemModel的子类,都提供一种表格形式的层次结构。
常用接口
item(int row, int column = 0) const
:根据行号和列号返回QStandardItem指针。invisibleRootItem() const
:主要用于获取顶层item(不可见item的子item)。itemFromIndex(const QModelIndex &index) const
:通过QModelIndex来获取item。rowCount()
\columnCount()
:获取行数和列数。data(const QModelIndex &index, int role = Qt::DisplayRole) const
:根据QModelIndex、role获取item中保存的数据。- data接口返回一个QVariant类型,这表明在item中可以存储QString等,也支持用户自定义类型,但你必须要先使用Q_DECLARE_METATYPE宏来向元对象系统进行注册声明。
findItems(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly, int column = 0) const
:返回在给定列中使用给定标志匹配给定文本的item列表。appendRow(QStandardItem *item)
:添加数据到模型的最后一行。setItem(int row, int column, QStandardItem *item)
:将item放置到表中。setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
:设置index对应的item数据。clear()
:从模型中删除所有项。removeRow()
\removeColumn()
:删除行,删除列。
QStringListModel + QListView
// 创建一个QStringListModel的对象。然后创建一个QStringList对象,并且把这个对象设置为model的数据
QStringListModel *model = new QStringListModel(this);
QStringList data;
data << "a" << "b" << "c";
// QListView数据展示:创建一个QListView的对象,并把model设置为它的model
QListView *view = new QListView(this);
view->setModel(model);
数据插入:
int irow = view->currentIndex().row(); // 获取QListView当前行
model->insertRows(row, 1);
QModelIndex index = model->index(row);
model->setData(index, text);
view->setCurrentIndex(index);
view->edit(index); // 这一行可以被编辑
几乎所有操作都是针对model的,model侦测到数据发生了变化,会立刻通知view刷新。这样,我们就可以把精力集中到对数据的操作上,而不用担心view的同步等操作。
QSortFilterProxyModel代理model
QSortFilterProxyModel类提供在其他的model和view之间排序和过滤数据的支持。QSortFilterProxyModel不能单独使用,是一个代理,真正的数据需要另外的模型提供。
QStringListModel *model = new QStringListModel(QColor::colorNames(), this);
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); // 代理(QListView必须用QSortFilterProxyModel代理,否则不生效)
proxy->setSourceModel(model); // 对model代理
proxy->setFilterKeyColumn(0); // 过滤第0列
QListView *view = new QListView(this);
view->setModel(proxy);
QRegExp::PatternSyntax syntax = QRegExp::PatternSyntax(....);
modelProxy->setFilterRegExp(regExp); // 设置proxy过滤器的表达式
自定义model
- 继承QAbstractTableModel(parent)
- 重写model中rowCount() \ columnCount() \ data() 方法。
CurrencyModel::CurrencyModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
QMap<QString, double> map;
int CurrencyModel::rowCount(const QModelIndex & parent) const
{
return map.count();
}
int CurrencyModel::columnCount(const QModelIndex & parent) const
{
return map.count();
}
// 虚函数 在用户编辑数据时会自动调用: 用户新修改的数据被作为参数value传入
QVariant CurrencyModel::data(const QModelIndex &index, const QVariant &value, int role) const
{
...
}
QVariant CurrencyModel::headerData(int section, Qt::Orientation orientation, int role) const // 重写表头
{
...
}
void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map)
{
...
}
// 调用
CurrencyModel *model = new CurrencyModel();
model->setCurrencyMap(data);
QTableView *view = new QTableView(this);
view->setModel(model);
Delegate 代理(委托)
代理在视图与模型之间交互操作时提供临时编辑组件的功能。(模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理功能会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又提交给模型:在QTableView组件上双击一个单元格编辑数据时,在单元格里就会出现一个QLineEdit组件,这个编辑框就是代理提供的临时编辑器。)
对于一些特殊的数据编辑需求,例如只允许输入整型数,使用一个QSpinBox作为代理组件更合适;从列表中选择一个数据,使用一个QComboBox作为代理组件更好。这时就需要从QStyledItemDelegate、QItemDelegate继承创建自定义代理类。
自定义代理:QStyledItemDelegate
、QItemDelegate
如果delegate没有支持为你的数据类型进行绘制,或者你希望自己绘制item,那么就可以继承 QStyledItemDelegate 类,并且重写 paint() 、sizeHint() 函数。
- paint() 函数会被每一个item独立调用。
- sizeHint()函数则可以定义每一个item 的大小。
方法
- 从QStyledItemDelegate继承
- 从QItemDelegate继承
默认的delegate是 QStyledItemDelegate。QStyledItemDelegate 使用当前的风格(style)去绘制组件。
- createEditor() 创建用于编辑模型数据的widget组件,如一个QSpinBox或一个QComboBox组件。
- setEditorData() 从模型获得数据,供widget组件进行编辑。
- setModelData() 将widget上的数据更新到数据模型
- updateEditorGeometry() 用于给widget组件设置合适的大小。
使用:
tableWidget->setItemDelegate(new TrackDelegate());
边栏推荐
- iNFTnews | What can NFTs bring to the sports industry and fans?
- fragment可见性判断
- Simple implementation of YOLOv7 pre-training model deployment based on OpenVINO toolkit
- Hypervisor related knowledge points
- [Fortune-telling-60]: "The Soldier, the Tricky Way"-2-Interpretation of Sun Tzu's Art of War
- the mechanism of ideology
- C语言实现简单猜数字游戏
- C student management system head to add a student node
- ARM Mailbox
- Apache DolphinScheduler新一代分布式工作流任务调度平台实战-中
猜你喜欢
[C language] Detailed explanation of stacks and queues (define, destroy, and data operations)
Quickly learn chess from zero to one
js中try...catch和finally的用法
Intel XDC 2022 Wonderful Review: Build an Open Ecosystem and Unleash the Potential of "Infrastructure"
Hypervisor related knowledge points
select tag custom style
Go 微服务开发框架 DMicro 的设计思路
正则表达式,匹配中间的某一段字符串
The design idea of DMicro, the Go microservice development framework
使用SuperMap iDesktopX数据迁移工具迁移地图文档和符号
随机推荐
[ROS] (10) ROS Communication - Service Communication
力扣-相同的树
[Fortune-telling-60]: "The Soldier, the Tricky Way"-2-Interpretation of Sun Tzu's Art of War
【日常训练】1403. 非递增顺序的最小子序列
采用redis缓存的linux主从同步服务器图片硬盘满了移到新目录要修改哪些指向
02 [Development Server Resource Module]
How to deal with your own shame
Flink 1.15.1 集群搭建(StandaloneSession)
shell statement to modify txt file or sh file
2022-08-04:输入:去重数组arr,里面的数只包含0~9。limit,一个数字。 返回:要求比limit小的情况下,能够用arr拼出来的最大数字。 来自字节。
sql语句多字段多个值如何进行排序
[ROS](10)ROS通信 —— 服务(Service)通信
如何模拟后台API调用场景,很细!
1484. 按日期分组销售产品
Semi-Decentralized Federated Learning for Cooperative D2D Local Model Aggregation
力扣-二叉树的前序遍历、中序遍历、后序遍历
Common hardware delays
CPDA|运营人如何从负基础学会数据分析(SQL)
Dotnet 6 Why does the network request not follow the change of the system network proxy and dynamically switch the proxy?
The design idea of DMicro, the Go microservice development framework