我发现了这个 hack,经过一番折腾,我设法将它翻译成 python 并进行了一些修改。您可以指定方向,或提供除整数级别之外的自定义标签文本数组。
例如,需要注意实际引用您必须使用的slider.slQSlider 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_())
和截图:
