我希望在用户点击qslider的某个位置时,不是步进式移动,而是直接跳到该位置。如何实现?
在使用 @spyke、@Massimo Callegari 和 @Ben 的所有版本中遇到问题(滑块位置不正确),我在 QSlider 源代码中找到了一些 Qt 样式功能:QStyle::SH_Slider_AbsoluteSetButtons
。
你需要创建一个新的 QStyle,这可能非常麻烦,或者你可以像用户 jpn 在http://www.qtcentre.org/threads/9208-QSlider-step-customize?p=49035#post49035中所示那样使用ProxyStyle
。
我已经添加了另一个构造函数并修复了一个拼写错误,但使用了原始源代码的其余部分。
#include <QProxyStyle>
class MyStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
int styleHint(QStyle::StyleHint hint, const QStyleOption* option = 0, const QWidget* widget = 0, QStyleHintReturn* returnData = 0) const
{
if (hint == QStyle::SH_Slider_AbsoluteSetButtons)
return (Qt::LeftButton | Qt::MidButton | Qt::RightButton);
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
};
现在您可以在滑块构造函数中设置滑块的样式(如果您的滑块是从QSlider派生的):
setStyle(new MyStyle(this->style()));
如果它是标准的QSlider,则应该按照这种方式工作:
standardSlider.setStyle(new MyStyle(standardSlider->style()));
所以你使用该元素的原始样式,但如果要求“QStyle::SH_Slider_AbsoluteSetButtons
”属性,你可以按照自己的方式返回;)
可能需要在滑块删除时销毁这些代理样式,尚未测试。
QProxyStyle
构造函数,因为有些构造函数被隐藏了,所以我需要显式地定义它,就像这样:MyStyle(QStyle* style = nullptr) : QProxyStyle(style){}
- Tim MB好的,我怀疑Qt没有直接用于此目的的函数。
尝试使用自定义小部件。这应该可以解决!
尝试以下逻辑
class MySlider : public QSlider
{
protected:
void mousePressEvent ( QMouseEvent * event )
{
if (event->button() == Qt::LeftButton)
{
if (orientation() == Qt::Vertical)
setValue(minimum() + ((maximum()-minimum()) * (height()-event->y())) / height() ) ;
else
setValue(minimum() + ((maximum()-minimum()) * event->x()) / width() ) ;
event->accept();
}
QSlider::mousePressEvent(event);
}
};
setValue(minimum() + (maximum() - minimum()) * (static_cast<float>(event->x()) / static_cast<float>(width())));
- Alexander Sorokinmaximum()
中减去计算出的值。 - Abderrahmene Rayene Mihoub我也需要这个功能,尝试了Spyke的解决方案,但是它缺少两个东西:
所以,这是经过审查的代码:
void MySlider::mousePressEvent ( QMouseEvent * event )
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (event->button() == Qt::LeftButton &&
sr.contains(event->pos()) == false)
{
int newVal;
if (orientation() == Qt::Vertical)
newVal = minimum() + ((maximum()-minimum()) * (height()-event->y())) / height();
else
newVal = minimum() + ((maximum()-minimum()) * event->x()) / width();
if (invertedAppearance() == true)
setValue( maximum() - newVal );
else
setValue(newVal);
event->accept();
}
QSlider::mousePressEvent(event);
}
这是一个使用QStyle.sliderValueFromPosition()在Python中的简单实现:
class JumpSlider(QtGui.QSlider):
def mousePressEvent(self, ev):
""" Jump to click position """
self.setValue(QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), ev.x(), self.width()))
def mouseMoveEvent(self, ev):
""" Jump to pointer position while moving """
self.setValue(QtGui.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), ev.x(), self.width()))
Massimo Callegari的答案几乎正确,但是newVal的计算忽略了滑块手柄的宽度。当您尝试点击滑块末端附近时,会出现此问题。
以下代码修复了水平滑块的这个问题
double halfHandleWidth = (0.5 * sr.width()) + 0.5; // Correct rounding
int adaptedPosX = event->x();
if ( adaptedPosX < halfHandleWidth )
adaptedPosX = halfHandleWidth;
if ( adaptedPosX > width() - halfHandleWidth )
adaptedPosX = width() - halfHandleWidth;
// get new dimensions accounting for slider handle width
double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
double normalizedPosition = (adaptedPosX - halfHandleWidth) / newWidth ;
newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// ...
connect(ui->mySlider, SIGNAL(valueChanged(int)),
this, SLOT(mySliderValueChanged(int)));
// ...
}
void MainWindow::mySliderValueChanged(int newPos)
{
// Make slider to follow the mouse directly and not by pageStep steps
Qt::MouseButtons btns = QApplication::mouseButtons();
QPoint localMousePos = ui->mySlider->mapFromGlobal(QCursor::pos());
bool clickOnSlider = (btns & Qt::LeftButton) &&
(localMousePos.x() >= 0 && localMousePos.y() >= 0 &&
localMousePos.x() < ui->mySlider->size().width() &&
localMousePos.y() < ui->mySlider->size().height());
if (clickOnSlider)
{
// Attention! The following works only for Horizontal, Left-to-right sliders
float posRatio = localMousePos.x() / (float )ui->mySlider->size().width();
int sliderRange = ui->mySlider->maximum() - ui->mySlider->minimum();
int sliderPosUnderMouse = ui->mySlider->minimum() + sliderRange * posRatio;
if (sliderPosUnderMouse != newPos)
{
ui->mySlider->setValue(sliderPosUnderMouse);
return;
}
}
// ...
}
基于周围注释的最终实现:
class ClickableSlider : public QSlider {
public:
ClickableSlider(QWidget *parent = 0) : QSlider(parent) {}
protected:
void ClickableSlider::mousePressEvent(QMouseEvent *event) {
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
if (event->button() == Qt::LeftButton &&
!sr.contains(event->pos())) {
int newVal;
if (orientation() == Qt::Vertical) {
double halfHandleHeight = (0.5 * sr.height()) + 0.5;
int adaptedPosY = height() - event->y();
if ( adaptedPosY < halfHandleHeight )
adaptedPosY = halfHandleHeight;
if ( adaptedPosY > height() - halfHandleHeight )
adaptedPosY = height() - halfHandleHeight;
double newHeight = (height() - halfHandleHeight) - halfHandleHeight;
double normalizedPosition = (adaptedPosY - halfHandleHeight) / newHeight ;
newVal = minimum() + (maximum()-minimum()) * normalizedPosition;
} else {
double halfHandleWidth = (0.5 * sr.width()) + 0.5;
int adaptedPosX = event->x();
if ( adaptedPosX < halfHandleWidth )
adaptedPosX = halfHandleWidth;
if ( adaptedPosX > width() - halfHandleWidth )
adaptedPosX = width() - halfHandleWidth;
double newWidth = (width() - halfHandleWidth) - halfHandleWidth;
double normalizedPosition = (adaptedPosX - halfHandleWidth) / newWidth ;
newVal = minimum() + ((maximum()-minimum()) * normalizedPosition);
}
if (invertedAppearance())
setValue( maximum() - newVal );
else
setValue(newVal);
event->accept();
} else {
QSlider::mousePressEvent(event);
}
}
};
class JumpSlider(QSlider):
def _FixPositionToInterval(self,ev):
""" Function to force the slider position to be on tick locations """
# Get the value from the slider
Value=QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), ev.x(), self.width())
# Get the desired tick interval from the slider
TickInterval=self.tickInterval()
# Convert the value to be only at the tick interval locations
Value=round(Value/TickInterval)*TickInterval
# Set the position of the slider based on the interval position
self.setValue(Value)
def mousePressEvent(self, ev):
self._FixPositionToInterval(ev)
def mouseMoveEvent(self, ev):
self._FixPositionToInterval(ev)
我认为可以使用QStyle::sliderValueFromPosition()函数。
http://qt-project.org/doc/qt-5/qstyle.html#sliderValueFromPosition
我一直在尝试并在网上搜索这个问题,并期望Qt能够以更智能的方式解决,但不幸的是没有什么大的帮助(也许是我没有正确搜索)
好吧,我已经在Qt Creator中完成了此操作:
在定义部分:
a. 检查对象是否为您的滑块类型,例如:ui->HSlider == Object
b. 检查鼠标单击事件,例如:QEvent::MouseButtonPress == event->type
c. 如果以上所有条件都通过,则表示您已在滑块上获得了鼠标事件,可以在定义部分执行以下操作:ui->HSlider->setValue(Qcursor::pos().x() - firstval); return QMainWindow::eventFilter(object, event);
注意:firstVal:可以通过打印光标位置来获取,0 = 滑块的初始位置(使用QCursor::pos().x()
帮助)。