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

告别千篇一律!用Qt的ItemDelegate打造一个带折叠、按钮和悬停效果的动态列表(附完整源码)

用Qt的ItemDelegate构建动态交互式列表从折叠效果到性能调优全解析在桌面应用开发中列表控件是最基础也最常用的界面元素之一。但传统的列表往往只提供简单的文本展示功能缺乏现代应用所需的动态交互体验。本文将带你深入Qt的ItemDelegate机制实现一个支持折叠展开、按钮交互和悬停动画的高级列表组件。1. 理解Qt的委托机制Qt的Model-View-Delegate架构是其GUI系统的核心设计之一。与直接将数据存储在控件中的传统方式不同Qt将数据(Model)、显示(View)和渲染/交互(Delegate)分离这种解耦带来了极大的灵活性。QStyledItemDelegate作为默认的委托类主要负责绘制列表项的外观提供编辑功能管理项的大小class CustomDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit CustomDelegate(QObject *parent nullptr); void paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const override; QSize sizeHint(const QStyleOptionViewItem option, const QModelIndex index) const override; // 其他需要重写的函数... };与直接使用QListWidget相比基于QListView自定义委托的方案具有明显优势特性QListWidgetQListViewDelegate性能一般更优定制程度有限完全自定义数据量支持中小规模大规模复杂度简单较高2. 设计动态列表的数据结构要实现复杂的交互效果首先需要设计合适的数据模型。我们创建一个自定义数据结构来存储每个列表项的状态struct ListItemData { QString title; QStringList details; QListbool buttonStates; bool isExpanded false; QDateTime timestamp; // 自定义绘制需要的其他属性... }; Q_DECLARE_METATYPE(ListItemData)在模型中注册和使用这个自定义类型// 设置数据 ListItemData itemData; itemData.title 项目任务; // ...填充其他字段 QStandardItem *item new QStandardItem(); item-setData(QVariant::fromValue(itemData), Qt::UserRole1); // 获取数据 QVariant var index.data(Qt::UserRole1); ListItemData data var.valueListItemData();3. 实现高级绘制效果3.1 基础绘制框架paint()方法是自定义外观的核心我们需要处理多种状态void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const { if (!index.isValid()) return; painter-save(); painter-setRenderHint(QPainter::Antialiasing); // 获取数据 ListItemData data index.data(Qt::UserRole1).valueListItemData(); // 绘制背景 drawBackground(painter, option, data); // 绘制内容 if (data.isExpanded) { drawExpandedItem(painter, option, data); } else { drawCollapsedItem(painter, option, data); } // 绘制按钮 drawActionButtons(painter, option, data); painter-restore(); }3.2 悬停和选中效果通过检测option.state可以实现状态敏感的绘制void drawBackground(QPainter *painter, const QStyleOptionViewItem option, const ListItemData data) { QRectF rect option.rect.adjusted(2, 2, -2, -2); QColor bgColor; if (option.state QStyle::State_Selected) { bgColor QColor(#e3f2fd); } else if (option.state QStyle::State_MouseOver) { bgColor QColor(#f5f5f5); } else { bgColor QColor(#ffffff); } painter-setPen(Qt::NoPen); painter-setBrush(bgColor); painter-drawRoundedRect(rect, 4, 4); }3.3 折叠/展开动画虽然Qt的委托本身不支持动画但我们可以通过属性动画实现平滑过渡// 在视图类中 QPropertyAnimation *anim new QPropertyAnimation(this, geometry); anim-setDuration(300); anim-setEasingCurve(QEasingCurve::OutQuad); connect(anim, QPropertyAnimation::valueChanged, [this]() { viewport()-update(); });4. 实现交互功能4.1 按钮点击处理在委托中检测按钮点击区域bool CustomDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem option, const QModelIndex index) { if (event-type() QEvent::MouseButtonPress) { QMouseEvent *me static_castQMouseEvent*(event); ListItemData data index.data(Qt::UserRole1).valueListItemData(); // 检查是否点击了按钮 for (int i 0; i data.buttonStates.size(); i) { QRect buttonRect calculateButtonRect(option, data, i); if (buttonRect.contains(me-pos())) { // 更新按钮状态 data.buttonStates[i] !data.buttonStates[i]; model-setData(index, QVariant::fromValue(data), Qt::UserRole1); // 发射自定义信号 emit buttonClicked(index.row(), i); return true; } } } return QStyledItemDelegate::editorEvent(event, model, option, index); }4.2 折叠/展开触发通过双击标题区域切换展开状态void ListView::mouseDoubleClickEvent(QMouseEvent *event) { QModelIndex index indexAt(event-pos()); if (index.isValid()) { QRect titleRect visualRect(index).adjusted(0, 0, 0, 30); if (titleRect.contains(event-pos())) { ListItemData data index.data(Qt::UserRole1).valueListItemData(); data.isExpanded !data.isExpanded; model()-setData(index, QVariant::fromValue(data), Qt::UserRole1); return; } } QListView::mouseDoubleClickEvent(event); }5. 性能优化技巧当列表项数量较大时绘制性能可能成为瓶颈。以下是几种优化策略5.1 按需绘制只绘制当前可见区域的内容void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const { if (!option.rect.intersects(view-viewport()-rect())) { return; // 跳过不可见项 } // ...正常绘制 }5.2 缓存绘制结果对于复杂项可以使用QPixmap缓存void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const { QString cacheKey QString(item_%1).arg(index.row()); if (QPixmapCache::find(cacheKey, cachedPixmap)) { painter-drawPixmap(option.rect.topLeft(), cachedPixmap); return; } // 首次绘制到临时pixmap QPixmap pixmap(option.rect.size()); QPainter tempPainter(pixmap); // ...绘制操作 QPixmapCache::insert(cacheKey, pixmap); painter-drawPixmap(option.rect.topLeft(), pixmap); }5.3 分批加载数据对于超大数据集实现自定义模型的分批加载QVariant CustomModel::data(const QModelIndex index, int role) const { if (!index.isValid()) return QVariant(); // 只在需要时加载数据 if (!m_loadedRows.contains(index.row())) { loadRowData(index.row()); } // ...返回数据 }6. 完整实现示例以下是一个集成所有功能的委托类框架class AdvancedItemDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit AdvancedItemDelegate(QListView *parent nullptr); void paint(QPainter *painter, const QStyleOptionViewItem option, const QModelIndex index) const override; QSize sizeHint(const QStyleOptionViewItem option, const QModelIndex index) const override; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem option, const QModelIndex index) override; signals: void actionButtonClicked(int row, int buttonIndex); void itemExpanded(int row, bool expanded); private: QRect calculateButtonRect(const QStyleOptionViewItem option, const ListItemData data, int buttonIndex) const; void drawBackground(QPainter *painter, const QStyleOptionViewItem option, const ListItemData data) const; // 其他辅助方法... };在实际项目中这种动态列表可以应用于多种场景任务管理系统中的任务卡片联系人列表中的详细信息展示文件管理器中的可展开项设置界面中的分组选项通过合理设计数据模型和绘制逻辑这种方案可以轻松扩展到数千项而保持流畅交互。我在一个项目管理工具中应用此方案即使加载5000任务项滚动和交互仍然保持60fps的流畅度。

相关文章:

告别千篇一律!用Qt的ItemDelegate打造一个带折叠、按钮和悬停效果的动态列表(附完整源码)

用Qt的ItemDelegate构建动态交互式列表:从折叠效果到性能调优全解析 在桌面应用开发中,列表控件是最基础也最常用的界面元素之一。但传统的列表往往只提供简单的文本展示功能,缺乏现代应用所需的动态交互体验。本文将带你深入Qt的ItemDelegat…...

用STM32F405的CAN总线做个遥控小车:从硬件接线到代码调试的完整实战

基于STM32F405的CAN总线遥控小车实战指南 1. 项目概述与核心设计思路 想象一下,当你亲手打造的遥控小车在房间里灵活穿梭,而控制信号通过工业级的CAN总线稳定传输时,那种成就感绝非普通蓝牙遥控可比。这正是本项目的魅力所在——用专业通信协…...

深度学习中的手工特征 vs 端到端学习:为什么你的模型效果不如预期?

深度学习中的手工特征 vs 端到端学习:为什么你的模型效果不如预期? 在深度学习项目的实际落地过程中,许多开发者都会遇到一个关键抉择:是采用传统的手工特征工程(hand-crafted features),还是拥…...

告别数据焦虑:手把手教你用PyTorch实现无源域自适应(SFUDA)实战,搞定模型跨域迁移

告别数据焦虑:手把手教你用PyTorch实现无源域自适应(SFUDA)实战,搞定模型跨域迁移 想象一下,你花费数月训练的视觉模型在晴天图片上表现优异,但面对雾天场景时准确率骤降30%。更棘手的是,原始训…...

生成式AI多集群推理负载不均问题全解析,深度解读GPU拓扑感知调度与动态权重分配机制

第一章:生成式AI应用多集群管理 2026奇点智能技术大会(https://ml-summit.org) 生成式AI应用在生产环境中常需跨多个Kubernetes集群部署——例如,模型训练在高性能GPU集群执行,推理服务运行于边缘低延迟集群,而数据预处理与评估则…...

生成式AI服务网格中的“幽灵服务”现象(Service Discovery黑洞深度溯源)

第一章:生成式AI服务网格中的“幽灵服务”现象(Service Discovery黑洞深度溯源) 2026奇点智能技术大会(https://ml-summit.org) 在生成式AI服务网格中,“幽灵服务”指那些已注册但长期无健康探针响应、未被主动注销、却持续占用服…...

别再只用Discover了!Kibana 7.10里这4种表格制作方法,到底该用哪个?

别再只用Discover了!Kibana 7.10里这4种表格制作方法,到底该用哪个? 在数据分析的日常工作中,表格是最基础也最频繁使用的可视化形式。但很多Kibana用户可能已经发现,同样的数据在不同工具中呈现的效果和操作体验差异巨…...

从Prompt失效到多模态行程编排:SITS2026专家亲授AI旅游生成的7个致命陷阱与规避清单

第一章:从Prompt失效到多模态行程编排:SITS2026专家亲授AI旅游生成的7个致命陷阱与规避清单 2026奇点智能技术大会(https://ml-summit.org) 在SITS2026(Smart Itinerary & Travel Synthesis Summit 2026)闭门工作坊中&#x…...

从Hello World到Goodbye OI:一个竞赛生的算法人生

1. 初识算法世界的震撼 记得第一次接触编程是在小学五年级的计算机兴趣班上。老师用Pascal语言演示了一个最简单的"Hello World"程序,当那个黑色窗口里跳出白色文字时,我感觉像是打开了新世界的大门。和很多OIer一样,我的启蒙教材是…...

Beyond Compare 5完整激活指南:免费生成永久授权密钥的实用教程

Beyond Compare 5完整激活指南:免费生成永久授权密钥的实用教程 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen 你是否正在寻找Beyond Compare 5的激活解决方案?这款强大…...

Python自动化实现自动关机重启,告别加班后忘关电脑

不知道你有没有遇到过这种情况:下班了,电脑还在跑着下载或者编译,结果人走了电脑开了一整夜。或者临时有事要离开,想让电脑跑完任务自动关机。 今天分享一个我写了很久一直在用的自动化脚本——Python定时关机重启管理工具,支持: 定时关机 延迟关机 定时重启 取消关机 任…...

告别电脑噪音困扰:FanControl终极风扇控制指南

告别电脑噪音困扰:FanControl终极风扇控制指南 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/fa/FanCon…...

StructBERT情感分类-中文-通用-base实战教程:GPU算力优化提升推理吞吐量

StructBERT情感分类-中文-通用-base实战教程:GPU算力优化提升推理吞吐量 1. 教程概述 今天我们来聊聊如何用StructBERT情感分类模型进行中文文本情感分析,并重点分享如何通过GPU算力优化来大幅提升推理速度。这个教程特别适合需要处理大量文本数据的开…...

信捷8轴焊锡机程序:显控触摸屏与XD5-60T10的电子齿轮比设置详解

信捷8轴焊锡机程序,采用显控触摸屏加XD5-60T10 每个轴的电子齿轮比单独设置,转盘式 机械手下料加料架,放料位置可以堆叠,放满一堆自动移动料架,直到整框装满。 程序带详细注释 原创程序 采用C语言算轴参数 含回原点…...

Windows11系统下Python运行环境设置

一、设置Python路径1、进入开始菜单-设置-系统-高级系统设置2、设置环境变量3、编辑系统变量Path4、增加Python的安装路径5、重启电脑二、修改系统的PowerShell的默认执行策略1、执行PowerShell脚本报错PowerShell执行策略‌:PowerShell的默认执行策略可能阻止了加载…...

好奇纸尿裤发现换尿布台无处不在

好奇纸尿裤推出了一项以印刷品为主的宣传活动,将日常的公共表面重新定义为临时的换尿布台,展现了有宝宝的生活中的随机应变。由Mischief No Fixed Address团队创作的这件作品将“如果你看到的是换尿布台,我们看到的是父母”这句话置于一系列…...

OpenCore Legacy Patcher逆向工程:硬件抽象层技术实现与老设备兼容性深度解析

OpenCore Legacy Patcher逆向工程:硬件抽象层技术实现与老设备兼容性深度解析 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Pa…...

RoboSense雷达数据采集实战:用Wireshark替代PCAP实现轻量级抓包

RoboSense雷达数据采集实战:用Wireshark替代PCAP实现轻量级抓包 当RSView新版取消PCAP保存功能时,许多自动驾驶算法工程师面临数据采集的困境。上周我在测试RS-LiDAR-16时,发现原始点云数据每小时竟占用超过50GB存储空间,而改用Wi…...

递归智能(RI):定义、核心机制与智能寒武纪新生命形态

递归智能(RI):定义、核心机制与智能寒武纪新生命形态方见华世毫九实验室摘要当前人工智能领域陷入以参数规模扩张、外部数据索取为核心的发展困境,单纯依靠算力提升与数据堆叠的技术路径难以实现真正意义上的认知觉醒与意识涌现&a…...

SSD202开发板刷机避坑指南:ISP、ETH、USB三种烧录方式实测与选择建议

SSD202开发板刷机实战:三种烧录方式深度解析与场景化选择 拿到一块SSD202开发板时,最令人头疼的莫过于系统烧录环节。面对ISP、ETH、USB三种烧录方式,新手往往手足无措,老手也可能在细节上翻车。本文将基于真实项目经验&#xff…...

OpenClaw是什么?2026年OpenClaw怎么搭建?OpenClaw部署与阿里云百炼Coding Plan喂饭级步骤

OpenClaw是什么?2026年OpenClaw怎么搭建?OpenClaw部署与阿里云百炼Coding Plan喂饭级步骤。本文面向零基础用户,完整说明在轻量服务器与本地Windows11、macOS、Linux系统中部署OpenClaw(Clawdbot)的流程,包…...

3步轻松备份QQ空间:GetQzonehistory让青春记忆永不丢失

3步轻松备份QQ空间:GetQzonehistory让青春记忆永不丢失 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否还记得十年前在QQ空间写下的第一条说说?那些记录青…...

别再死记硬背unlink公式了!用GDB动态调试带你直观理解glibc双向链表拆解过程

用GDB动态调试彻底掌握glibc unlink操作原理 在堆漏洞利用领域,unlink操作一直是个令人头疼的概念。许多初学者会死记硬背unlink宏的公式,却难以真正理解其背后的双向链表操作逻辑。本文将带你通过GDB动态调试的方式,直观理解unlink如何操作…...

VoxCPM-1.5-WEBUI入门到精通:完整功能体验与使用教程

VoxCPM-1.5-WEBUI入门到精通:完整功能体验与使用教程 1. 为什么选择VoxCPM-1.5-WEBUI? 在当今数字内容爆炸式增长的时代,语音合成技术正变得越来越重要。无论是内容创作者需要为视频配音,还是企业需要自动化客服系统&#xff0c…...

面试官: MySQL 索引作用解析(答案深度解析)持续更新

索引的作用 —— 面试官想听的「不止是加速查询」的深度答案💡 面试开场提醒:当面试官问“索引的作用”,千万别只答“加快查询速度”——这就像说“汽车的作用是跑得快”,完全没体现你对数据库底层机制的理解。下面我用真实生产场…...

从PLA到ABS:一份给创客的FDM 3D打印材料实战指南(含温度、平台、后处理全攻略)

从PLA到ABS:一份给创客的FDM 3D打印材料实战指南(含温度、平台、后处理全攻略) 当你第一次站在3D打印机前,面对琳琅满目的打印材料,是否感到无从下手?PLA、ABS、PETG、TPU...这些字母组合背后隐藏着怎样的特…...

面试官: MySQL 索引概念解析(答案深度解析)持续更新

什么是索引?——面试官想听的不只是“目录类比”⚠️ 注意:如果你只答“索引就像书的目录”,面试官大概率会微微一笑,然后问:“那B树和哈希索引的区别呢?为什么MySQL默认用B树?覆盖索引怎么避免…...

FinalShell不止是SSH客户端:挖掘它的服务器监控、进程管理和网络诊断隐藏功能

FinalShell隐藏功能全解析:从SSH客户端到全能运维工作台 如果你还在把FinalShell当作一个普通的SSH客户端使用,那可能只发挥了它20%的潜力。这款被低估的工具集成了服务器监控、进程管理、网络诊断等专业级功能,完全可以替代多个独立工具。让…...

你的 Vue 3 defineOptions(),VuReact 会编译成什么样的 React?

VuReact 是一个能将 Vue 3 代码编译为标准、可维护 React 代码的工具。今天就带大家直击核心:Vue 中常见的 defineOptions 宏经过 VuReact 编译后会变成什么样的 React 代码? 前置约定 为避免示例代码冗余导致理解偏差,先明确两个小约定&am…...

ESP32连接PS3手柄总失败?试试这个Windows配对+MAC修改的保姆级流程

ESP32连接PS3手柄全流程指南:从Windows配对到MAC修改的终极解决方案 在智能硬件开发领域,ESP32凭借其出色的性价比和丰富的功能接口,已经成为众多创客和开发者的首选。而PS3手柄作为经典的游戏控制器,其蓝牙功能为各种DIY项目提供…...