我想设计一个QSlider,它在设计表中看起来像这样:
无论滑块手柄是否位于其中心,所有滑块部件的滑块凹槽颜色都是相同的。在设计表中,如果滑块手柄向左或向右移动,它看起来像这样:
AFAIK,可以使用样式表更改滑块的添加/子页面颜色。
但是,我不知道如何更改它,使更改从中心到左侧和右侧部分完成,而不是从滑块的最左侧点完成。
我怎样才能实现这个目标?
虽然可以使用样式表实现这一点,但不建议这样做,因为实现的努力和复杂性不值得得到脆弱且参差不齐的结果。样式表有太多的移动部分,无法在保持平滑、连贯的外观/行为的同时有效处理。
基本思想是对 sub-page/add-page 子控件使用 QLinearGradient,并用一小部分(例如 0.00001)分隔颜色停止点。这样就实现了分段的外观。
每次移动手柄时尝试调整值会完成繁重的工作。
#include <QApplication>
#include <QtWidgets>
class StyleSheetCenteredSlider : public QSlider
{
public:
StyleSheetCenteredSlider(QWidget *parent = nullptr) : QSlider(parent)
{
setOrientation(Qt::Horizontal);
setMinimum(0);
setMaximum(100);
setValue(50);
styleSheet = QString(
"QSlider::sub-page:horizontal { background: black;}"
"QSlider::add-page:horizontal { background: black;}"
"QSlider::groove:horizontal {"
"border: 1px solid black;"
"}");
connect(this, &QSlider::valueChanged, this, &StyleSheetCenteredSlider::updateSliderStyle);
}
void updateSliderStyle(int value)
{
qreal stopPosition = static_cast<qreal>(value) / maximum();
qreal progressScaledDownStartingPosition;
qreal backgroundColorPosition;
if(stopPosition < 0.5)
{
//scale down from the whole groove to the add page
/*
Black is half of the whole slider : 0.5
EXAMPLE
0 0.3 0.5 1
|-Black-|-white-|----Black-----|
0 x 1
|-white-|----Black-----|
The white part above = 0.2, which is 0.5 - 0.3
but 0.2 is taken from the whole groove,
so scale down to the add page (devide by the length of the add page, which is (1 - 0.3 (stopPosition)))
*/
progressScaledDownStartingPosition = ((qreal)0.5 - stopPosition) / (1 - stopPosition);
backgroundColorPosition = progressScaledDownStartingPosition + (qreal)0.000001;
styleSheet.replace(QRegularExpression("QSlider::add-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::add-page:horizontal {"
"background: qlineargradient(x1:0, y1:0,"
"x2:1, y2:0,"
"stop:0 white,"
"stop:%1 white,"
"stop:%2 black,"
"stop:1 black);"
"}").arg(progressScaledDownStartingPosition)
.arg(backgroundColorPosition));
}
else
{
if(stopPosition > 0.5)
{
/*
Black is half of the whole slider : 0.5
EXAMPLE
0 0.5 0.6 1
|---Black----|-white--|---Black----|
0 x 1
|---Black----|-white--|
The white part above = 0.1, which is 0.6 - 0.5
but 0.1 is taken from the whole groove,
so again, scale down to the sub page (devide by the length of the sub page)
the scaled down position is to be subtracted from 1, which is the full length of the sub page
*/
progressScaledDownStartingPosition = 1 - ((stopPosition - 0.5) / stopPosition);
backgroundColorPosition = progressScaledDownStartingPosition + 0.000001;
styleSheet.replace(QRegularExpression("QSlider::sub-page:horizontal \\{([^}]*)\\}"),
QString(
"QSlider::sub-page:horizontal { "
"background: qlineargradient(x1:0, y1:0,"
"x2:1, y2:0,"
"stop:0 black,"
"stop:%1 black,"
"stop:%2 white,"
"stop:1 white);"
"}").arg(progressScaledDownStartingPosition)
.arg(backgroundColorPosition));
}
else
{
styleSheet.replace(QRegularExpression("QSlider::sub-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::sub-page:horizontal { background: black;}"));
styleSheet.replace(QRegularExpression("QSlider::add-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::add-page:horizontal { background: black;}"));
}
}
setStyleSheet(styleSheet);
}
QString styleSheet;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
StyleSheetCenteredSlider styleSheetSlider;
styleSheetSlider.show();
styleSheetSlider.resize(500,100);
return a.exec();
}
注意中心如何摇晃:
那是没有手柄的,加上它会让事情变得更加复杂:
#include <QApplication>
#include <QtWidgets>
class StyleSheetCenteredSlider : public QSlider
{
public:
StyleSheetCenteredSlider(QWidget *parent = nullptr) : QSlider(parent)
{
setOrientation(Qt::Horizontal);
setMinimum(0);
setMaximum(100);
setValue(50);
styleSheet = QString(
"QSlider::sub-page:horizontal { background: black;}"
"QSlider::add-page:horizontal { background: black;}"
"QSlider {"
"height: 20px;"
"background: rgb(50,50,50);"
"}"
"QSlider::groove:horizontal {"
"border: 1px solid #262626;"
"height: 5px;"
"margin: 0 12px;"
"}"
"QSlider::handle:horizontal {"
"background: cyan;"
"border: 1px solid black;"
"width: 23px;"
"border-radius: 12px;"
"height: 100px;"
"margin: -24px -12px;"
"}");
connect(this, &QSlider::valueChanged, this, &StyleSheetCenteredSlider::updateSliderStyle);
}
void updateSliderStyle(int value)
{
qreal stopPosition = static_cast<qreal>(value) / maximum();
qreal progressScaledDownStartingPosition;
qreal backgroundColorPosition;
if(stopPosition < 0.5)
{
//scale down from the whole groove to the add page
/*
Black is half of the whole slider : 0.5
EXAMPLE
0 0.3 0.5 1
|-Black-|-white-|----Black-----|
0 x 1
|-white-|----Black-----|
The white part above = 0.2, which is 0.5 - 0.3
but 0.2 is taken from the whole groove,
so scale down to the add page (devide by the length of the add page, which is (1 - 0.3 (stopPosition)))
*/
qreal whateverMargin = (8/(qreal)width());
progressScaledDownStartingPosition = std::max(((qreal)0.5 - stopPosition) / (1 - stopPosition) - whateverMargin, (qreal)0);
backgroundColorPosition = progressScaledDownStartingPosition + (qreal)0.000001;
styleSheet.replace(QRegularExpression("QSlider::add-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::add-page:horizontal {"
"background: qlineargradient(x1:0, y1:0,"
"x2:1, y2:0,"
"stop:0 white,"
"stop:%1 white,"
"stop:%2 black,"
"stop:1 black);"
"}").arg(progressScaledDownStartingPosition)
.arg(backgroundColorPosition));
}
else
{
if(stopPosition > 0.5)
{
/*
Black is half of the whole slider : 0.5
EXAMPLE
0 0.5 0.6 1
|---Black----|-white--|---Black----|
0 x 1
|---Black----|-white--|
The white part above = 0.1, which is 0.6 - 0.5
but 0.1 is taken from the whole groove,
so again, scale down to the sub page (devide by the length of the sub page)
the scaled down position is to be subtracted from 1, which is the full length of the sub page
*/
qreal whateverMargin = (8/(qreal)width());
progressScaledDownStartingPosition = std::min(1 - ((stopPosition - 0.5) / stopPosition) + whateverMargin, (qreal)0.999999);
backgroundColorPosition = progressScaledDownStartingPosition + 0.000001;
styleSheet.replace(QRegularExpression("QSlider::sub-page:horizontal \\{([^}]*)\\}"),
QString(
"QSlider::sub-page:horizontal { "
"background: qlineargradient(x1:0, y1:0,"
"x2:1, y2:0,"
"stop:0 black,"
"stop:%1 black,"
"stop:%2 white,"
"stop:1 white);"
"}").arg(progressScaledDownStartingPosition)
.arg(backgroundColorPosition));
}
else
{
styleSheet.replace(QRegularExpression("QSlider::sub-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::sub-page:horizontal { background: black;}"));
styleSheet.replace(QRegularExpression("QSlider::add-page:horizontal \\{([^}]*)\\}"),
QString("QSlider::add-page:horizontal { background: black;}"));
}
}
setStyleSheet(styleSheet);
}
QString styleSheet;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
StyleSheetCenteredSlider styleSheetSlider;
styleSheetSlider.show();
styleSheetSlider.resize(500,100);
return a.exec();
}
现在需要考虑边距,并且中心已偏离。
细节可能会被纠正和最终确定,但考虑到结果,这种方法不值得。
更好的方法(就实现简易性和最终结果而言)是使用绘制事件:
#include <QApplication>
#include <QtWidgets>
class PaintEventCenteredSlider : public QSlider
{
public:
PaintEventCenteredSlider(QWidget *parent = nullptr) : QSlider(parent) {}
protected:
void paintEvent(QPaintEvent *event) override
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QStyleOptionSlider option;
option.initFrom(this);
QRect grooveRect = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderGroove, this);
QRect handleRect = style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, this);
QPoint grooveCenter = grooveRect.center();
QPoint handlePoint;
int handlePos = 0;
//example
//example
//440 = 0 + (( 500 - 15 ) * ( 90 - 0 ) / ( 99 - 0 ) )
//(value() - minimum()) / (maximum() - minimum()) -Example> ( 90 - 0 ) / ( 99 - 0 ) ) = 99/90 = 0.9
//basically just the percentage of progress
//grooveRect.left() is just in case we have a margin or something like that, it's usually 0
//grooveRect.width() is obvious, the full length, the distance
//handleRect.width() this is used to take into account the handle rect, without it, it will "go past the groove beyond the border"
//try to comment `- handleRect.width()`
handlePos = grooveRect.left() + ((grooveRect.width() - handleRect.width()) * (value() - minimum()) / (maximum() - minimum()));
//use the below to get more output in order to understand the above
qDebug()<<handlePos<< "=" << grooveRect.left() << "+" << "((" << grooveRect.width() << "-" << handleRect.width()<<")"<< "*"<< "("<<value() <<"-"<< minimum()<<")"<< "/"<< "("<<maximum() <<"-"<< minimum()<<")"<<")";
handlePoint = QPoint(handlePos, grooveCenter.y());
handleRect.moveLeft(handlePoint.x() );
painter.setBrush(Qt::black);
painter.setPen(Qt::black);
painter.drawRoundedRect(grooveRect, 3,3);
QRect progressRect = grooveRect;
//from the center
progressRect.setLeft(grooveCenter.x());
//to the handle's position
progressRect.setRight(handlePoint.x());
painter.setBrush(Qt::white);
painter.setPen(Qt::white);
painter.drawRect(progressRect);
painter.setBrush(Qt::cyan);
painter.setPen(Qt::cyan);
painter.drawEllipse(handleRect);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PaintEventCenteredSlider paintEventSlider;
paintEventSlider.setOrientation(Qt::Horizontal);
paintEventSlider.show();
paintEventSlider.resize(500,100);
return a.exec();
}
中心是稳定的,边距更容易计算,并且在添加到布局中时它可以工作,这与样式表方法不同,后者只会变得更糟:
有关自定义 QSlider 的更多信息: