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

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

相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能16 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G16 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt