在 Qt 开发中,许多项目需要处理标签管理功能,例如分类管理、标签筛选等需求。本文将分享如何利用 Qt/C++ 实现一个具备动态增删标签、展开折叠功能的控件。此控件由 TagWindow
和 TagItemWidget
两个类组成,前者负责整个标签管理窗口的布局与逻辑,后者表示单个标签项。文章将详细介绍控件实现的核心逻辑与思路。
设计思路
功能需求
- 标签项展示:显示带编号和颜色的标签项。
- 动态增删标签项:允许用户动态添加或删除标签项。
- 窗口展开折叠:控制标签窗口的展开与折叠,优化界面显示空间。
架构概述
TagWindow
类用于管理标签项的整体窗口布局,包括标签的列表显示、添加按钮、展开折叠按钮等。TagItemWidget
类表示单个标签项,包含标签编号、可编辑的标签名称、删除按钮等。
接下来我们将从 TagWindow
和 TagItemWidget
的具体实现入手,逐步解析各个模块的实现逻辑和思路。
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 的标签管理控件的实现,包括标签项的创建、删除和窗口的展开折叠效果。通过对 TagWindow
和 TagItemWidget
的详细讲解,相信大家能更好地理解如何设计和实现一个可复用的 Qt 控件。在实际开发中,您可以根据需求对控件功能进行扩展,如增加多标签选择、标签项排序等功能。希望本文能为您在 Qt 开发中的自定义控件设计提供参考。