def mousePressEvent(self, event): if not event.button(): event.ignore() return event.accept() opt = QStyleOptionSlider() self.initStyleOption(opt) self.__active_slider = -1 for i, value in enumerate((self.__min_position, self.__max_position)): opt.sliderPosition = value if self._hitTestHandle(opt, event.pos()): self.__active_slider = i self.__pressed_control = QStyle.SC_SliderHandle self.triggerAction(self.SliderMove) self.setRepeatAction(self.SliderNoAction) self.setSliderDown(True) break else: # If the user clicks the groove between the handles, the whole # interval is moved self.__pressed_control = QStyle.SC_SliderGroove self.__click_offset = self._pixelPosToRangeValue(self._pick(event.pos())) self.triggerAction(self.SliderMove) self.setRepeatAction(self.SliderNoAction)
def mousePressEvent(self, event): """ Handle the mouse press event for the control. In a typical slider control, when the user clicks on a point in the slider's total range but not on the thumbtrack, the control would jump to the value of the click location. For this control, clicks which are not direct hits will activate both slider handles for synchronized moving. """ if event.button() != Qt.LeftButton: event.ignore() return event.accept() style = self.style() pos = event.pos() opt = QStyleOptionSlider() self.initStyleOption(opt) low, high = self._low, self._high # hit-test the high handle opt.sliderPosition = high high_rect = style.subControlRect( style.CC_Slider, opt, style.SC_SliderHandle, self ) high_test = high_rect.contains(pos) # hit-test the low handle if needed. if high_test: low_test = False else: opt.sliderPosition = low low_rect = style.subControlRect( style.CC_Slider, opt, style.SC_SliderHandle, self ) low_test = low_rect.contains(pos) # Set the internal state for painting and request an update. # The click offsets when clicking a thumbtrack are stored in # units of pixels. The offset for a click in the empty slider # area is stored in units of value. self._pressed_control = style.SC_SliderHandle if high_test: self._active_thumb = self.HighThumb self._click_offset = self._pick(pos - high_rect.topLeft()) elif low_test: self._active_thumb = self.LowThumb self._click_offset = self._pick(pos - low_rect.topLeft()) else: self._active_thumb = self.BothThumbs offset = self._pixelPosToRangeValue(self._pick(pos), opt) self._click_offset = offset self.setSliderDown(True) self.update()
def drawHandle(self, painter, handle): opt = QStyleOptionSlider() self._initStyleOption(opt, handle) opt.subControls = QStyle.SC_SliderHandle pressed = self.upperPressed if handle == QxtSpanSlider.LowerHandle: pressed = self.lowerPressed if pressed == QStyle.SC_SliderHandle: opt.activeSubControls = pressed opt.state |= QStyle.State_Sunken painter.drawComplexControl(QStyle.CC_Slider, opt)
def drawSpan(self, painter, rect): opt = QStyleOptionSlider() QSlider.initStyleOption(self, opt) # area groove = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderGroove, self) if opt.orientation == QtCore.Qt.Horizontal: groove.adjust(0, 0, -1, 0) else: groove.adjust(0, 0, 0, -1) # pen & brush painter.setPen(QPen(self.gradientLeftColor, 0)) if opt.orientation == QtCore.Qt.Horizontal: self.setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom()) else: self.setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y()) # draw groove intersected = QtCore.QRectF(rect.intersected(groove)) gradient = QLinearGradient(intersected.topLeft(), intersected.topRight()) gradient.setColorAt(0, self.gradientLeft) gradient.setColorAt(1, self.gradientRight) painter.fillRect(intersected, gradient)
def mouseMoveEvent(self, event): if self.__pressed_control not in (QStyle.SC_SliderGroove, QStyle.SC_SliderHandle): event.ignore() return event.accept() opt = QStyleOptionSlider() self.initStyleOption(opt) pos = self._pixelPosToRangeValue(self._pick(event.pos())) if self.__active_slider < 0: offset = pos - self.__click_offset self.__max_position = min(self.__max_position + offset, self.maximum()) self.__min_position = max(self.__min_position + offset, self.minimum()) self.__click_offset = pos else: if self.__active_slider == 0: self.__min_position = max(self.minimum(), pos) self.__max_position = min(self.maximum(), max(self.__max_position, self.__min_position + 1)) else: self.__max_position = min(self.maximum(), pos) self.__min_position = max(self.minimum(), min(self.__min_position, self.__max_position - 1)) self.update() self.slidersMoved.emit(self.__min_position, self.__max_position) # This is different from QAbstractSlider, which sets the value # insider triggerAction() which would be called here instead. # But I don't want to override that as well, so simply: if self.hasTracking(): self.setValues(self.__min_position, self.__max_position)
def _subControlRect(self, subcontrol): """Replaces QStyle.subControlRect() in _pixelPosToRangeValue(). Return QRect for subcontrol which is one of QStyle.SC_SliderGrove or QStyle.SC_SliderHandle. Override this in subclasses that don't use the default groove/handles. This is used because PyQt<5.5 doesn't expose QProxyStyle. """ opt = QStyleOptionSlider() self.initStyleOption(opt) return self.style().subControlRect(QStyle.CC_Slider, opt, subcontrol, self)
def mouseMoveEvent(self, event): """ Handle the mouse move event for the control. If the user has previously pressed the control, this will move the slider(s) to the appropriate position and request an update. """ if self._pressed_control != QStyle.SC_SliderHandle: event.ignore() return event.accept() opt = QStyleOptionSlider() self.initStyleOption(opt) point = self._pick(event.pos()) click_offset = self._click_offset thumb = self._active_thumb if thumb == self.BothThumbs: new_pos = self._pixelPosToRangeValue(point, opt) offset = new_pos - click_offset self._high += offset self._low += offset if self._low < self.minimum(): diff = self.minimum() - self._low self._low += diff self._high += diff if self._high > self.maximum(): diff = self.maximum() - self._high self._low += diff self._high += diff self._click_offset = new_pos self.lowValueChanged.emit(new_pos) self.highValueChanged.emit(new_pos) elif thumb == self.LowThumb: new_pos = self._pixelPosToRangeValue(point - click_offset, opt) if new_pos >= self._high: new_pos = self._high - 1 self._low = new_pos self.lowValueChanged.emit(new_pos) elif thumb == self.HighThumb: new_pos = self._pixelPosToRangeValue(point - click_offset, opt) if new_pos <= self._low: new_pos = self._low + 1 self._high = new_pos self.highValueChanged.emit(new_pos) else: raise ValueError('Invalid thumb enum value.') self.update()
def mouseMoveEvent(self, event): if self.lowerPressed != QStyle.SC_SliderHandle and self.upperPressed != QStyle.SC_SliderHandle: event.ignore() return opt = QStyleOptionSlider() self.initStyleOption(opt) m = self.style().pixelMetric(QStyle.PM_MaximumDragDistance, opt, self) newPosition = self.pixelPosToRangeValue( self.pick(event.pos()) - self.offset) if m >= 0: r = self.rect().adjusted(-m, -m, m, m) if not r.contains(event.pos()): newPosition = self.position # pick the preferred handle on the first movement if self.firstMovement: if self.lower == self.upper: if newPosition < self.lowerValue: self.swapControls() self.firstMovement = False else: self.firstMovement = False if self.lowerPressed == QStyle.SC_SliderHandle: if self.movement == QxtSpanSlider.NoCrossing: newPosition = min(newPosition, self.upper) elif self.movement == QxtSpanSlider.NoOverlapping: newPosition = min(newPosition, self.upper - 1) if (self.movement == QxtSpanSlider.FreeMovement and newPosition > self.upper): self.swapControls() self.setUpperPosition(newPosition) else: self.setLowerPosition(newPosition) elif self.upperPressed == QStyle.SC_SliderHandle: if self.movement == QxtSpanSlider.NoCrossing: newPosition = max(newPosition, self.lowerValue) elif self.movement == QxtSpanSlider.NoOverlapping: newPosition = max(newPosition, self.lowerValue + 1) if (self.movement == QxtSpanSlider.FreeMovement and newPosition < self.lower): self.swapControls() self.setLowerPosition(newPosition) else: self.setUpperPosition(newPosition) event.accept()
def handleMousePress(self, pos, control, value, handle): opt = QStyleOptionSlider() self._initStyleOption(opt, handle) oldControl = control control = self.style().hitTestComplexControl(QStyle.CC_Slider, opt, pos, self) sr = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self) if control == QStyle.SC_SliderHandle: self.position = value self.offset = self.pick(pos - sr.topLeft()) self.lastPressed = handle self.setSliderDown(True) self.emit(SIGNAL("sliderPressed(PyQt_PyObject)"), handle) if control != oldControl: self.update(sr) return control
def paintEvent(self, event): painter = QStylePainter(self) # ticks opt = QStyleOptionSlider() self.initStyleOption(opt) opt.subControls = QStyle.SC_SliderTickmarks painter.drawComplexControl(QStyle.CC_Slider, opt) # groove opt.sliderPosition = 20 opt.sliderValue = 0 opt.subControls = QStyle.SC_SliderGroove painter.drawComplexControl(QStyle.CC_Slider, opt) # handle rects opt.sliderPosition = self.lowerPos lr = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self) lrv = self.pick(lr.center()) opt.sliderPosition = self.upperPos ur = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self) urv = self.pick(ur.center()) # span minv = min(lrv, urv) maxv = max(lrv, urv) c = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderGroove, self).center() spanRect = QRect(QPoint(c.x() - 2, minv), QPoint(c.x() + 1, maxv)) if self.orientation() == QtCore.Qt.Horizontal: spanRect = QRect(QPoint(minv, c.y() - 2), QPoint(maxv, c.y() + 1)) self.drawSpan(painter, spanRect) # handles if self.lastPressed == QxtSpanSlider.LowerHandle: self.drawHandle(painter, QxtSpanSlider.UpperHandle) self.drawHandle(painter, QxtSpanSlider.LowerHandle) else: self.drawHandle(painter, QxtSpanSlider.LowerHandle) self.drawHandle(painter, QxtSpanSlider.UpperHandle)
def pixelPosToRangeValue(self, pos): opt = QStyleOptionSlider() self.initStyleOption(opt) gr = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderGroove, self) sr = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self) if self.orientation() == QtCore.Qt.Horizontal: slider_length = sr.width() slider_min = gr.x() slider_max = gr.right() - slider_length + 1 else: slider_length = sr.height() slider_min = gr.y() slider_max = gr.bottom() - slider_length + 1 return QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), pos - slider_min, slider_max - slider_min, opt.upsideDown)
def paintEvent(self, event): """ Override the paint event to draw both slider handles. """ # based on the paintEvent for QSlider: # http://qt.gitorious.org/qt/qt/blobs/master/src/gui/widgets/qslider.cpp painter = QPainter(self) style = self.style() low, high = self._low, self._high # Draw the low handle along with the groove and ticks. opt = QStyleOptionSlider() self.initStyleOption(opt) opt.subControls = QStyle.SC_SliderGroove | QStyle.SC_SliderHandle if self.tickPosition() != self.NoTicks: opt.subControls |= QStyle.SC_SliderTickmarks if (self._pressed_control and self._active_thumb == self.LowThumb or self._active_thumb == self.BothThumbs): opt.activeSubControls = self._pressed_control opt.state |= QStyle.State_Sunken else: opt.activeSubControls = QStyle.SC_None opt.sliderPosition = low opt.sliderValue = low style.drawComplexControl(QStyle.CC_Slider, opt, painter, self) # Draw high handle. The groove and ticks do not need repainting. opt = QStyleOptionSlider() self.initStyleOption(opt) opt.subControls = QStyle.SC_SliderHandle if (self._pressed_control and self._active_thumb == self.HighThumb or self._active_thumb == self.BothThumbs): opt.activeSubControls = self._pressed_control opt.state |= QStyle.State_Sunken else: opt.activeSubControls = QStyle.SC_None opt.sliderPosition = high opt.sliderValue = high style.drawComplexControl(QStyle.CC_Slider, opt, painter, self)
def get_slider_position(self, slider): style = slider.style() opt = QStyleOptionSlider() return style.subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle)
def paintEvent(self, event): # based on # https://github.com/qt/qtbase/blob/f40dbe0d0b54ce83d2168e82905cf4f75059a841/src/widgets/widgets/qslider.cpp#L315 # https://github.com/enthought/traitsui/blob/master/traitsui/qt4/extra/range_slider.py painter = QStylePainter(self) minpos = self.__min_position maxpos = self.__max_position # Draw the groove opt = QStyleOptionSlider() self.initStyleOption(opt) # Draw empty grove opt.sliderPosition = opt.minimum opt.subControls = QStyle.SC_SliderGroove if self.tickPosition() != self.NoTicks: opt.subControls |= QStyle.SC_SliderTickmarks painter.drawComplexControl(QStyle.CC_Slider, opt) # Draw the highlighted part on top # Qt4.8 and Qt5.3 draw the highlighted groove in a weird way because they # transpose opt.rect. Qt5.7 works fine. if QT_VERSION_STR >= '5.7.0': opt.subControls = QStyle.SC_SliderGroove opt.sliderPosition = opt.maximum if self.orientation() == Qt.Horizontal: _w = opt.rect.width() / opt.maximum x = round(_w * minpos) w = round(_w * (maxpos - minpos)) opt.rect = QRect(x, 0, w, opt.rect.height()) else: _h = opt.rect.height() / opt.maximum y = round(_h * minpos) h = round(_h * (maxpos - minpos)) opt.rect = QRect(0, y, opt.rect.width(), h) painter.drawComplexControl(QStyle.CC_Slider, opt) # Draw the handles for i, position in enumerate((minpos, maxpos)): opt = QStyleOptionSlider() self.initStyleOption(opt) opt.subControls = QStyle.SC_SliderHandle if self.__pressed_control and (self.__active_slider == i or self.__active_slider < 0): opt.activeSubControls = self.__pressed_control opt.state |= QStyle.State_Sunken else: opt.activeSubControls = self.__hovered_control opt.sliderPosition = position opt.sliderValue = position painter.drawComplexControl(QStyle.CC_Slider, opt)