Я довольно хорошо понял, как настроить QSlider с помощью таблиц стилей, но мне интересно, можно ли сделать следующее:
Мне бы хотелось, чтобы ручка ползунка сменила цвет с синего на желтый. Когда установлено слева, это синий; и когда вы переместите его вправо, он будет иметь градиент от синего до желтого.
Если это возможно через таблицы стилей, как? И если нет, как я могу реализовать это в paintEvent подкласса QSlider?
На самом деле вам не нужно делать что-то необычное, запас 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 и использует его для смешивания from
а также to
цвета, чтобы установить цвет ручки на соответствующее значение. Довольно странно, QColor
не было операторов для умножения и сложения, что может пригодиться.
Кроме того, вместо смешения двух цветов вы можете создать цвет в формате HSL, что даст вам немного другой градиент. изменения from/to
от QColor
с оттенками 42 и 202 соответственно вы можете:
QColor c = QColor::fromHsl(205 - (205 - 42) * rv, 200, 135);
Это даст вам цветовой оттенок для оттенка, а не для смешивания двух фиксированных цветов, что может быть более применимо в контексте температуры:
Обратите внимание, что теперь посередине вы получаете цвет голубого цвета, а не «зомби» зеленый, и вы получаете чистый зеленый, прежде чем перейти к оранжевому цвету.
Я не верю, что вы можете сделать это, используя простую таблицу стилей. Но это легко выполнимо, специализируясь 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::blue
, Qt::yellow
(заканчивается немного другой окраской).
Это сделает это: