不使用样式表自定义QScrollBar:滑块边框与翻页控件异常求助
自定义QScrollArea滚动条(无样式表)的问题与修复
目标:移除滚动条顶部和底部箭头,仅绘制带圆角的滑块。当前实现存在两个问题:
- 滑块周围出现黑色边框
- 翻页控件(点击滚动条空白区域的翻页功能)无法正确更新/响应

原始代码
主窗口代码
#include "scrollarea.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::dellClass()) { ui->setupUi(this); ScrollArea* scrollArea = new ScrollArea(this); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setLineWidth(0); scrollArea->setWidgetResizable(true); QWidget* widget = new QWidget(scrollArea); widget->setObjectName("w"); widget->setStyleSheet("#w { background-color: rgba(40, 80, 120, 50); }"); QVBoxLayout* layout = new QVBoxLayout(widget); for (int i = 0; i < 20; i++) { QPushButton* btn = new QPushButton(widget); btn->setFixedHeight(32); layout->insertWidget(i, btn); } scrollArea->setWidget(widget); ui->centralWidget->layout()->addWidget(scrollArea); }
scrollarea.h代码
class VProxyStyle : public QProxyStyle { public: VProxyStyle(QStyle *style = nullptr) : QProxyStyle(style) { } QRect subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex* opt, QStyle::SubControl sc, const QWidget *widget = nullptr) const override { // https://doc.qt.io/qt-6/qstyle.html // SC_ScrollBarGroove: // Special sub-control which contains the area in which the slider handle may move if (cc == CC_ScrollBar || sc == QStyle::SC_ScrollBarGroove) { QRect rect = QProxyStyle::subControlRect(cc, opt, sc, widget); // Exclude the top and bottom arrows area. rect.setTop(0); rect.setBottom(rect.bottom() + 17); return rect; } // The rect of top and bottom arrows. if (sc == QStyle::SC_ScrollBarAddLine || sc == QStyle::SC_ScrollBarSubLine) { return QRect(); } return QProxyStyle::subControlRect(cc, opt, sc, widget); } }; class VScrollBar : public QScrollBar { Q_OBJECT public: VScrollBar(Qt::Orientation orientation, QWidget *parent) : QScrollBar(orientation, parent) { VProxyStyle *proxyStyle = new VProxyStyle(style()); setStyle(proxyStyle); } void paintEvent(QPaintEvent *event) override { //QScrollBar::paintEvent(event); QPainter painter(this); //painter.eraseRect(event->rect()); painter.setRenderHint(QPainter::Antialiasing); QRect sliderRect = sliderHandleRect(); painter.setBrush(Qt::red); painter.drawRoundedRect(sliderRect, 8, 8); } QRect sliderHandleRect() const { QStyleOptionSlider opt; initStyleOption(&opt); return style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); } }; class ScrollArea : public QScrollArea { Q_OBJECT public: ScrollArea(QWidget* parent = 0) : QScrollArea(parent) { setVerticalScrollBar(new VScrollBar(Qt::Vertical, this)); } };
修复方案
1. 移除滑块黑色边框
在VScrollBar::paintEvent中,绘制圆角矩形前设置画笔为无,避免默认黑色边框:
painter.setPen(Qt::NoPen);
2. 修复翻页控件的响应与绘制
调整VProxyStyle中滚动条子控件区域的计算逻辑,确保翻页区域(点击滚动条空白处)的坐标正确,同时清理滚动条背景避免绘制残留:
修改后的VProxyStyle:
class VProxyStyle : public QProxyStyle { public: VProxyStyle(QStyle *style = nullptr) : QProxyStyle(style) { } QRect subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex* opt, QStyle::SubControl sc, const QWidget *widget = nullptr) const override { if (cc == CC_ScrollBar) { const QStyleOptionSlider* sliderOpt = qstyleoption_cast<const QStyleOptionSlider*>(opt); if (!sliderOpt) return QProxyStyle::subControlRect(cc, opt, sc, widget); // 隐藏上下箭头控件 if (sc == SC_ScrollBarAddLine || sc == SC_ScrollBarSubLine) { return QRect(); } // 调整凹槽区域为整个滚动条高度 if (sc == SC_ScrollBarGroove) { QRect rect = QProxyStyle::subControlRect(cc, opt, sc, widget); rect.setTop(0); rect.setBottom(widget->height()); return rect; } // 重新计算翻页区域(滑块上方/下方的空白区域) if (sc == SC_ScrollBarPageUp || sc == SC_ScrollBarPageDown) { QRect sliderRect = subControlRect(cc, opt, SC_ScrollBarSlider, widget); QRect grooveRect = subControlRect(cc, opt, SC_ScrollBarGroove, widget); if (sc == SC_ScrollBarPageUp) { return QRect(grooveRect.left(), grooveRect.top(), grooveRect.width(), sliderRect.top() - grooveRect.top()); } else { return QRect(grooveRect.left(), sliderRect.bottom(), grooveRect.width(), grooveRect.bottom() - sliderRect.bottom()); } } } return QProxyStyle::subControlRect(cc, opt, sc, widget); } };
修改后的VScrollBar::paintEvent:
void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 清理背景,避免旧绘制残留 painter.fillRect(event->rect(), Qt::transparent); // 绘制带圆角的滑块 QRect sliderRect = sliderHandleRect(); painter.setPen(Qt::NoPen); painter.setBrush(Qt::red); painter.drawRoundedRect(sliderRect, 8, 8); }
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

