当前位置: 首页 > news >正文

QT M/V架构开发实战:QAbstractItemModel介绍

·

目录

    • · @[TOC](目录)
  • 前言
  • 一、QAbstractItemModel初步介绍
  • 二、核心接口
  • 三、关键信号

前言


本文主要介绍的是使用代码生成的情况下对控件的介绍,包括拥有的功能及能修改的样式,也会说明在qtdesiner拖拽控件生成和使用代码生成控件的区别(如果有的话,遇到了的会说),此版本不属于最终版本,以后遇到什么新奇的点会继续更新!本文基于QT官方的文档进行的编写,QT版本为qt 5.14.0,编写环境为Windows11。不得不说官方文档真是个好东西,有时候有些不会的上去一看就能有灵感解决了,可惜没有中文版本的。

一、QAbstractItemModel初步介绍

QAbstractItemModel是所有模型类的基类,他的一些方法是模型类使用的关键,接下来介绍QAbstractItemModel类的一些核心接口和使用方法。

模型中的数据项通过 QModelIndex对象来定位。索引包含行、列和父索引信息。

data()和 setData()函数使用 int role参数来区分数据的不同用途(显示文本、图标、对齐方式、背景色、编辑值等)。Qt 定义了一系列标准角色 (Qt::DisplayRole, Qt::EditRole等),你也可以定义自定义角色 (Qt::UserRole及以上)。

当模型中的数据发生变化(增删改)时,模型会发出特定的信号(如 dataChanged, rowsInserted),视图监听这些信号并自动更新显示区域。

二、核心接口

1)rowCount()
返回给定父索引 (parent) 下的​​直接子项​​的行数。

int rowCount(const QModelIndex &parent = QModelIndex()) const​​

参数
parent指定要查询其子项数量的父节点。如果 parent是无效索引 (QModelIndex()),则表示查询根节点的子项数量(通常是整个表格的行数或树的顶级节点数)。
实现要点:​​
对于​​列表或表格模型​​(扁平结构),通常忽略 parent(或检查 parent.isValid()),直接返回总行数。
对于​​树形模型​​,需要根据 parent指向的节点返回其直接子节点的数量。
如果 parent指向一个无效的子节点(比如一个叶子节点),应该返回 0。

2)columnCount()
返回给定父索引 (parent) 下的​​直接子项​​的列数。

int rowCount(const QModelIndex &parent = QModelIndex()) const​​

参数
同上rowCount()
实现要点:​​
在大多数情况下(尤其是树形结构),同一父节点下的所有子项具有相同的列数。通常可以忽略 parent(或检查 parent.isValid()),直接返回模型的固定列数。
如果不同层级的节点列数不同,需要根据 parent来返回相应的列数。

3)data()
这是最重要的函数!!!!​​ 返回指定索引 (index) 和角色 (role) 对应的数据。

​​QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const​​

参数:​​
index: 要获取数据的项的位置(包含行、列、父索引信息)。
role: 指定需要数据的​​用途​​。
常见标准角色:
Qt::DisplayRole(0): 用于在视图中显示的文本 (QString)。
Qt::EditRole(2): 用于编辑器的数据(通常是 QString或基本类型)。
Qt::DecorationRole(1): 用于显示的图标 (QIcon, QPixmap, QColor)。
Qt::TextAlignmentRole(7): 文本对齐方式 (Qt::Alignment或 int)。
Qt::BackgroundRole(8): 项的背景画刷 (QBrush)。
Qt::ForegroundRole(9): 项的前景(文本)画刷 (QBrush)。
Qt::CheckStateRole(10): 项的勾选状态 (Qt::Checked, Qt::Unchecked, Qt::PartiallyChecked),通常用于树/列表的第一列。
Qt::UserRole(0x0100) 及以上: 用于自定义数据。
返回值
​​ QVariant类型,可以包装任何 Qt 支持的数据类型。如果该索引/角色组合没有有效数据,应返回一个​​无效的 QVariant​​ (QVariant()或 QVariant::Invalid),视图会忽略它或使用默认行为。
实现要点:​​
首先检查 index是否有效 (index.isValid()) 以及 index的行/列是否在有效范围内。
根据 index定位到底层数据结构中的具体数据项。
根据 role参数返回相应的数据。例如,对于 Qt::DisplayRole,返回要显示的字符串;对于 Qt::DecorationRole,返回图标。
对于不支持的角色,返回无效 QVariant。

4)index()
根据给定的行 (row)、列 (column) 和父索引 (parent),创建并返回对应的模型索引 (QModelIndex)。

​QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const​​​​

参数
row: 在父节点下的行号(从 0 开始)。
column: 在父节点下的列号(从 0 开始)。
parent: 父节点的索引。如果指向根节点,则为无效索引 (QModelIndex())。
返回值:​​
一个有效的 QModelIndex(如果参数有效且数据项存在),否则返回无效索引。
实现要点:​​
在构建树形结构时非常关键。
检查 row和 column是否在父节点 parent的有效范围内(使用 rowCount(parent)和 columnCount(parent))。
根据你的底层数据结构,找到位于 parent节点下第 row行、第 column列的子数据项。
使用 createIndex(row, column, void internalPointer)创建索引。internalPointer是一个指向该数据项内部表示(如指针、ID)的 void。视图不会直接使用它,但你的模型在 data()和 parent()中可以通过 index.internalPointer()获取它,从而快速定位到数据项。​​对于简单的模型(如基于二维数组的表格),internalPointer可以设为 nullptr。​​
确保返回的索引包含正确的行、列和父节点信息。

5)parent()
提供水平或垂直表头的数据。

QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const​​​​​​

参数
section: 表头的第几段(对于水平表头是列号,对于垂直表头是行号)。
orientation: Qt::Horizontal或 Qt::Vertical。
role: 数据角色(同 data())。
实现要点:​​
通常重写此函数以返回列名或行号。例如:

if (role == Qt::DisplayRole) {if (orientation == Qt::Horizontal) {return QString("Column %1").arg(section);} else {return section + 1; // 显示行号 (1-based)}
}
return QVariant(); // 其他角色返回无效

6)headerData()
返回给定子索引 (child) 的父索引。

QModelIndex parent(const QModelIndex &child) const​​​​

参数
child是你要查找其父节点的子项索引。
返回值:​​
父节点的 QModelIndex。如果 child是根节点下的项(即没有父节点),则返回无效索引 (QModelIndex())。
实现要点:​​
对于​​扁平模型​​(列表、表格),所有项的父节点都是根节点,因此总是返回无效索引 (QModelIndex())。
对于​​树形模型​​:检查child是否有效;从 child索引中(通常通过 child.internalPointer())获取它所代表的数据项;找到该数据项的父项。使用 createIndex()创建指向该父项的索引。创建父索引时,需要知道该父项在其自己的父节点(即祖父节点)中的行号和列号(通常是 0)。这通常需要你的数据结构能向上追溯。;如果 child本身就是顶级节点(根节点的直接子节点),则返回无效索引。

7)setData()
尝试将索引 index处角色 role的数据设置为 value。

bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)​​​​​​

返回值:​​
设置成功返回 true,失败返回 false。
重写要点:​​
检查 index是否有效且可编辑(通常还需检查 flags(index)是否包含 Qt::ItemIsEditable)。
根据 role(通常是 Qt::EditRole)获取 value中包含的新数据。
更新底层数据结构中对应的数据项。
如果数据成功改变,​​必须​​在返回 true​​之前​​发出 dataChanged(index, index, QVector() << role)信号,通知视图该区域需要刷新。如果多个连续项改变,可以扩大索引范围 (index.topLeft()到 index.bottomRight())。
返回 true表示成功。

8)flags()
返回索引 index对应项的标志,描述其状态和行为(是否可选、可编辑、可拖放等)。

Qt::ItemFlags flags(const QModelIndex &index) const​​​​​​

返回值:​​
​​ Qt::ItemFlags的组合(enum值通过 |组合)。
常用标志:​​
Qt::ItemIsSelectable(0x1): 项可以被选择。
Qt::ItemIsEditable(0x2): 项可以被编辑(需要实现 setData)。
Qt::ItemIsDragEnabled(0x4): 项可以作为拖拽操作的源。
Qt::ItemIsDropEnabled(0x8): 项可以作为拖拽操作的目标。
Qt::ItemIsUserCheckable(0x10): 项可以显示一个复选框(需要处理 Qt::CheckStateRole)。
Qt::ItemIsEnabled(0x20): 项可以被交互(默认包含)。
Qt::ItemIsAutoTristate(0x40): 用于复选框,父节点根据子节点状态自动变为部分选中状态。
Qt::ItemIsTristate(0x40): 已弃用,同 ItemIsAutoTristate。
实现要点:​​
通常组合基础标志 (QAbstractItemModel::flags(index)) 和你的自定义标志。例如,使第一列可编辑:

Qt::ItemFlags f = QAbstractItemModel::flags(index);
if (index.column() == 0) { // 假设第一列可编辑f |= Qt::ItemIsEditable;
}
return f;

9)insertRows() \ insertColumns()
在父节点 parent下,从行 row开始插入 count行。

bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex())​​​​​​

参数
child是你要查找其父节点的子项索引。
返回值:​​
​​ 成功返回 true。
实现要点:​​
​在修改数据结构之前​​,调用 beginInsertRows(parent, row, row + count - 1)。​​这是必须的!​​ 它通知连接的视图即将发生的变化。
在底层数据结构中实际插入 count行空数据(或默认数据)。
​​在修改数据结构之后​​,调用 endInsertRows()。​​这也是必须的!​​ 它通知视图插入已完成,视图可以刷新。
insertColumns同理。

10)removeRows() \ removeColumns()
在父节点 parent下,从行 row开始删除 count行。

bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex())​​​​​​

参数
child是你要查找其父节点的子项索引。
返回值:​​
成功返回 true。
实现要点:​​
​在修改数据结构之前​​,调用 beginRemoveRows(parent, row, row + count - 1)。​​必须!​​告诉view去刷新等一些操作,不然可能导致崩溃!
从底层数据结构中删除这 count行数据。
​​在修改数据结构之后​​,调用 endRemoveRows()。​​必须!​​
removeColumns同理。

三、关键信号

1)dataChanged

dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>())​​

​触发时机:​​ 一个或多个数据项的内容(特定角色的值)发生了变化。
​​参数:​​
topLeft, bottomRight: 定义发生变化的矩形区域(左上角和右下角索引)。如果只有一个项变化,两者相同。
roles: 发生变化的角色列表。如果未指定或为空,表示所有角色都可能发生了变化(视图会刷新所有内容)。如果指定了角色,视图可以只更新与该角色相关的部分(更高效)。
​​何时发出:​​ 在 setData()成功修改数据后立即发出。

2)rowsAboutToBeInserted

rowsAboutToBeInserted(const QModelIndex &parent, int first, int last)​​

触发时机:​​ 在行被插入​​之前​​发出(由 beginInsertRows()内部发出)。
​​参数:​​ parent是父节点索引,firstlast是将要插入的行范围(闭区间)。
​​作用:​​ 视图据此准备更新(如调整滚动条)。
3)rowsInserted

rowsInserted(const QModelIndex &parent, int first, int last)​​

触发时机:​​ 在行被插入​​之后​​发出(由 endInsertRows()内部发出)。
​​参数:​​ 同上。
​​作用:​​ 视图据此更新显示内容。

4)rowsAboutToBeRemoved

rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)​​

5)rowsRemoved
与插入类似,用于行删除。由 beginRemoveRows()/ endRemoveRows()发出。

rowsRemoved(const QModelIndex &parent, int first, int last)​​

6)​modelAboutToBeReset()/ modelReset()​​
触发时机:​​ 当模型的数据结构发生​​剧烈变化​​(无法通过增删行/列来通知),需要视图完全重置时(例如,加载一个全新的数据集)。
用法:​​

beginResetModel(); // 发出 modelAboutToBeReset()
// ... 彻底清空或重新加载底层数据 ...
endResetModel();   // 发出 modelReset()

作用:​​ 视图收到 modelAboutToBeReset()后会清理内部状态(如选择),收到 modelReset()后会重新从模型获取所有数据。

本次分享就到这里了,如果有什么错误的话请指正,或者有什么疑问的,也可以在评论区一起探讨!


文章转载自:

http://YAn1LfVc.xbwnp.cn
http://DHLOcG9a.xbwnp.cn
http://j78hq2e6.xbwnp.cn
http://6ZE87MjR.xbwnp.cn
http://BOU3laUK.xbwnp.cn
http://RSIhXonX.xbwnp.cn
http://CqWHJNZg.xbwnp.cn
http://Ied3H99W.xbwnp.cn
http://fW0VO6SD.xbwnp.cn
http://bGUcO1qD.xbwnp.cn
http://paMoz3KM.xbwnp.cn
http://myexgvpo.xbwnp.cn
http://9Ny3sq1h.xbwnp.cn
http://81j29zs3.xbwnp.cn
http://DkgTrI76.xbwnp.cn
http://QdQhhdZJ.xbwnp.cn
http://VKvRoNez.xbwnp.cn
http://vbwupQij.xbwnp.cn
http://n2CVEf1C.xbwnp.cn
http://gPYQCiTE.xbwnp.cn
http://ZyZFRDyP.xbwnp.cn
http://LlPsY6FF.xbwnp.cn
http://keet6F3d.xbwnp.cn
http://JRGybItd.xbwnp.cn
http://IdoJKT2F.xbwnp.cn
http://gwtIcwzb.xbwnp.cn
http://TqJTaQxz.xbwnp.cn
http://CExhLrhV.xbwnp.cn
http://mv6io8DQ.xbwnp.cn
http://xFjdvmTl.xbwnp.cn
http://www.dtcms.com/a/380630.html

相关文章:

  • PHP 与 WebAssembly 的 “天然隔阂”
  • QML 的第一步
  • IP验证学习之env集成编写
  • Android8 binder源码学习分析笔记(四)——ServiceManager启动
  • fastapi搭建Ansible Playbook执行器
  • 第四阶段C#通讯开发-1:通讯基础理论,串口,通讯模式,单位转换,代码示例
  • 微信小程序——云函数【使用使用注意事项】
  • 【java】常见排序算法详解
  • HarmonyOS 应用开发深度解析:基于声明式UI的现代化状态管理实践
  • Linux 中 exec 等冷门命令的执行逻辑探究
  • Qt多语言翻译实战指南:常见陷阱与动态切换解决方案
  • 【秋招笔试】2025.09.11阿里云秋招算法岗笔试真题
  • Ethernaut Level 1: Fallback - 回退函数权限提升攻击
  • 【VPX637】基于XCKU115 FPGA+ZU15EG MPSOC的6U VPX双FMC接口通用信号处理平台
  • Flutter基础(②④事件回调与交互处理)
  • 软考系统架构设计师之软件架构篇
  • 软考-系统架构设计师 访问控制和数字签名技术详细讲解
  • C语言初学者笔记【预处理】
  • android中ViewModel 和 onSaveInstanceState 的最佳使用方法
  • 达梦:将sql通过shell脚本的方式放在后台执行
  • 进阶向:从零开始理解Python音频处理系统
  • Centos7安装nginx
  • 数字图像处理-巴特沃斯高通滤波、低通滤波
  • Knockout数据绑定语法的入门教程
  • Serdes专题(1)Serdes综述
  • 2025年机器人项目管理推荐:三款工具破解机械设计到量产交付的协同难题
  • 后端post请求返回页面,在另一个项目中请求过来会出现的问题
  • 前端菜单权限方案
  • 【运维】-- 前端会话回放与产品分析平台之 openreplay
  • 前后端开发Mock作用说明,mock.ts