Range slider in Qt (two handles in a QSlider)
Asked Answered
T

1

35

I need a range selection using QSlider. Is it possible to get two handles, if not, is there any piece of code available to handle that smartly?

Below is an image illustrating what I need.

Example Image

Throughput answered 28/6, 2013 at 9:51 Comment(10)
That's not possible. You'd need to develop a custom widget. Maybe try this one: libqxt.bitbucket.org/doc/0.6/qxtspanslider.htmlSurbeck
Please Suggest me how to do. Any snippets means pls post.Throughput
I cannot able to find the QxtSpanslider in Qt5.0.1, whether i have add any plugin?Throughput
You need to use Qxt library.Coaster
I've been able to "cut off" QxtSpanSlider from qxt and compile it with Qt 5.3 with minimum changes to code. All that has to be done is to rewrite interconnection of QxtSpanSlider and QxtSpanSliderPrivate (in code those are qxt_d and qxt_p pointers)Wipe
or use this one commontk.org/docs/html/classctkRangeSlider.htmlWipe
@otopolsky I think I would like to go your way and cut off the spanSlider, but I failed. Could you post an answer below explaining your method?Moonlighting
@Moonlighting no I cannot because the question is closed.. posting whole code would be quite big.. please ask new question about what would be the changes to qxt span slider and I will give you the "diffs" about what should be done, and let me know somehow.. maybe hereWipe
@otopolsky done, please follow this link #30621316Moonlighting
When I had to do something similar I placed two sliders next to each other and manipulated their mid-point (slider_1's max and slider_2's min) to be always the average of the two values. I also then calculated the widths as a ratio of the total width. It actually looks and works very smoothly as long as you get rid of the padding between the two sliders.Appellation
A
16

I had the exact same problem. I thought about using the QxtSpanSlider, and although they have beautiful code, they don't keep their code up to date anymore. So, I decided to make my own. I haven't spent that much time on this, so there are kludges and probably bugs. However, it should be at a point to where you can copy/paste it into your project. I hope it'll point you in the right direction.

*note, before you use this, here is a list of all of the problems I've found and am currently working on:

  • The second handle isn't nearly as sensitive as the first handle, resulting in slight agitation for the user.
  • Some of the values are hard coded and as a result the slider isn't as dynamic as it could be.
  • The second handle doesn't handle (haa...) negative values yet.

Here's a good solution:

SuperSlider.h

#pragma once

#include "qslider.h"
#include "qlabel.h"


/*
*  Super sick nasty awesome double handled slider! 
*
*   @author Steve
*/
class SuperSliderHandle;

class SuperSlider: public QSlider
{
  Q_OBJECT
public:
  /** Constructor */
  SuperSlider(QWidget *parent = 0);

  /** Store the alternate handle for this slider*/
  SuperSliderHandle *alt_handle;

  /** Overridden mouse release event */
  void mouseReleaseEvent(QMouseEvent *event);

  /** Returns the slider value for the alternate handle */
  int alt_value();

  /** Convenience function for setting the value of the alternate handle */
  void alt_setValue(int value);

  /** Resets the alternate handle to the right side of the slider */
  void Reset();

  /** Used to update the position of the alternate handle through the use of an event filter */
  void alt_update();
signals:
  /** Constructor */
  void alt_valueChanged(int);
};

class SuperEventFilter : public QObject
{
public:
  /** Constructor */
  SliderEventFilter(SuperSlider *_grandParent) 
  {grandParent = _grandParent;};

protected:
  /*
  * overloaded functions for object that inherit from this class
  */
  bool eventFilter(QObject* obj, QEvent* event);

private:
  /** Store the SuperSlider that this event filter is used within. */
  SuperSlider *grandParent;
};

class SuperSliderHandle: public QLabel
{
  Q_OBJECT
public:
  /** Constructor */
  SuperSliderHandle(SuperSlider *parent = 0);

  /** An overloaded mousePressevent so that we can start grabbing the cursor and using it's position for the value */
  void mousePressEvent(QMouseEvent *event);

  /** Returns the value of this handle with respect to the slider */
  int value();

  /** Maps mouse coordinates to slider values */
  int mapValue();

  /** Store the parent as a slider so that you don't have to keep casting it  */
  SuperSlider *parent;

  /** Store a bool to determine if the alternate handle has been activated  */
  bool handleActivated;

private:
  /** Store the filter for installation on the qguiapp */
  SliderEventFilter *filter;

  public slots:
    /** Sets the value of the handle with respect to the slider */
    void setValue(double value);
};

SuperSlider.cpp

//Project
#include "SuperSlider.h"

//Qt
#include <QMouseEvent>
#include "qmimedata.h"
#include "qdrag.h"
#include "qwidgetaction.h"
#include "qapplication.h"
#include "qpixmap.h"
#include "qcursor.h"
#include "qguiapplication.h"
#include "qdir.h"
#include <QProxyStyle>

class SliderProxy : public QProxyStyle
{
public:
  int pixelMetric ( PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const
  {
    switch(metric) {
    case PM_SliderThickness  : return 25;
    case PM_SliderLength     : return 25;
    default                  : return (QProxyStyle::pixelMetric(metric,option,widget));
    }
  }
};

SuperSlider::SuperSlider(QWidget *parent)
  : QSlider(parent)
{
  //styling
  setOrientation(Qt::Horizontal);
  setAcceptDrops(true);
  SliderProxy *aSliderProxy = new SliderProxy();

  //hard coded path to image :/ sorry 
  QString path = QDir::fromNativeSeparators(ImagesPath("handle.png")); 
  setStyleSheet("QSlider::handle { image: url(" + path + "); }");
  setStyle(aSliderProxy);

  //setting up the alternate handle
  alt_handle = new SuperSliderHandle(this);
  addAction(new QWidgetAction(alt_handle));
  alt_handle->move(this->pos().x() + this->width()- alt_handle->width(), this->pos().y() );

}

SuperSliderHandle::SuperSliderHandle(SuperSlider *_parent)
  : QLabel(_parent)
{
  parent = _parent;
  filter = new SliderEventFilter(parent);

  //styling
  setAcceptDrops(true);
  //hard coded path to image :/ sorry 
  QPixmap pix = QPixmap(ImagesPath("handle.png"));
  pix =  pix.scaled(25, 25, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
  setPixmap(pix);
}

int SuperSlider::alt_value()
{
  return alt_handle->value();
}

void SuperSlider::alt_setValue(int value)
{
  alt_handle->setValue(value);
}

void SuperSlider::mouseReleaseEvent(QMouseEvent *mouseEvent)
{
  if (mouseEvent->button() == Qt::LeftButton) 
  {
    alt_handle->show();
    alt_handle->handleActivated = false;
  }
  mouseEvent->accept();
}

void SuperSlider::alt_update()
{
  QPoint posCursor(QCursor::pos());
  QPoint posParent(mapToParent(mapToGlobal(pos())));
  QPoint point(alt_handle->mapToParent(alt_handle->mapFromGlobal(QCursor::pos())).x(),alt_handle->y());
  int horBuffer = (alt_handle->width());
  bool lessThanMax = mapToParent(point).x() < pos().x()+ width() - horBuffer;
  bool greaterThanMin = mapToParent(point).x() > pos().x();
  if(lessThanMax && greaterThanMin)
    alt_handle->move(point);
  emit alt_valueChanged(alt_value());
}

void SuperSliderHandle::mousePressEvent(QMouseEvent *mouseEvent)
{
  qGuiApp->installEventFilter(filter);
  parent->clearFocus();
}

bool SliderEventFilter::eventFilter(QObject* obj, QEvent* event)
{
  switch(event->type())
  {
  case QEvent::MouseButtonRelease:
    qGuiApp->removeEventFilter(this);
    return true;
    break;
  case QEvent::MouseMove:
    grandParent->alt_update();
    return true;
    break;
  default:
    return QObject::eventFilter(obj, event);
  }
  return false;
}

void SuperSliderHandle::setValue(double value)
{
  double width = parent->width(), position = pos().x();
  double range = parent->maximum() - parent->minimum();
  int location = (value - parent->minimum())/range; 
  location = location *width;
  move(y(),location);
}

int SuperSliderHandle::value()
{
  double width = parent->width(), position = pos().x();
  double value = position/width;
  double range = parent->maximum() - parent->minimum();
  return parent->minimum() + (value * range); 
}
void SuperSlider::Reset()
{
  int horBuffer = (alt_handle->width());
  QPoint myPos = mapToGlobal(pos()); 
  QPoint point(myPos.x() + width() - horBuffer, myPos.y()- alt_handle->height());
  point = alt_handle->mapFromParent(point);

  alt_handle->move(point);
  alt_handle->show();
  alt_handle->raise();

}
Ahithophel answered 24/9, 2014 at 20:21 Comment(3)
To compile it, SuperEventFilter must be changed to SliderEventFilter in the header, at the class definition. Could you put and example of how to use it? I'm trying to figure out how it works.Stortz
Can you explain how to use your class in QT?Emmery
In vertical mode, second handler only moves horizontally.Klopstock

© 2022 - 2024 — McMap. All rights reserved.