Package IDAscope :: Package idascope :: Package widgets :: Module RangeSlider
[hide private]
[frames] | no frames]

Source Code for Module IDAscope.idascope.widgets.RangeSlider

  1  #!/usr/bin/python 
  2  """ 
  3  This software is OSI Certified Open Source Software. 
  4  OSI Certified is a certification mark of the Open Source Initiative. 
  5   
  6  Copyright (c) 2006, Enthought, Inc. 
  7  All rights reserved. 
  8   
  9  Redistribution and use in source and binary forms, with or without 
 10  modification, are permitted provided that the following conditions are met: 
 11   
 12   * Redistributions of source code must retain the above copyright notice, this 
 13     list of conditions and the following disclaimer. 
 14   * Redistributions in binary form must reproduce the above copyright notice, 
 15     this list of conditions and the following disclaimer in the documentation 
 16     and/or other materials provided with the distribution. 
 17   * Neither the name of Enthought, Inc. nor the names of its contributors may 
 18     be used to endorse or promote products derived from this software without 
 19     specific prior written permission. 
 20   
 21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 22  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 23  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 24  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
 25  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 26  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 28  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 29  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 30  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 31   
 32  The software contained in the traits/protocols/ directory is 
 33  the pyprotocols project (http://peak.telecommunity.com/PyProtocols.html), 
 34  it is originaly licensed under the terms of the Python Software 
 35  Foundation License, which is compatible with the above terms. 
 36  """ 
 37   
 38  from PySide import QtGui, QtCore 
 39   
 40   
41 -class RangeSlider(QtGui.QSlider):
42 """ 43 A slider for ranges. 44 This class provides a dual-slider for ranges, where there is a defined 45 maximum and minimum, as is a normal slider, but instead of having a 46 single slider value, there are 2 slider values. 47 This class emits the same signals as the QSlider base class, with the 48 exception of valueChanged 49 """
50 - def __init__(self, *args):
51 super(RangeSlider, self).__init__(*args) 52 53 self.QtGui = QtGui 54 self.QtCore = QtCore 55 56 self._low = self.minimum() 57 self._high = self.maximum() 58 59 self.pressed_control = self.QtGui.QStyle.SC_None 60 self.hover_control = self.QtGui.QStyle.SC_None 61 self.click_offset = 0 62 63 # 0 for the low, 1 for the high, -1 for both 64 self.active_slider = 0
65
66 - def low(self):
67 return self._low
68
69 - def setLow(self, low):
70 self._low = low 71 self.update()
72
73 - def high(self):
74 return self._high
75
76 - def setHigh(self, high):
77 self._high = high 78 self.update()
79
80 - def paintEvent(self, event):
81 # based on http://qt.gitorious.org/qt/qt/blobs/master/src/gui/widgets/qslider.cpp 82 83 painter = self.QtGui.QPainter(self) 84 style = self.QtGui.QApplication.style() 85 86 for i, value in enumerate([self._low, self._high]): 87 opt = self.QtGui.QStyleOptionSlider() 88 self.initStyleOption(opt) 89 90 # Only draw the groove for the first slider so it doesn't get drawn 91 # on top of the existing ones every time 92 if i == 0: 93 opt.subControls = self.QtGui.QStyle.SC_SliderGroove | self.QtGui.QStyle.SC_SliderHandle 94 else: 95 opt.subControls = self.QtGui.QStyle.SC_SliderHandle 96 97 if self.tickPosition() != self.NoTicks: 98 opt.subControls |= self.QtGui.QStyle.SC_SliderTickmarks 99 100 if self.pressed_control: 101 opt.activeSubControls = self.pressed_control 102 opt.state |= self.QtGui.QStyle.State_Sunken 103 else: 104 opt.activeSubControls = self.hover_control 105 106 opt.sliderPosition = value 107 opt.sliderValue = value 108 style.drawComplexControl(self.QtGui.QStyle.CC_Slider, opt, painter, self)
109
110 - def mousePressEvent(self, event):
111 event.accept() 112 113 style = self.QtGui.QApplication.style() 114 button = event.button() 115 116 # In a normal slider control, when the user clicks on a point in the 117 # slider's total range, but not on the slider part of the control the 118 # control would jump the slider value to where the user clicked. 119 # For this control, clicks which are not direct hits will slide both 120 # slider parts 121 122 if button: 123 opt = self.QtGui.QStyleOptionSlider() 124 self.initStyleOption(opt) 125 126 self.active_slider = -1 127 128 for i, value in enumerate([self._low, self._high]): 129 opt.sliderPosition = value 130 hit = style.hitTestComplexControl(style.CC_Slider, opt, event.pos(), self) 131 if hit == style.SC_SliderHandle: 132 self.active_slider = i 133 self.pressed_control = hit 134 135 self.triggerAction(self.SliderMove) 136 self.setRepeatAction(self.SliderNoAction) 137 self.setSliderDown(True) 138 break 139 140 if self.active_slider < 0: 141 self.pressed_control = self.QtGui.QStyle.SC_SliderHandle 142 self.click_offset = self.__pixelPosToRangeValue(self.__pick(event.pos())) 143 self.triggerAction(self.SliderMove) 144 self.setRepeatAction(self.SliderNoAction) 145 else: 146 event.ignore()
147
148 - def mouseMoveEvent(self, event):
149 if self.pressed_control != self.QtGui.QStyle.SC_SliderHandle: 150 event.ignore() 151 return 152 153 event.accept() 154 new_pos = self.__pixelPosToRangeValue(self.__pick(event.pos())) 155 opt = self.QtGui.QStyleOptionSlider() 156 self.initStyleOption(opt) 157 if self.active_slider < 0: 158 offset = new_pos - self.click_offset 159 self._high += offset 160 self._low += offset 161 if self._low < self.minimum(): 162 diff = self.minimum() - self._low 163 self._low += diff 164 self._high += diff 165 if self._high > self.maximum(): 166 diff = self.maximum() - self._high 167 self._low += diff 168 self._high += diff 169 elif self.active_slider == 0: 170 if new_pos >= self._high: 171 new_pos = self._high - 1 172 self._low = new_pos 173 else: 174 if new_pos <= self._low: 175 new_pos = self._low + 1 176 self._high = new_pos 177 self.click_offset = new_pos 178 self.update() 179 self.emit(self.QtCore.SIGNAL('sliderMoved(int)'), new_pos)
180
181 - def __pick(self, pt):
182 if self.orientation() == self.QtCore.Qt.Horizontal: 183 return pt.x() 184 else: 185 return pt.y()
186
187 - def __pixelPosToRangeValue(self, pos):
188 opt = self.QtGui.QStyleOptionSlider() 189 self.initStyleOption(opt) 190 style = self.QtGui.QApplication.style() 191 192 gr = style.subControlRect(style.CC_Slider, opt, style.SC_SliderGroove, self) 193 sr = style.subControlRect(style.CC_Slider, opt, style.SC_SliderHandle, self) 194 195 if self.orientation() == self.QtCore.Qt.Horizontal: 196 slider_length = sr.width() 197 slider_min = gr.x() 198 slider_max = gr.right() - slider_length + 1 199 else: 200 slider_length = sr.height() 201 slider_min = gr.y() 202 slider_max = gr.bottom() - slider_length + 1 203 return style.sliderValueFromPosition(self.minimum(), self.maximum(), pos - slider_min, slider_max - \ 204 slider_min, opt.upsideDown)
205