Bootstrap

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

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

设计思路

功能需求

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

架构概述

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

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


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

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;