How can I make QSlider's paintEvent's rect a little smaller? I want to have a really thin slider but I want it to be able to receive clicks easily so it's actual size has to be a bit bigger than what's painted. If the slider is really thin, 2 pixels for example, it's so annoying to try and catch the handle or click on it. I tried creating custom QPaintEvent within paintEvent
method and making it's rect height equal to 1-2 and then passing it to super().paintEvent(my_new_paint_event)
but it didn't work. How can this be done?
CodePudding user response:
Besides using a QSS (which requires to style all properties), the only reliable solution is to use a proxy style.
While overriding the paintEvent()
of the slider is possible, there are two main issues:
- changing the event region is completely pointless, as it will only change the exposed region that the function will draw upon (and that region might also be completely ignored);
- the default
paintEvent()
of QSlider updates its option based on the current state and active/hovered subcontrols before calling the style functions, and unless you're willing to override a bunch of other functions (enter/leave, mouse press/move/release, keyboard events, etc), you'll only get a partially working result that won't reflect the actual widget state;
A QSlider draws itself using the drawComplexControl()
function of QStyle, and this is achieved by providing a set of flags used for the subControls
and activeSubControls
of QStyleOptionSlider.
Since one of those controls is SC_SliderGroove
, which is the part of the slider on which the handle moves, the solution is to remove that from the subControls
, and do the painting on your own. Remember that painting happens from bottom to top, so the custom groove must be drawn before calling the base implementation.
class Style(QtWidgets.QProxyStyle):
def drawComplexControl(self, control, opt, qp, widget=None):
if control == self.CC_Slider:
# get the default rectangle of the groove
groove = self.subControlRect(
control, opt, self.SC_SliderGroove, widget)
# create a small one
if opt.orientation == QtCore.Qt.Horizontal:
rect = QtCore.QRectF(
groove.x(), groove.center().y() - .5,
groove.width(), 2)
else:
rect = QtCore.QRectF(
groove.center().x() - .5, groove.y(),
2, groove.height())
qp.save()
qp.setBrush(opt.palette.mid())
qp.setPen(opt.palette.dark().color())
qp.setRenderHints(qp.Antialiasing)
qp.drawRoundedRect(rect, 1, 1)
qp.restore()
# remove the groove flag from the subcontrol list
opt.subControls &= ~self.SC_SliderGroove
super().drawComplexControl(control, opt, qp, widget)
The above is generic for PyQt5 and PySide, for PyQt6 you need to change the flag names using their type, like Qt.Orientation.Horizontal
, etc.