Qt自定义滚动条(不使用样式表)

时间:2022-02-22 23:27:34

前面使用Qt 样式表实现滚动条,在实际工作中,发现存在一些瑕疵,例如如果在主窗口中绘制背景,则有可能给滚动条染色,还有如果想实现特殊的效果,则必须使用自定义风格,即从QStyle的子类派生出新的类型。以下从QProxyStyl派生出新的风格来实现自定义滚动(Qt4、Qt5均存在QCommonStyle、QProxyStyle,其余的风格在Qt5中已经不存在,不过原理一致)。

首先QScrollbar重绘时直接调用

drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const

由此开始我们的自定绘制滚动条:

void MyStyle::drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const
{
switch (control)
{
case CC_ScrollBar:
drawScrollbar(control, option, painter, widget);
return;
default:
break;
}
__super::drawComplexControl(control, option, painter, widget);
}

绘制代码的实现(模拟QQ)

void MyStyle::drawScrollbar( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const
{
//QQ中滚动条的效果是:如果不在滚动条上,默认上下箭头不显示,AddPage, SubPage也不显示。
//如果在滚动条上,如果不在箭头所在的区域,则显示normal,否则显示hot,按下显示down。
//这里按这个标准实现。
const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option);
if(!scrollbar) return;
bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
if(maxedOut) return;

painter
->save();
painter
->setRenderHint(QPainter::Antialiasing);
QRect r
= option->rect;

const State state = option->state;
const SubControls sub = option->subControls;

State flags
= option->state;
if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
flags
|= State_MouseOver;

QRect drawRect;

if (maxedOut)
flags
&= ~State_Enabled;

const bool pressed = flags & State_Sunken;
const bool mouseOver = flags & State_MouseOver;
const bool isHorz = flags & State_Horizontal;
const bool isRTL = option->direction == Qt::RightToLeft;

QColor bkFillColor
= Qt::white;
QColor bkSliderNormal
= Qt::lightGray;
QColor bkSliderHover
= Qt::gray;
QColor bkSliderPress
= Qt::darkGray;

//清除所有子控件背景
//上下(左右)箭头可能和沟槽的背景不一样
drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
painter
->fillRect(drawRect, bkFillColor);

drawRect
= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
painter
->fillRect(drawRect, bkFillColor);

//沟槽的颜色
drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget);
painter
->fillRect(drawRect, Qt::green);
//在mouse over状态下,才绘制上下左右箭头。
//scrollbar->activeSubControls用于指定当前鼠标所在的子控件
const QStyle::SubControls sc = scrollbar->activeSubControls;
if(!mouseOver)
{
//只绘制Slider,其余清除
drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);

QPainterPath painterPath;
painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(),
4, 4);
painter
->fillPath(painterPath, bkSliderNormal);
}
else
{
//合并绘制AddPage SubPage
drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget);
painter
->fillRect(drawRect, QColor(192, 192, 192, 127));

if (sub & SC_ScrollBarAddLine) //画加号,应该区分水平和垂直
{
drawRect
= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
QPixmap pixmap;
if(isHorz)
{
if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowright_down.png");
else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowright_highlight.png");
else pixmap.load(":/ScrollBar/scrollbar_arrowright_normal.png");
}
else
{
if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowdown_down.png");
else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowdown_highlight.png");
else pixmap.load(":/ScrollBar/scrollbar_arrowdown_normal.png");
}

painter
->drawPixmap(drawRect, pixmap);
}
if (sub & SC_ScrollBarSubLine)
{
drawRect
= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
QPixmap pixmap;

if(isHorz)
{
if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowleft_down.png");
else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowleft_highlight.png");
else pixmap.load(":/ScrollBar/scrollbar_arrowleft_normal.png");
}
else
{
if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowup_down.png");
else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowup_highlight.png");
else pixmap.load(":/ScrollBar/scrollbar_arrowup_normal.png");
}

painter
->drawPixmap(drawRect, pixmap);
}
if (sub & SC_ScrollBarSlider)
{
drawRect
= subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);

QPainterPath painterPath;
painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(),
4, 4);
if(sc & SC_ScrollBarSlider)
{
painter
->fillPath(painterPath, bkSliderPress);
}
else
{
painter
->fillPath(painterPath, bkSliderHover);
}
}

}
painter
->restore();
}

效果如下:

Qt自定义滚动条(不使用样式表) Qt自定义滚动条(不使用样式表)

左为鼠标不在滚动条区域,右边为鼠标在滚动条区域

这里使用绿色做沟槽的背景,方便调试。