04 自定义Button组件:令人抓狂的QToolButton文本图标居中问题

系列文章目录

01 Qt自定义风格控件的基本原则-CSDN博客

02 从QLabel聊起:自定义控件扩展-图片控件-CSDN博客

03 从QLabel聊起:自定义控件扩展-文本控件-CSDN博客

目录

系列文章目录

前言

一、从QToolButton的渲染源码讲起

1. QToolButton渲染源码

2. 文本+图标居中显示繁琐的原因

二、自定义Button控件的使用场景

1.单一文本按钮风格:

 2.单一图标风格:

3.图标+文本风格:

三、实现思路 

1.概述

2.功能接口举例

3.按钮渲染部分代码示例

总结


前言

相信按钮控件对于任何一个QtUI开发者都不陌生,真的是极其普遍与通用的一个基础控件!而且,在Qt-Widget框架中, QToolButton类不仅具备基本的按钮点击功能, 而且还预置了几种风格:

  • Qt::ToolButtonIconOnly  只显示图标
  • Qt::ToolButtonTextOnly 只显示文本
  • Qt::ToolButtonTextBesideIcon 图标在左、文本在右
  • Qt::ToolButtonTextUnderIcon 图标在上、文本在下
  • Qt::ToolButtonFollowStyle

乍一看,似乎已经满足开发者的各种业务需要。

然而,有一天我突然发现,当我想实现居中显示文本+图标这一极其简单的业务场景(ToolButtonTextBesideIcon )时,竟然复杂的让人恼火!

这也是我决定封装一个自定义的按钮组件功能类的一个起因!

至于为什么实现文本+图标居中显示效果极其繁琐,下面的篇幅我会简单陈述!

既聊思路,也说代码!我们开始今天的Button扩展控件类的分享!


一、从QToolButton的渲染源码讲起

1. QToolButton渲染源码

以下为Qt源码中关于QToolButton按钮渲染的部分代码:

//Qt::ToolButtonTextBesideIcon  
pr.setWidth(pmSize.width() + 4);
tr.adjust(pr.width(), 0, 0, 0);
pr.translate(shiftX, shiftY);
if (!hasArrow) 
{
   proxy()->drawItemPixmap(p, QStyle::visualRect(opt->direction, rect, pr),Qt::AlignCenter, pm);
} else {
     drawArrow(proxy(), toolbutton, pr, p, widget);
}
 alignment |= Qt::AlignLeft | Qt::AlignVCenter;

tr.translate(shiftX, shiftY);
const QString text = d->toolButtonElideText(toolbutton, tr, alignment);

 proxy()->drawItemText(p, QStyle::visualRect(opt->direction, rect, tr), alignment, toolbutton->palette,toolbutton->state & State_Enabled, text,QPalette::ButtonText);

 详情见qcommonstyle.cpp中函数:QCommonStyle::drawControl

2. 文本+图标居中显示繁琐的原因

在以上源码中, pr为pixmap的绘制Rect, tr为text的绘制Rect。drawItemPixmap为绘制按钮图标,drawItemText为绘制按钮文本。

从第1、2行就可以知道,Qt源码中图标显示区域与文本显示区域的间隙是固定的4像素,

所以,如果想图标、文本居中显示,需要在qss样式表中, 结合按钮宽高值,图标的宽高值、各种设置margin、padding参数,才能达到居中效果!

着实让我有些头痛!

二、自定义Button控件的使用场景

1.单一文本按钮风格:

示意图 2-1

 2.单一图标风格:

示意图2-2

3.图标+文本风格:

 

示意图2-3
​​​​​​

三、实现思路 

1.概述

为了沿用按钮的基本功能接口, 我们需要继承QAbstractButton类, 然后在paintEvent中实时绘制图标与文本内容, 必要的话还要区分三态效果(包括:默认态、Hover态、Press态)

2.功能接口举例

public:
    //按钮风格
    enum ButtonStyle
    {
        IconOnly = 0,
        TextOnly,
        TextBesideIcon,
        TextUnderIcon
    };

    explicit QUiButton(QWidget *parent = nullptr);
    //设置按钮风格
    void setButtonStyle(ButtonStyle style);
    //设置按钮图标
    void setIcons(const QString& normal);
    //设置文本颜色
    void setTextColor(const QColor& clr);
    //图标、文本间距
    void setSpacing(int spacing);
    //设置按钮圆角
    void setBorderRadius(int radius);
    //设置按钮背景色
    void setBackground(const QColor& clr);

3.按钮渲染部分代码示例

void QUiButton::paintEvent(QPaintEvent *pEvt)
{
    QStylePainter paint(this);
    paint.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    drawBackground(&paint);
    if(m_eButtonStyle == IconOnly)
    {
        drawIconOnly(&paint);
    }
    else if(m_eButtonStyle == TextOnly)
    {
        drawTextOnly(&paint);
    }
    else if(m_eButtonStyle == TextBesideIcon)
    {
        drawTextBesideIcon(&paint);
    }
    else if(m_eButtonStyle == TextUnderIcon)
    {
       drawTextUnderIcon(&paint);
    }
}

 绘制图标+文本居中效果部分参考代码:

void QUiButton::drawTextBesideIcon(QStylePainter *paint)
{
    QRect rcContents = contentsRect();
    QFontMetrics metric(font());
    int iTextW = metric.horizontalAdvance(text());
    int iTextH = metric.leading() + metric.ascent() + metric.descent();
    int iTotalWidth = iconSize().width() + m_iSpacing + iTextW;
    int iTotalHeight = qMax(iconSize().height(), iTextH);
    QRect rcDisplay( (rcContents.width() - iTotalWidth)/2
                     , (rcContents.height() - iTotalHeight)/2
                     , iTotalWidth, iTotalHeight);

    QRect rcPix = rcDisplay;
    QRect rcText = rcDisplay;
    rcPix.setWidth(iconSize().width());//左上角不变,缩小宽度
    rcText.adjust(rcPix.width() + m_iSpacing, 0, 0, 0);//
    //绘制图标
    QPixmap pix = m_iconNormal.pixmap(qt_getWindow(this), rcDisplay.size().boundedTo(iconSize()));
    paint->drawItemPixmap(rcPix, Qt::AlignCenter, pix);
    //绘制文本
    paint->setFont(font());
    paint->setPen(m_textClrNormal);
    paint->drawText(rcText, Qt::AlignCenter, text());
}

总结

以上就是今天要分享的内容,本篇主要针对QToolButton功能接口进行功能仿写与扩展,然后独立出一个自定义的组件功能类Button。

既聊思路,也说代码!我们下次继续分享自定义风格扩展组件!

PS:本专栏所有篇幅涉及的UI扩展组件类,后面会封装成插件动态库,感兴趣的同学可以留言哦!

你可能感兴趣的:(Qt-GUI:自定义组件的思路,qt)