我有一个 QSlider,当用户按下鼠标左键时,我想将其移动到鼠标光标的位置。我一直在四处寻找,但找不到任何最近可以解决我的问题的东西。
这是我的滑块。我希望能够单击使滑块跳转到鼠标单击的位置。我可以拖动滑块,但我希望能够单击。我测试了单击 Dolphin 文件管理器中的滑块。它递增而不是跳转到鼠标的确切位置。
查看Qt5文档
QSlider 自己的功能很少[...]
这表明没有内置方法可以做到这一点。有没有办法获取鼠标点击的位置并将滑块移动到该点?
解决方案是计算位置并将其设置在mousePressEvent中,计算不像算术计算那么容易,因为它取决于每个操作系统的样式和样式表,所以我们必须使用QStyle,如下所示:
from PyQt5 import QtCore, QtWidgets
class Slider(QtWidgets.QSlider):
def mousePressEvent(self, event):
super(Slider, self).mousePressEvent(event)
if event.button() == QtCore.Qt.LeftButton:
val = self.pixelPosToRangeValue(event.pos())
self.setValue(val)
def pixelPosToRangeValue(self, pos):
opt = QtWidgets.QStyleOptionSlider()
self.initStyleOption(opt)
gr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderGroove, self)
sr = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderHandle, self)
if self.orientation() == QtCore.Qt.Horizontal:
sliderLength = sr.width()
sliderMin = gr.x()
sliderMax = gr.right() - sliderLength + 1
else:
sliderLength = sr.height()
sliderMin = gr.y()
sliderMax = gr.bottom() - sliderLength + 1;
pr = pos - sr.center() + sr.topLeft()
p = pr.x() if self.orientation() == QtCore.Qt.Horizontal else pr.y()
return QtWidgets.QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), p - sliderMin,
sliderMax - sliderMin, opt.upsideDown)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
flay = QtWidgets.QFormLayout(w)
w1 = QtWidgets.QSlider(QtCore.Qt.Horizontal)
w2 = Slider(QtCore.Qt.Horizontal)
flay.addRow("default: ", w1)
flay.addRow("modified: ", w2)
w.show()
sys.exit(app.exec_())
QSlider
没有这样的功能,因此实现此功能的唯一方法是编写自定义小部件并覆盖其中的鼠标单击:
class Slider(QSlider):
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
e.accept()
x = e.pos().x()
value = (self.maximum() - self.minimum()) * x / self.width() + self.minimum()
self.setValue(value)
else:
return super().mousePressEvent(self, e)
请注意,此代码仅适用于水平滑块。
我相信我有一个不那么复杂的解决方案:
from PyQt5.QtWidgets import QSlider
class ClickSlider(QSlider):
"""A slider with a signal that emits its position when it is pressed. Created to get around the slider only updating when the handle is dragged, but not when a new position is clicked"""
sliderPressedWithValue = QSignal(int)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sliderPressed.connect(self.on_slider_pressed)
def on_slider_pressed(self):
"""emits a more descriptive signal when pressed (with slider value during the press event)"""
self.sliderPressedWithValue.emit(self.value())
然后确保连接到您正在更新的任何内容,如下所示:
# example if you're updating a QMediaPlayer object
from PyQt5.QtMultimedia import QMediaPlayer
player = QMediaPlayer()
slider = ClickSlider()
slider.sliderPressedWithValue.connect(player.setPosition) # updates on click
slider.sliderMoved.connect(player.setPosition) # updates on drag
我没有尝试 eyllanesc 的代码。另外两个没用。
运行 Ingvar 的代码,滑块移动到单击的位置,并在视频运行时立即跳回来。 在 Nevermore 的代码中,只有单击滑块的手柄时才会发出信号。单击条/槽没有发出任何东西。
所以我结合了这两种解决方案并且它有效。
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QSlider
class Slider(QSlider):
sliderBarClicked = pyqtSignal(int)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
event.accept()
x = event.pos().x()
value = (self.maximum() - self.minimum()) * x / self.width() + self.minimum()
self.sliderBarClicked.emit(value)
else:
return super().mousePressEvent(event)
与 Nevermore 相同的 QMediaPlayer 示例:
from PyQt5.QtMultimedia import QMediaPlayer
player = QMediaPlayer()
slider = Slider(Qt.Horizontal)
slider.sliderBarClicked.connect(player.setPosition)
slider.sliderMoved.connect(player.setPosition)