QLabel 是Qt中一个很常用的原生基础控件,一般用于显示文本内容(支持富文本),文本可以设置支持鼠标选中复制,也可以设置在可视宽度内文本自动换行,另外还可以用于显示图片。
但是,使用QLabel显示图片时,qss样式设置的border-radius圆角属性是不生效的!
QLabel显示纯文本时,qss中设置了背景颜色后,border-radius圆角属性是有效的,而在显示图片时,border-radius圆角属性却会失效;另外,同一个QLabel无法同时显示图片和文本(设置富文本的方式可以,但是不灵活,图片和文字的间距无法调整)。
下图所示,同一个QLabel设置一样的qss样式后,显示图片和显示文本的圆角属性效果不同,显示图片时根本没有圆角:
所以我对 QLabel 的需求是:
1.显示图片时,可以给图片设置圆角;
2.同一个QLabel可以同时显示图片和文本,且可以设置图片在文字的左边或者右边,间距也可以调整,甚至文本会随尺寸变化(当文本不能完全显示时,变成省略号截断:右边截断/中间截断/左边截断);
以上两点需求都是原生QLabel无法支持的,所以需要继承QLbel重绘。
以下代码是简陋实现的Demo,看懂了你需要自己去完善它,封装成更加通用的控件,例如:提供更多接口去设置上下左右内边距,内容间距,图片尺寸,图片在文本的上下左右等。。。使劲魔改成适合自己的Label吧
mylabel.h
cpp
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
class MyLabel : public QLabel
{
public:
MyLabel(QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags());
// 设置需要显示的图片/图标等
void setPixmap(const QPixmap &pix);
protected:
// 重绘
void paintEvent(QPaintEvent *event) override;
private:
int m_leftPadding; // 左内边距
int m_rightPadding;// 右内边距
int m_topPadding;// 顶部内边距
int m_bottomPadding;// 底部内边距
int m_spacing;// 内容间距(图片和文本的间距)
QSize m_iconSize;// 图片尺寸
QPixmap m_pixmap;// 源图片缓存
};
#endif // MYLABEL_H
mylabel.cpp
cpp
#include "mylabel.h"
#include <QPaintEvent>
#include <QPainter>
#include <QPainterPath>
MyLabel::MyLabel(QWidget *parent, Qt::WindowFlags f) :
QLabel(parent,f)
{
// 部分属性初始化
m_leftPadding = 10;
m_rightPadding = 10;
m_topPadding = 10;
m_bottomPadding = 10;
m_spacing = 10;
m_iconSize = QSize(64,64);
// 获取 QLabel 文本所使用的字体尺寸
QFont labelFont = this->font(); // 获取 QLabel 的字体
QFontMetrics fontMetrics(labelFont); // 使用 QFontMetrics 获取字体尺寸信息
int textHeight = fontMetrics.height(); // 获取文本高度
// 设置最小尺寸
int h = textHeight + m_topPadding + m_bottomPadding;
this->setMinimumHeight(h); // 高度至少可以刚好显示文本高度
this->setMinimumWidth(textHeight*2);
}
void MyLabel::setPixmap(const QPixmap &pix)
{
// 缓存一下原图像,后续显示可能需要将原图像裁剪成圆角矩形再显示
m_pixmap = pix;
}
void MyLabel::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 设置画笔抗锯齿,平滑变换等属性
painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing|QPainter::SmoothPixmapTransform,true);
painter.setFont(this->font()); // 设置绘制文本的字体
QPalette palette = this->palette(); // 获取QSS样式中设置的调色板
QColor textColor = palette.color(QPalette::WindowText); // 获取文本颜色
painter.setBrush(Qt::NoBrush);//背景无颜色透明
painter.setPen(textColor);
// 如果有设置图片,就绘制图片再绘制文本,文本绘制需要避开图片显示区域
if(!m_pixmap.isNull())
{
int h = m_iconSize.height() + m_topPadding + m_bottomPadding;
this->setMinimumHeight(h);
this->setMinimumWidth(m_iconSize.width() + m_leftPadding + m_rightPadding);
QPixmap pix = m_pixmap.scaled(m_iconSize,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
QPainterPath pImgPah;
QRect rImage(m_leftPadding,m_topPadding,m_iconSize.width(),m_iconSize.height());
pImgPah.addRoundedRect(rImage,10,10);// 给路径添加一个圆角矩形区域,rImage就是图像要显示的地方的rect,然后10.0,10.0是指x和y的圆角半径。
painter.setClipPath(pImgPah);// 裁剪路径(把矩形裁剪成圆角矩形)
painter.drawPixmap(rImage,pix);// 把图像画在被裁剪后的目标区域
painter.setClipping(false);// 结束裁剪
// 绘制文本
QRect textRect = event->rect();
textRect = textRect.adjusted(m_leftPadding,m_topPadding,-m_rightPadding,-m_bottomPadding);
textRect = textRect.adjusted(m_iconSize.width()+m_spacing,0,0,0);// 图标与文本的间距
// 判断空间是否足够容纳文本,不够则省略号右截断模式
QFontMetrics fontMetrics(this->font());
QString elidedText = fontMetrics.elidedText(this->text(), Qt::ElideRight, textRect.width());
painter.drawText(textRect,Qt::AlignVCenter|Qt::AlignLeft,elidedText);
}
else // 没有设置图片,绘制纯文本,要忽略预留给图片显示的位置
{
// 绘制纯文本
QRect textRect = event->rect();
textRect = textRect.adjusted(m_leftPadding,m_topPadding,-m_rightPadding,-m_bottomPadding);
painter.setBrush(Qt::NoBrush);
// 判断空间是否足够容纳文本,不够则省略号右截断模式
QFontMetrics fontMetrics(this->font());
QString elidedText = fontMetrics.elidedText(this->text(), Qt::ElideRight, textRect.width());
painter.drawText(textRect,Qt::AlignVCenter|Qt::AlignLeft,elidedText);
}
}
注意:重写paintEvent后会导致原生QLabel支持的鼠标选中文本复制的功能异常,所以此种方式重绘不支持文本选中复制,需要自己想办法重写文本选中复制功能。由于文本选中复制功能过于复杂,一般需要文本复制功能的话我就直接使用QLabel了。
使用效果:
先给MyLabel设置QLabel的QSS(由于MyLabel是继承自QLabel所以可以使用它的部分qss属性例如:字体,字体颜色等,但是其他属性(background,border等)是无效的)
html
QLabel
{
color: rgb(88, 148, 67);
font-size:16px;
}
一个MyLabel同时显示圆角图片和文本,文本支持随尺寸变化而右边省略号截断;
再补充一种非绘制手段将QPixmap的直角图片处理成圆角的QPixmap图片,直接塞给QLabel显示,此种方法不需要继承QLabel重绘,更简单:
cpp
QPixmap getRoundedPixmap(const QPixmap &srcPixmap, const int &radius, const int &width, const int &height)
{
// 目标图片尺寸
QSize desSize(width, height);
// 新建一个目标大小的画布Qpixmap
QPixmap desPixMap(desSize);
// 填充透明色作为背景
desPixMap.fill(Qt::transparent);
//以QPixmap 为绘画背景进行画笔绘制
QPainter painter(&desPixMap);
painter.setRenderHints(QPainter::Antialiasing); //抗锯齿
painter.setRenderHints(QPainter::SmoothPixmapTransform); //平滑像素图变换
QPainterPath path;//绘制路径
//绘制圆角矩形,其中最后两个参数值的范围为(0-99),就是圆角的px值
path.addRoundedRect(0, 0, desSize.width(), desSize.height(), radius, radius);
// 将绘制的圆角矩形路径中内容进行裁剪
painter.setClipPath(path);
//将图片绘制到desPixmap中,IgnoreAspectRatio忽视图片比例
painter.drawPixmap(0, 0, desSize.width(), desSize.height(), srcPixmap.scaled(desSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
painter.setClipping(false); // 关闭裁剪
return desPixMap;
}
注意:不建议在上面的PaintEvent绘制事件中调用这个函数获取处理成圆角的QPixmap再绘制,因为它比上面的方法多一次拷贝QPixmap图片,我感觉会影响效率。