Issue
For example, we have a QSlider instance of PYQT5, from left to right, 0 to 100% When I clicked the 50% location, the handle won't move to 50% directly but just move a constant stride. What should I do?
Solution
What you're referring to is the "absolute set" option, which completely relies on the current style. That behavior is checked by the slider by querying the QStyle styleHint()
about the SH_Slider_AbsoluteSetButtons
, which replies with a (possibly empty) Qt.MouseButton mask.
By default, pressing outside the handle with the left button just repeatedly scrolls from the current slider position toward the mouse cursor, while pressing with the middle button it places the slider exactly where the cursor is (depending on the OS and QStyle).
If you want to override this there are two possibilities.
Proxy style override
This works by using a QProxyStyle and overriding the aforementioned styleHint
. Unfortunately, QSlider just queries the style about the hint without providing the widget argument, so there's no way to know which slider sent the request. The result is that the behavior will become global for all QSliders in the application, unless you apply the style only to those slider for which you want this behavior, but this can result in some issues and inconsistencies, especially if you already need to use a proxy style.
class ProxyStyle(QtWidgets.QProxyStyle):
def styleHint(self, hint, opt=None, widget=None, returnData=None):
res = super().styleHint(hint, opt, widget, returnData)
if hint == self.SH_Slider_AbsoluteSetButtons:
res |= QtCore.Qt.LeftButton
return res
app = QtWidgets.QApplication(sys.argv)
# set the style globally for the application
app.setStyle(ProxyStyle())
slider = QtWidgets.QSlider()
# or just for the slider
slider.setStyle(ProxyStyle())
Override mouseButtonPress
This option relies on subclassing and partially overriding the mouse button press event. The trick is to check the pressed button and if the slider is not currently pressed (to avoid unexpected behavior when pressing multiple buttons), then move the slider at the mouse position and finally call the base mouseButtonPress
implementation: since at that point the handle will be under the mouse, the slider will "believe" that the handle was already there, thus beginning an actual slider move.
class SliderCustom(QtWidgets.QSlider):
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton and not self.isSliderDown():
opt = QtWidgets.QStyleOptionSlider()
self.initStyleOption(opt)
sliderRect = self.style().subControlRect(
QtWidgets.QStyle.CC_Slider, opt,
QtWidgets.QStyle.SC_SliderHandle, self)
if event.pos() not in sliderRect:
# the mouse is not over the handle, let's move it; this is based
# on the original C++ code that moves the handle when the
# "absolute button" is pressed
grooveRect = self.style().subControlRect(
QtWidgets.QStyle.CC_Slider, opt,
QtWidgets.QStyle.SC_SliderGroove, self)
center = sliderRect.center() - sliderRect.topLeft()
pos = event.pos() - center
if self.orientation() == QtCore.Qt.Horizontal:
sliderLength = sliderRect.width()
sliderMin = grooveRect.x()
sliderMax = grooveRect.right() - sliderLength + 1
pos = pos.x()
else:
sliderLength = sliderRect.height()
sliderMin = grooveRect.y()
sliderMax = grooveRect.bottom() - sliderLength + 1
pos = pos.y()
value = self.style().sliderValueFromPosition(
self.minimum(), self.maximum(), pos - sliderMin,
sliderMax - sliderMin, opt.upsideDown
)
self.setSliderPosition(value)
super().mousePressEvent(event)
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.