Qt/C++ 开源控件 可折叠的标签管理控件

在 Qt 开发中,许多项目需要处理标签管理功能,例如分类管理、标签筛选等需求。本文将分享如何利用 Qt/C++ 实现一个具备动态增删标签、展开折叠功能的控件。此控件由 TagWindowTagItemWidget 两个类组成,前者负责整个标签管理窗口的布局与逻辑,后者表示单个标签项。文章将详细介绍控件实现的核心逻辑与思路。
在这里插入图片描述

设计思路

功能需求

  1. 标签项展示:显示带编号和颜色的标签项。
  2. 动态增删标签项:允许用户动态添加或删除标签项。
  3. 窗口展开折叠:控制标签窗口的展开与折叠,优化界面显示空间。

架构概述

  • TagWindow 类用于管理标签项的整体窗口布局,包括标签的列表显示、添加按钮、展开折叠按钮等。
  • TagItemWidget 类表示单个标签项,包含标签编号、可编辑的标签名称、删除按钮等。

接下来我们将从 TagWindowTagItemWidget 的具体实现入手,逐步解析各个模块的实现逻辑和思路。


TagItemWidget 类:实现单个标签项

TagItemWidget 代表单个标签项,包含标签的编号、名称和删除按钮。下面是 TagItemWidget.h 中的定义:

class TagItemWidget : public QWidget {Q_OBJECTpublic:TagItemWidget(int id, const QString &text, const QColor &color, QWidget *parent = nullptr);QString getText() const;         ///< 获取标签的文本内容void setText(const QString &text); ///< 设置标签的文本内容void enableEditing(bool enable); ///< 控制标签名称是否可编辑signals:void requestDeletion(TagItemWidget *item); ///< 请求删除该标签项的信号protected:bool eventFilter(QObject *obj, QEvent *event) override; ///< 事件过滤器用于双击进入编辑模式private:QLabel *idLabel;        ///< 显示标签编号QLineEdit *nameEdit;    ///< 标签名称编辑框QPushButton *deleteButton; ///< 删除按钮
};

构造函数解析

构造函数的主要任务是初始化各组件,并实现标签编号的圆角背景色效果。以下是构造函数的实现:

TagItemWidget::TagItemWidget(int id, const QString &text, const QColor &color, QWidget *parent): QWidget(parent) {QHBoxLayout *layout = new QHBoxLayout(this);// 标签编号idLabel = new QLabel(QString::number(id), this);idLabel->setFixedSize(20, 20);idLabel->setAlignment(Qt::AlignCenter);idLabel->setStyleSheet("background-color: " + color.name() + "; border-radius: 10px; color: white;");// 标签名称编辑框nameEdit = new QLineEdit(text, this);nameEdit->setReadOnly(true); // 默认不可编辑// 删除按钮deleteButton = new QPushButton("删除", this);deleteButton->setFixedSize(40, 20);connect(deleteButton, &QPushButton::clicked, this, [this]() {emit requestDeletion(this);});layout->addWidget(idLabel);layout->addWidget(nameEdit);layout->addWidget(deleteButton);layout->addStretch();// 双击进入编辑模式connect(nameEdit, &QLineEdit::editingFinished, this, [this]() {nameEdit->setReadOnly(true);});nameEdit->installEventFilter(this);
}

事件过滤器

为了实现双击标签名称进入编辑模式,TagItemWidget 类重写了 eventFilter 方法:

bool TagItemWidget::eventFilter(QObject *obj, QEvent *event) {if (obj == nameEdit && event->type() == QEvent::MouseButtonDblClick) {enableEditing(true);return true;}return QWidget::eventFilter(obj, event);
}

该方法会在检测到双击事件时调用 enableEditing,使标签名称进入可编辑状态。


TagWindow 类:实现标签管理窗口

TagWindow 类负责管理多个标签项,包括标签项的添加、删除、窗口的展开和折叠等功能。以下是 TagWindow.h 中的定义:

class TagWindow : public QWidget {Q_OBJECTpublic:explicit TagWindow(QWidget *parent = nullptr);private slots:void toggleExpandCollapse();    ///< 控制窗口的展开/折叠void addItem();                 ///< 添加标签项void deleteItem(TagItemWidget *item); ///< 删除指定的标签项void adjustListWidgetHeight();  ///< 调整 listWidget 的高度private:QVBoxLayout *mainLayout;        ///< 主布局QPushButton *expandCollapseButton; ///< 展开/折叠按钮QPushButton *addButton;         ///< 添加按钮QListWidget *listWidget;        ///< 标签列表QPropertyAnimation *animation;  ///< 控制展开/折叠的动画int collapsedHeight;            ///< 折叠时的高度QString getRandomText();        ///< 生成随机文本QColor getRandomColor();        ///< 生成随机颜色
};

初始化布局

TagWindow 的构造函数实现了控件的初始化和布局:

TagWindow::TagWindow(QWidget *parent) : QWidget(parent) {mainLayout = new QVBoxLayout(this);mainLayout->setContentsMargins(0, 0, 0, 0);// 标题和展开/折叠按钮QHBoxLayout *headerLayout = new QHBoxLayout();QLabel *titleLabel = new QLabel(tc("标签类别:"));expandCollapseButton = new QPushButton("+");connect(expandCollapseButton, &QPushButton::clicked, this, &TagWindow::toggleExpandCollapse);headerLayout->addWidget(titleLabel);headerLayout->addWidget(expandCollapseButton);mainLayout->addLayout(headerLayout);// 添加按钮addButton = new QPushButton("+");connect(addButton, &QPushButton::clicked, this, &TagWindow::addItem);mainLayout->addWidget(addButton);// 标签列表listWidget = new QListWidget(this);listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);listWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);listWidget->setVisible(false);mainLayout->addWidget(listWidget);// 折叠高度collapsedHeight = addButton->sizeHint().height() + expandCollapseButton->sizeHint().height();animation = new QPropertyAnimation(this, "maximumHeight");animation->setDuration(300);setFixedHeight(collapsedHeight);
}

添加与删除标签项

用户点击添加按钮会调用 addItem,随机生成标签内容和颜色,创建新标签项并添加到 listWidget 中:

void TagWindow::addItem() {QColor color = getRandomColor();QString randomText = getRandomText();TagItemWidget *itemWidget = new TagItemWidget(listWidget->count() + 1, randomText, color);QListWidgetItem *listItem = new QListWidgetItem(listWidget);listItem->setSizeHint(itemWidget->sizeHint());listWidget->setItemWidget(listItem, itemWidget);connect(itemWidget, &TagItemWidget::requestDeletion, this, &TagWindow::deleteItem);listWidget->setVisible(true);adjustListWidgetHeight();
}

删除标签项时通过 deleteItem 实现:

void TagWindow::deleteItem(TagItemWidget *item) {for (int i = 0; i < listWidget->count(); ++i) {QListWidgetItem *listItem = listWidget->item(i);if (listWidget->itemWidget(listItem) == item) {delete listWidget->takeItem(i);break;}}if (listWidget->count() == 0) {listWidget->setVisible(false);}adjustListWidgetHeight();
}

控制窗口的展开和折叠

点击展开/折叠按钮会触发 toggleExpandCollapse 方法,通过动画效果平滑展开或折叠窗口:

void TagWindow::toggleExpandCollapse() {if (listWidget->count() == 0) return;bool isExpanded = !listWidget->isVisible();int targetHeight = isExpanded ? (collapsedHeight + listWidget->sizeHint().height()) : collapsedHeight;animation->stop();animation->setStartValue(height());animation->setEndValue(targetHeight);animation->start();expandCollapseButton->setText(isExpanded ? "-" : "+");connect(animation, &QPropertyAnimation::finished, this, [=]() {setFixedHeight(targetHeight);listWidget->setVisible(isExpanded);});adjustListWidgetHeight();
}

总结

本文介绍了一个基于 Qt 的标签管理控件的实现,包括标签项的创建、删除和窗口的展开折叠效果。通过对 TagWindowTagItemWidget 的详细讲解,相信大家能更好地理解如何设计和实现一个可复用的 Qt 控件。在实际开发中,您可以根据需求对控件功能进行扩展,如增加多标签选择、标签项排序等功能。希望本文能为您在 Qt 开发中的自定义控件设计提供参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/14719.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Jmeter中的监听器(三)

9--断言结果 功能特点 显示断言结果&#xff1a;列出所有断言的结果&#xff0c;包括通过和失败的断言。详细信息&#xff1a;显示每个断言的详细信息&#xff0c;如断言类型、实际结果和期望结果。错误信息&#xff1a;显示断言失败时的错误信息&#xff0c;帮助调试。颜色编…

七牛云上传图片成功,但是无法访问显示{error : document not found}

上传图片成功&#xff0c;但是访问不了的问题&#xff0c;直接把地址放进浏览器显示{error : document not found}&#xff0c;直接访问 DCNF 404是符合预期的&#xff0c;因为还没有去空间复制外链&#xff0c;要访问实际存在的资源才可以的. 配置区域和访问域名 设置没问题了…

虚拟与现实交融,线上元宇宙会议应用场景有哪些?

随着科技的飞速发展&#xff0c;元宇宙技术正逐渐渗透到我们生活的各个领域&#xff0c;为企业会议、学术会议、行业展会以及文化娱乐等带来了前所未有的变革。线上元宇宙会议打破了地域和物理空间的限制&#xff0c;让人们能够在虚拟世界中实现跨时空的交互与合作。本文将深入…

构建高效在线商店:Spring Boot框架应用

1 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的大环境让那些止步不前&#…

鸿蒙网络编程系列47-仓颉版UDP客户端

1. UDP通讯简介 本系列的第1篇文章《鸿蒙网络编程系列1-UDP通讯示例》中基于ArkTS语言在API 9的环境下演示了UDP通讯的基础用法&#xff0c;本文将使用仓颉语言在API 12的环境中实现类似的功能。这可能听起来有点不太现实&#xff0c;在ArkTS语言下可以利用kit.NetworkKit下的…

Redis与IO多路复用

1. Redis与IO多路复用概述 1.1 Redis的单线程特性 Redis是一个高性能的键值存储系统&#xff0c;其核心优势之一便是单线程架构。在Redis 6.0之前&#xff0c;其所有网络IO和键值对的读写操作都是由一个主线程顺序串行处理的。这种设计简化了多线程编程中的锁和同步问题&…

HarmonyOS Next 组件或页面之间的所有通信(传参)方法总结

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;上&#xff09; 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;下&#xff09; 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇&#xff08;上&#xff09; 【…

API接口:助力汽车管理与安全应用

随着汽车行业的飞速发展&#xff0c;越来越多的汽车管理技术被应用到交通安全和智慧交通系统中。在这一过程中&#xff0c;API接口起到了至关重要的作用。通过API接口&#xff0c;我们可以实现诸如车主身份验核、车辆信息查询等功能&#xff0c;从而为汽车智慧交通发展与安全应…

C哈的刷题计划之输出数字螺旋矩阵(1)

1、盲听C哈说 都说数据结构与算法是编程的核心&#xff0c;它们两个是内功与心法&#x1f600;&#xff0c;其它编程工具只是招式&#xff0c;学会了内功与心法&#xff0c;学习新事物&#xff08;这里特指层出不穷的IT技术&#xff09;就没有那么难了&#xff0c;实际上&#…

AD22Duplicate Net Names Wire问题

在验证的时候发现报了这个错误 我这个原理图都是用自定义的元件 只写在name引脚名字是会报这个错的 但是换成designator引脚标识就不会了 建议是name引脚名字和designator引脚标识都写 写成一样都行&#xff0c;就不会报这个错了&#xff0c;别空着

centos7上安装mysql

1.现查看虚拟机上有没有wget包&#xff0c;如果没有的话进行安装 yum install -y wget 2.进入MySQL :: Download MySQL Yum Repository下载mysql安装源 找到与linux相应的版本&#xff0c;复制地址&#xff0c;如果找不到地址&#xff0c;可以复制如下 3.下载mysql官方yum源 …

hadoop报错找不到主类

错误&#xff1a; (base) mpsmps3:~$ hadoop hadoop_map_redce-1.0-SNAPSHOT.jar MovieDriver /input/movies-to-be-predicted.txt Error: Could not find or load main class hadoop_map_redce-1.0-SNAPSHOT.jar解决办法&#xff1a; 1.输入命令 hadoop classpath配置好了ha…

使用 start-local 脚本在本地运行 Elasticsearch

警告&#xff1a;请勿将这些说明用于生产部署 本页上的说明仅适用于本地开发。请勿将此配置用于生产部署&#xff0c;因为它不安全。请参阅部署选项以获取生产部署选项列表。 使用 start-local 脚本在 Docker 中快速设置 Elasticsearch 和 Kibana 以进行本地开发或测试。 此设…

Day14 - CV项目实战:SAR飞机检测识别

论文原文&#xff1a; ​​​​​​SAR-AIRcraft-1.0:高分辨率SAR飞机检测识别数据集 - 中国知网 第一排的7张图片&#xff0c;普通人肉眼很难看出对应的是第二排的飞机。 还有上图里标注的飞机&#xff0c;外行根本看不明白&#xff0c;为什么这些是&#xff0c;其他的不是。…

Threejs 材质贴图、光照和投影详解

1. 材质和贴图 材质&#xff08;Material&#xff09;定义了物体表面的外观&#xff0c;包括颜色、光泽度、透明度等。贴图&#xff08;Textures&#xff09;是应用于材质的图像&#xff0c;它们可以增加物体表面的细节和真实感。 1.1材质类型 MeshBasicMaterial&#xff1a…

笔记整理—linux驱动开发部分(11)中断上下文

触摸屏分为两种&#xff0c;一种为电阻式触摸屏&#xff0c;另一种为电容式触摸屏。电阻式触摸屏&#xff08;x、x-、y、y-、AD&#xff09;有两种接口&#xff0c;一种为SOC自带的接口&#xff08;miscinput或platform&#xff09;&#xff0c;第二种为外部IC&#xff0c;通过…

网络编程示例之开发板测试

编译elf1_cmd_net程序 &#xff08;一&#xff09;设置交叉编译环境。 &#xff08;二&#xff09;查看elf1_cmd_net文件夹Makefile文件。查看当前编译规则&#xff0c;net_demo是编译整个工程&#xff0c;clean是清除工程。 &#xff08;三&#xff09;输入命令。 &#xff0…

【GD32】(一) 开发方式简介及标准库开发入门

文章目录 0 前言1 开发方式选择2 标准库模板的创建3 遇到的问题和解决方法 0 前言 因为项目关系&#xff0c;需要使用GD32。之前对此早有耳闻&#xff0c;知道这个是一个STM32的替代品&#xff0c;据说甚至可以直接烧录STM32的程序&#xff08;一般是同型号&#xff09;&#x…

Java NIO 核心知识总结

NIO 简介 在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。也就是说&#xff0c;当一个线程执行一个 I/O 操作时&#xff0c;它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈&#xff0c;因为需…

Spring如何解决循环依赖的问题

Spring 如何解决循环依赖的问题 Spring 是通过三级缓存来解决循环依赖问题&#xff0c;第一级缓存里面存储完整的Bean实例&#xff0c;这些实例是可以直接被使用的&#xff0c;第二级缓存存储的是实例化后但是还没有设置属性值的Bean实例&#xff0c;也就是Bean里面的 依赖注入…