QSlider鼠标直接跳转

时间:2012-06-21 06:21:20

标签: qt qslider

当用户点击qslider上的某个位置时,我想让滑块跳转到该位置,而不是踩踏。如何实施?

12 个答案:

答案 0 :(得分:23)

在@spyke @Massimo Callegari和@Ben的所有版本出现问题后(滑块位置对整个区域都不正确)我在QSlider源代码中找到了一些Qt Style功能:QStyle::SH_Slider_AbsoluteSetButtons

您必须创建一个非常烦人的新QStyle,或者使用ProxyStyle中的用户jpn所示的#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); } };

我添加了另一个构造函数并修复了拼写错误,但使用了原始源代码的其余部分。

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

现在您可以在滑块构造函数中设置滑块的样式(如果滑块是从QSlider派生的):

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

或者它应该以这种方式工作,如果它是标准的QSlider:

QStyle::SH_Slider_AbsoluteSetButtons

所以你使用该元素的原始样式,但是如果要求你{{1}}“属性”,你可以根据需要返回;)

也许你必须在滑块删除时销毁这些代理方式,尚未测试。

答案 1 :(得分:20)

好吧,我怀疑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);
  }
};

答案 2 :(得分:18)

我也需要这个并尝试过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);
}

答案 3 :(得分:15)

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

答案 4 :(得分:8)

这是python中使用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()))

答案 5 :(得分:6)

以下代码实际上是一个hack,但它没有对QSlider进行子类化,它可以正常工作。您唯一需要做的就是将QSlider valueChanged信号连接到您的容器。

注1:您必须设置pageStep&gt;滑块中的0

注意2:它仅适用于水平,从左到右的滑块(您应该将“sliderPosUnderMouse”的计算更改为使用垂直方向或反转外观)

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;
        }
    }
    // ...
}

答案 6 :(得分:2)

我的最终实施基于周围的评论:

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

答案 7 :(得分:1)

我一直在尝试并在网上搜索这个并且期待Qt以更聪明的方式做到这一点,不幸的是没有大的帮助(可能是我没有正确搜索)

我在Qt创建者中做到了这一点:

  1. 在标头中添加eventFilter(以QObject和QEvent作为参数)(bool返回类型)
  2. 在构造函数中初始化..例如..如果你的滑块是HSlider那么      UI-&GT; HSlider-&GT; installEventFilter(本);
  3. 在定义中:

    一个。检查对象是否是滑块类型:ui->HSlider == Object

    湾检查鼠标点击事件,例如:QEvent::MouseButtonPress == event->type

    ℃。如果以上所有通过意味着你在滑块上有鼠标事件,请执行以下操作:在定义中:ui->HSlider->setValue( Qcursor::pos().x() - firstval ); return QMainWindow::eventFilter(object, event);

  4. 注意:fistVal:可以通过在0 =滑块初始位置打印cursur位置取出(借助QCursor::pos().x()

    希望这会有所帮助

答案 8 :(得分:1)

我想,

可以使用QStyle :: sliderValueFromPosition()函数。

http://qt-project.org/doc/qt-5/qstyle.html#sliderValueFromPosition

答案 9 :(得分:0)

一种简单的方法是从QSlider派生并重新实现mousePressEvent(....)以使用setSliderPosition(int)设置标记位置。

答案 10 :(得分:0)

我也遇到了这个问题。我的解决方案如下所示。

slider->installEventFilter(this);
---    
bool MyDialog::eventFilter(QObject *object, QEvent *event)
    {
        if (object == slider && slider->isEnabled())
        {
            if (event->type() == QEvent::MouseButtonPress)
            {
                auto mevent = static_cast<QMouseEvent *>(event);
                qreal value = slider->minimum() + (slider->maximum() - slider->minimum()) * mevent->localPos().x() / slider->width();
                if (mevent->button() == Qt::LeftButton)
                {
                    slider->setValue(qRound(value));
                }
                event->accept();
                return true;
            }
            if (event->type() == QEvent::MouseMove)
            {
                auto mevent = static_cast<QMouseEvent *>(event);
                qreal value = slider->minimum() + (slider->maximum() - slider->minimum()) * mevent->localPos().x() / slider->width();
                if (mevent->buttons() & Qt::LeftButton)
                {
                    slider->setValue(qRound(value));
                }
                event->accept();
                return true;
            }
            if (event->type() == QEvent::MouseButtonDblClick)
            {
                event->accept();
                return true;
            }
        }
        return QDialog::eventFilter(object, event);
    }

您还可以覆盖QSlider的这些事件处理程序。

  • QSlider::mousePressedEvent
  • QSlider::mouseMoveEvent
  • QSlider::mouseDoubleClickEvent

答案 11 :(得分:0)

对上面的JumpSlider的此修改在PyQt5中有效:

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)