QLabel重绘实现圆角矩形图片/文本和图片同时显示

QLabel 是Qt中一个很常用的原生基础控件,一般用于显示文本内容(支持富文本),文本可以设置支持鼠标选中复制,也可以设置在可视宽度内文本自动换行,另外还可以用于显示图片。

但是,使用QLabel显示图片时,qss样式设置的border-radius圆角属性是不生效的!

QLabel显示纯文本时,qss中设置了背景颜色后,border-radius圆角属性是有效的,而在显示图片时,border-radius圆角属性却会失效;另外,同一个QLabel无法同时显示图片和文本(设置富文本的方式可以,但是不灵活,图片和文字的间距无法调整)。

下图所示,同一个QLabel设置一样的qss样式后,显示图片和显示文本的圆角属性效果不同,显示图片时根本没有圆角:

QLabel重绘实现圆角矩形图片/文本和图片同时显示_第1张图片

所以我对 QLabel 的需求是:

1.显示图片时,可以给图片设置圆角;

2.同一个QLabel可以同时显示图片和文本,且可以设置图片在文字的左边或者右边,间距也可以调整,甚至文本会随尺寸变化(当文本不能完全显示时,变成省略号截断:右边截断/中间截断/左边截断);

以上两点需求都是原生QLabel无法支持的,所以需要继承QLbel重绘。

以下代码是简陋实现的Demo,看懂了你需要自己去完善它,封装成更加通用的控件,例如:提供更多接口去设置上下左右内边距,内容间距,图片尺寸,图片在文本的上下左右等。。。使劲魔改成适合自己的Label吧

mylabel.h

#ifndef MYLABEL_H
#define MYLABEL_H

#include 

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

#include "mylabel.h"
#include 
#include 
#include 

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等)是无效的)

QLabel
{
	color: rgb(88, 148, 67);
	font-size:16px;
}

一个MyLabel同时显示圆角图片和文本,文本支持随尺寸变化而右边省略号截断;

QLabel重绘实现圆角矩形图片/文本和图片同时显示_第2张图片

再补充一种非绘制手段将QPixmap的直角图片处理成圆角的QPixmap图片,直接塞给QLabel显示,此种方法不需要继承QLabel重绘,更简单:

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图片,我感觉会影响效率。

你可能感兴趣的:(qt)