PyXR

c:\projects\bitpim\src \ rangedslider.py



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