在 Qt 中,可以通过设置 QSlider 的属性来实现两个滑块的效果。具体步骤如下:
设置 QSlider 的 range 属性为一个二元组 (min_value, max_value),表示可选取的范围。
设置 QSlider 的 values 属性为一个二元组 (value1, value2),表示两个滑块当前所在的位置。其中 value1 和 value2 应当满足 min_value <= value1 <= value2 <= max_value。
设置 QSlider 的 handleWidth 属性为一个正整数,表示滑块的宽度。
通过继承 QStyle 类并重写 drawComplexControl 方法,绘制出两个滑块之间的轨道,并将其中一个滑块标记为 active 状态,以便区分两个滑块。
将 QSlider 的样式表设置为空字符串,使得样式控件使用自定义风格。
在界面中显示该 QSlider 控件即可。
示例代码如下:
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QStyleOptionSlider, QStylePainter, QWidget, QVBoxLayout, QLabel, QHBoxLayout, \
QSlider
class CustomSlider(QSlider):
def __init__(self, parent=None):
super().__init__(parent)
self.range = (0, 100)
self.values = (30, 70)
self.handle_width = 20
def paintEvent(self, event):
opt = QStyleOptionSlider()
self.initStyleOption(opt)
painter = QStylePainter(self)
painter.setRenderHint(QStylePainter.Antialiasing)
slider_rect = self.style().subControlRect(self.style().CC_Slider, opt, self)
# draw groove
groove_rect = self.style().subControlRect(self.style().CC_Slider, opt, self.style().SC_SliderGroove)
painter.fillRect(groove_rect, Qt.gray)
# draw handle1
handle1_pos = slider_rect.left() + (slider_rect.width() - self.handle_width) * (
self.values[0] - self.range[0]) / (self.range[1] - self.range[0])
handle1_rect = self.style().subControlRect(self.style().CC_Slider, opt, self.style().SC_SliderHandle,
QStyle.SubControl.SC_SliderHandle)
handle1_rect.moveCenter(Qt.Point(handle1_pos + handle1_rect.width() / 2,
groove_rect.center().y()))
opt.subControls = QStyle.SubControl.SC_SliderHandle | QStyle.SubControl.SC_SliderHandleDrag |
QStyle.SubControl.SC_SliderHandleHover
if opt.activeSubControls == QStyle.SubControl.SC_SliderHandle:
opt.state |= QStyle.State_Active
painter.fillRect(handle1_rect.adjusted(0, 2, 0, -2), Qt.green)
painter.drawPixmap(handle1_rect.topLeft(), QPixmap("path/to/handle_image"))
painter.drawText(handle1_rect.translated(0, -20), Qt.AlignCenter | Qt.TextDontClip,
str(self.values[0]))
else:
painter.fillRect(handle1_rect.adjusted(0, 2, 0, -2), Qt.white)
painter.drawRect(handle1_rect.adjusted(0, 2, 0, -2))
painter.drawText(handle1_rect.translated(0, -20), Qt.AlignCenter | Qt.TextDontClip,
str(self.values[0]))
# draw handle2
handle2_pos = slider_rect.left() + (slider_rect.width() - self.handle_width) * (
self.values[1] - self.range[0]) / (self.range[1] - self.range[0])
handle2_rect = self.style().subControlRect(self.style().CC_Slider, opt, self.style().SC_SliderHandle,
QStyle.SubControl.SC_SliderHandle)
handle2_rect.moveCenter(Qt.Point(handle2_pos + handle2_rect.width() / 2,
groove_rect.center().y()))
opt.subControls = QStyle.SubControl.SC_SliderHandle | QStyle.SubControl.SC_SliderHandleDrag |
QStyle.SubControl.SC_SliderHandleHover
if opt.activeSubControls == QStyle.SubControl.SC_SliderHandle:
opt.state |= QStyle.State_Active
painter.fillRect(handle2_rect.adjusted(0, 2, 0, -2), Qt.green)
painter.drawPixmap(handle2_rect.topLeft(), QPixmap("path/to/handle_image"))
painter.drawText(handle2_rect.translated(0, -20), Qt.AlignCenter | Qt.TextDontClip,
str(self.values[1]))
else:
painter.fillRect(handle2_rect.adjusted(0, 2, 0, -2), Qt.white)
painter.drawRect(handle2_rect.adjusted(0, 2, 0, -2))
painter.drawText(handle2_rect.translated(0, -20), Qt.AlignCenter | Qt.TextDontClip,
str(self.values[1]))
def sizeHint(self):
return QSize(self.handle_width * 10 + 20, self.handle_width * 3)
def minimumSizeHint(self):
return QSize(self.handle_width * 2 + 20, self.handle_width)
class Example(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
slider1 = CustomSlider()
slider1.range = (0, 100)
slider1.values = (30, 70)
slider1.handle_width = 20
slider2 = CustomSlider()
slider2.range = (-50, 50)
slider2.values = (-10, 20)
slider2.handle_width = 15
layout.addWidget(QLabel("Slider with range from 0 to 100"))
layout.addWidget(slider1)
layout.addWidget(QLabel("Slider with range from -50 to 50"))
layout.addWidget(slider2)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication([])
ex = Example()
ex.show()
app.exec_()
在上述代码中,我们通过自定义 QSlider 类并重写 paintEvent 方法来绘制滑块。首先绘制轨道,并根据 values 属性计算出两个滑块的位置。然后分别绘制每个滑块,并将其中一个标记为 active 状态以便区分。
最后,在界面中添加两个 CustomSlider 控件即可。注意需要设置每个控件的 range 和 values 属性以便正确显示初始状态。