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()