当前位置:网站首页>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());
边栏推荐
- 力扣-二叉树的前序遍历、中序遍历、后序遍历
- Go 微服务开发框架 DMicro 的设计思路
- "Dilili, wait for the lights, wait for the lights", the prompt sound for safe production in the factory
- 蚁剑高级模块开发
- Short domain name bypass and xss related knowledge
- leetcode 15
- [深入研究4G/5G/6G专题-51]: URLLC-16-《3GPP URLLC相关协议、规范、技术原理深度解读》-11-高可靠性技术-2-链路自适应增强(根据无线链路状态动态选择高可靠性MCS)
- 2022-08-04:输入:去重数组arr,里面的数只包含0~9。limit,一个数字。 返回:要求比limit小的情况下,能够用arr拼出来的最大数字。 来自字节。
- 如何模拟后台API调用场景,很细!
- Access Characteristics of Constructor under Inheritance Relationship
猜你喜欢
随机推荐
程序员失眠时的数羊列表 | 每日趣闻
正则表达式,匹配中间的某一段字符串
Images using redis cache Linux master-slave synchronization server hard drive full of moved to the new directory which points to be modified
剑指offer专项突击版第20天
在这个超连接的世界里,你的数据安全吗
【日常训练】1403. 非递增顺序的最小子序列
fragment可见性判断
Greenplum Database Fault Analysis - Can a Soft Connection Be Made to the Database Base Folder?
Access Characteristics of Constructor under Inheritance Relationship
树形查找(二叉查找树)
C学生管理系统 指定位置插入学生节点
SDC简介
浅谈数据安全治理与隐私计算
2022-08-04:输入:去重数组arr,里面的数只包含0~9。limit,一个数字。 返回:要求比limit小的情况下,能够用arr拼出来的最大数字。 来自字节。
回顾51单片机
Hypervisor related knowledge points
用@Mapper查询oracle的分区情况报错
注意潍坊开具发票一般需要注意
Pisanix v0.2.0 发布|新增动态读写分离支持
【genius_platform软件平台开发】第七十六讲:vs预处理器定义的牛逼写法!!!!(其他组牛逼conding人员告知这么配置来取消宏定义)