PySide and QSlider problem

336 views
Skip to first unread message

Rémi Deletrain

unread,
Mar 17, 2017, 5:15:03 AM3/17/17
to Python Programming for Autodesk Maya
Hello everyone, I have a little problem with the PySide QSlider. I'm trying to draw text on the ticks of my QSlider. I saw that we could use "sliderValueFromPosition" to find the position but impossible to make it work correctly ... It always returns 0. I'll show you my code.

def __range_to_pos(self, value):

    qt_options
= QtGui.QStyleOptionSlider()
   
self.initStyleOption(qt_options)
    style
= QtGui.QApplication.style()
   
    gr
= style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderGroove, self)
    sr
= style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderHandle, self)
   
   
if self.orientation() == QtCore.Qt.Horizontal:
        slider_length
= sr.width()
        slider_min
= gr.x()
        slider_max
= gr.right() - slider_length + 1
   
else:
        slider_length
= sr.height()
        slider_min
= gr.y()
        slider_max
= gr.bottom() - slider_length + 1
       
   
return style.sliderValueFromPosition(
       
self.minimum(),
       
self.maximum(),
        value
,
        slider_max
- slider_min,
        qt_options
.upsideDown
   
)



I made an alternative but I have a small lag on my offset.
def __range_to_pos(self, value):

    i_percent_interval
= 100.0 / (self.maximum() - 1.0)

   
# Size from orientation
   
if self.orientation() == QtCore.Qt.Horizontal:
        f_size
= self.rect().width()
   
else:
        f_size
= self.rect().height()

   
#   Calculate offset
    qt_font_metric
= QtGui.QPainter(self).fontMetrics()
    f_text_size
= float(qt_font_metric.width(str(value)))
    i_percent_offset
= (f_text_size / f_size) * 100.0
    f_offset
= f_size / 100.0 * i_percent_offset

   
if value == 0:
        f_offset
= 0.0
   
elif value != self.maximum() - 1:
        f_offset
/= 2.0

   
#   Position
    x_pos
= f_size / 100.0 * (i_percent_interval * value)
   
if self.orientation() == QtCore.Qt.Horizontal:
        qt_pos
= QtCore.QPoint(x_pos - f_offset, self.rect().bottom())
   
else:
        qt_pos
= QtCore.QPoint(self.rect().bottom(), x_pos - f_offset)

   
return qt_pos


I find it a pity not to be able to operate the command of PySide. Does anyone know the solution?

Justin Israel

unread,
Mar 17, 2017, 4:48:27 PM3/17/17
to python_in...@googlegroups.com
On Fri, Mar 17, 2017 at 10:15 PM Rémi Deletrain <remi.de...@gmail.com> wrote:
Hello everyone, I have a little problem with the PySide QSlider. I'm trying to draw text on the ticks of my QSlider. I saw that we could use "sliderValueFromPosition" to find the position but impossible to make it work correctly ... It always returns 0. I'll show you my code.

def __range_to_pos(self, value):

    qt_options
= QtGui.QStyleOptionSlider()
   
self.initStyleOption(qt_options)
    style
= QtGui.QApplication.style()
   
    gr
= style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderGroove, self)
    sr
= style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderHandle, self)
   
   
if self.orientation() == QtCore.Qt.Horizontal:
        slider_length
= sr.width()
        slider_min
= gr.x()
        slider_max
= gr.right() - slider_length + 1
   
else:
        slider_length
= sr.height()
        slider_min
= gr.y()
        slider_max
= gr.bottom() - slider_length + 1
       
   
return style.sliderValueFromPosition(
       
self.minimum(),
       
self.maximum(),
        value
,
        slider_max
- slider_min,
        qt_options
.upsideDown
   
)




Can you provide a small working reproduction of your problem? It was not clear to me what you were passing for "value" because the name of the function is misleading. It is called "range to pos", yet the value parameter should mean the pixel position and the return value should be the slider value.

A quick test seems to be working:

from PySide import QtCore, QtGui

slider = QtGui.QSlider()
slider.setRange(0,100)
slider.resize(200,30)
slider.setOrientation(QtCore.Qt.Horizontal)
slider.show()
slider.raise_()

qt_options = QtGui.QStyleOptionSlider()
slider.initStyleOption(qt_options)
style = QtGui.QApplication.style()

gr = style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderGroove, slider)
sr = style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderHandle, slider)

if slider.orientation() == QtCore.Qt.Horizontal:
    slider_length = sr.width()
    slider_min = gr.x()
    slider_max = gr.right() - slider_length + 1

else:
    slider_length = sr.height()
    slider_min = gr.y()
    slider_max = gr.bottom() - slider_length + 1


pos = 100

ret = style.sliderValueFromPosition(
    slider.minimum(),
    slider.maximum(),
    pos,
    slider_max - slider_min,
    qt_options.upsideDown
)
print ret
# 53
 
--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/9e2f012c-0fad-4687-81e6-d6561c2bdfed%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rémi Deletrain

unread,
Mar 20, 2017, 5:39:23 AM3/20/17
to Python Programming for Autodesk Maya
I'm so stupid...
I have read the API, I have modified and given the correct variable but I forgot to change the name of the function ... Lose as much time besause i'm stupid ... It is by reading your post justin that I realized it. Because actually the name of the function does not match what I call for my return value. So it works very well ... I have a small problem but that is linked to what I read on the internet to QT / PySide with the draw text. I have a Direct crash during the drawText. I know how to get around it with labels but it's not very clean. If I find a solution I will post it here. That's where I am at the moment. I picked up two things on the internet to make a slider with text and also a range slider.

# ================================================================================
#    Import Modules
# ================================================================================

import os
import sys

from PySide import QtGui, QtCore

# ==========================================================
#    TreeView
# ==========================================================

class RDSlider(QtGui.QSlider):

    # ======================================================
    #    Init
    # ======================================================

    def __init__(self, qt_orientation=QtCore.Qt.Horizontal, qt_parent=None, **kwargs):
        super(RDSlider, self).__init__(qt_orientation, qt_parent)

        #   Init UI
        self.setMinimum(kwargs.get("min", 0))
        self.setMaximum(kwargs.get("max", 10))
        self.setSingleStep(1)
        self.setPageStep(1)
        if kwargs.get("ticks", True) is True:
            self.setTickPosition(kwargs.get("ticks_position", QtGui.QSlider.TicksBelow))

        self.__b_range_slider = kwargs.get("range_slider", False)
        self.__b_ticks_text = kwargs.get("ticks_text", False)

        #   Range slider
        self.__low = self.minimum()
        self.__high = self.maximum()
        self.__pressed_control = QtGui.QStyle.SC_None
        self.__hover_control = QtGui.QStyle.SC_None
        self.__click_offset = 0
        self.__active_slider = 0

    # ======================================================
    #    QT EVENT
    # ======================================================

    def paintEvent(self, event):

        event.accept()

        qt_painter = QtGui.QPainter(self)
        style = QtGui.QApplication.style()

        # ==================================================
        #   For range slider


        if self.__b_range_slider is True:

            for i, value in enumerate([self.__low, self.__high]):

                qt_options = QtGui.QStyleOptionSlider()
                self.initStyleOption(qt_options)

                qt_options.subControls = QtGui.QStyle.SC_SliderHandle

                if self.tickPosition() != self.NoTicks:
                    qt_options.subControls |= QtGui.QStyle.SC_SliderTickmarks

                if self.__pressed_control:
                    qt_options.activeSubControls = self.__pressed_control
                    qt_options.state |= QtGui.QStyle.State_Sunken
                else:
                    qt_options.activeSubControls = self.__hover_control

                qt_options.sliderPosition = value
                qt_options.sliderValue = value                                  
                style.drawComplexControl(QtGui.QStyle.CC_Slider, qt_options, qt_painter, self)

        else:

            super(RDSlider, self).paintEvent(event)

        # ==================================================
        #   For text

        if self.__b_ticks_text is True:

            i_range = (self.maximum() - self.minimum()) + 1
            for i in xrange(i_range):

                #    Get text and offset
                s_text = "toto %s" % i
                i_text_offset = -self.__get_text_offset(s_text)

                if i == 0:
                    i_text_offset = 0.0
                elif i != i_range - 1:
                    i_text_offset /= 2.0

                #    Draw text
                if self.orientation() == QtCore.Qt.Horizontal:
                    qt_pos = QtCore.QPoint(self.__get_pos_from_value(i) + i_text_offset, self.rect().bottom())
                else:
                    qt_pos = QtCore.QPoint(self.rect().left(), self.__get_pos_from_value(i) + i_text_offset)

                qt_painter.drawText(qt_pos, s_text)

    def mousePressEvent(self, event):

        if self.__b_range_slider is True:

            event.accept()
            
            style = QtGui.QApplication.style()
            button = event.button()
            
            # In a normal slider control, when the user clicks on a point in the 
            # slider's total range, but not on the slider part of the control the
            # control would jump the slider value to where the user clicked.
            # For this control, clicks which are not direct hits will slide both
            # slider parts
                    
            if button:

                qt_options = QtGui.QStyleOptionSlider()
                self.initStyleOption(qt_options)

                self.__active_slider = -1
                
                for i, value in enumerate([self.__low, self.__high]):

                    qt_options.sliderPosition = value                
                    hit = style.hitTestComplexControl(style.CC_Slider, qt_options, event.pos(), self)

                    if hit == style.SC_SliderHandle:
                        self.__active_slider = i
                        self.__pressed_control = hit
                        
                        self.triggerAction(self.SliderMove)
                        self.setRepeatAction(self.SliderNoAction)
                        self.setSliderDown(True)
                        break

                if self.__active_slider < 0:
                    self.__pressed_control = QtGui.QStyle.SC_SliderHandle
                    self.__click_offset = self.__get_value_from_pos(self.__pick(event.pos()))
                    self.triggerAction(self.SliderMove)
                    self.setRepeatAction(self.SliderNoAction)

            else:

                event.ignore()

        else:

            super(RDSlider, self).mousePressEvent(event)
            event.ignore()

    def mouseMoveEvent(self, event):

        if self.__b_range_slider is True:

            if self.__pressed_control != QtGui.QStyle.SC_SliderHandle:
                event.ignore()
                return
            
            event.accept()

            new_pos = self.__get_value_from_pos(self.__pick(event.pos()))

            qt_options = QtGui.QStyleOptionSlider()
            self.initStyleOption(qt_options)
            
            if self.__active_slider < 0:

                offset = new_pos - self.__click_offset
                self.__high += offset
                self.__low += offset

                if self.__low < self.minimum():
                    diff = self.minimum() - self.__low
                    self.__low += diff
                    self.__high += diff

                if self.__high > self.maximum():
                    diff = self.maximum() - self.__high
                    self.__low += diff
                    self.__high += diff  

            elif self.__active_slider == 0:

                if new_pos >= self.__high:
                    new_pos = self.__high - 1
                self.__low = new_pos

            else:

                if new_pos <= self.__low:
                    new_pos = self.__low + 1
                self.__high = new_pos

            self.__click_offset = new_pos

            self.update()

            self.emit(QtCore.SIGNAL('sliderMoved(int)'), new_pos)

        else:

            super(RDSlider, self).mouseMoveEvent(event)
            event.ignore()

    # ======================================================
    #    MISC
    # ======================================================

    def __pick(self, qt_point):

        """
        !@Brief Mouse position from slider orientation

        @type qt_point: QtGui.QPoint
        @param qt_point: Mouse position

        @rtype: float
        @return: Value X or Y from slider orientation.
        """

        if self.orientation() == QtCore.Qt.Horizontal:
            return qt_point.x()
        else:
            return qt_point.y()

    def __get_pos_from_value(self, i_value):
        
        """
        !@Brief Get Pixel position of slider ticks.

        @type i_value: int
        @param i_value: Tick value.

        @rtype: int
        @return: Tick pixel position.
        """

        qt_options = QtGui.QStyleOptionSlider()
        self.initStyleOption(qt_options)
        qt_style = QtGui.QApplication.style()
        
        slider_groove = qt_style.subControlRect(qt_style.CC_Slider, qt_options, qt_style.SC_SliderGroove, self)
        slider_handle = qt_style.subControlRect(qt_style.CC_Slider, qt_options, qt_style.SC_SliderHandle, self)
        
        if self.orientation() == QtCore.Qt.Horizontal:
            slider_length = slider_handle.width()
            slider_min = slider_groove.x()
            slider_max = slider_groove.right() - slider_length + 1
        else:
            slider_length = slider_handle.height()
            slider_min = slider_groove.y()
            slider_max = slider_groove.bottom() - slider_length + 1
            
        return qt_style.sliderPositionFromValue(
            self.minimum(),
            self.maximum(),
            i_value,
            slider_max - slider_min,
            qt_options.upsideDown
        )

    def __get_value_from_pos(self, f_pos):

        """
        !@Brief Get Tick value from position.

        @type f_pos: float
        @param f_pos: Mouse position.

        @rtype: int
        @return: Tick value.
        """

        qt_options = QtGui.QStyleOptionSlider()
        self.initStyleOption(qt_options)
        style = QtGui.QApplication.style()
        
        slider_groove = style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderGroove, self)
        slider_handle = style.subControlRect(style.CC_Slider, qt_options, style.SC_SliderHandle, self)
        
        if self.orientation() == QtCore.Qt.Horizontal:
            slider_length = slider_handle.width()
            slider_min = slider_groove.x()
            slider_max = slider_groove.right() - slider_length + 1
        else:
            slider_length = slider_handle.height()
            slider_min = slider_groove.y()
            slider_max = slider_groove.bottom() - slider_length + 1
            
        return style.sliderValueFromPosition(
            self.minimum(),
            self.maximum(),
            f_pos - slider_min,
            slider_max - slider_min,
            qt_options.upsideDown
        )

    def __get_text_offset(self, s_text):

        """
        !@Brief Get text length offset.

        @type s_text: string
        @param s_text: Text for get size.

        @rtype: int
        @return: Offset length.
        """

        #   Size from orientation
        if self.orientation() == QtCore.Qt.Horizontal:
            f_size = self.rect().width()
        else:
            f_size = self.rect().height()

        qt_painter = QtGui.QPainter(self)
        qt_font_metric = qt_painter.fontMetrics()
        f_text_size = float(qt_font_metric.width(s_text))
        i_percent_offset = (f_text_size / f_size) * 100.0
        f_offset = f_size / 100.0 * i_percent_offset

        return f_offset

# =============================================================
#    INIT Script
# =============================================================

def main():

    """
    !@Brief Main function for launch ui.
    """

    app = QtGui.QApplication(sys.argv)
    slider = RDSlider(min=0, max=6, range_slider=True)
    slider.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()




Reply all
Reply to author
Forward
0 new messages