Python PyQt4 slider with tick labels

前端 未结 2 2094
时光说笑
时光说笑 2021-01-03 01:34

I am new to PyQt4 and i am making a gui with QSlider.

By default the QSlider doesnt show you the values of your ticks. I searched and didnt find an easy way to do it

相关标签:
2条回答
  • 2021-01-03 02:03

    I've cooked up a solution that deals with adding labels to sliders, in vertical or horizontal orientations.

    Result screenshot:

    Code here (note that there are some import changes between PyQt4 and 5).

    import sys
    from PyQt5 import QtWidgets
    from PyQt5.QtGui import QPainter
    from PyQt5.QtWidgets import QStyle, QStyleOptionSlider
    from PyQt5.QtCore import QRect, QPoint, Qt
    
    
    class LabeledSlider(QtWidgets.QWidget):
        def __init__(self, minimum, maximum, interval=1, orientation=Qt.Horizontal,
                labels=None, parent=None):
            super(LabeledSlider, self).__init__(parent=parent)
    
            levels=range(minimum, maximum+interval, interval)
            if labels is not None:
                if not isinstance(labels, (tuple, list)):
                    raise Exception("<labels> is a list or tuple.")
                if len(labels) != len(levels):
                    raise Exception("Size of <labels> doesn't match levels.")
                self.levels=list(zip(levels,labels))
            else:
                self.levels=list(zip(levels,map(str,levels)))
    
            if orientation==Qt.Horizontal:
                self.layout=QtWidgets.QVBoxLayout(self)
            elif orientation==Qt.Vertical:
                self.layout=QtWidgets.QHBoxLayout(self)
            else:
                raise Exception("<orientation> wrong.")
    
            # gives some space to print labels
            self.left_margin=10
            self.top_margin=10
            self.right_margin=10
            self.bottom_margin=10
    
            self.layout.setContentsMargins(self.left_margin,self.top_margin,
                    self.right_margin,self.bottom_margin)
    
            self.sl=QtWidgets.QSlider(orientation, self)
            self.sl.setMinimum(minimum)
            self.sl.setMaximum(maximum)
            self.sl.setValue(minimum)
            if orientation==Qt.Horizontal:
                self.sl.setTickPosition(QtWidgets.QSlider.TicksBelow)
                self.sl.setMinimumWidth(300) # just to make it easier to read
            else:
                self.sl.setTickPosition(QtWidgets.QSlider.TicksLeft)
                self.sl.setMinimumHeight(300) # just to make it easier to read
            self.sl.setTickInterval(interval)
            self.sl.setSingleStep(1)
    
            self.layout.addWidget(self.sl)
    
        def paintEvent(self, e):
    
            super(LabeledSlider,self).paintEvent(e)
    
            style=self.sl.style()
            painter=QPainter(self)
            st_slider=QStyleOptionSlider()
            st_slider.initFrom(self.sl)
            st_slider.orientation=self.sl.orientation()
    
            length=style.pixelMetric(QStyle.PM_SliderLength, st_slider, self.sl)
            available=style.pixelMetric(QStyle.PM_SliderSpaceAvailable, st_slider, self.sl)
    
            for v, v_str in self.levels:
    
                # get the size of the label
                rect=painter.drawText(QRect(), Qt.TextDontPrint, v_str)
    
                if self.sl.orientation()==Qt.Horizontal:
                    # I assume the offset is half the length of slider, therefore
                    # + length//2
                    x_loc=QStyle.sliderPositionFromValue(self.sl.minimum(),
                            self.sl.maximum(), v, available)+length//2
    
                    # left bound of the text = center - half of text width + L_margin
                    left=x_loc-rect.width()//2+self.left_margin
                    bottom=self.rect().bottom()
    
                    # enlarge margins if clipping
                    if v==self.sl.minimum():
                        if left<=0:
                            self.left_margin=rect.width()//2-x_loc
                        if self.bottom_margin<=rect.height():
                            self.bottom_margin=rect.height()
    
                        self.layout.setContentsMargins(self.left_margin,
                                self.top_margin, self.right_margin,
                                self.bottom_margin)
    
                    if v==self.sl.maximum() and rect.width()//2>=self.right_margin:
                        self.right_margin=rect.width()//2
                        self.layout.setContentsMargins(self.left_margin,
                                self.top_margin, self.right_margin,
                                self.bottom_margin)
    
                else:
                    y_loc=QStyle.sliderPositionFromValue(self.sl.minimum(),
                            self.sl.maximum(), v, available, upsideDown=True)
    
                    bottom=y_loc+length//2+rect.height()//2+self.top_margin-3
                    # there is a 3 px offset that I can't attribute to any metric
    
                    left=self.left_margin-rect.width()
                    if left<=0:
                        self.left_margin=rect.width()+2
                        self.layout.setContentsMargins(self.left_margin,
                                self.top_margin, self.right_margin,
                                self.bottom_margin)
    
                pos=QPoint(left, bottom)
                painter.drawText(pos, v_str)
    
            return
    
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        frame=QtWidgets.QWidget()
        ha=QtWidgets.QHBoxLayout()
        frame.setLayout(ha)
    
        w = LabeledSlider(1, 10 , 1, orientation=Qt.Horizontal)
    
        ha.addWidget(w)
        frame.show()
        sys.exit(app.exec_())
    
    0 讨论(0)
  • 2021-01-03 02:03

    Sadly, there's no easy way to do this. You will have to make it manually. I just had to do it too, so you can have my implementation. Take into account that I only re-implemented what I needed, I hacked a bit the alignments and the valueChanged() API exhibits a very different behaviour.

    from PyQt4 import QtCore
    from PyQt4.QtGui import QSlider, QWidget, QVBoxLayout, QHBoxLayout, QLabel
    __author__ = 'Andres'
    
    
    class QCustomSlider(QWidget):
        def __init__(self, sliderOrientation=None):
            super(QCustomSlider, self).__init__()
            self._slider = QSlider(sliderOrientation)
    
            self.setLayout(QVBoxLayout())
    
            self._labelTicksWidget = QWidget(self)
            self._labelTicksWidget.setLayout(QHBoxLayout())
            self._labelTicksWidget.layout().setContentsMargins(0, 0, 0, 0)
    
            self.layout().addWidget(self._slider)
            self.layout().addWidget(self._labelTicksWidget)
    
        def setTickLabels(self, listWithLabels):
            lengthOfList = len(listWithLabels)
            for index, label in enumerate(listWithLabels):
                label = QLabel(str(label))
                label.setContentsMargins(0, 0, 0, 0)
                if index > lengthOfList/3:
                    label.setAlignment(QtCore.Qt.AlignCenter)
                if index > 2*lengthOfList/3:
                    label.setAlignment(QtCore.Qt.AlignRight)
                self._labelTicksWidget.layout().addWidget(label)
    
        def setRange(self, mini, maxi):
            self._slider.setRange(mini, maxi)
    
        def setPageStep(self, value):
            self._slider.setPageStep(value)
    
        def setTickInterval(self, value):
            self._slider.setTickInterval(value)
    
        def setTickPosition(self, position):
            self._slider.setTickPosition(position)
    
        def setValue(self, value):
            self._slider.setValue(value)
    
        def onValueChangedCall(self, function):
            self._slider.valueChanged.connect(function)
    
    0 讨论(0)
提交回复
热议问题