【问题标题】:PyQt Slider widget alignment issuePyQt Slider 小部件对齐问题
【发布时间】:2018-08-14 12:54:36
【问题描述】:

我有一个滑块,它的刻度应该有年份的名称。下面的代码有效,但刻度的标签没有正确对齐。每一年的数字都应该是一个勾号。

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

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

【问题讨论】:

    标签: python pyqt pyqt4 pyqtgraph qslider


    【解决方案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_())
    

    【讨论】:

    • 非常感谢您的帮助。不过,我不明白为什么单击滑块会将光标直接带到最大值,而不是一次一个刻度。我已经为此创建了一个问题,如果您可以查看this
    • 我尝试了这种方法,它只是在级别数较少且尺寸不太大的情况下“看起来”像对齐。如果我放大小部件,它似乎已关闭。 PS 我正在使用 PyQt5,以防万一。
    【解决方案2】:

    我找到了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_())
    

    还有截图:

    【讨论】:

    • 尝试将left = self.left_margin - rect.width()更改为left = 2
    猜你喜欢
    • 1970-01-01
    • 2021-04-01
    • 2013-03-05
    • 2015-12-20
    • 2012-06-03
    • 2018-07-28
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多