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为例子,来实现一个自定义模型

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

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

  1. static string BlogAdress = "https://www.cnblogs.com/wanghongyang/";
  2. virtual int rowCount(const QModelIndex &parent=QModelIndex()) const;
  3. virtual int columnCount(const QModelIndex &parent=QModelIndex()) const;
  4. QVariant data(const QModelIndex &index, int role) const;
  5. QVariant headerData(int section, Qt::Orientation orientation, int role) const;

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

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

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

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

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

  1. 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支持的类型,可直接赋值,但是取值时,对于存入的是什么类型,取出也要为这个类型

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

或者

  1. QVariant var=12;
  2. 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文件声明

  1. struct MyClass{
  2. int id;
  3. QString name;
  4. };
  5. Q_DECLARE_METATYPE(MyClass)

.cpp文件定义

  1. //存储数据
  2. MyClass myClass;
  3. myClass.id=0;
  4. myClass.name=QString("LiMing");
  5. data[0]=QString("ddd");
  6. data[1]=123;
  7. data[3]=QVariant::fromValue(myClass);
  8. //获取数据
  9. QString str=data.value(0).toString();
  10. int val=data.value(1).toInt();
  11. // 注意,先判断
  12. if(data[3].canConvert<MyClass>())
  13. {
  14. MyClass myClass=data[3].value<MyClass>();
  15. int id=myClass.id;
  16. QString name=myClass.name;
  17. }

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

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

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

2.5 具体实现

构造函数

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

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

  1. QVector<short> army;
  2. QVector<short> weaponType;
  3. QMap<short,QString> armyMap;
  4. QMap<short,QString> weaponTypeMap;
  5. QStringList weapon;
  6. QStringList header;

下面是populateModel()函数

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

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

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

  1. int ModelEx::columnCount(const QModelIndex &parent) const
  2. {
  3. return 3;
  4. }

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

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

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

  1. QVariant ModelEx::data(const QModelIndex &index, int role) const
  2. {
  3. if(!index.isValid())
  4. return QVariant();// 这里不能直接返回0
  5. if(role==Qt::DisplayRole)
  6. {
  7. switch(index.column())
  8. {
  9. case 0:
  10. return armyMap[army[index.row()]];
  11. break;
  12. case 1:
  13. return weaponTypeMap[weaponType[index.row()]];
  14. break;
  15. case 2:
  16. return weapon[index.row()];
  17. default:
  18. return QVariant();
  19. }
  20. }
  21. return QVariant();
  22. }

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

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

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

  1. QVariant ModelEx::headerData(int section, Qt::Orientation orientation, int role) const
  2. {
  3. if(role==Qt::DisplayRole&&orientation==Qt::Horizontal)
  4. return header[section];
  5. return QAbstractTableModel::headerData(section,orientation,role);
  6. }

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

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

  1. 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. Django(48)drf请求模块源码分析

    前言 APIView中的dispatch是整个请求生命过程的核心方法,包含了请求模块,权限验证,异常模块和响应模块,我们先来介绍请求模块 请求模块:request对象 源码入口 APIView类中di ...

  2. 安全利器 — SELinux

    在 Linux 系统中一切皆文件,资源也属于某种文件.用户在访问文件的时候,系统对权限(读.写 .执行)进行检查.只要用户对文件有足够的权限,就可以任意操作资源.root 用户对所有资源拥有所有权限, ...

  3. Deeplearning知识蒸馏

    Deeplearning知识蒸馏 merge paddleslim.dist.merge(teacher_program, student_program, data_name_map, place, ...

  4. pytorch生成对抗示例

    pytorch生成对抗示例 本文对ML(机器学习)模型的安全漏洞的认识,并将深入了解对抗性机器学习的热门话题.图像添加难以察觉的扰动会导致模型性能大不相同.通过图像分类器上的示例探讨该主题.使用第一种 ...

  5. 用NVIDIA NsightcComputeRoofline分析加速高性能HPC的应用

    用NVIDIA NsightcComputeRoofline分析加速高性能HPC的应用 编写高性能的软件不是一件简单的任务.当有了可以编译和运行的代码之后,当您尝试并理解它在可用硬件上的执行情况时,将 ...

  6. MapReduce —— MapTask阶段源码分析(Input环节)

    不得不说阅读源码的过程,极其痛苦 .Dream Car 镇楼 ~ ! 虽说整个MapReduce过程也就只有Map阶段和Reduce阶段,但是仔细想想,在Map阶段要做哪些事情?这一阶段具体应该包含数 ...

  7. go语言的排序和搜索(转载)

    http://studygolang.com/articles/1598 go语言的排序和搜索 晚上准备动手写点 go 的程序的时候,想起 go 如何排序的问题.排序 sort 是个基本的操作,当然搜 ...

  8. 白日梦的MySQL专题(第38篇文章)8分钟回顾MySQL的索引

    目录 公众号首发-推荐阅读原文-格式更好看 一.导读 二.聚簇索引 三.二级索引 四.联合索引 4.1.什么是联合索引 4.2.左前缀原则 4.3.联合索引的分组&排序 五.覆盖索引 六.倒排 ...

  9. NOIP模拟测试13「矩阵游戏&#183;跳房子&#183;优美序列」

    矩阵游戏 考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分 题解 算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值 算第一列我们只需要让当前点* ...

  10. Golang写文件的坑

    Golang写文件一般使用os.OpenFile返回文件指针的Write方法或者WriteString或者WriteAt方法,但是在使用这三个方法时候经常会遇到写入的内容和实际内容有出入,因为这几个函 ...