170 lines
7.3 KiB
Python
170 lines
7.3 KiB
Python
import wx
|
|
|
|
class RangeSlider(wx.Panel):
|
|
def __init__(self, parent, id=wx.ID_ANY, minValue=0, maxValue=100,
|
|
minRange=20, maxRange=80, orientation=wx.HORIZONTAL, **kwargs):
|
|
super().__init__(parent, id, **kwargs)
|
|
self.minValue = minValue
|
|
self.maxValue = maxValue
|
|
self.minRange = minRange
|
|
self.maxRange = maxRange
|
|
self.orientation = orientation
|
|
|
|
self.dragging = None
|
|
self.offset = 0
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
|
|
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
|
|
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
|
self.Bind(wx.EVT_SIZE, self.OnResize)
|
|
|
|
def OnPaint(self, event):
|
|
dc = wx.PaintDC(self)
|
|
self.Draw(dc)
|
|
|
|
def OnResize(self, event):
|
|
self.Refresh()
|
|
event.Skip()
|
|
|
|
def OnLeftDown(self, event):
|
|
pos = event.GetPosition()
|
|
min_rect = self.GetMinRangeRect()
|
|
max_rect = self.GetMaxRangeRect()
|
|
|
|
if min_rect.Contains(pos):
|
|
self.dragging = 'min'
|
|
self.offset = pos - min_rect.GetPosition()
|
|
elif max_rect.Contains(pos):
|
|
self.dragging = 'max'
|
|
self.offset = pos - max_rect.GetPosition()
|
|
elif self.GetRangeRect().Contains(pos):
|
|
self.dragging = 'range'
|
|
self.offset = pos - self.GetRangeRect().GetPosition()
|
|
|
|
self.CaptureMouse()
|
|
|
|
def OnLeftUp(self, event):
|
|
if self.HasCapture():
|
|
self.ReleaseMouse()
|
|
self.dragging = None
|
|
|
|
def OnMouseMove(self, event):
|
|
if self.dragging:
|
|
pos = event.GetPosition()
|
|
size = self.GetClientSize()
|
|
width = size.GetWidth() if self.orientation == wx.HORIZONTAL else size.GetHeight()
|
|
total_range = self.maxValue - self.minValue
|
|
step = total_range / width
|
|
|
|
if self.dragging == 'min':
|
|
new_min = self.minValue + int((pos.x - self.offset.x) * step) if self.orientation == wx.HORIZONTAL else self.minValue + int((pos.y - self.offset.y) * step)
|
|
new_min = max(self.minValue, min(new_min, self.maxRange - 1))
|
|
self.minRange = new_min
|
|
elif self.dragging == 'max':
|
|
new_max = self.minValue + int((pos.x - self.offset.x) * step) if self.orientation == wx.HORIZONTAL else self.minValue + int((pos.y - self.offset.y) * step)
|
|
new_max = min(self.maxValue, max(new_max, self.minRange + 1))
|
|
self.maxRange = new_max
|
|
elif self.dragging == 'range':
|
|
range_width = self.maxRange - self.minRange
|
|
new_min = self.minValue + int((pos.x - self.offset.x) * step) if self.orientation == wx.HORIZONTAL else self.minValue + int((pos.y - self.offset.y) * step)
|
|
new_min = max(self.minValue, min(new_min, self.maxValue - range_width))
|
|
self.minRange = new_min
|
|
self.maxRange = new_min + range_width
|
|
|
|
self.Refresh()
|
|
|
|
def Draw(self, dc):
|
|
size = self.GetClientSize()
|
|
width, height = size.GetWidth(), size.GetHeight()
|
|
|
|
total_range = self.maxValue - self.minValue
|
|
if self.orientation == wx.HORIZONTAL:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * width)
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * width)
|
|
else:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * height)
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * height)
|
|
|
|
# Background
|
|
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
|
|
dc.DrawRectangle(0, 0, width, height)
|
|
|
|
# Draw range bar
|
|
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)))
|
|
if self.orientation == wx.HORIZONTAL:
|
|
dc.DrawRectangle(min_pos, 0, max_pos - min_pos, height)
|
|
else:
|
|
dc.DrawRectangle(0, min_pos, width, max_pos - min_pos)
|
|
|
|
# Draw min and max handles
|
|
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)))
|
|
handle_width = 10 if self.orientation == wx.HORIZONTAL else width
|
|
handle_height = 10 if self.orientation == wx.VERTICAL else height
|
|
|
|
if self.orientation == wx.HORIZONTAL:
|
|
dc.DrawRectangle(min_pos - 5, 0, handle_width, handle_height)
|
|
dc.DrawRectangle(max_pos - 5, 0, handle_width, handle_height)
|
|
else:
|
|
dc.DrawRectangle(0, min_pos - 5, handle_width, handle_height)
|
|
dc.DrawRectangle(0, max_pos - 5, handle_width, handle_height)
|
|
|
|
def GetMinRangeRect(self):
|
|
size = self.GetClientSize()
|
|
width, height = size.GetWidth(), size.GetHeight()
|
|
total_range = self.maxValue - self.minValue
|
|
|
|
if self.orientation == wx.HORIZONTAL:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * width)
|
|
return wx.Rect(min_pos - 5, 0, 10, height)
|
|
else:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * height)
|
|
return wx.Rect(0, min_pos - 5, width, 10)
|
|
|
|
def GetMaxRangeRect(self):
|
|
size = self.GetClientSize()
|
|
width, height = size.GetWidth(), size.GetHeight()
|
|
total_range = self.maxValue - self.minValue
|
|
|
|
if self.orientation == wx.HORIZONTAL:
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * width)
|
|
return wx.Rect(max_pos - 5, 0, 10, height)
|
|
else:
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * height)
|
|
return wx.Rect(0, max_pos - 5, width, 10)
|
|
|
|
def GetRangeRect(self):
|
|
size = self.GetClientSize()
|
|
width, height = size.GetWidth(), size.GetHeight()
|
|
total_range = self.maxValue - self.minValue
|
|
|
|
if self.orientation == wx.HORIZONTAL:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * width)
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * width)
|
|
return wx.Rect(min_pos, 0, max_pos - min_pos, height)
|
|
else:
|
|
min_pos = int((self.minRange - self.minValue) / total_range * height)
|
|
max_pos = int((self.maxRange - self.minValue) / total_range * height)
|
|
return wx.Rect(0, min_pos, width, max_pos - min_pos)
|
|
|
|
class MainFrame(wx.Frame):
|
|
def __init__(self):
|
|
super().__init__(None, title='Range Slider Example', size=(400, 200))
|
|
panel = wx.Panel(self)
|
|
|
|
self.horizontal_slider = RangeSlider(panel, minValue=0, maxValue=100, minRange=20, maxRange=80,
|
|
orientation=wx.HORIZONTAL, pos=(10, 10), size=(380, 50))
|
|
self.vertical_slider = RangeSlider(panel, minValue=0, maxValue=100, minRange=20, maxRange=80,
|
|
orientation=wx.VERTICAL, pos=(10, 70), size=(50, 120))
|
|
|
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer.Add(self.horizontal_slider, 1, wx.EXPAND | wx.ALL, 10)
|
|
sizer.Add(self.vertical_slider, 0, wx.EXPAND | wx.ALL, 10)
|
|
panel.SetSizer(sizer)
|
|
|
|
self.Show()
|
|
|
|
if __name__ == '__main__':
|
|
app = wx.App(False)
|
|
frame = MainFrame()
|
|
app.MainLoop() |