按上一篇绘制自定义QSpinBox的过程,再来绘制一个QSpinBox。
设计图:
把按钮放上面
在这之前先看一下成品:
上一篇说了,绘制自定义QSpinBox实际上就是给QSpinBox中的这些原始组成元素指定好位置并绘制出来。
设计这些元素的尺寸如下:
即确定子控件位置的subControlRect()函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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()函数开始,首先绘制上下按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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),即给四周留下了一点空间显得不拥挤。绘制完上下按钮的背景时的效果:
然后是绘制按钮的图标,
1 2 3 | 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);效果:
不太好看,我们按设计图给它画上三角形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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加上边框:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); |
此时的效果:
到这基本完成,然而编辑框里的数字太小了,这里可以设置它的字体并设置文本居中:
1 2 3 4 | ui->spinBox->setAlignment(Qt::AlignCenter); QFont f; f.setPixelSize(24); ui->spinBox->setFont(f); |
最终效果:
样式完整代码:
.h文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | #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); } } |