QT实现自定义tag标签

需求

近期项目上需要用到QT,刚接触不久就遇到一个需求。需要在form表单上实现标签,标签长度随着文字的长度而变化,并且支持关闭和选中两种状态。

从遇到问题到解决问题总共用了3天时间,有一段时间没有钻研技术问题了,决定记录一下。

需求两种情况如下图所示(下图是已经实现的结果):
支持选中的状态支持关闭的状态

思考

遇到这个需求的第一反映是去网上找现成的控件来使用,找了一圈发现没有。

期间还尝试用QViewList+委托来实现,但是遇到item大小都是固定的问题,没法根据文字的长度进行分别显示,花了一天时间没有解决,果断放弃!

进而想到QT官方给的示例很强大,于是到官方示例是去寻找解药,发现有一个差不多的示例,叫:Fridge Magnets Example(冰箱磁铁示例),如下图所示:
在这里插入图片描述翻看了官方示例的源码之后,决定在其基础上进行改造。
主要就是添加点击事件、在右侧绘制关闭按钮。
想要实现这些功能,需要自义Label类、自定义关闭按钮,在自定义的类文件中添加个性化实现方法即可满足需求。

代码

我的代码目录结构如下图所示:
在这里插入图片描述主要代码如下:

#ifndef CLUETAG_H
#define CLUETAG_H

#include <QLabel>
#include "mainwindow.h"

class ClueTag : public QLabel
{
public:
    /**
     * @brief ClueTag  标签构造函数
     * @param tagText  标签显示文字
     * @param allowSelect  是否允许选中
     * @param isShowBtn  是否显示关闭按钮
     * @param selectedTags  已选择的标签集合指针
     * @param mainWindow  主窗口
     * @param parent  父窗口
     */
    ClueTag(const QString& tagText, const bool allowSelect, const bool isShowBtn, QStringList* selectedTags, MainWindow* mainWindow, QWidget *parent);
    //获取标签文字
    QString tagText() const;
    //设置标签选中
    void setSelected();
    //设置标签未选中
    void setUnSelected();

protected:
    //鼠标点击事件
    void mousePressEvent(QMouseEvent* event);

private:
    //标签文字
    QString m_tagText;
    //被选中的标签指针变量
    QStringList* m_selectedTags;
    //是否显示关闭按钮
    bool m_isShowBtn;
    //是否允许选中
    bool m_allowSelect;

};

#endif // CLUETAG_H

#ifndef CLUETAGBTN_H
#define CLUETAGBTN_H

#include <QObject>
#include <QPushButton>
#include "mainwindow.h"

class ClueTagBtn : public QPushButton
{
    Q_OBJECT

public:
    ClueTagBtn(QString tagText, QStringList* selectedTags, MainWindow* mainWindow, QWidget *parent);

signals:
    void delSelectedTagSignal(QString tagText);

};

#endif // CLUETAGBTN_H

#include "cluetag.h"
#include "mainwindow.h"

#include <QtWidgets>
#include "cluetagbtn.h"

ClueTag::ClueTag(const QString& tagText, const bool allowSelect, const bool isShowBtn,  QStringList* selectedTags, MainWindow* mainWindow, QWidget *parent)
    : QLabel(parent)
{
    m_isShowBtn = isShowBtn;
    m_allowSelect = allowSelect;

    QFontMetrics metric(font());
    QSize size = metric.size(Qt::TextSingleLine, tagText);

    QImage image(size.width() + 12 + (m_isShowBtn ? 30 : 0), size.height() + 12, QImage::Format_ARGB32_Premultiplied);
    image.fill(qRgba(0, 0, 0, 0));

    QFont font;
    font.setStyleStrategy(QFont::ForceOutline);
    //font.setFamily("Microsoft YaHei");

    QLinearGradient gradient(0, 0, 0, image.height()-1);
    gradient.setColorAt(0.0, Qt::white);
    //gradient.setColorAt(0.2, QColor(200, 200, 255));
    //gradient.setColorAt(0.8, QColor(200, 200, 255));
    //gradient.setColorAt(1.0, QColor(200, 200, 255));

    QPainter painter;
    painter.begin(&image);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(gradient);
    painter.setPen(QPen(QColor(175, 125, 255)));
    painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
                            20, 80, Qt::RelativeSize);

    painter.setFont(font);
    painter.setBrush(Qt::blue);
    painter.setPen(QPen(QColor(175, 125, 255)));
    painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, tagText);
    painter.end();

    setPixmap(QPixmap::fromImage(image));
    m_tagText = tagText;
    m_selectedTags = selectedTags;
    this->setToolTip("点击关闭标签");

    if(isShowBtn)
    {
        ClueTagBtn *btnIco = new ClueTagBtn(tagText, selectedTags, mainWindow, this);
        btnIco->setIcon(QIcon(":/img/close.png"));
        btnIco->setFlat(true);
        btnIco->setCursor(QCursor(Qt::PointingHandCursor));

        QHBoxLayout * pLay = new QHBoxLayout();
        pLay->setContentsMargins(0,0,0,0);
        pLay->addSpacing(size.width() + 12);
        pLay->addWidget(btnIco);
        pLay->addStretch(5);
        setLayout(pLay);
        setContentsMargins(0,0,0,0);
    }
}

QString ClueTag::tagText() const
{
    return m_tagText;
}

void ClueTag::mousePressEvent(QMouseEvent* event)
{
    Q_UNUSED(event);
    if(!m_allowSelect)
    {
        return;
    }

    //先判断是否加入列表中,如果已经加入,则移除并设置未选中状态
    if(m_selectedTags->contains(tagText()))
    {
        //设置未选中状态
        setUnSelected();
        for(int i=0; i<m_selectedTags->size(); i++)
        {
            if(m_selectedTags->at(i) == tagText())
            {
                m_selectedTags->removeAt(i);
            }
        }
        qDebug() << m_selectedTags[0];
    }
    else
    {
        //设置选中状态
        setSelected();
        m_selectedTags->append(tagText());
    }

    qDebug() << m_selectedTags[0];
}

void ClueTag::setSelected()
{
    QString text = tagText();

    QFontMetrics metric(font());
    QSize size = metric.size(Qt::TextSingleLine, text);

    QImage image(size.width() + 12 + (m_isShowBtn ? 30 : 0), size.height() + 12, QImage::Format_ARGB32_Premultiplied);
    image.fill(qRgba(0, 0, 0, 0));

    QFont font;
    font.setStyleStrategy(QFont::ForceOutline);

    QLinearGradient gradient(0, 0, 0, image.height()-1);
    gradient.setColorAt(0.0, Qt::white);
    gradient.setColorAt(0.2, QColor(200, 200, 255));
    gradient.setColorAt(0.8, QColor(200, 200, 255));
    gradient.setColorAt(1.0, QColor(200, 200, 255));

    QPainter painter;
    painter.begin(&image);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(gradient);
    painter.setPen(QPen(QColor(175, 125, 255)));
    painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
                            20, 80, Qt::RelativeSize);

    painter.setFont(font);
    painter.setBrush(Qt::black);
    painter.setPen(QPen(QColor(255, 255, 255)));
    painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
    painter.end();

    setPixmap(QPixmap::fromImage(image));
}

void ClueTag::setUnSelected()
{
    QString text = tagText();

    QFontMetrics metric(font());
    QSize size = metric.size(Qt::TextSingleLine, text);

    QImage image(size.width() + 12 + (m_isShowBtn ? 30 : 0), size.height() + 12, QImage::Format_ARGB32_Premultiplied);
    image.fill(qRgba(0, 0, 0, 0));

    QFont font;
    font.setStyleStrategy(QFont::ForceOutline);

    QLinearGradient gradient(0, 0, 0, image.height()-1);
    gradient.setColorAt(0.0, Qt::white);
    //gradient.setColorAt(0.2, QColor(200, 200, 255));
    //gradient.setColorAt(0.8, QColor(200, 200, 255));
    //gradient.setColorAt(1.0, QColor(200, 200, 255));

    QPainter painter;
    painter.begin(&image);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(gradient);
    painter.setPen(QPen(QColor(175, 125, 255)));
    painter.drawRoundedRect(QRectF(0.5, 0.5, image.width()-1, image.height()-1),
                            20, 80, Qt::RelativeSize);

    painter.setFont(font);
    painter.setBrush(Qt::black);
    painter.setPen(QPen(QColor(175, 125, 255)));
    painter.drawText(QRect(QPoint(6, 6), size), Qt::AlignCenter, text);
    painter.end();

    setPixmap(QPixmap::fromImage(image));
}
#include "cluetagbtn.h"

ClueTagBtn::ClueTagBtn(QString tagText, QStringList* selectedTags, MainWindow* mainWindow, QWidget *parent)
    : QPushButton(parent)
{

    connect(this, &QPushButton::clicked, this, [=](){
        emit delSelectedTagSignal(tagText);
    });
    connect(this, &ClueTagBtn::delSelectedTagSignal, mainWindow, &MainWindow::delSelectedTagSignal);

}

调用类代码如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "cluetag.h"
#include <QDebug>
#include <QCompleter>
#include <QMessageBox>

//已选择的标签列表
QStringList* m_selectedTags;
//待选择的标签列表
QStringList m_tags;
//是否显示关闭按钮
bool isShowBtn;
//是否允许选中
bool allowSelect;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    initData();
    initWidget();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initData()
{
    m_tags << "<请选择>" << "小宋" << "小李" << "java" << "c++" << "cad" << "solidworks" << "ug" << "table tennes";
    m_selectedTags = new QStringList;
    //m_selectedTags[0] = m_tags;
    isShowBtn = true;
    allowSelect = false;
}

void MainWindow::initWidget()
{
    //初始化标签区域
    int x = 5;
    int y = 5;

    for (int i = 0; i < m_tags.size(); ++i) {

        QString text = QString(m_tags[i]).arg(i);

        if(!text.isEmpty())
        {
            ClueTag *clueTag = new ClueTag(text, allowSelect, isShowBtn, m_selectedTags, this, ui->widget);

            if(allowSelect)
            {
                //设置鼠标悬浮状态变成手形
                clueTag->setCursor(QCursor(Qt::PointingHandCursor));
            }
            clueTag->move(x, y);
            clueTag->show();
            clueTag->setAttribute(Qt::WA_DeleteOnClose);
            x += clueTag->width() + 5;
            if (x >= ui->widget->width() + 350)  //445
            {
                x = 5;
                y += clueTag->height() + 5;
            }
        }

        setMinimumSize(400, qMax(200, y + 5));
        setWindowTitle(tr("自定义标签"));
        setAcceptDrops(true);
    }
}

void MainWindow::delSelectedTagSignal(QString tagText)
{
    //清空所有内容
    QList<ClueTag*> tags = ui->widget->findChildren<ClueTag*>();
    foreach (ClueTag* tag, tags)
    {
        delete tag;
    }

    for(int i=0; i<m_selectedTags[0].size(); i++)
    {
        if(m_selectedTags[0][i] == tagText)
        {
            m_selectedTags[0].removeAt(i);
        }
    }

    int x = 5;
    int y = 5;

    qDebug() << ui->widget->width();

    for (int i = 0; i < m_selectedTags[0].size(); ++i) {

        QString text = QString(m_selectedTags[0][i]);

        if(!text.isEmpty())
        {
            ClueTag *clueTag = new ClueTag(text, allowSelect, isShowBtn, m_selectedTags, this, ui->widget);

            //设置鼠标悬浮状态变成手形
            //wordLabel->setCursor(QCursor(Qt::PointingHandCursor));
            clueTag->move(x, y);
            clueTag->show();
            clueTag->setAttribute(Qt::WA_DeleteOnClose);
            x += clueTag->width() + 5;
            if (x >= + ui->widget->width() - 200)  //445
            {
                x = 5;
                y += clueTag->height() + 5;
            }
        }
        setMinimumSize(200, qMax(200, y + 5));
    }
}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值