You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用样式表自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 12:29:58