0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2005 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: rangedslider.py 2249 2005-03-22 07:13:19Z rogerb $ 0009 0010 "A ranged slider that has a current position and a start/end" 0011 0012 0013 import wx 0014 import wx.lib.newevent 0015 0016 import guihelper 0017 0018 PosChangedEvent, EVT_POS_CHANGED = wx.lib.newevent.NewCommandEvent() 0019 ChangingEvent, EVT_CHANGING = wx.lib.newevent.NewCommandEvent() 0020 0021 class RangedSlider(wx.PyWindow): 0022 0023 THICKNESS=7 0024 0025 def __init__(self, parent, id=-1, size=wx.DefaultSize, pos=wx.DefaultPosition, style=0): 0026 wx.PyWindow.__init__(self, parent, id=id, size=size, pos=pos, style=style|wx.FULL_REPAINT_ON_RESIZE) 0027 0028 self.bg=wx.Brush(parent.GetBackgroundColour()) 0029 0030 self.imgcurrent=guihelper.getimage("ranged-slider-current") 0031 self.bmpcurrent=self.imgcurrent.ConvertToBitmap() 0032 self.imgstart=guihelper.getimage("ranged-slider-start") 0033 self.bmpstart=self.imgstart.ConvertToBitmap() 0034 self.imgend=guihelper.getimage("ranged-slider-end") 0035 self.bmpend=self.imgend.ConvertToBitmap() 0036 0037 # first coord is added to desired x to get where to draw x 0038 # second is added to centre of line to get where to draw y 0039 self.hotcurrent=-self.imgcurrent.GetWidth()/2, -self.imgcurrent.GetHeight()+2 0040 self.hotstart=-13, -20 0041 self.hotend=-7,-20 0042 0043 self.pickers=(self.imgcurrent, self.bmpcurrent,self.hotcurrent), \ 0044 (self.imgstart, self.bmpstart, self.hotstart), \ 0045 (self.imgend, self.bmpend, self.hotend) 0046 0047 self.padleft=2+max([0]+[-h[0] for _,_,h in self.pickers]) 0048 self.padright=2+max([0]+[i.GetWidth()+h[0] for i,_,h in self.pickers]) 0049 0050 assert self.padleft>=2 0051 assert self.padright>=2 0052 self.padleft=max(self.padleft,self.padright) 0053 self.padright=self.padleft 0054 0055 self.poscurrent=0.5 0056 self.posstart=0.0 0057 self.posend=1.0 0058 0059 self._bufbmp=None 0060 0061 self.pen=wx.Pen(wx.NamedColour("LIGHTSTEELBLUE3"), self.THICKNESS) 0062 self.pen.SetCap(wx.CAP_BUTT) 0063 0064 self.clickinfo=None 0065 0066 wx.EVT_ERASE_BACKGROUND(self, lambda evt: None) 0067 wx.EVT_PAINT(self, self.OnPaint) 0068 wx.EVT_LEFT_DOWN(self, self.OnLeftDown) 0069 wx.EVT_LEFT_UP(self, self.OnLeftUp) 0070 wx.EVT_MOTION(self, self.OnMotion) 0071 0072 def DoGetBestSize(self): 0073 sz=self.GetClientSize() 0074 halfheight=self.THICKNESS 0075 for i,_,h in self.pickers: 0076 halfheight=max(halfheight, abs(h[1]), abs(h[1]+i.GetHeight())) 0077 0078 return min(100,sz.width),halfheight*2 0079 0080 0081 0082 def OnPaint(self, _): 0083 sz=self.GetClientSize() 0084 if self._bufbmp is None or sz.width>self._bufbmp.GetWidth() or sz.height>self._bufbmp.GetHeight(): 0085 self._bufbmp=wx.EmptyBitmap((sz.width+64)&~8, (sz.height+64)&~8) 0086 dc=wx.BufferedPaintDC(self, self._bufbmp, style=wx.BUFFER_VIRTUAL_AREA) 0087 dc.SetBackground(self.bg) 0088 dc.Clear() 0089 dc.SetPen(self.pen) 0090 y=sz.height/2 0091 dc.DrawLine(self.padleft,y, sz.width-self.padright, y) 0092 dc.SetPen(wx.NullPen) 0093 start=self.padleft 0094 width=sz.width-self.padleft-self.padright 0095 0096 dc.DrawBitmap(self.bmpcurrent, start+int(width*self.poscurrent)+self.hotcurrent[0], y+self.hotcurrent[1], True) 0097 dc.DrawBitmap(self.bmpstart, start+int(width*self.posstart)+self.hotstart[0], y+self.hotstart[1], True) 0098 dc.DrawBitmap(self.bmpend, start+int(width*self.posend)+self.hotend[0], y+self.hotend[1], True) 0099 0100 def _isinimage(self, image, relx, rely): 0101 "Works out if click is in image, and not transparent area of image" 0102 if image.HasAlpha(): 0103 return image.GetAlpha(relx, rely)>0 0104 if image.HasMask(): 0105 return not (image.GetRed(relx, rely)==image.GetMaskRed() and 0106 image.GetGreen(relx, rely)==image.GetMaskGreen() and 0107 image.GetBlue(relx, rely)==image.GetMaskBlue()) 0108 # we have no way of telling, so say yes 0109 return True 0110 0111 def OnLeftDown(self, evt): 0112 x,y=evt.GetX(), evt.GetY() 0113 sz=self.GetClientSize() 0114 liney=sz.height/2 0115 start=self.padleft 0116 width=sz.width-self.padleft-self.padright 0117 # now try to work out what was clicked on 0118 xx,yy=start+int(width*self.poscurrent)+self.hotcurrent[0], liney+self.hotcurrent[1] 0119 if xx<=x<xx+self.imgcurrent.GetWidth() and yy<=y<yy+self.imgcurrent.GetHeight(): 0120 relx,rely=x-xx,y-yy 0121 if self._isinimage(self.imgcurrent, relx, rely): 0122 print "oncurrent" 0123 self.clickinfo=0,x,self.poscurrent 0124 return 0125 xx,yy=start+int(width*self.posstart)+self.hotstart[0], liney+self.hotstart[1] 0126 if xx<=x<xx+self.imgstart.GetWidth() and yy<=y<yy+self.imgstart.GetHeight(): 0127 relx,rely=x-xx,y-yy 0128 if self._isinimage(self.imgstart, relx, rely): 0129 print "onstart" 0130 self.clickinfo=1,x,self.posstart 0131 return 0132 xx,yy=start+int(width*self.posend)+self.hotend[0], liney+self.hotend[1] 0133 if xx<=x<xx+self.imgend.GetWidth() and yy<=y<yy+self.imgend.GetHeight(): 0134 relx,rely=x-xx,y-yy 0135 if self._isinimage(self.imgend, relx, rely): 0136 print "onend" 0137 self.clickinfo=2,x,self.posend 0138 return 0139 if start<=x<start+width and liney-self.THICKNESS/2<=y<=liney+self.THICKNESS/2: 0140 self.poscurrent=(x-start)/float(width) 0141 self.Refresh(False) 0142 self.clickinfo=0,x,y 0143 return 0144 self.clickinfo=None 0145 0146 def OnMotion(self, evt): 0147 if self.clickinfo is None or not evt.Dragging(): 0148 return 0149 x,y=evt.GetX(), evt.GetY() 0150 sz=self.GetClientSize() 0151 liney=sz.height/2 0152 start=self.padleft 0153 width=sz.width-self.padleft-self.padright 0154 0155 origpos=self.clickinfo[2] 0156 origx=self.clickinfo[1] 0157 delta=origx-x 0158 newpos=origpos-delta/float(width) 0159 if newpos<0: newpos=0.0 0160 elif newpos>1: newpos=1.0 0161 if self.clickinfo[0]==0: self.poscurrent=newpos 0162 elif self.clickinfo[0]==1: self.posstart=min(newpos,self.posend) 0163 elif self.clickinfo[0]==2: self.posend=max(newpos, self.posstart) 0164 if newpos!=origpos: 0165 wx.PostEvent(self, ChangingEvent(self.GetId())) 0166 self.Refresh(False) 0167 0168 def OnLeftUp(self, evt): 0169 if self.clickinfo is not None and self.clickinfo[0]==0: 0170 wx.PostEvent(self, PosChangedEvent(self.GetId(), poscurrent=self.poscurrent)) 0171 0172 # main API 0173 def GetCurrent(self): 0174 return self.poscurrent 0175 def SetCurrent(self, pos): 0176 assert 0<=pos<=1 0177 self.poscurrent=pos 0178 self.Refresh(False) 0179 def GetStart(self): 0180 return self.posstart 0181 def SetStart(self, pos): 0182 assert 0<=pos<=1 0183 self.posstart=pos 0184 self.Refresh(False) 0185 def GetEnd(self): 0186 return self.posend 0187 def SetEnd(self, pos): 0188 assert 0<=pos<=1 0189 self.posend=pos 0190 self.Refresh(False) 0191 0192 0193 if __name__=='__main__': 0194 app=wx.PySimpleApp() 0195 0196 import sys 0197 import common 0198 sys.excepthook=common.formatexceptioneh 0199 0200 f=wx.Frame(None, title="Ranged Slider Tester") 0201 rs=RangedSlider(f) 0202 f.Show() 0203 0204 app.MainLoop() 0205
Generated by PyXR 0.9.4