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