PyQt Slider小部件对齐问题

时间:2018-03-06 15:40:10

标签: python pyqt pyqt4 pyqtgraph qslider

我有一个滑块,其刻度应该包含年份的名称。下面的代码工作,但刻度的标签没有正确对齐。每年的数字应该是一个滴答。

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;")

此外,调整滑块大小也不起作用。我不确定我在这里做错了什么。刻度标签目前看起来像这样。

enter image description here

2 个答案:

答案 0 :(得分:1)

您的代码中的一个主要问题是您正在水平而不是垂直排列标签,除了将它们放在索引依赖于最小值的列中,而不是从0开始到所需的数量,另一个问题是你必须从最大值到最小值。

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_())

enter image description here

答案 1 :(得分:0)

我发现了this hack,经过一番混乱之后,我设法进行了一些修改将其翻译成python。您可以指定方向,也可以给定自定义标签文本数组,而不是整数级别。

需要注意,要实际引用QSlider,必须使用slider.sl,例如slider.sl.valueChanged.connect(someFunc)

(我使用的是PyQt5,并且导入内容与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_())

和屏幕截图:

enter image description here