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. (重磅)Internal: Failed to call ThenRnnForward with model config问题的解决(Keras 2.4.3和Tensorflow2.0系列)

    与此问题斗争了整整十天.win10,keras2.4.3,CUDA 10.1,CUDNN 7.6, tensorflow 2.3.0,驱动程序nvida 452 该问题出现在BiLSTM(GPU加速) ...

  2. MPC算法

    MPC算法 一.    引言 在工程技术方面,MPC全称可指Model Predictive Control模型预测控制(又称RHC, Receding Horizon  ). 模型预测控制算法 一种 ...

  3. GPU上创建目标检测Pipeline管道

    GPU上创建目标检测Pipeline管道 Creating an Object Detection Pipeline for GPUs 今年3月早些时候,展示了retinanet示例,这是一个开源示例 ...

  4. 面试官:说一下JVM常用垃圾回收器的特点、优劣势、使用场景和参数设置

    今天去看牙医,他问我年级轻轻牙齿怎么磨损这么严重?我说,没有人点赞的这些年,我都是咬着牙过来的. Java中的垃圾回收器几乎是面试中的必考点,无论是面试初级,中级还是高级,总免不了要问一问垃圾回收器的 ...

  5. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  6. 『言善信』Fiddler工具 — 5、Fiddler界面布局详解【会话列表】

    目录 1.会话列表说明 2.会话列表不同颜色的含义 3.会话列表图标说明 4.会话列表应用设置 (1)给Fiddler会话列表增加IP列 (2)添加自定义列 (3)添加完成请求时间 (4)其他操作 1 ...

  7. Python_Selenium 之以login_page为例实现对basepage封装好的方法调用和对common中公共方法的调用

    目的:简化代码,提供框架该有的东西每一个函数 -提供了一个功能 - 公共的功能有了basepage,在PageObjects当中直接调用元素操作. 以下以login_page 为例,实现从配置文件中读 ...

  8. 「题解」PA2019 Terytoria

    本文将同步发布于: 洛谷博客: csdn: 博客园: 因为简书系统升级,所以本文未在简书上发布. 题目 题目链接:洛谷 P5987.LOJ 3320.官网. 题意概述 在二维平面直角坐标系上,有一个长 ...

  9. 利用 iOS 14 Vision 的手势估测功能 实作无接触即可滑动的 Tinder App

    Vision 框架在 2017 年推出,目的是为了让行动 App 开发者轻松利用电脑视觉演算法.具体来说,Vision 框架中包含了许多预先训练好的深度学习模型,同时也能充当包裹器 (wrapper) ...

  10. 【题解】Luogu P2875 [USACO07FEB]牛的词汇The Cow Lexicon

    题目描述 Few know that the cows have their own dictionary with W (1 ≤ W ≤ 600) words, each containing no ...