web-dev-qa-db-ja.com

QSliderマウスの直接ジャンプ

ユーザーがqsliderのどこかをクリックしたときにステップするのではなく、スライダーをその位置にジャンプさせたいのです。これはどのように実装できますか?

36
Vihaan Verma

まあ、私は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);
  }
};
22
ScarCode

@spyke @Massimo Callegariおよび@Benのすべてのバージョンで問題が発生した後(スライダーの位置がエリア全体で正しくありませんでした)QSliderソースコード内にQtスタイルの機能がいくつか見つかりました:QStyle::SH_Slider_AbsoluteSetButtons

非常に煩わしい新しいQStyleを作成する必要があります。または、ユーザーjpnが http://www.qtcentre.org/threads/9208-QSlider-stepに示すようにProxyStyleを使用します-customize?p = 49035#post49035

別のコンストラクタを追加してタイプミスを修正しましたが、元のソースコードの残りを使用しました。

#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 "プロパティ"は、必要に応じて戻るように求められます;)

まだテストされていませんが、スライダーの削除時にこれらのプロキシスタイルを破棄する必要があるかもしれません。

27
Micka

私もこれが必要で、spykeソリューションを試しましたが、次の2つがありません。

  • 反転した外観
  • ハンドルピッキング(マウスがハンドルの上にあるとき、直接ジャンプは必要ありません)

だから、これがレビューされたコードです:

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

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

以下は、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()))
9
Satara

次のコードは実際にはハックですが、QSliderをサブクラス化しなくても正常に機能します。行う必要があるのは、QSlider valueChanged信号をコンテナーに接続することだけです。

注1:スライダーでpageStep> 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
Fivos Vilanakis

周囲のコメントに基づく私の最終的な実装:

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

私はこれをネットで検索して検索していて、Qtがこれを行うよりスマートな方法を期待していましたが、残念ながら大きな助けはありませんでした(適切に検索していない可能性があります)

よく私はQtクリエーターでこれを行いました:

  1. ヘッダーにeventFilterを追加します(引数としてQObjectとQEventを取ります)(ブール値の戻り値の型)
  2. コンストラクターで初期化します。たとえば、urスライダーがHSliderの場合、ui-> HSlider-> installEventFilter(this);
  3. 定義では:

    a。オブジェクトが次のようなスライダータイプであるかどうかを確認します:_ui->HSlider == Object_

    b。次のようなマウスクリックイベントを確認します:_QEvent::MouseButtonPress == event->type_

    c。上記のすべてのパスが、スライダーでマウスイベントを取得したことを意味する場合、次のようなことを行います。定義では:ui->HSlider->setValue( Qcursor::pos().x() - firstval ); return QMainWindow::eventFilter(object, event);

注:fistVal:カーソルの位置を0 =スライダーの初期位置に印刷することで取り出すことができます(QCursor::pos().x()を使用)

お役に立てれば

1
Biraj Borah

おもう、

qStyle :: sliderValueFromPosition()関数を使用できます。

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

1
Tim S

単純な方法は、QSliderから派生し、mousePressEvent(....)を再実装して、setSliderPosition(int)を使用してマーカー位置を設定することです。

0
cmannett85

上記の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)
0
Jim Graham

私もこの問題に遭遇しました。私の解決策を以下に示します。

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
0
Dana Yan