是否可以根据其位置改变QSlider手柄的颜色?

时间:2016-10-19 13:18:23

标签: c++ qt qtstylesheets qslider

我非常了解如何通过样式表自定义QSlider,但我想知道是否可以执行以下操作:

enter image description here

我希望滑块的手柄从蓝色变为黄色。当设置在左侧时,它是蓝色的;当你向右移动时,它会有一个从蓝色到黄色的渐变。

如果可以通过样式表,怎么做?如果没有,如何在QSlider的子类的paintEvent中实现它?

2 个答案:

答案 0 :(得分:11)

实际上你并不需要做任何花哨的事情,股票QSlider已经有valueChanged(int)信号,所以你可以将它连接到一个根据位置混合两种颜色的函数并设置样式颜色。这是一个最小的例子:

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;
};

这适用于任何滑块范围和方向,代码基本上找到0.0到1.0范围内的相对句柄位置,并使用它来混合fromto颜色以将句柄颜色设置为各自的价值。奇怪的是,QColor没有运算符乘法和加法,这可以派上用场。

enter image description here

此外,您可以使用HSL格式构建颜色,而不是在两种颜色之间混合,这将为您提供略微不同的渐变。将from/toQColor分别更改为42和202,您可以:

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

这将为您提供色调的颜色扫描,而不是在两种固定颜色之间混合,这可能更适用于温度:

enter image description here

请注意,现在在中间你会得到一个青色的颜色,而不是“僵尸”绿色,你会在你变成橙色之前通过干净的绿色。

答案 1 :(得分:4)

我不相信你可以使用简单的样式表来做到这一点。但是,通过专门化QSlider类并在用户移动光标时应用适当的样式表(即:valueChanged被激活时),这很容易实现。

这是我写的一个可以解决问题的课程。它适用于水平和垂直光标,可以自定义使用任何颜色。它从QImage创建QLinearGradient以存储渐变颜色贴图,然后,当滑块值更改时,它会根据滑块的位置从图像中提取相应的颜色,并通过样式表应用它。

尝试使该类具有可重用性的通用性,但如果您不需要自定义颜色并且仅使用水平滑块,则可以简化它。

<强> 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() + "}" );
}

现在就做:

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 ) );

从原始问题帖子中的图片中选取了颜色QColor(79,174,231)(〜蓝色)和QColor(251,192,22)(〜黄色),可以用Qt::blueQt::yellow替换(结果略有不同的颜色)。

这样做:

enter image description here enter image description here enter image description here