本文共 12544 字,大约阅读时间需要 41 分钟。
按上一篇绘制自定义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#includeclass 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#include #include 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); }}
转载地址:http://hxvx.baihongyu.com/