1. Model/View结构

这种结构,其实就是将界面组件与所编辑的数据分离开来,又通过数据源的方式连接起来,相当于解耦,视图层只关心显示和与用户交互,而数据层负责与实际的数据进行通信,并为视图组件提供数据接口

网上比较经典的图如下

是不是很清晰明了

关于MV的实例之前已经发过一期,这里就不再赘述,链接如下

Qt Model/view 小实例 文件目录浏览器

2. 自定义模型

2.1 定义

实现自定义模型可以通过QAbstractItemModel类继承,也可以通过QAbstractListModel,QAbstractTableModel类继承实现列表模型或表格模型。

2.2 标准数据模型

Qt实现了4类标准数据模型供我们在不同的场景下使用:

  1. QStringListModel:存储字符串列表
  2. QStandardItemModel:存储树状结构的任意数据
  3. QFileSystemModel:存储本地文件系统上的文件和目录信息
  4. QSqlQueryModel、QSqlRelationalTableModel、QSqlTableModel:存储关系型数据库中的数据

如果使用情况和上述情况之一比较相似,则可以考虑继承对应的模型类,并重新实现少数虚函数。

2.3 抽象模型

抽象数据模型有3类:

  1. QAbstractItemModel:项模型,这是所有数据模型的基类。
  2. QAbstractListModel:列表模型,结合QListView使用最合适。
  3. QAbstractTableModel:表模型,结合QTableView使用最合适。

2.4 自定义模型实例

我们以继承QAbstractTableModel为例子,来实现一个自定义模型

如果我们继承一个类,就必须得实现它的全部的纯虚函数

对于这个抽象表格模型类,我们得继承下面这些纯虚函数

static string BlogAdress = "https://www.cnblogs.com/wanghongyang/";

virtual int rowCount(const QModelIndex &parent=QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;

接下来说一下这4个虚函数的作用

virtual int rowCount(const QModelIndex &parent=QModelIndex()) const;

返回给定父节点下的行数。当父节点有效时,意味着rowCount返回父节点的子节点数。

virtual int columnCount(const QModelIndex &parent=QModelIndex()) const;

与前面的对应,返回给定父节点的子节点的列数。

QVariant data(const QModelIndex &index, int role) const;

为索引引用的项返回存储在给定角色下的数据。

注意:如果你没有返回值,返回一个无效的QVariant而不是返回0。

这里我们说明一下,role是个什么东东,在这里我直接列出官方的文档

比较常用的有下面这些


这里提到了QVariant,那我们再简单的谈谈QVariant的用法

QVariant 类用于封装数据成员的类型及取值等信息,该类类似于 C++共用体 union,一个QVariant 对象,一次只能保存一个单一类型的值。该类封装了 Qt 中常用的类型,对于QVariant 不支持的类型 ( 比如用户自定义类型 ) ,则需要使用Q_DECLARE_METATYPE( Type )宏进行注册

QVariant 拥有常用类型的单形参构造函数,因此可把这些常用类型转换为 QVariant 类型,同时 QVariant 还重载了赋值运算符,因此可把常用类型的值直接赋给 QVariant 对象。

注意:QVariant 没有 char 类型的构造函数,若使用 char 值会被转换为对应的 int 型

下面分情况讨论QVariant的使用

1) 支持的类型

对于QVariant支持的类型,可直接赋值,但是取值时,对于存入的是什么类型,取出也要为这个类型

QVariant var;
var.setValue(12);
int data=var.toInt();

或者

QVariant var=12;
int data=var.toInt();

2) 对于不支持的类型(自定义类型为例)

如自己定义的结构体。由于Qt都是基于元对象系统,故要在头文件里面要注册此类属于元类型。存储用到了QVariant QVariant::fromValue(const T &value)void QVariant::setValue(const T &value)。获取用到了T QVariant::value() const,在这之前一般要bool QVariant::canConvert(int targetTypeId) const先用进行判断,是否可以转换。例子如下:

.h文件声明

 struct MyClass{
int id;
QString name;
};
Q_DECLARE_METATYPE(MyClass)

.cpp文件定义

//存储数据
MyClass myClass;
myClass.id=0;
myClass.name=QString("LiMing"); data[0]=QString("ddd");
data[1]=123;
data[3]=QVariant::fromValue(myClass); //获取数据
QString str=data.value(0).toString();
int val=data.value(1).toInt();
// 注意,先判断
if(data[3].canConvert<MyClass>())
{
MyClass myClass=data[3].value<MyClass>();
int id=myClass.id;
QString name=myClass.name;
}

说完了QVariant,我们继续说自定义模型最后一个虚函数

QVariant headerData(int section, Qt::Orientation orientation, int role) const;

返回标头中指定方向的给定角色和区段的数据。对于水平标头,节号对应于列号。类似地,对于垂直标题,节号对应于行号。

2.5 具体实现

构造函数

ModelEx::ModelEx(QObject *parent) :
QAbstractTableModel(parent)
{
armyMap[1]=tr("空军");
armyMap[2]=tr("海军");
armyMap[3]=tr("陆军");
armyMap[4]=tr("海军陆战队"); weaponTypeMap[1]=tr("轰炸机");
weaponTypeMap[2]=tr("战斗机");
weaponTypeMap[3]=tr("航空母舰");
weaponTypeMap[4]=tr("驱逐舰");
weaponTypeMap[5]=tr("直升机");
weaponTypeMap[6]=tr("坦克");
weaponTypeMap[7]=tr("两栖攻击舰");
weaponTypeMap[8]=tr("两栖战车");
populateModel();
}

构造函数中,存放的是数据,下面是定义的私有成员变量

    QVector<short> army;
QVector<short> weaponType; QMap<short,QString> armyMap;
QMap<short,QString> weaponTypeMap; QStringList weapon;
QStringList header;

下面是populateModel()函数

void ModelEx::populateModel()
{
header<<tr("军种")<<tr("种类")<<tr("武器");
army<<1<<2<<3<<4<<2<<4<<3<<1;
weaponType<<1<<3<<5<<7<<4<<8<<6<<2;
weapon<<tr("B-2")<<tr("尼米兹级")<<tr("阿帕奇")<<tr("黄蜂级")<<tr("阿利伯克级")<<tr("AAAV")<<tr("M1A1")<<tr("F-22");
}

简单来说,army和weapon就是将数字与具体的值关联起来而存储值的容器

然后看看我们重新实现的纯虚函数(重点)

int ModelEx::columnCount(const QModelIndex &parent) const
{
return 3;
}

因为模型的列固定为3,所以这里我们直接返回3

int ModelEx::rowCount(const QModelIndex &parent) const
{
return army.size();
}

模型的行数要根据数量的大小来定,所以返回size();

QVariant ModelEx::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();// 这里不能直接返回0 if(role==Qt::DisplayRole)
{
switch(index.column())
{
case 0:
return armyMap[army[index.row()]];
break;
case 1:
return weaponTypeMap[weaponType[index.row()]];
break;
case 2:
return weapon[index.row()];
default:
return QVariant();
}
}
return QVariant();
}

这个函数用来返回指定索引的数据,将数值映射为文字

其中 role==Qt::DisplayRole: 模型中的条目能够有不同的角色,这样可以在不同的情况下提供不同的数据。例如,Qt::DisplayRole用来存取视图中显示的文字,角色由枚举类Qt::ItemDataRole定义。

其中index.column()是用来选择是第几列,根据列的不同来选择不同的数据

QVariant ModelEx::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
return header[section];
return QAbstractTableModel::headerData(section,orientation,role);
}

headerData()函数返回固定的表头数据,设置水平表头的标题

这里的orientation==Qt::Horizontal是设置为水平标题

return QAbstractTableModel::headerData(section,orientation,role);

这一行,是当条件不满足时,调用父类的headerData函数,来处理剩下的问题

2.6 运行结果

3. 总结

根据这篇博客,完整的梳理了一下,自定义模型需要干的事情,如果有错误的话,请在评论区进行说明,我会修改

博主博客:https://www.cnblogs.com/wanghongyang/

Qt5MV自定义模型与实例浅析的更多相关文章

  1. Esfog_UnityShader教程_UnityShader语法实例浅析

    距离上次首篇前言已经有一段时间了,一直比较忙,今天是周末不可以再拖了,经过我一段时间的考虑,我决定这一系列的教程会避免过于深入细节,一来可以避免一些同学被误导,二来会避免文章过于冗长难读, 三来可以让 ...

  2. ASP.NET MVC 的自定义模型属性别名绑定

    最近在研究 ASP.NET MVC 模型绑定,发现 DefaultModelBinder 有一个弊端,就是无法实现对浏览器请求参数的自定义,最初的想法是想为实体模型的属性设置特性(Attribute) ...

  3. TensorFlow 自定义模型导出:将 .ckpt 格式转化为 .pb 格式

    本文承接上文 TensorFlow-slim 训练 CNN 分类模型(续),阐述通过 tf.contrib.slim 的函数 slim.learning.train 训练的模型,怎么通过人为的加入数据 ...

  4. (转)Esfog_UnityShader教程_UnityShader语法实例浅析

    距离上次首篇前言已经有一段时间了,一直比较忙,今天是周末不可以再拖了,经过我一段时间的考虑,我决定这一系列的教程会避免过于深入细节,一来可以避免一些同学被误导,二来会避免文章过于冗长难读, 三来可以让 ...

  5. Flask之自定义模型类

    4.3自定义模型类 定义模型 模型表示程序使用的数据实体,在Flask-SQLAlchemy中,模型一般是Python类,继承自db.Model,db是SQLAlchemy类的实例,代表程序使用的数据 ...

  6. Django中自定义模型管理器(Manager)及方法

    1.自定义管理器(Manager) 在语句Book.objects.all()中,objects是一个特殊的属性,通过它来查询数据库,它就是模型的一个Manager.每个Django模型至少有一个ma ...

  7. Django 自定义模型管理器(Manager)及方法

    转载自:https://www.cnblogs.com/sui776265233/p/11571418.html 1.自定义管理器(Manager) 在语句Book.objects.all()中,ob ...

  8. 关于DEDECMS自定义模型当中添加自定义字段后在后台添加内容后不显示解决方案

    用DEDECMS的时间也不长,最近在做一个站时,就遇到了这个问题(自定义字段在后台不显示内容)中添加自定义字段后在后台编辑打开后发现我之前添加的内容不显示,如果是只是看看不单击确定的话,那么在前台数据 ...

  9. dedecms(织梦)自定义表单后台显示不全 自定义模型当中添加自定义字段后在后台添加内容后不显示解决方案

    我们常用dedecms 自定义表单做留言功能.但是偶尔会遇到这样一个问题,就是 在前台提交表单后..后天显示不全.特别是中文字符  都不会显示, 比如下图: 这是因为  如果你织梦是gbk的话那就对了 ...

随机推荐

  1. 多级中间表示概述MLIR

    多级中间表示概述MLIR MLIR项目是一种构建可重用和可扩展的编译器基础结构的新颖方法.MLIR旨在解决软件碎片,改善异构硬件的编译,显着降低构建特定于域的编译器的成本以及帮助将现有编译器连接在一起 ...

  2. IDEA骚技巧

    1. var 声明 2. null 判空 3. notnull 判非空 4. nn 判非空 5. for 遍历 6. fori 带索引的遍历 7. not 取反 8. if 条件判断 9. cast ...

  3. jsp中basa标签的使用

    <base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${page ...

  4. FlinkSQL写入Kafka/ES/MySQL示例-JAVA

    一.背景说明 Flink的API做了4层的封装,上两层TableAPI.SQL语法相对简单便于编写,面对小需求可以快速上手解决,本文参考官网及部分线上教程编写source端.sink端代码,分别读取s ...

  5. Linux CentOS 配置Yaf框架

    简介 Yaf框架想必大家都有所了解,它是一个开源的高性能的PHP框架 官网地址:https://www.php.net/manual/zh/book.yaf.php Yaf开发文档:https://w ...

  6. Linux添加永久静态路由

    1.首先查看一下本机的路由 route -n 2.比如我们添加两条静态路由,访问192.168.142.100时通过192.168.142.10:访问192.168.142.200时通过192.168 ...

  7. 框架篇:分布式全局唯一ID

    前言 每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值.但在分布 ...

  8. 【Azure API 管理】APIM集成内网虚拟网络后,启用自定义路由管理外出流量经过防火墙(Firewall),遇见APIs加载不出来问题

    问题描述 使用 Azure 虚拟网络,Azure APIM 可以管理无法通过 Internet 访问的 API,达到以保护企业内部的后端API的目的.在虚拟网络中,启用网络安全组(NSG:Networ ...

  9. 学会使用Python的threading模块、掌握并发编程基础

    threading模块 Python中提供了threading模块来实现线程并发编程,官方文档如下: 官方文档 添加子线程 实例化Thread类 使用该方式新增子线程任务是比较常见的,也是推荐使用的. ...

  10. 开发小技巧之:unicode的排序和正则匹配

    目录 简介 ASCII字符的排序 本地字符的排序 为什么不使用unicode进行排序 emoji的正则匹配 总结 简介 我们知道计算机最先兴起是在国外,出于当时计算机性能的考虑和外国常用字符的考虑,最 ...