QT风格(QStyle):绘制一个自定义QSpinBox(2)

按上一篇绘制自定义QSpinBox的过程,再来绘制一个QSpinBox。

设计图:

 把按钮放上面

在这之前先看一下成品:

 上一篇说了,绘制自定义QSpinBox实际上就是给QSpinBox中的这些原始组成元素指定好位置并绘制出来。

设计这些元素的尺寸如下:

即确定子控件位置的subControlRect()函数:

QRect mySpinboxStyle::subControlRect(ComplexControl whichControl,const QStyleOptionComplex *option,SubControl whichSubControl,const QWidget *widget) const
{
    if (whichControl == CC_SpinBox)
    {
        switch (whichSubControl)
        {
            case SC_SpinBoxFrame:
                return option->rect;
            case SC_SpinBoxEditField:
                return QRect(option->rect.x(), option->rect.height() * 0.4 , option->rect.width(), option->rect.height() * 0.6).adjusted(+10, +10, -10, -10);
            case SC_SpinBoxDown:
                return QRect(option->rect.width()/2,0,option->rect.width()/2,option->rect.height()*0.4);
            case SC_SpinBoxUp:
                return QRect(0,0,option->rect.width()/2,option->rect.height()*0.4);
            default:
                return QRect();
        }
    }
    else
    {
        return QProxyStyle::subControlRect(whichControl, option,whichSubControl, widget);
    }
}

然后开始绘制:

绘制从drawComplexControl()函数开始,首先绘制上下按钮

 

void mySpinboxStyle::drawBronzeSpinBoxButton(SubControl which, const QStyleOptionComplex *option,QPainter *painter,const QWidget * widget) const
{
    PrimitiveElement element;
    QRect buttonRect = option->rect;

    if (which == SC_SpinBoxUp)//上按钮
    {
        buttonRect.translate(0, 0);//translate矩形移到指定位置
        element = PE_IndicatorSpinPlus;
    }
    else if(which == SC_SpinBoxDown)
    {
        buttonRect.translate(buttonRect.width() / 2, 0);
        element = PE_IndicatorSpinMinus;
    }
    buttonRect.setWidth(buttonRect.width() / 2);
    buttonRect.setHeight(buttonRect.width() * 0.4);
    QStyleOption buttonOpt(*option);
    buttonOpt.rect = buttonRect;

    //绘制背景开始
    painter->save();
    painter->setClipRect(buttonRect);//在此范围内绘制背景
    if (option->activeSubControls != which)//不是当前活动的子控件
    {
        buttonOpt.state &= ~(State_MouseOver/*在鼠标下面*/ | State_On/*按下*/ | State_Sunken/*凹陷*/);
    }
    QLinearGradient gradient(0, 0, 0, buttonOpt.rect.height());//y轴使用渐变
    gradient.setColorAt(0.0, QColor("#5ee7df"));
    gradient.setColorAt(1.0, QColor("#b490ca"));
    painter->setPen(Qt::NoPen);
    painter->setBrush(gradient);
    QRect roundRect = buttonOpt.rect.adjusted(+1, +1, -1, -1);
    int diameter = 12;
    int cx = 100 * diameter / buttonOpt.rect.width();
    int cy = 100 * diameter / buttonOpt.rect.height();

    painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
    if (buttonOpt.state & (State_On | State_Sunken))//按下时,绘制一层暗色
    {
        QColor slightlyOpaqueBlack(0, 0, 0, 63);
        painter->setBrush(slightlyOpaqueBlack);
        painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
    }
    //绘制背景结束
    painter->restore();

    //绘制图标
    QStyleOption arrowOpt(buttonOpt);
    QRect subRect = subControlRect(CC_SpinBox, option, which);
    arrowOpt.rect = subRect.adjusted(+subRect.width() * 0.3, +subRect.height() * 0.3,
                                     -subRect.width() * 0.3, -subRect.height() * 0.3);
    drawPrimitive(element, &arrowOpt, painter);
}

这个函数用来绘制上下按钮的背景和按钮上面的图标

背景使用了线性渐变,如果没有好的渐变配色方案可以参考这里:收藏 | 四个免费的渐变配色网站!

这里绘制的时候使用了adjusted(+1, +1, -1, -1),即给四周留下了一点空间显得不拥挤。绘制完上下按钮的背景时的效果:

然后是绘制按钮的图标,

    QRect subRect = subControlRect(CC_SpinBox, option, which);
    arrowOpt.rect = subRect.adjusted(+subRect.width() * 0.3, +subRect.height() * 0.3,
                                     -subRect.width() * 0.3, -subRect.height() * 0.3);

这里从subControlRect()获取上下按钮的范围之后再次压缩了范围

在此范围内绘制图标

这里设置了上下按钮分别使用PE_IndicatorSpinPlus / PE_IndicatorSpinMinus,默认是代表加减号的意思。

如果使用默认设置,在drawPrimitive()中直接调用 QProxyStyle::drawPrimitive(which, option, painter, widget);效果:

不太好看,我们按设计图给它画上三角形。 

void mySpinboxStyle::drawPrimitive(PrimitiveElement which, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
    switch (which)
    {
        case PE_IndicatorSpinPlus:
        {
            painter->save();
            painter->translate(option->rect.x(),option->rect.y());
            QPainterPath drawtriangle;  //画三角形
            drawtriangle.moveTo(0, option->rect.height());//左下角
            drawtriangle.lineTo(option->rect.width()/2, 0);//第二点坐标为(width/2,width/2)
            drawtriangle.lineTo(option->rect.width(), option->rect.height());//右下角,第三坐标(width, height),移动到右下角结束点,整体形成一个闭合路径
            drawtriangle.lineTo(0, option->rect.height());
            painter->setPen(QPen(QColor("#128bf1"), 2));
            painter->drawPath(drawtriangle);  //绘制出图形
            painter->restore();
        }
        break;
        case PE_IndicatorSpinMinus:
        {
            painter->save();
            painter->translate(option->rect.x(),option->rect.y());
            QPainterPath drawtriangle;  //画三角形
            drawtriangle.moveTo(0,0);
            drawtriangle.lineTo(option->rect.width()/2,option->rect.height());
            drawtriangle.lineTo(option->rect.width(),0);
            drawtriangle.lineTo(0,0);
            painter->setPen(QPen(QColor("#128bf1"), 2));
            painter->drawPath(drawtriangle);  //绘制出图形
            painter->restore();
        }
        break;
        default:
            QProxyStyle::drawPrimitive(which, option, painter, widget);
    }
}

 

这里注意:

painter指针是绘制一开始起从drawComplexControl()函数传过来的,每次绘制前设置painter->save();保存设置绘制完成painter->restore();恢复设置,到drawPrimitive()绘制的原点还是在上下按钮的(0,0)点,设置painter->translate(option->rect.x(),option->rect.y());是将绘制的原点设为绘制图标的原点,就比较方便

下一步是给SC_SpinBoxEditField加上边框:

        QRect rect = subControlRect(CC_SpinBox, option,SC_SpinBoxEditField).adjusted(-10, -10, +10, +10);
        painter->save();
        QLinearGradient gradient(0, 0, rect.width(), rect.height());//x轴使用渐变
        gradient.setColorAt(0.0, QColor("#fa709a"));
        gradient.setColorAt(1.0, QColor("#fee140"));
        painter->setPen(Qt::NoPen);
        painter->setBrush(gradient);
        QRect roundRect = rect.adjusted(+1, +1, -1, -1);
        int diameter = 12;
        int cx = 100 * diameter / rect.width();
        int cy = 100 * diameter / rect.height();
        painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
        painter->restore();

此时的效果:

到这基本完成,然而编辑框里的数字太小了,这里可以设置它的字体并设置文本居中:

    ui->spinBox->setAlignment(Qt::AlignCenter);
    QFont f;
    f.setPixelSize(24);
    ui->spinBox->setFont(f);

 最终效果:

样式完整代码:

.h文件:

#ifndef MYSPINBOXSTYLE_H
#define MYSPINBOXSTYLE_H

#include <QProxyStyle>

class mySpinboxStyle : public QProxyStyle
{
public:
    mySpinboxStyle();
    void drawComplexControl(ComplexControl which,const QStyleOptionComplex *option,QPainter *painter,const QWidget *widget = nullptr) const override;
    void drawBronzeSpinBoxButton(SubControl which, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const;
    QRect subControlRect(ComplexControl whichControl,const QStyleOptionComplex *option,SubControl whichSubControl,const QWidget *widget = nullptr) const override;
    void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
};

#endif // MYSPINBOXSTYLE_H

.cpp文件:

#include "myspinboxstyle.h"
#include <QPainter>
#include <QStyleOptionComplex>
#include <QDebug>

mySpinboxStyle::mySpinboxStyle()
{

}

QRect mySpinboxStyle::subControlRect(ComplexControl whichControl,const QStyleOptionComplex *option,SubControl whichSubControl,const QWidget *widget) const
{
    if (whichControl == CC_SpinBox)
    {
        switch (whichSubControl)
        {
            case SC_SpinBoxFrame:
                return option->rect;
            case SC_SpinBoxEditField:
                return QRect(option->rect.x(), option->rect.height() * 0.4 , option->rect.width(), option->rect.height() * 0.6).adjusted(+10, +10, -10, -10);
            case SC_SpinBoxDown:
                return QRect(option->rect.width()/2,0,option->rect.width()/2,option->rect.height()*0.4);
            case SC_SpinBoxUp:
                return QRect(0,0,option->rect.width()/2,option->rect.height()*0.4);
            default:
                return QRect();
        }
    }
    else
    {
        return QProxyStyle::subControlRect(whichControl, option,whichSubControl, widget);
    }
}

void mySpinboxStyle::drawComplexControl(ComplexControl which,const QStyleOptionComplex *option,QPainter *painter,const QWidget *widget) const
{
    if (which == CC_SpinBox)
    {
        drawBronzeSpinBoxButton(SC_SpinBoxDown, option, painter,widget);
        drawBronzeSpinBoxButton(SC_SpinBoxUp, option, painter,widget);

        QRect rect = subControlRect(CC_SpinBox, option,SC_SpinBoxEditField).adjusted(-10, -10, +10, +10);
        painter->save();
        QLinearGradient gradient(0, 0, rect.width(), rect.height());//x轴使用渐变
        gradient.setColorAt(0.0, QColor("#fa709a"));
        gradient.setColorAt(1.0, QColor("#fee140"));
        painter->setPen(Qt::NoPen);
        painter->setBrush(gradient);
        QRect roundRect = rect.adjusted(+1, +1, -1, -1);
        int diameter = 12;
        int cx = 100 * diameter / rect.width();
        int cy = 100 * diameter / rect.height();
        painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
        painter->restore();
    }
    else
    {
        return QProxyStyle::drawComplexControl(which, option, painter,widget);
    }
}

void mySpinboxStyle::drawBronzeSpinBoxButton(SubControl which, const QStyleOptionComplex *option,QPainter *painter,const QWidget * widget) const
{
    PrimitiveElement element;
    QRect buttonRect = option->rect;

    if (which == SC_SpinBoxUp)//上按钮
    {
        buttonRect.translate(0, 0);//translate矩形移到指定位置
        element = PE_IndicatorSpinPlus;//PE_IndicatorSpinPlus
    }
    else if(which == SC_SpinBoxDown)
    {
        buttonRect.translate(buttonRect.width() / 2, 0);
        element = PE_IndicatorSpinMinus;
    }
    buttonRect.setWidth(buttonRect.width() / 2);
    buttonRect.setHeight(buttonRect.width() * 0.4);
    QStyleOption buttonOpt(*option);
    buttonOpt.rect = buttonRect;

    //绘制背景开始
    painter->save();
    painter->setClipRect(buttonRect);//在此范围内绘制背景
    if (option->activeSubControls != which)//不是当前活动的子控件
    {
        buttonOpt.state &= ~(State_MouseOver/*在鼠标下面*/ | State_On/*按下*/ | State_Sunken/*凹陷*/);
    }
    QLinearGradient gradient(0, 0, 0, buttonOpt.rect.height());//y轴使用渐变
    gradient.setColorAt(0.0, QColor("#5ee7df"));
    gradient.setColorAt(1.0, QColor("#b490ca"));
    painter->setPen(Qt::NoPen);
    painter->setBrush(gradient);
    QRect roundRect = buttonOpt.rect.adjusted(+1, +1, -1, -1);
    int diameter = 12;
    int cx = 100 * diameter / buttonOpt.rect.width();
    int cy = 100 * diameter / buttonOpt.rect.height();

    painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
    if (buttonOpt.state & (State_On | State_Sunken))//按下时,绘制一层暗色
    {
        QColor slightlyOpaqueBlack(0, 0, 0, 63);
        painter->setBrush(slightlyOpaqueBlack);
        painter->drawRoundRect(roundRect, cx, cy);//绘制圆角矩形
    }
    //绘制背景结束
    painter->restore();

    //绘制图标
    QStyleOption arrowOpt(buttonOpt);
    QRect subRect = subControlRect(CC_SpinBox, option, which);
    arrowOpt.rect = subRect.adjusted(+subRect.width() * 0.3, +subRect.height() * 0.3,
                                     -subRect.width() * 0.3, -subRect.height() * 0.3);
    drawPrimitive(element, &arrowOpt, painter);
}

void mySpinboxStyle::drawPrimitive(PrimitiveElement which, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
    switch (which)
    {
        case PE_IndicatorSpinPlus:
        {
            painter->save();
            painter->translate(option->rect.x(),option->rect.y());
            QPainterPath drawtriangle;  //画三角形
            drawtriangle.moveTo(0, option->rect.height());//左下角
            drawtriangle.lineTo(option->rect.width()/2, 0);//第二点坐标为(width/2,width/2)
            drawtriangle.lineTo(option->rect.width(), option->rect.height());//右下角,第三坐标(width, height),移动到右下角结束点,整体形成一个闭合路径
            drawtriangle.lineTo(0, option->rect.height());
            painter->setPen(QPen(QColor("#128bf1"), 2));
            painter->drawPath(drawtriangle);  //绘制出图形
            painter->restore();
        }
        break;
        case PE_IndicatorSpinMinus:
        {
            painter->save();
            painter->translate(option->rect.x(),option->rect.y());
            QPainterPath drawtriangle;  //画三角形
            drawtriangle.moveTo(0,0);
            drawtriangle.lineTo(option->rect.width()/2,option->rect.height());
            drawtriangle.lineTo(option->rect.width(),0);
            drawtriangle.lineTo(0,0);
            painter->setPen(QPen(QColor("#128bf1"), 2));
            painter->drawPath(drawtriangle);  //绘制出图形
            painter->restore();
        }
        break;
        default:
            QProxyStyle::drawPrimitive(which, option, painter, widget);
    }
}

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值