3

As reported here (https://bugreports.qt.io/browse/QTBUG-98093), QSlider component in QT is not working well in the new MacOS update.

If I add two or more horizontal sliders in the same window, dragging the grip in one slider affects the other ones. It may cause all of them to move together or may make the next one jump to an unexpected position.

This code below can reproduce the issues:

#include <QApplication>
#include <QDialog>
#include <QVBoxLayout>
#include <QSlider>

class Dialog: public QDialog
{
    QSlider* brokenSlider;
public:
    explicit Dialog(QWidget *parent = nullptr):QDialog(parent){
        auto mainLayout = new QVBoxLayout;
        brokenSlider = new QSlider(Qt::Horizontal, this);
        mainLayout->addWidget(brokenSlider);
        connect(brokenSlider, &QSlider::valueChanged, [&](){this->update();});

        mainLayout->addWidget(new QSlider(Qt::Horizontal, this));
        mainLayout->addWidget(new QSlider(Qt::Horizontal, this));
        setLayout(mainLayout);
    }
};


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Dialog g;
    g.exec();
}

I'm looking for a workaround for this Apple/QT bug.

Adriel Jr
  • 2,451
  • 19
  • 25

1 Answers1

2

I was able to fix the issue applying a custom stylesheet to the slider. However, doing that also creates a problem with the ticks that are not displayed. The solution I found was to extend QSlider and paint then manually:

myslider.h:

#pragma once

#include <QStylePainter>
#include <QStyleOptionSlider>
#include <QStyleOptionComplex>
#include <QSlider>
#include <QColor>
#include "math.h"

class MySlider:public QSlider
{
public:
    explicit MySlider(Qt::Orientation orientation, QWidget *parent = nullptr):QSlider(orientation, parent){};
    explicit MySlider(QWidget *parent = nullptr):QSlider(parent){
        this->setStyleSheet("\
                            QSlider::groove:horizontal {\
                                height: 8px; /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ \
                                background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4);\
                                margin: 2px 0;\
                            }\
                            \
                            QSlider::handle:horizontal {\
                                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);\
                                border: 1px solid #5c5c5c;\
                                width: 18px;\
                                margin: -2px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ \
                                border-radius: 3px;\
                            }\
                        ");
    };
protected:
    virtual void paintEvent(QPaintEvent *ev)
    {
        QStylePainter p(this);
        QStyleOptionSlider opt;
        initStyleOption(&opt);

        QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);

        // draw tick marks
        // do this manually because they are very badly behaved with style sheets
        int interval = tickInterval();
        if (interval == 0)
        {
            interval = pageStep();
        }

        if (tickPosition() != NoTicks)
        {
            for (int i = minimum(); i <= maximum(); i += interval)
            {
                int x = std::round((double)((double)((double)(i - this->minimum()) / (double)(this->maximum() - this->minimum())) * (double)(this->width() - handle.width()) + (double)(handle.width() / 2.0))) - 1;
                int h = 4;
                p.setPen(QColor("#a5a294"));
                if (tickPosition() == TicksBothSides || tickPosition() == TicksAbove)
                {
                    int y = this->rect().top();
                    p.drawLine(x, y, x, y + h);
                }
                if (tickPosition() == TicksBothSides || tickPosition() == TicksBelow)
                {
                    int y = this->rect().bottom();
                    p.drawLine(x, y, x, y - h);
                }
            }
        }

        QSlider::paintEvent(ev);
    }
};

In QT Creator, if using forms, the file above needs to be added to the promoted widgets list and then each QSlider needs to be promoted to use this class.

Partial credits to: https://stackoverflow.com/a/27535264/6050364

Update (Dec 2021): QT fixed this issue in QT 6.2.3

Adriel Jr
  • 2,451
  • 19
  • 25