pythonpython-3.xpyqtpyqt5qslider

Moving QSlider to Mouse Click Position


I have a QSlider that I want to move to the position of the mouse cursor when the user presses the left mouse button. I've been hunting around and couldn't find anything that was recent and solved my problem.

Progress Bar

This is the slider I have. I want to be able to click to have the slider jump to the position where the mouse clicks. I can drag the slider, but I want to be able to click. I tested out clicking on the slider in the Dolphin file manager. It incremented rather than jumping to the exact position of the mouse.

Looking at the Qt5 documentation

QSlider has very few of its own functions [...]

This would indicate that there is no built-in way to do this. Is there no way to get where the mouse clicked and move the slider to that point?


Solution

  • The solution is to make a calculation of the position and set it in the mousePressEvent, the calculation is not easy as an arithmetic calculation since it depends on the style of each OS and the stylesheet so we must use QStyle as shown below:

    from PyQt5 import QtCore, QtWidgets
    
    
    class Slider(QtWidgets.QSlider):
        def mousePressEvent(self, event):
            super(Slider, self).mousePressEvent(event)
            if event.button() == QtCore.Qt.LeftButton:
                val = self.pixelPosToRangeValue(event.pos())
                self.setValue(val)
    
        def pixelPosToRangeValue(self, pos):
            opt = QtWidgets.QStyleOptionSlider()
            self.initStyleOption(opt)
            gr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderGroove, self)
            sr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderHandle, self)
    
            if self.orientation() == QtCore.Qt.Horizontal:
                sliderLength = sr.width()
                sliderMin = gr.x()
                sliderMax = gr.right() - sliderLength + 1
            else:
                sliderLength = sr.height()
                sliderMin = gr.y()
                sliderMax = gr.bottom() - sliderLength + 1;
            pr = pos - sr.center() + sr.topLeft()
            p = pr.x() if self.orientation() == QtCore.Qt.Horizontal else pr.y()
            return QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), p - sliderMin,
                                                   sliderMax - sliderMin, opt.upsideDown)
    
    
    if __name__ == '__main__':
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = QtWidgets.QWidget()
        flay = QtWidgets.QFormLayout(w)
        w1 = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        w2 = Slider(QtCore.Qt.Horizontal)
        flay.addRow("default: ", w1)
        flay.addRow("modified: ", w2)
        w.show()
        sys.exit(app.exec_())