pythonpyqtpyqt5qslider

PyQt5 QSlider is off by one depending on which direction the slider is moved


I'm having a problem with the QSlider element in PyQt5. Below is a simplified version of my code that only calls the slider. I have two methods: One to print the slider value when it is moving, and one to print the slider value once the user releases the button.

If you drag the slider to a position (let's say 10) and hold it there, it should print out 10. If you release it, it will print out 11 (if you dragged from the left-hand side) or it will print out 9 (if you dragged from the right-hand side.

I'm really at a loss for why it is off by 1 depending on which direction the slider is moved. I would like the two methods to print off the same number.

from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QSlider
from PyQt5.QtCore import Qt
import sys


class GUIWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(300, 0, 900, 900)

        # Slider
        self.eventSlider = QSlider(Qt.Horizontal)
        self.eventSlider.setFixedWidth(600)
        self.eventSlider.setSingleStep(1)
        self.eventSlider.setValue(0)
        self.eventSlider.setMinimum(0)
        self.eventSlider.setMaximum(20)
        self.eventSlider.setTickInterval(1)
        self.eventSlider.setTickPosition(QSlider.TicksBelow)
        self.eventSlider.sliderMoved.connect(self.slider_changing)
        self.eventSlider.sliderReleased.connect(self.slider_changed)

        GUILayout = QGridLayout()
        GUILayout.setContentsMargins(0, 0, 0, 0)
        GUILayout.addWidget(self.eventSlider, 0, 0, 1, 9, alignment=Qt.AlignCenter)
        self.setLayout(GUILayout)

    def slider_changing(self):
        print(self.eventSlider.value())

    def slider_changed(self):
        print(self.eventSlider.value())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = GUIWindow()
    window.show()
    sys.exit(app.exec_())

Solution

  • You are confused by the mechanism of QAbstractSlider (which is the base class for widgets like QSlider).

    Let's see the explanation in the documentation.

    QAbstractSlider.sliderMoved(value):

    This signal is emitted when sliderDown is true and the slider moves. This usually happens when the user is dragging the slider. The value is the new slider position.

    QAbstractSlider.valueChanged(value)

    This signal is emitted when the slider value has changed, with the new slider value as argument.

    The difference is that the sliderMoved signal is emitted when the slider is moved, not when the value is changed.
    In fact, if you disable the tracking, you'll see that the slider_changing will always print the same current value, until the slider is released.

    That's what happens when tracking is disabled (the default):

    Since you're checking the currentValue within the sliderMoved signal connection, at that point the value has not been updated.

    To get the actual value when the slider is moved you've two options: