Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When customizing a Qt slider with a Style in code, the handle goes off the groove

Tags:

c++

qt

In most examples, customizing the Qt slider is done like this (with a stylesheet):

mySlider = new QSlider(centralWidget);
mySlider->setObjectName(QStringLiteral("mySlider"));
mySlider->setGeometry(QRect(645, 678, 110, 21));
mySlider->setOrientation(Qt::Horizontal);
mySlider->setStyleSheet("QSlider::groove:horizontal {background-image:url(:/main/graphics/mySliderBackround.png);}"
               "QSlider::handle:horizontal {background-image:url(:/main/graphics/mySliderHandle.png); height:21px; width: 21px;}");

This works fine for me as well.

I have a situation where I need to programmatically set the background using a dyamically created pixmap. Using the code below, this is how I accomplished it. The problem is that when I am on Fedora Linux, this slider works fine. When I'm on OSX or Windows, the slider handle goes off the goove.

Here's what it looks like on OSX. Notice how the handle is off the groove. The left side is customized with a stylesheet, the right is customized with the Style object below.

Style sheet customization vs customization with a Style object

Create the slider and assign the style:

mySlider = new QSlider(centralWidget);
mySlider->setObjectName(QStringLiteral("mySlider"));
mySlider->setGeometry(QRect(645, 678, 110, 21));
mySlider->setOrientation(Qt::Horizontal);
mySlider->setStyle(new MySliderStyle(mySlider->style()));

The custom slider style code:

Header

#ifndef MYSTYLE_H
#define MYSTYLE_H

#include <QObject>
#include <QWidget>
#include <QProxyStyle>
#include <QPainter>
#include <QStyleOption>
#include <QtWidgets/QCommonStyle>

class MySliderStyle : public QProxyStyle
{
      private:
    QPixmap groovePixmap;
    QPixmap handlePixmap;

      public:
    LightingSliderStyle(QStyle *style)
        : QProxyStyle(style)
    {
        setColor(QColor::fromRgba(0));

        this->handlePixmap = <snip initialize the pixmap>;
        this->grooveMaskPixmap = <snip initialize the pixmap>;
    }

    void drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const;

    QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const;

    void setColor(QColor color);
};

#endif // MYSTYLE_H

Implementation*

#include "MySliderStyle.h"

QRect MySliderStyle::subControlRect(ComplexControl control,
                      const QStyleOptionComplex *option,
                      SubControl subControl,
                      const QWidget *widget) const
{
    QRect rect;

    rect = QCommonStyle::subControlRect(control, option, subControl, widget);

    if (control == CC_Slider && subControl == SC_SliderHandle)
    {
        // this is the exact pixel dimensions of the handle png files
        rect.setWidth(21);
        rect.setHeight(21);
    }
    else if (control == CC_Slider && subControl == SC_SliderGroove)
    {
        // this is the exact pixel dimensions of the slider png files
        rect.setWidth(widget->width());
        rect.setHeight(widget->height());
    }

    return rect;
}

void MySliderStyle::drawComplexControl(QStyle::ComplexControl control,
                         const QStyleOptionComplex *option,
                         QPainter *painter,
                         const QWidget *widget) const
{
    if (control == CC_Slider)
    {
        if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
        {
            QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
            QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);

            if ((slider->subControls & SC_SliderGroove) && groove.isValid())
            {
                Qt::BGMode oldMode = painter->backgroundMode();
                painter->setBackgroundMode(Qt::TransparentMode);
                painter->drawPixmap(groove, groovePixmap);
                painter->setBackgroundMode(oldMode);
            }

            if ((slider->subControls & SC_SliderHandle) && handle.isValid())
            {
                Qt::BGMode oldMode = painter->backgroundMode();
                painter->setBackgroundMode(Qt::TransparentMode);
                painter->drawPixmap(handle, handlePixmap);
                painter->setBackgroundMode(oldMode);
            }
        }
    }
    else
    {
        QProxyStyle::drawComplexControl(control, option, painter, widget);
    }
}

void MySliderStyle::setColor(QColor color)
{
  QImage myGrooveImage;

  // <snip>
  // Code to create the custom pixmap
  // <snip>

    groovePixmap = QPixmap::fromImage(myGrooveImage);
}

UPDATE The code for this project is open source and available here

like image 495
101010 Avatar asked Jul 26 '17 13:07

101010


1 Answers

A call to QCommonStyle::subControlRect and adjusting the width/height is not enough. You must also recalculate the x/y position.

You can therfore use the QCommonStyle::subControlRect function as reference to calculate the proper rectangle:

QRect LightingSliderStyle::subControlRect(ComplexControl control,
                      const QStyleOptionComplex *option,
                      SubControl subControl,
                      const QWidget *widget) const
{
    if (control == CC_Slider)
    {
        if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
            QRect ret;

            int tickOffset = 0;
            int thickness = 21;     // height
            int len = 21;           // width

            switch (subControl) {
            case SC_SliderHandle: {
                int sliderPos = 0;
                bool horizontal = slider->orientation == Qt::Horizontal;
                sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum+1,
                                                    slider->sliderPosition,
                                                    (horizontal ? slider->rect.width()
                                                                : slider->rect.height()) - len,
                                                    slider->upsideDown);
                if (horizontal)
                    ret.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness);
                else
                    ret.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len);
                break; }
            case SC_SliderGroove:
                if (slider->orientation == Qt::Horizontal)
                    ret.setRect(slider->rect.x(), slider->rect.y() + tickOffset,
                                slider->rect.width(), thickness);
                else
                    ret.setRect(slider->rect.x() + tickOffset, slider->rect.y(),
                                thickness, slider->rect.height());
                break;
            default:
                break;
            }
            return visualRect(slider->direction, slider->rect, ret);
        }
    }

    return QCommonStyle::subControlRect(control, option, subControl, widget);
}
like image 107
veeman Avatar answered Oct 12 '22 21:10

veeman