QSlider mouse direct jump

Instead of stepping when the user clicks somewhere on the qslider I want to make the slider jump to that position. How can this be implemented ?


Solution 1:

after having problems with all versions of @spyke @Massimo Callegari and @Ben (slider position wasnt correct for the whole area) I found some Qt Style functionality within QSlider sourcecode: QStyle::SH_Slider_AbsoluteSetButtons.

You have to create a new QStyle which can be a very annoying, or you use ProxyStyle as shown by user jpn in http://www.qtcentre.org/threads/9208-QSlider-step-customize?p=49035#post49035

I've added another constructor and fixed a typo, but used the rest of the original source code.

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

now you can set the style of your slider in the sliders constructor (if your slider is derived from QSlider):

setStyle(new MyStyle(this->style()));

or it should work this way if it is a standard QSlider:

standardSlider.setStyle(new MyStyle(standardSlider->style()));

so you use the original style of that element, but if the QStyle::SH_Slider_AbsoluteSetButtons "property" is asked you return as you want ;)

maybe you'll have to destroy these proxystyles on slider deletion, not tested yet.

Solution 2:

Well, I doubt that Qt has a direct function for this purpose.

Try to use custom widgets. This should work!

Try the following logic

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

Solution 3:

I needed this too and tried spyke solution, but it's missing two things:

  • inverted appearance
  • handle picking (when the mouse is over the handle, direct jump is not necessary)

So, here's the reviewed code:

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

Solution 4:

The answer of Massimo Callegari is almost right, but the calculation of newVal ignores the slider handle width. This problem comes up when you try to click near the end of the slider.

The following code fixes this for horizontal sliders

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

Solution 5:

Here is a simple implementation in python using QStyle.sliderValueFromPosition():

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