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

Qt 树模型(Tree Model)的增删改查实战解析

1. Qt树模型基础概念解析第一次接触Qt的树模型时我完全被那些抽象概念绕晕了。直到做了几个实际项目后才明白Tree Model本质上就是个数据管家它帮我们管理树形结构的数据并让这些数据能通过Qt的视图组件比如QTreeView显示出来。举个生活中的例子想象你正在整理公司组织架构。CEO是根节点下面有技术部、市场部等子部门每个部门又有各自的员工。这种层级关系用树模型来管理再合适不过了。在代码层面Qt提供了QAbstractItemModel这个基类我们要做的就是继承它并实现几个关键方法class TreeModel : public QAbstractItemModel { Q_OBJECT public: // 必须实现的纯虚函数 QModelIndex index(int row, int column, const QModelIndex parent) const override; QModelIndex parent(const QModelIndex child) const override; int rowCount(const QModelIndex parent) const override; int columnCount(const QModelIndex parent) const override; QVariant data(const QModelIndex index, int role) const override; };这几个函数构成了树模型的基础骨架index()和parent()负责建立父子关系rowCount()和columnCount()告诉视图有多少数据要显示data()则是实际提供显示内容的我刚开始总记不住这些函数的调用顺序后来发现视图组件的工作流程是这样的先通过rowCount()知道有多少行然后为每行调用index()获取索引最后用data()获取显示内容。这个流程就像快递员送包裹先看有多少件rowCount再按门牌号index一件件投递data。2. 树模型数据结构设计实战要实现一个可编辑的树模型光有骨架还不够我们需要设计底层数据结构。官方示例中使用的是经典的父指针子列表结构class TreeItem { public: explicit TreeItem(const QVectorQVariant data, TreeItem *parent nullptr); ~TreeItem(); // 子节点操作 TreeItem *child(int number); bool insertChildren(int position, int count, int columns); bool removeChildren(int position, int count); // 数据操作 QVariant data(int column) const; bool setData(int column, const QVariant value); private: QVectorTreeItem* childItems; // 子节点列表 QVectorQVariant itemData; // 节点数据 TreeItem *parentItem; // 父节点指针 };这个设计有几个精妙之处双向链接每个节点都知道自己的父节点和子节点这样在实现parent()函数时就非常方便数据与结构分离itemData存储节点内容childItems管理结构关系自动内存管理析构函数里调用qDeleteAll自动删除所有子节点我在实际项目中遇到过内存泄漏问题就是因为忘了实现析构函数。后来养成了好习惯只要类里有指针成员第一时间先把析构函数写上。3. 增删改查完整实现3.1 数据查询与显示要让树模型正常工作这几个查询函数必须正确实现QVariant TreeModel::data(const QModelIndex index, int role) const { if (!index.isValid() || role ! Qt::DisplayRole) return QVariant(); TreeItem *item getItem(index); return item-data(index.column()); } int TreeModel::rowCount(const QModelIndex parent) const { TreeItem *parentItem getItem(parent); return parentItem ? parentItem-childCount() : 0; } QModelIndex TreeModel::index(int row, int column, const QModelIndex parent) const { TreeItem *parentItem getItem(parent); TreeItem *childItem parentItem-child(row); return childItem ? createIndex(row, column, childItem) : QModelIndex(); }这里有个容易踩的坑createIndex()的第三个参数是内部指针我们通常传入对应的TreeItem指针。这个指针会在parent()函数中通过index.internalPointer()取出来。3.2 插入和删除节点动态编辑是树模型的核心功能。以插入行为例bool TreeModel::insertRows(int position, int rows, const QModelIndex parent) { TreeItem *parentItem getItem(parent); beginInsertRows(parent, position, position rows - 1); const bool success parentItem-insertChildren(position, rows, rootItem-columnCount()); endInsertRows(); return success; }关键点在于必须先调用beginInsertRows()通知视图准备更新实际操作完成后要调用endInsertRows()这两个调用之间的操作要尽可能快我曾在项目中忘记调用beginInsertRows()结果视图显示完全错乱。后来才明白这些begin/end函数不仅是通知视图还能保持数据一致性。3.3 数据修改与更新实现编辑功能需要重写这两个函数bool TreeModel::setData(const QModelIndex index, const QVariant value, int role) { if (role ! Qt::EditRole) return false; TreeItem *item getItem(index); bool result item-setData(index.column(), value); if (result) { emit dataChanged(index, index, {role}); } return result; } Qt::ItemFlags TreeModel::flags(const QModelIndex index) const { return index.isValid() ? Qt::ItemIsEditable | QAbstractItemModel::flags(index) : Qt::NoItemFlags; }特别注意flags()中必须包含Qt::ItemIsEditable才能使项目可编辑修改数据后要发射dataChanged()信号通知视图更新可以通过role参数区分不同类型的修改4. 高级功能与性能优化4.1 拖放功能实现要让树模型支持拖放操作需要重写更多函数Qt::DropActions TreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } bool TreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex parent) const { Q_UNUSED(data); Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(column); Q_UNUSED(parent); return true; }实现拖放最复杂的是处理dropMimeData()需要考虑多种情况拖到节点之间 vs 拖到节点上移动 vs 复制操作跨层级拖放4.2 大数据量优化当树节点很多时性能问题就会显现。我总结了几点优化经验延迟加载只加载当前可见的节点滚动时再动态加载批量操作对于大批量更新使用beginResetModel()/endResetModel()缓存数据对计算代价高的data()结果进行缓存按需加载列不是所有列都需要立即加载// 批量更新的正确姿势 void TreeModel::bulkUpdate() { beginResetModel(); // 这里进行大规模数据修改 endResetModel(); }4.3 自定义数据显示通过data()函数的role参数我们可以实现丰富多样的显示效果QVariant TreeModel::data(const QModelIndex index, int role) const { TreeItem *item getItem(index); switch (role) { case Qt::DisplayRole: return item-data(index.column()); case Qt::FontRole: if (item-isImportant()) { QFont boldFont; boldFont.setBold(true); return boldFont; } break; case Qt::BackgroundRole: return item-color(); } return QVariant(); }5. 调试技巧与常见问题调试树模型时我常用的几个方法打印模型结构递归打印整个树确保父子关系正确重写validate()函数检查索引和指针的有效性使用QDebug输出在关键函数中添加调试输出void TreeModel::dumpTree(TreeItem *item, int indent) { QString spacer(indent, ); qDebug() spacer item-data(0).toString(); for (int i 0; i item-childCount(); i) { dumpTree(item-child(i), indent 4); } }常见问题排查视图不更新检查是否漏掉了begin/end函数对崩溃问题通常是索引或指针无效导致的显示错乱检查rowCount()和columnCount()返回值编辑无效确认flags()包含Qt::ItemIsEditable记得第一次实现树模型时我花了整整两天才找出一个parent()函数的错误。后来发现是createIndex()时行列参数传反了。这种错误很难从现象直接看出来只能通过仔细检查每个索引的创建和使用位置来定位。

相关文章:

Qt 树模型(Tree Model)的增删改查实战解析

1. Qt树模型基础概念解析 第一次接触Qt的树模型时,我完全被那些抽象概念绕晕了。直到做了几个实际项目后才明白,Tree Model本质上就是个数据管家,它帮我们管理树形结构的数据,并让这些数据能通过Qt的视图组件(比如QTre…...

中文语料分词+生成词表+词频排序

缘起 近日批改学生毕业论文,有篇初稿的话题是研究《红楼梦》文化负载词的汉英翻译,其研究方法一节有以下表述: This study adopts a random sampling method. Representative culture-loaded vocabulary is selected from the first 12 chap…...

手把手教你用Event Viewer和Log Parser分析Windows安全日志(附玄机靶场实战)

从零到一:Windows安全日志分析实战指南 开篇:日志分析的价值与挑战 想象一下,你正面对一台疑似被入侵的Windows服务器,系统管理员递给你一个Security.evtx文件,说"看看能不能找到入侵者的痕迹"。作为安全新…...

3分钟搞定!在macOS上实现Google Nearby Share的终极指南

3分钟搞定!在macOS上实现Google Nearby Share的终极指南 【免费下载链接】NearDrop An unofficial Google Nearby Share/Quick Share app for macOS 项目地址: https://gitcode.com/gh_mirrors/ne/NearDrop 还在为Mac和Android设备间的文件传输而烦恼吗&…...

Windows安卓应用安装终极指南:APK Installer让跨平台体验更简单

Windows安卓应用安装终极指南:APK Installer让跨平台体验更简单 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否厌倦了在电脑上运行安卓应用时需要安…...

第一篇java代码

第一篇java代码 初次接触java,令我印象最深的是# 我写的第一行 Java 代码,不只是 “Hello World”大一新生,刚学 Java几周,尚无大的突破, 可我记得我第一次接触java代码时的思考。所以我将我最初的思考记录,并由此作为…...

二分查找力扣题(leetcode)味

一、语言特性:Java 26 与模式匹配进化 1.1 Java 26 语言级别支持 IDEA 2026.1 EAP 最引人注目的变化之一,就是新增 Java 26 语言级别支持。这意味着开发者可以提前体验和测试即将在 JDK 26 中正式发布的语言特性。 其中最重要的变化是对 JEP 530 的全面支…...

控制工程系统稳定性的影响因素

控制工程系统稳定性的影响因素题目 下列哪种措施对提高系统的稳定性没有效果© A、增加开环零点 B、引入串联超前校正装置 C、增加开环极点 D、在积分环节外加单位负反馈 稳定性 在经典控制理论中, 评判一个闭环系统稳不稳定的核心标准是: 相位裕度(Phase Margin, PM)和根轨…...

WarcraftHelper:如何解决魔兽争霸III在现代系统上的兼容性问题

WarcraftHelper:如何解决魔兽争霸III在现代系统上的兼容性问题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一个专为魔…...

如何用PRoot在Android上构建完整Linux环境:无需root权限的5个实战技巧

如何用PRoot在Android上构建完整Linux环境:无需root权限的5个实战技巧 【免费下载链接】proot An chroot-like implementation using ptrace. 项目地址: https://gitcode.com/gh_mirrors/pro/proot PRoot是一款革命性的开源工具,它能让你的Androi…...

RV1106驱动ST7735S踩坑实录:从设备树到LVGL显示,我遇到的3个关键问题

RV1106驱动ST7735S踩坑实录:从设备树到LVGL显示的三个关键陷阱 最近在Luckfox Pico Pro Max(RV1106平台)上折腾ST7735S SPI屏幕时,遇到了几个颇具代表性的问题。这些问题不仅让我熬了几个通宵,也让我对嵌入式Linux的显…...

DAMOYOLO-S多场景落地:智能硬件产品出厂前目标检测功能自动化校验

DAMOYOLO-S多场景落地:智能硬件产品出厂前目标检测功能自动化校验 1. 引言:从质检痛点说起 想象一下这个场景:你是一家智能硬件公司的生产线负责人。每天,成千上万的摄像头、扫地机器人、智能门锁从流水线上下来。每个产品都内置…...

GLM-4.1V-9B-Base一键部署教程:Python入门级环境配置指南

GLM-4.1V-9B-Base一键部署教程:Python入门级环境配置指南 1. 开篇:为什么选择GLM-4.1V-9B-Base 如果你刚接触AI开发,想快速体验多模态大模型的能力,GLM-4.1V-9B-Base是个不错的起点。这个开源模型不仅能处理文本,还能…...

AIAgent架构安全审计倒计时:监管新规Q3强制实施,你还在用传统API网关日志做AI风控?

第一章:AIAgent架构安全审计与日志 2026奇点智能技术大会(https://ml-summit.org) 安全审计的核心关注点 AI Agent 架构在多模态交互、自主决策与外部系统集成过程中,面临权限越界、提示注入、推理链污染及敏感数据泄露等新型攻击面。安全审计需覆盖运…...

终极Windows驱动签名绕过指南:3步解决硬件兼容性问题

终极Windows驱动签名绕过指南:3步解决硬件兼容性问题 【免费下载链接】DSEFix Windows x64 Driver Signature Enforcement Overrider 项目地址: https://gitcode.com/gh_mirrors/ds/DSEFix DSEFix是一款专为Windows x64系统设计的驱动签名强制覆盖工具&#…...

如何快速迁移Ziglings项目:从GitHub到Codeberg的完整指南

如何快速迁移Ziglings项目:从GitHub到Codeberg的完整指南 【免费下载链接】ziglings Learn the Zig programming language by fixing tiny broken programs. 项目地址: https://gitcode.com/gh_mirrors/zi/ziglings Ziglings是一个通过修复小型破损程序来学习…...

ams OSRAM 将娱乐与工业灯具业务出售给 Ushio

事件核心摘要交易双方:ams OSRAM(卖方,奥地利/德国半导体巨头) vs. Ushio, Inc.(买方,日本光学技术公司)。交易内容:出售 Entertainment & Industry Lamps(娱乐与工业…...

Nginx 学习总结从

1. 引入 在现代 AI 工程中,Hugging Face 的 tokenizers 库已成为分词器的事实标准。不过 Hugging Face 的 tokenizers 是用 Rust 来实现的,官方只提供了 python 和 node 的绑定实现。要实现与 Hugging Face tokenizers 相同的行为,最好的办法…...

终极指南:5分钟学会用Virtual Kubelet在非K8s环境部署容器

终极指南:5分钟学会用Virtual Kubelet在非K8s环境部署容器 【免费下载链接】virtual-kubelet Virtual Kubelet is an open source Kubernetes kubelet implementation. 项目地址: https://gitcode.com/gh_mirrors/vi/virtual-kubelet Virtual Kubelet是一个开…...

10 分钟搞定答辩 PPT!Paperxie AI 神器,终结本科生熬夜改稿魔咒

paperxie-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/AIPPThttps://www.paperxie.cn/ppt/createhttps://www.paperxie.cn/ppt/create 一、 答辩 PPT:压垮毕业生的最后一根稻草 毕业论文定稿的喜悦,往往会被答辩 PPT 的焦虑瞬间冲淡。对…...

Rust 异步函数调用栈分析

Rust异步函数调用栈分析:深入理解异步执行机制 在当今高并发的编程场景中,异步编程已成为提升性能的关键技术。Rust通过async/await语法和Future机制提供了高效的异步支持,但其底层调用栈的复杂性常常让开发者感到困惑。本文将深入分析Rust异…...

终极Dockertest错误处理指南:从连接失败到超时重试的完整解决方案

终极Dockertest错误处理指南:从连接失败到超时重试的完整解决方案 【免费下载链接】dockertest Write better integration tests! Dockertest helps you boot up ephermal docker images for your Go tests with minimal work. 项目地址: https://gitcode.com/gh_…...

FireRedASR-AED-L模型Java八股文精讲:多线程并发调用与连接池管理

FireRedASR-AED-L模型Java八股文精讲:多线程并发调用与连接池管理 最近在做一个智能客服项目,需要把用户的语音实时转成文字。我们选用了FireRedASR-AED-L这个语音识别模型,效果确实不错。但上线没多久,就遇到了问题:…...

STM32F030K6T6 定时器触发ADC采样的DMA传输实战

1. 为什么需要定时器触发ADC采样? 在嵌入式开发中,ADC(模数转换器)采样是获取模拟信号的关键环节。传统的手动触发或查询式ADC采样存在两个明显痛点:一是需要CPU频繁介入,二是采样间隔难以精确控制。比如用…...

Proteus与Keil5实战:RS485多机通信仿真全解析

1. RS485多机通信基础与仿真环境搭建 第一次接触RS485通信时,我被它"一根总线挂多个设备"的特性惊艳到了。相比RS232的点对点通信,RS485就像个高效的快递中转站,能同时处理多个包裹收发。在实际工业现场,这种特性让布线…...

抓取不规则表面物体机械手的设计

目 录 第一章 绪论 1 1.1 课题研究的意义及背景 1 1.2 机械手研究概况 2 1.2.1国外研究现状 2 1.2.2国内研究现状 2 1.3 研究的内容 2 第二章 抓取物体机械手总体结构设计 4 2.1机械手设计思路 4 2.2总体方案的设计 5 2.2.1驱动方式的选择 5 2.2.2传动结构的分析 6 2.2.3传动方…...

Win11Debloat深度解析:模块化架构设计如何实现Windows系统70%性能优化

Win11Debloat深度解析:模块化架构设计如何实现Windows系统70%性能优化 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to d…...

新手避坑指南:微信小程序组件通信最常见的3个错误用法(附正确示范)

微信小程序组件通信避坑手册:3个高频错误与实战修复方案 刚接触微信小程序开发的工程师,往往会在组件通信环节踩中一些隐蔽的"地雷"。这些陷阱轻则导致页面渲染异常,重则引发内存泄漏和性能劣化。本文将解剖三个最具迷惑性的典型错…...

Mechanize最佳实践:提升Web自动化脚本性能的8个实用技巧

Mechanize最佳实践:提升Web自动化脚本性能的8个实用技巧 【免费下载链接】mechanize Mechanize is a ruby library that makes automated web interaction easy. 项目地址: https://gitcode.com/gh_mirrors/me/mechanize Mechanize是一款强大的Ruby库&#x…...

猫抓Cat-Catch:解锁网页媒体资源的终极免费解决方案

猫抓Cat-Catch:解锁网页媒体资源的终极免费解决方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为无法保存心爱的在线视频而苦…...