Qt从入门到入土(九) -model/view(模型/视图)框架
简介
Qt的模型/视图(Model/View)架构是一种用于分离数据处理和用户界面展示的设计模式。它允许开发者将数据存储和管理(模型)与数据的显示和交互(视图)解耦,从而提高代码的可维护性和可扩展性。
Model/View的基本结构如下图所示:
各部分功能如下所示:
- 数据(Data):表示实际的数据
- 模型(Model):与实际数据连接,并为视图组件提供数据接口。从原始数据中提取所需内容,用于视图进行显示和编辑。
- 视图(View):从模型中获取每个数据项的模型索引,通过模型索引获取数据,然后为界面组件提供显示数据。
在Qt的模型/视图架构中:
-
数据模型和显示界面分离,允许同一数据模型在多个视图中展示,且可在不修改模型的情况下自定义视图。
-
代理功能允许用户自定义数据的显示和编辑方式。在标准视图中,代理通过模型索引与数据模型通信,并提供编辑器(如
QLineEdit
)。 -
模型、视图和代理通过信号和槽进行通信:
-
数据模型变化时,通知视图更新。
-
视图操作时,通知模型和代理。
-
编辑数据时,代理通知模型和视图编辑器状态。
-
数据模型
所有基于项数据的模型都继承自QAbstractItemModel
类,该类定义了视图和代理访问数据的接口。数据模型中无需直接存储数据,数据可以来自其他类、文件、数据库或其他任何数据源。
抽象类不能直接使用,需要子类继承并实现一些虚函数。Qt中包含了用于项数据处理的模型类,如下表:
Model 类 | 用途 |
---|---|
QStringListModel | 用于处理字符串列表数据的数据模型类 |
QStandardltemModel | 标准的基于项数据的数据模型类,每个项数据可以是任何数据类型 |
QFileSystemModel | 计算机上文件系统的数据模型类 |
QSortFilterProxyModel | 与其他数据模型结合,提供排序和过滤功能的数据模型类 |
QSqlQueryModel | 用于数据库SQL查询结果的数据模型类 |
QSqlTableModel | 用于数据库的一个数据表的数据模型类 |
QSqlRelationalTableModel | 用于关系型数据表的数据模型类 |
如果现有的这些模型类无法满足需求,可以从 QAbstractltemModel、QAbstractListModel 或 QAbstractTableModel 继承,生成自己的数据模型类。
视图组件
视图组件(View)就是显示数据模型的数据的界面组件。
Qt中提供的视图组件,如下表:
QListView | 用于显示单列的列表数据,适用于一维数据的操作 |
QTreeView | 用于显示树状结构数据,适用于树状结构数据的操作 |
QTableView | 用于显示表格状数据,适用于二维表格型数据的操作 |
QColumnView | 用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示 |
QHeaderView | 提供行表头或列表头的视图组件,如QTableView的行表头和列表头 |
代理(Delegate)
在Qt中,代理(Delegate)用于在视图组件中编辑数据,负责从模型获取数据并显示在编辑器中,编辑完成后将数据保存回模型。默认情况下,Qt使用QStyledItemDelegate
作为代理,它基于QAbstractItemDelegate
(抽象基类)。对于特殊数据编辑需求(如整数输入使用QSpinBox
,选择数据使用QComboBox
),可以通过继承QStyledItemDelegate
创建自定义代理。
Model/View结构
如上图,在Qt的模型/视图架构中,所有数据模型类都继承自QAbstractItemModel
,并以表格的层次结构表示数据,为视图组件和代理提供统一的数据存取接口。无论底层数据结构如何组织,数据模型的表现形式可以是列表、表格或树状结构。数据模型的基本单元是项(item),每个项由行号、列号和父项定义其位置。在列表和表格中,所有项共享一个顶层项,实际顶层项并不存在;而在树状结构中,行号、列号和父项的关系更复杂,但足以唯一确定一个项的位置并存取其数据。
模型索引
为了隔离数据表示和存取方式,Qt引入了模型索引(QModelIndex
)的概念。每个数据项通过模型索引进行存取,视图组件和代理通过模型索引与数据模型交互。模型索引是一个临时指针,用于提取或修改数据。由于数据模型的内部结构可能变化,模型索引是临时的。如果需要持久化的模型索引,则使用QPersistentModelIndex
。
获取索引
数据模型的基本形式是表格数据,但底层数据不一定以二维数组存储。行号和列号仅是为了方便组件间交互的约定。通过模型索引(QModelIndex
)的行号、列号和父项索引可以存取数据。
-
列表和表格模型:顶层节点用
QModelIndex()
表示,所有数据项的父项是顶层项。
// 顶层节点总是用 QModelIndex() 表示
QModelIndex index1 = model->index(0, 0, QModelIndex());
QModelIndex index2 = model->index(1, 1, QModelIndex());
-
树状结构模型:节点可以有父节点,构造模型索引时需指定行号、列号和父节点索引。例如,节点B的父节点是节点A,则其索引通过
model->index(row, column, parentIndex)
生成。
// 节点1 3的父节点是顶层节点,节点2的父节点是节点1
QModelIndex index1= model->index(0, 0, QModelIndex());
QModelIndex index3 = model->index(2, 1, QModelIndex());QModelIndex index2 = model->index(1,0,index1);
项的角色
在Qt的数据模型中,每个项可以设置不同角色(role
)的数据,用于满足不同的显示或交互需求。QStandardItemModel
中的QStandardItem
通过setData(const QVariant &value, int role)
方法设置数据,其中role
指定数据的角色,默认为Qt::UserRole + 1
。常见的角色包括:
-
Qt::DisplayRole
:用于视图组件中显示的字符串。 -
Qt::ToolTipRole
:用于鼠标悬停时的提示信息。 -
Qt::DecorationRole
:用于装饰显示(如图标)。 -
Qt::UserRole
:用于自定义数据。
获取项的数据时,也需要通过role
指定获取哪种角色的数据,例如data(int role)
。视图组件和代理会根据角色来解释和显示数据,不同组件对角色数据的处理方式可能不同,某些角色的数据也可能被忽略。
模型视图的基本使用
创建视图
视图就是一个窗口,所以使用模型/视图框架时无需使用QWidget,可以直接调用视图内置显示函数用于显示
// 创建视图
QListView view;// 把模型交给视图显示
view.setModel(model);
view.show();
创建模型
// 创建模型
auto model = new QStandardItemModel(&a);
添加项
// 给模型添加数据项
model->appendRow(new QStandardItem("肖战"));
model->insertRow(0,new QStandardItem("蔡徐坤"));
添加子项
给模型中的QStandardItem添加子项,只对QTreeView起作用
QStandardItem* item = new QStandardItem("蔡徐坤");
auto* subItem = new QStandardItem("个人信息");
subItem->appendRow(new QStandardItem("18岁"));
subItem->appendRow(new QStandardItem("身高190cm"));
item->appendRow(subItem);
item->appendRow(new QStandardItem("生活习惯"));
model->appendRow(item);
修改项
// 修改数据项
model->setItem(1,new QStandardItem("陈立农"));model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole); //也可用于修改数据项
删除项
// 删除数据项,从模型中移除并释放内存
//model->removeRow(0);
//model->removeRows(0,2); // 指定起始行和行数
// 删除数据项,但不释放内存
auto itemLists = model->takeRow(1);
qDebug()<<itemLists.size();
for(auto &item:itemLists)
{
// 需要手动释放内存
delete item;
}
qDebug()<<model->rowCount()<<" "<<model->columnCount();
获取项的索引
// item索引
auto item = new QStandardItem("wyf");
model->appendRow(item);
QModelIndex index = model->indexFromItem(item);
qDebug()<<index;
查找项
// 查询数据项
auto res = model->findItems("蔡徐坤");
if(res.isEmpty())
qDebug()<<"未找到该数据!";
for(auto& r:res)
{
qDebug()<<"找到数据:"<<r->data(Qt::ItemDataRole::DisplayRole).toString();
}
设置角色数据
// 角色(ItemDataRole)
model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole); //也可用于修改数据项
item->setData(QColor(0,0,0),Qt::ItemDataRole::DecorationRole); //设置装饰角色
item->setData("我是帅哥",Qt::ItemDataRole::UserRole); //设置用户角色
qDebug()<<item->data(Qt::UserRole).toString(); //获取用户角色信息
视图信号连接槽
// 连接信号与槽
QObject::connect(&view,&QListView::clicked,[=](const QModelIndex& idx){
qDebug()<<idx.data(Qt::DisplayRole).toString();
});
综合代码
#include <QStandardItemModel>
#include <QListView>
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 创建模型auto model = new QStandardItemModel(&a);// 创建视图QListView view;// 把模型交给视图显示view.setModel(model);view.show();// 给模型添加数据项model->appendRow(new QStandardItem("肖战"));model->insertRow(0,new QStandardItem("蔡徐坤"));// 修改数据项model->setItem(1,new QStandardItem("陈立农"));// 删除数据项,从模型中移除并释放内存//model->removeRow(0);//model->removeRows(0,2); // 指定起始行和行数// 删除数据项,但不释放内存auto itemLists = model->takeRow(1);qDebug()<<itemLists.size();for(auto &item:itemLists){// 需要手动释放内存delete item;}qDebug()<<model->rowCount()<<" "<<model->columnCount();// item索引auto item = new QStandardItem("wyf");model->appendRow(item);QModelIndex index = model->indexFromItem(item);qDebug()<<index;// 角色(ItemDataRole)model->setData(index,"大碗宽面",Qt::ItemDataRole::DisplayRole); //也可用于修改数据项item->setData(QColor(0,0,0),Qt::ItemDataRole::DecorationRole); //设置装饰角色item->setData("我是帅哥",Qt::ItemDataRole::UserRole); //设置用户角色qDebug()<<item->data(Qt::UserRole).toString(); //获取用户角色信息// 查询数据项auto res = model->findItems("蔡徐坤");if(res.isEmpty())qDebug()<<"未找到该数据!";for(auto& r:res){qDebug()<<"找到数据:"<<r->data(Qt::ItemDataRole::DisplayRole).toString();}// 设置是否可选item->setCheckable(true);// 连接信号与槽QObject::connect(&view,&QListView::clicked,[=](const QModelIndex& idx){qDebug()<<idx.data(Qt::DisplayRole).toString();});// 添加子项,只对QTreeView视图起作用// auto subItem = new QStandardItem("个人信息");// subItem->appendRow(new QStandardItem("18岁"));// subItem->appendRow(new QStandardItem("身高180cm"));// model->item(0)->appendRow(subItem);return a.exec();
}
相关文章:

Qt从入门到入土(九) -model/view(模型/视图)框架
简介 Qt的模型/视图(Model/View)架构是一种用于分离数据处理和用户界面展示的设计模式。它允许开发者将数据存储和管理(模型)与数据的显示和交互(视图)解耦,从而提高代码的可维护性和可扩展性。…...

缓存之美:Guava Cache 相比于 Caffeine 差在哪里?
大家好,我是 方圆。本文将结合 Guava Cache 的源码来分析它的实现原理,并阐述它相比于 Caffeine Cache 在性能上的劣势。为了让大家对 Guava Cache 理解起来更容易,我们还是在开篇介绍它的原理: Guava Cache 通过分段(…...

[漏洞篇]XSS漏洞详解
[漏洞篇]XSS漏洞 一、 介绍 概念 XSS:通过JS达到攻击效果 XSS全称跨站脚本(Cross Site Scripting),为避免与层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故缩写为XSS。这是一种将任意 Javascript 代码插入到其他Web用户页面里执行以…...
【Leetcode 每日一题】2269. 找到一个数字的 K 美丽值
问题背景 一个整数 n u m num num 的 k k k 美丽值定义为 n u m num num 中符合以下条件的 子字符串 数目: 子字符串长度为 k k k。子字符串能整除 n u m num num。 给你整数 n u m num num 和 k k k,请你返回 n u m num num 的 k k k 美丽值…...

IO进程线程(线程)
作业 1.创建两个线程,分支线程1拷贝文件的前一部分,分支线程2拷贝文件的后一部分 2.创建三个线程,实现线程A打印A,线程B打印B,线程C打印C;重复打印顺序ABC。 信号量实现: 条件变量实现&#x…...
1-002:MySQL InnoDB引擎中的聚簇索引和非聚簇索引有什么区别?
在 MySQL InnoDB 存储引擎 中,索引主要分为 聚簇索引(Clustered Index) 和 非聚簇索引(Secondary Index)。它们的主要区别如下: 1. 聚簇索引(Clustered Index) 定义 聚簇索引是表数…...

tomcat单机多实例部署
一、部署方法 多实例可以运行多个不同的应用,也可以运行相同的应用,类似于虚拟主机,但是他可以做负载均衡。 方式一: 把tomcat的主目录挨个复制,然后把每台主机的端口给改掉就行了。 优点是最简单最直接,…...

论文阅读分享——UMDF(AAAI-24)
概述 题目:A Unified Self-Distillation Framework for Multimodal Sentiment Analysis with Uncertain Missing Modalities 发表:The Thirty-Eighth AAAI Conference on Artificial Intelligence (AAAI-24) 年份:2024 Github:暂…...

解决asp.net mvc发布到iis下安全问题
解决asp.net mvc发布到iis下安全问题 环境信息1.The web/application server is leaking version information via the "Server" HTTP response2.确保您的Web服务器、应用程序服务器、负载均衡器等已配置为强制执行Strict-Transport-Security。3.在HTML提交表单中找不…...

概念|RabbitMQ 消息生命周期 待消费的消息和待应答的消息有什么区别
目录 消息生命周期 一、消息创建与发布阶段 二、消息路由与存储阶段 三、消息存活与过期阶段 四、消息投递与消费阶段 五、消息生命周期终止 关键配置建议 待消费的消息和待应答的消息 一、待消费的消息(Unconsumed Messages) 二、待应答的消息…...

springboot三层架构详细讲解
目录 springBoot三层架构 0.简介1.各层架构 1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系 2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2…...

2025最新群智能优化算法:云漂移优化(Cloud Drift Optimization,CDO)算法求解23个经典函数测试集,MATLAB
一、云漂移优化算法 云漂移优化(Cloud Drift Optimization,CDO)算法是2025年提出的一种受自然现象启发的元启发式算法,它模拟云在大气中漂移的动态行为来解决复杂的优化问题。云在大气中受到各种大气力的影响,其粒子的…...

2025年Draw.io最新版本下载安装教程,附详细图文
2025年Draw.io最新版本下载安装教程,附详细图文 大家好,今天给大家介绍一款非常实用的流程图绘制软件——Draw.io。不管你是平时需要设计流程图、绘制思维导图,还是制作架构图,甚至是简单的草图,它都能帮你轻松搞定。…...
记录--洛谷 P1451 求细胞数量
如果想查看完整题目,请前往洛谷 P1451 求细胞数量 P1451 求细胞数量 题目描述 一矩形阵列由数字 0 0 0 到 9 9 9 组成,数字 1 1 1 到 9 9 9 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形…...

Android Studio 配置国内镜像源
Android Studio版本号:2022.1.1 Patch 2 1、配置gradle国内镜像,用腾讯云 镜像源地址:https\://mirrors.cloud.tencent.com/gradle 2、配置Android SDK国内镜像 地址:Index of /AndroidSDK/...
做到哪一步才算精通SQL
做到哪一步才算精通SQL-Structured Query Language 数据定义语言 DDL for StructCREATE:用来创建数据库、表、索引等对象ALTER:用来修改已存在的数据库对象DROP:用来删除整个数据库或者数据库中的表TRUNCATE:用来删除表中所有的行…...
Manus演示案例: 英伟达财务估值建模 解锁投资洞察的深度剖析
在当今瞬息万变的金融投资领域,精准剖析企业价值是投资者决胜市场的关键。英伟达(NVIDIA),作为科技行业的耀眼明星,其在人工智能和半导体领域的卓越表现备受瞩目。Manus 凭借专业的财务估值建模能力,深入挖…...

postman接口请求中的 Raw是什么
前言 在现代的网络开发中,API 的使用已经成为数据交换的核心方式之一。然而,在与 API 打交道时,关于如何发送请求体(body)内容类型的问题常常困扰着开发者们,尤其是“raw”和“json”这两个术语之间的区别…...

DeepSeek大语言模型下几个常用术语
昨天刷B站看到复旦赵斌老师说的一句话“科幻电影里在人脑中植入芯片或许在当下无法实现,但当下可以借助AI人工智能实现人类第二脑”(大概是这个意思) 💞更多内容,可关注公众号“ 一名程序媛 ”,我们一起从 …...
ctf-WEB: 关于 GHCTF Message in a Bottle plus 与 Message in a Bottle 的非官方wp解法
Message in a Bottle from bottle import Bottle, request, template, runapp Bottle()# 存储留言的列表 messages [] def handle_message(message):message_items "".join([f"""<div class"message-card"><div class"me…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...