Is it possible to change the color of a QSlider's handle according to its position?

北慕城南 提交于 2019-12-23 08:00:56

问题


I pretty well understood how to customize a QSlider through style sheets, but I am wondering if it is possible to do the following thing:

                                              

I'd like the handle of the slider to go from blue to yellow. When set on the left, it's blue; and when you move it to the right, it will have a gradient from blue to yellow.

If it is possible through the style sheets, how? And if not, how can I implement that in the paintEvent of a subclass of a QSlider?


回答1:


Actually you don't really have to do anything fancy, the stock QSlider already has the valueChanged(int) signal, so you can connect that to a function which mixes between the two colors based on the position and sets the style color. Here is a minimal example:

static QColor operator+(const QColor & a, const QColor & b) {
  return QColor(a.red() + b.red(), a.green() + b.green(), a.blue() + b.blue());
}
static QColor operator*(const QColor & c, const qreal r) {
  return QColor(c.red() * r, c.green() * r, c.blue() * r);
}

class Widget : public QWidget {
    Q_OBJECT
  public:
    Widget(QWidget *parent = 0) : QWidget(parent), from(248, 181, 20), to(64, 150, 214) {
      auto l = new QHBoxLayout(this);
      setLayout(l);
      s = new QSlider(Qt::Horizontal, this);
      s->setMinimum(0);
      s->setMaximum(100);
      l->addWidget(s);
      connect(s, &QSlider::valueChanged, this, &Widget::colorize);
      colorize(s->value());
    }
  private:
    void colorize(int v) {
      int d = s->maximum() - s->minimum();
      v = v - s->minimum();
      qreal rv = qreal(v) / d;
      QColor c = from * rv + to * (1.0 - rv);
      s->setStyleSheet(QString("QSlider::handle:horizontal {background-color: %1;}").arg(c.name()));
    }
    QSlider * s;
    QColor from, to;
};

This will work for any slider range and orientation, the code basically finds the relative handle position in range 0.0 to 1.0 and uses that to mix the from and to colors to set the handle color to the respective value. Oddly enough, QColor didn't have the operators to multiply and add, which can come quite in handy.

Additionally, instead of mixing between two colors you can construct a color in HSL format, which will give you a slightly different gradient. Changing from/to from QColor to hues 42 and 202 respectively, you can then:

QColor c = QColor::fromHsl(205 - (205 - 42) * rv, 200, 135);

This will give you a color sweep for the hue rather than mix between two fixed colors, which may be more applicable in the context of temperature:

Note that now in the middle you get a cyan-ish color rather than "zombie" green and you get through clean green before you get to orange.




回答2:


I don't believe you can do this using a simple style sheet. But that's easily doable by specializing QSlider class and applying an appropriate stylesheet when user moves the cursor (i.e: when valueChanged is emited).

Here is a class I wrote that does the trick. It works for horizontal and vertical cursor and can be customized to use any color. It create a QImage from a QLinearGradient to store the gradient color map, then, when slider value changes, it extracts the appropriate color from the image based on slider's position and applies it through a stylesheet.

Tried to make the class generic for reusability, but it could be simplified if you don't need to customize colors and only use horizontal sliders.

gradientslider.h:

#include <QSlider>
#include <QImage>
#include <QColor>

class GradientSlider : public QSlider
{
    Q_OBJECT
public:
    GradientSlider( QColor from, QColor to, Qt::Orientation orientation, QWidget* parent );

private slots:
    void changeColor( int );

private:
    QImage gradient;
};

gradientslider.cpp:

#include "gradientslider.h"
#include <QLinearGradient>
#include <QPainter>

GradientSlider::GradientSlider( QColor from, QColor to, Qt::Orientation orientation, QWidget* parent ) :
    QSlider( orientation, parent ),
    gradient( QSize(100,100), QImage::Format_RGB32 )
{
    // create linear gradient
    QLinearGradient linearGrad( QPointF(0, 0), (orientation==Qt::Horizontal) ? QPointF(100, 0) : QPointF(0, 100) );
    linearGrad.setColorAt(0, from);
    linearGrad.setColorAt(1, to);

    // paint gradient in a QImage:
    QPainter p(&gradient);
    p.fillRect(gradient.rect(), linearGrad);

    connect( this, SIGNAL(valueChanged(int)), this, SLOT(changeColor(int)) );

    // initialize
    changeColor( value() );
}

void GradientSlider::changeColor( int pos )
{
    QColor color;

    if ( orientation() == Qt::Horizontal )
    {
        // retrieve color index based on cursor position
        int posIndex = gradient.size().width() * ( pos - minimum() ) / (maximum() - minimum());
        posIndex = std::min( posIndex, gradient.width() - 1 );

        // pickup appropriate color
        color = gradient.pixel( posIndex, gradient.size().height()/2 );
    }
    else
    {
        // retrieve color index based on cursor position
        int posIndex = gradient.size().height() * ( pos - minimum() ) / (maximum() - minimum());
        posIndex = std::min( posIndex, gradient.height() - 1 );

        // pickup appropriate color
        color = gradient.pixel( gradient.size().width()/2, posIndex );
    }

    // create and apply stylesheet!
    // can be customized to change background and handle border!
    setStyleSheet( "QSlider::handle:" + (( orientation() == Qt::Horizontal ) ? QString("horizontal"):QString("vertical")) + "{ \
                               border-radius: 5px; \
                               border: 2px solid #FFFFFF; \
                               width: 20px; \
                               margin: -5px 0;   \
                               background: " + color.name() + "}" );
}

Now just do:

QHBoxLayout* layout = new QHBoxLayout( this );
// horizontal slider:
layout->addWidget( new GradientSlider( QColor(79,174,231), QColor(251,192,22), Qt::Horizontal, this ) );
// or, vertical slider:
layout->addWidget( new GradientSlider( QColor(79,174,231), QColor(251,192,22), Qt::Vertical, this ) );

Colors QColor(79,174,231) (~blue) and QColor(251,192,22) (~yellow) were picked up from the image in the original question post and can be replaced by Qt::blue, Qt::yellow (ending up with slightly different coloration).

This will do it:



来源:https://stackoverflow.com/questions/40132553/is-it-possible-to-change-the-color-of-a-qsliders-handle-according-to-its-positi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!