简体   繁体   中英

PyQt Slider widget alignment issue

I have a slider whose ticks should should have the names of the year. Below code works, but the label of the ticks are not aligned properly. Each year number should be besides a tick.

class Slider(QWidget):
    def __init__(self, minimum, maximum, parent=None):
        super(Slider, self).__init__(parent=parent)
        container = QtGui.QWidget(self)        
        layout = QHBoxLayout(container)
        self.sl = QSlider(Qt.Vertical)
        self.sl.setMinimum(minimum)
        self.sl.setMaximum(maximum)
        self.sl.setValue(minimum)
        self.sl.setTickPosition(QSlider.TicksLeft)
        self.sl.setTickInterval(1)
        self.sl.setSingleStep(1)
        self.sl.valueChanged.connect(self.valuechange)
        self.setLayout(layout)
        self.sl.resize(100,3000)        
        layout.addWidget(self.sl)

        for i in range(minimum, maximum + 1):
            label = QLabel(str(i))
            label.setContentsMargins(0, 0, 0, 0)
            label.setAlignment(QtCore.Qt.AlignLeft)
            layout.addWidget(label)

        container.setStyleSheet("background-color:red;")

Moreover, resizing the slider is also not working. I am not sure what wrong I am doing here.The tick labels look like this currently.

在此处输入图片说明

One of the main problems in your code is that you are arranging the labels horizontally instead of vertically, besides placing them in columns with indexes that depend on the minimum value instead of starting at 0 to the number needed, and the other problem is that you must start from the maximum to the minimum.

import sys
from pyqtgraph.Qt import QtCore, QtGui

class Slider(QtGui.QWidget):
    def __init__(self, minimum, maximum, parent=None):
        super(Slider, self).__init__(parent=parent)
        layout = QtGui.QGridLayout(self)
        self.sl = QtGui.QSlider(QtCore.Qt.Vertical)
        self.sl.setMinimum(minimum)
        self.sl.setMaximum(maximum)
        self.sl.setValue(minimum)
        self.sl.setTickPosition(QtGui.QSlider.TicksLeft)
        self.sl.setTickInterval(1)
        self.sl.setSingleStep(1)

        self.sl.valueChanged.connect(lambda value: print(value))

        for index, value in enumerate(range(maximum, minimum-1, -1)):
            label = QtGui.QLabel("{}".format(value))
            layout.addWidget(label, index, 0, QtCore.Qt.AlignLeft)

        layout.addWidget(self.sl, 0, 1, maximum - minimum + 1, 1, QtCore.Qt.AlignLeft)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = Slider(2015, 2019)
    w.show()
    sys.exit(app.exec_())

在此处输入图片说明

I found this hack , and after a bit of messing around, I've managed to translate it to python with some modifications. You can specify the orientation, or give an array of custom label texts other than the integer levels.

Need to be careful that to actually refer to the QSlider you'll have to use slider.sl , for instance slider.sl.valueChanged.connect(someFunc) .

(I'm using PyQt5 and the imports are a bit different from pyqtgraph.)

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(1999, 2006 , 1, orientation=Qt.Vertical,
            labels=['Y%d' %ii for ii in range(1999,2007)])

    ha.addWidget(w)
    frame.show()
    sys.exit(app.exec_())

And a screenshot:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM