Voxel Calibration Wizard

Source code for views.QxtSpanSlider

#
# TI Voxel Viewer component.
#
# Copyright (c) 2014 Texas Instruments Inc.
#
#  -- Borrowed from https://github.com/mkilling/QxtSpanSlider.py with modifications (MIT license)
#


from PySide.QtCore import QRect, QPoint
from PySide.QtGui import QWidget, QAbstractSlider, QSlider, QStyle, QGridLayout, QLabel, QDoubleSpinBox, QGridLayout, QStylePainter, QStyleOption, QStyleOptionSlider, QPen, QPalette, QLinearGradient, QStyleFactory
from PySide import QtCore, QtGui

[docs]def clamp(v, lower, upper): return min(upper, max(lower, v))
[docs]class QxtSpanSlider(QSlider): NoHandle = None LowerHandle = 1 UpperHandle = 2 FreeMovement = None NoCrossing = 1 NoOverlapping = 2 spanChanged = QtCore.Signal(int, int) lowerValueChanged = QtCore.Signal(int) upperValueChanged = QtCore.Signal(int) lowerPositionChanged = QtCore.Signal(int) upperPositionChanged = QtCore.Signal(int) floatLowerPositionChanged = QtCore.Signal(float) floatUpperPositionChanged = QtCore.Signal(float) sliderPressed = QtCore.Signal(object) def __init__(self, parent = None): QSlider.__init__(self, QtCore.Qt.Horizontal, parent) self.rangeChanged.connect(self.updateRange) self.sliderReleased.connect(self.movePressedHandle) self.setStyle(QStyleFactory.create('Plastique')) self.floatMin = 0 self.floatMax = 0 self.floatStep = 0 self.lower = 0 self.upper = 0 self.lowerPos = 0 self.upperPos = 0 self.offset = 0 self.position = 0 self.lastPressed = QxtSpanSlider.NoHandle self.upperPressed = QStyle.SC_None self.lowerPressed = QStyle.SC_None self.movement = QxtSpanSlider.FreeMovement self.mainControl = QxtSpanSlider.LowerHandle self.firstMovement = False self.blockTracking = False p = self.palette() p.setColor(QPalette.Dark, QtGui.QColor('black')) self.setPalette(p) self.gradientLeft = self.palette().color(QPalette.Dark).lighter(100) self.gradientRight = self.palette().color(QPalette.Dark).darker(200)
[docs] def setFloatRange(self, min, max, step): self.floatMin = min self.floatMax = max self.floatStep = step try: self.setRange(min/step, max/step) self.setSpan(min/step, max/step) self.updateToolTip() except RuntimeError: pass
[docs] def updateToolTip(self): if self.floatStep != 0: self.message = "Current = [%f, %f], Maximum = [%f, %f]"%(self.lowerPos*self.floatStep, self.upperPos*self.floatStep, self.minimum()*self.floatStep, self.maximum()*self.floatStep) else: self.message = "Current = [%d, %d], Maximum = [%d, %d]"%(self.lowerPos, self.upperPos, self.minimum(), self.maximum()) self.setToolTip(self.message)
[docs] def lowerValue(self): return min(self.lower, self.upper)
@QtCore.Slot(int)
[docs] def setLowerValue(self, lower): self.setSpan(lower, self.upper)
[docs] def upperValue(self): return max(self.lower, self.upper)
@QtCore.Slot(int)
[docs] def setUpperValue(self, upper): self.setSpan(self.lower, upper)
[docs] def handleMovementMode(self): return self.movement
[docs] def setHandleMovementMode(self, mode): self.movement = mode
@QtCore.Slot(int, int)
[docs] def setSpan(self, lower, upper): low = clamp(min(lower, upper), self.minimum(), self.maximum()) upp = clamp(max(lower, upper), self.minimum(), self.maximum()) changed = False if low != self.lower: self.lower = low self.lowerPos = low changed = True if upp != self.upper: self.upper = upp self.upperPos = upp changed = True if changed: self.updateToolTip() self.spanChanged.emit(self.lower, self.upper) self.update()
[docs] def lowerPosition(self): return self.lowerPos
@QtCore.Slot(int)
[docs] def setLowerPosition(self, lower): if self.lowerPos != lower: lower = clamp(lower, self.minimum(), self.maximum()) self.lowerPos = lower self.updateToolTip() if not self.hasTracking(): self.update() if self.isSliderDown(): self.lowerPositionChanged.emit(lower) if self.floatStep != 0: self.floatLowerPositionChanged.emit(lower*self.floatStep) if self.hasTracking() and not self.blockTracking: main = (self.mainControl == QxtSpanSlider.LowerHandle) self.triggerAction(QxtSpanSlider.SliderMove, main)
[docs] def upperPosition(self): return self.upperPos
@QtCore.Slot(int)
[docs] def setUpperPosition(self, upper): if self.upperPos != upper: upper = clamp(upper, self.minimum(), self.maximum()) self.upperPos = upper self.updateToolTip() if not self.hasTracking(): self.update() if self.isSliderDown(): self.upperPositionChanged.emit(upper) if self.floatStep != 0: self.floatUpperPositionChanged.emit(upper*self.floatStep) if self.hasTracking() and not self.blockTracking: main = (self.mainControl == QxtSpanSlider.UpperHandle) self.triggerAction(QxtSpanSlider.SliderMove, main)
[docs] def gradientLeftColor(self): return self.gradientLeft
@QtCore.Slot(object)
[docs] def setGradientLeftColor(self, color): self.gradientLeft = color self.update()
[docs] def gradientRightColor(self): return self.gradientRight
@QtCore.Slot(object)
[docs] def setGradientRightColor(self, color): self.gradientRight = color self.update()
@QtCore.Slot()
[docs] def movePressedHandle(self): if self.lastPressed == QxtSpanSlider.LowerHandle: if self.lowerPos != self.lower: main = (self.mainControl == QxtSpanSlider.LowerHandle) self.triggerAction(QAbstractSlider.SliderMove, main) elif self.lastPressed == QxtSpanSlider.UpperHandle: if self.upperPos != self.upper: main = (self.mainControl == QxtSpanSlider.UpperHandle) self.triggerAction(QAbstractSlider.SliderMove, main)
[docs] def pick(self, p): if self.orientation() == QtCore.Qt.Horizontal: return p.x() else: return p.y()
[docs] def triggerAction(self, action, main): value = 0 no = False up = False my_min = self.minimum() my_max = self.maximum() altControl = QxtSpanSlider.LowerHandle if self.mainControl == QxtSpanSlider.LowerHandle: altControl = QxtSpanSlider.UpperHandle self.blockTracking = True isUpperHandle = (main and self.mainControl == QxtSpanSlider.UpperHandle) or (not main and altControl == QxtSpanSlider.UpperHandle) if action == QAbstractSlider.SliderSingleStepAdd: if isUpperHandle: value = clamp(self.upper + self.singleStep(), my_min, my_max) up = True else: value = clamp(self.lower + self.singleStep(), my_min, my_max) elif action == QAbstractSlider.SliderSingleStepSub: if isUpperHandle: value = clamp(self.upper - self.singleStep(), my_min, my_max) up = True else: value = clamp(self.lower - self.singleStep(), my_min, my_max) elif action == QAbstractSlider.SliderToMinimum: value = my_min if isUpperHandle: up = True elif action == QAbstractSlider.SliderToMaximum: value = my_max if isUpperHandle: up = True elif action == QAbstractSlider.SliderMove: if isUpperHandle: up = True no = True elif action == QAbstractSlider.SliderNoAction: no = True if not no and not up: if self.movement == QxtSpanSlider.NoCrossing: value = min(value, self.upper) elif self.movement == QxtSpanSlider.NoOverlapping: value = min(value, self.upper - 1) if self.movement == QxtSpanSlider.FreeMovement and value > self.upper: self.swapControls() self.setUpperPosition(value) else: self.setLowerPosition(value) elif not no: if self.movement == QxtSpanSlider.NoCrossing: value = max(value, self.lower) elif self.movement == QxtSpanSlider.NoOverlapping: value = max(value, self.lower + 1) if self.movement == QxtSpanSlider.FreeMovement and value < self.lower: self.swapControls() self.setLowerPosition(value) else: self.setUpperPosition(value) self.blockTracking = False self.setLowerValue(self.lowerPos) self.setUpperValue(self.upperPos)
[docs] def swapControls(self): self.lower, self.upper = self.upper, self.lower self.lowerPressed, self.upperPressed = self.upperPressed, self.lowerPressed if self.lastPressed == QxtSpanSlider.LowerHandle: self.lastPressed = QxtSpanSlider.UpperHandle else: self.lastPressed = QxtSpanSlider.LowerHandle if self.mainControl == QxtSpanSlider.LowerHandle: self.mainControl = QxtSpanSlider.UpperHandle else: self.mainControl = QxtSpanSlider.LowerHandle
@QtCore.Slot(int, int)
[docs] def updateRange(self, min, max): # setSpan() takes care of keeping span in range self.setSpan(self.lower, self.upper)
[docs] 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)
[docs] def setupPainter(self, painter, orientation, x1, y1, x2, y2): highlight = self.palette().color(QPalette.Highlight) gradient = QLinearGradient(x1, y1, x2, y2) gradient.setColorAt(0, highlight.darker(120)) gradient.setColorAt(1, highlight.lighter(108)) painter.setBrush(gradient) if orientation == QtCore.Qt.Horizontal: painter.setPen(QPen(highlight.darker(130), 0)) else: painter.setPen(QPen(highlight.darker(150), 0))
[docs] 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)
[docs] 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 _initStyleOption(self, option, handle): self.initStyleOption(option) option.sliderPosition = self.upperPos if handle == QxtSpanSlider.LowerHandle: option.sliderPosition = self.lowerPos option.sliderValue = self.upper if handle == QxtSpanSlider.LowerHandle: option.sliderPosition = self.lower
[docs] 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.sliderPressed.emit(handle) if control != oldControl: self.update(sr) return control
[docs] def mousePressEvent(self, event): if self.minimum() == self.maximum() or event.buttons() ^ event.button(): event.ignore() return self.upperPressed = self.handleMousePress(event.pos(), self.upperPressed, self.upper, QxtSpanSlider.UpperHandle) if self.upperPressed != QStyle.SC_SliderHandle: self.lowerPressed = self.handleMousePress(event.pos(), self.lowerPressed, self.lower, QxtSpanSlider.LowerHandle) self.firstMovement = True event.accept()
[docs] 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()
[docs] def mouseReleaseEvent(self, event): QSlider.mouseReleaseEvent(self, event) self.setSliderDown(False) self.lowerPressed = QStyle.SC_None self.upperPressed = QStyle.SC_None self.update()
[docs] def pixelPosToRangeValue(self, pos): opt = QStyleOptionSlider() self.initStyleOption(opt) sliderMin = 0 sliderMax = 0 sliderLength = 0 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: sliderLength = sr.width() sliderMin = gr.x() sliderMax = gr.right() - sliderLength + 1 else: sliderLength = sr.height() sliderMin = gr.y() sliderMax = gr.bottom() - sliderLength + 1 return QStyle.sliderValueFromPosition(self.minimum(), self.maximum(), pos - sliderMin, sliderMax - sliderMin, opt.upsideDown)
lowerValue = QtCore.Property("int", lowerValue, setLowerValue) upperValue = QtCore.Property("int", upperValue, setUpperValue) upperPosition = QtCore.Property("int", upperPosition, setUpperPosition) lowerPosition = QtCore.Property("int", lowerPosition, setLowerPosition) handleMovementMode = QtCore.Property("PyQt_PyObject", handleMovementMode, setHandleMovementMode) gradientLeftColor = QtCore.Property("PyQt_PyObject", gradientLeftColor, setGradientLeftColor) gradientRightColor = QtCore.Property("PyQt_PyObject", gradientRightColor, setGradientRightColor)