需求
近期项目上需要用到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));
}
}