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. Python+Selenium学习笔记9 - 警告框处理

    如下图所示,这种窗口是不能通过前端工具对其进行定位的,这里可以通过switch_to_alert()方法去接受这个弹窗 1 # coding = utf-8 2 3 from selenium imp ...

  2. nginx 重定向到系统维护页面

    上周末兄弟项目准备扩展服务器以便提供更好的服务,兄弟项目有一些功能是实时提供到我这边的,需要我这边暂时把对应系统功能屏蔽,因为使用nginx,所以可以直接配置nginx重定向到固定系统维护页面. ng ...

  3. 安装Keras出现的问题

    先是pip install tensorflow  给装好了,但是pip install  keras出现如下的问题: 只好搜帖子,参考如下的帖子,我直接 conda install keras wi ...

  4. GPU编程和流式多处理器(四)

    GPU编程和流式多处理器(四) 3.2. 单精度(32位) 单精度浮点支持是GPU计算的主力军.GPU已经过优化,可以在此数据类型上原生提供高性能,不仅适用于核心标准IEEE操作(例如加法和乘法),还 ...

  5. 将十进制数据转换为二进制AND将二进制数据转换为十进制!

    一,将十进制数据转换为二进制:  //***********************************************            Console.WriteLine(&quo ...

  6. 狂神说redis笔记(三)

    八.Redis.conf 容量单位不区分大小写,G和GB有区别 可以使用 include 组合多个配置问题 网络配置 日志 # 日志 # Specify the server verbosity le ...

  7. thymeleaf模板引擎基础知识

    一.表达式 分为四类: 1.变量表达式 ${} :获取容器上下文变量的值. 举例: 获取application域中的username: ${application.username} 获取sessio ...

  8. P1091 [NOIP2004 提高组] 合唱队形

    题目描述 $N$位同学站成一排,音乐老师要请其中的$\left ( N-K\right )$位同学出列,使得剩下的$K$位同学排成合唱队形. 合唱队形是指这样的一种队形:设$K$位同学从左到右依次编号 ...

  9. 如果在num1的任何位置有一个数字的连续三倍,并且在num2中有一个数字的连续两倍,则返回1。 如果不是这样,则返回0

    ''' 它接受数字num1和num2,如果在num1的任何位置有一个数字的连续三倍,并且在num2中有一个数字的连续两倍,则返回1. 如果不是这样,则返回0 例子 triple_double(4519 ...

  10. python django中restful框架的使用

    在使用django进行前后台分离开发时通常会搭配django-rest-framework框架创建RESTful风格的接口API.框架介绍及版本要求可参考官方地址:https://www.django ...