]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/floatcanvas/FloatCanvas.py 
   2      from  Numeric 
import  array
, asarray
, Float
, cos
,  sin
,  pi
, sum , minimum
, maximum
, Int32
, zeros
,  ones
,  concatenate
,  sqrt
,  argmin
,  power
,  absolute
,  matrixmultiply
,  transpose
,  sometrue
   5          from  numarray 
import  array
,  asarray
,  Float
,  cos
,  sin
,  pi
,  sum ,  minimum
,  maximum
,  Int32
,  zeros
,  concatenate
,  matrixmultiply
,  transpose
,  sometrue
   7          raise  ImportError ( "I could not import either Numeric or numarray" )    9  from  time 
import  clock
,  sleep
  16  ## A global variable to hold the Pixels per inch that wxWindows thinks is in use   17  ## This is used for scaling fonts.   18  ## This can't be computed on module __init__, because a wx.App might not have iniitalized yet.   21  ## a custom Exceptions:   23  class  FloatCanvasError ( Exception ):   26  ## Create all the mouse events   27  # I don't see a need for these two, but maybe some day!   28  #EVT_FC_ENTER_WINDOW = wx.NewEventType()   29  #EVT_FC_LEAVE_WINDOW = wx.NewEventType()   30  EVT_FC_LEFT_DOWN 
=  wx
. NewEventType ()    31  EVT_FC_LEFT_UP  
=  wx
. NewEventType ()   32  EVT_FC_LEFT_DCLICK 
=  wx
. NewEventType ()    33  EVT_FC_MIDDLE_DOWN 
=  wx
. NewEventType ()    34  EVT_FC_MIDDLE_UP 
=  wx
. NewEventType ()    35  EVT_FC_MIDDLE_DCLICK 
=  wx
. NewEventType ()    36  EVT_FC_RIGHT_DOWN 
=  wx
. NewEventType ()    37  EVT_FC_RIGHT_UP 
=  wx
. NewEventType ()    38  EVT_FC_RIGHT_DCLICK 
=  wx
. NewEventType ()    39  EVT_FC_MOTION 
=  wx
. NewEventType ()    40  EVT_FC_MOUSEWHEEL 
=  wx
. NewEventType ()    41  ## these two are for the hit-test stuff, I never make them real Events   42  EVT_FC_ENTER_OBJECT 
=  wx
. NewEventType ()   43  EVT_FC_LEAVE_OBJECT 
=  wx
. NewEventType ()   45  ##Create all mouse event binding functions   46  #def EVT_ENTER_WINDOW( window, function ):   47  #    window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function )    48  #def EVT_LEAVE_WINDOW( window, function ):   49  #    window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function )    50  def  EVT_LEFT_DOWN (  window
,  function 
):     51      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_DOWN 
,  function 
)   52  def  EVT_LEFT_UP (  window
,  function 
):   53      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_UP 
,  function 
)   54  def  EVT_LEFT_DCLICK   (  window
,  function 
):   55      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_DCLICK 
,  function 
)   56  def  EVT_MIDDLE_DOWN   (  window
,  function 
):   57      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_DOWN 
,  function 
)   58  def  EVT_MIDDLE_UP   (  window
,  function 
):   59      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_UP 
,  function 
)   60  def  EVT_MIDDLE_DCLICK   (  window
,  function 
):   61      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_DCLICK 
,  function 
)   62  def  EVT_RIGHT_DOWN   (  window
,  function 
):   63      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_DOWN 
,  function 
)   64  def  EVT_RIGHT_UP (  window
,  function 
):   65      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_UP 
,  function 
)   66  def  EVT_RIGHT_DCLICK (  window
,  function 
):   67      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_DCLICK 
,  function 
)   68  def  EVT_MOTION (  window
,  function 
):   69      window
. Connect ( - 1 , - 1 , EVT_FC_MOTION 
,  function 
)   70  def  EVT_MOUSEWHEEL (  window
,  function 
):   71      window
. Connect ( - 1 , - 1 , EVT_FC_MOUSEWHEEL 
,  function 
)   73  class  _MouseEvent ( wx
. PyCommandEvent
):   77      This event class takes a regular wxWindows mouse event as a parameter,   78      and wraps it so that there is access to all the original methods. This   79      is similar to subclassing, but you can't subclass a wxWindows event   81      The goal is to be able to it just like a regular mouse event.   85      GetCoords() , which returns and (x,y) tuple in world coordinates.   87      Another difference is that it is a CommandEvent, which propagates up   88      the window hierarchy until it is handled.   92      def  __init__ ( self
,  EventType
,  NativeEvent
,  WinID
,  Coords 
=  None ):   93          wx
. PyCommandEvent
.__ init
__ ( self
)   95          self
. SetEventType (  EventType 
)   96          self
._ NativeEvent 
=  NativeEvent
  99  # I don't think this is used.  100  #    def SetCoords(self,Coords):  101  #        self.Coords = Coords  106      def  __getattr__ ( self
,  name
):  107          #return eval(self.NativeEvent.__getattr__(name) )  108          return  getattr ( self
._ NativeEvent
,  name
)  110  def  _cycleidxs ( indexcount
,  maxvalue
,  step
):  113      Utility function used by _colorGenerator  119          for  idx 
in  xrange ( 0 ,  maxvalue
,  step
):  120              for  tail 
in  _cycleidxs ( indexcount 
-  1 ,  maxvalue
,  step
):  123  def  _colorGenerator ():  127      Generates a seris of unique colors used to do hit-tests with the HIt  132      if  sys
. platform 
==  'darwin' :  135          b 
=  wx
. EmptyBitmap ( 1 , 1 )  142          raise  "ColorGenerator does not work with depth =  %s "  %  depth
 143      return  _cycleidxs ( indexcount
= 3 ,  maxvalue
= 256 ,  step
= step
)  146  #### I don't know if the Set objects are useful, beyond the pointset  147  #### object The problem is that when zoomed in, the BB is checked to see  148  #### whether to draw the object.  A Set object can defeat this. ONe day  149  #### I plan to write some custon C++ code to draw sets of objects  151  ##class ObjectSetMixin:  153  ##    A mix-in class for draw objects that are sets of objects  155  ##    It contains methods for setting lists of pens and brushes  158  ##    def SetPens(self,LineColors,LineStyles,LineWidths):  160  ##        This method used when an object could have a list of pens, rather than just one  161  ##        It is used for LineSet, and perhaps others in the future.  163  ##        fixme: this should be in a mixin  165  ##        fixme: this is really kludgy, there has got to be a better way!  170  ##        if type(LineColors) == types.ListType:  171  ##            length = len(LineColors)  173  ##            LineColors = [LineColors]  175  ##        if type(LineStyles) == types.ListType:  176  ##            length = len(LineStyles)  178  ##            LineStyles = [LineStyles]  180  ##        if type(LineWidths) == types.ListType:  181  ##            length = len(LineWidths)  183  ##            LineWidths = [LineWidths]  186  ##            if len(LineColors) == 1:  187  ##                LineColors = LineColors*length  188  ##            if len(LineStyles) == 1:  189  ##                LineStyles = LineStyles*length  190  ##            if len(LineWidths) == 1:  191  ##                LineWidths = LineWidths*length  194  ##        for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):  195  ##            if LineColor is None or LineStyle is None:  196  ##                self.Pens.append(wx.TRANSPARENT_PEN)  197  ##                # what's this for?> self.LineStyle = 'Transparent'  198  ##            if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):  199  ##                Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])  200  ##                self.Pens.append(Pen)  202  ##                self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])  204  ##            self.Pens = self.Pens[0]  208      This is the base class for all the objects that can be drawn.  210      One must subclass from this (and an assortment of Mixins) to create  215      def  __init__ ( self
, InForeground  
=  False ):  216          self
. InForeground 
=  InForeground
 221          self
. CallBackFuncs 
= {}  223          ## these are the defaults  227          self
. MinHitLineWidth 
=  3  228          self
. HitLineWidth 
=  3  ## this gets re-set by the subclasses if necessary  233          self
. FillStyle 
=  "Solid"  235      # I pre-define all these as class variables to provide an easier  236      # interface, and perhaps speed things up by caching all the Pens  237      # and Brushes, although that may not help, as I think wx now  238      # does that on it's own. Send me a note if you know!  241              (  None , "Transparent" )  :  wx
. TRANSPARENT_BRUSH
,  242              ( "Blue" , "Solid" )       :  wx
. BLUE_BRUSH
,  243              ( "Green" , "Solid" )      :  wx
. GREEN_BRUSH
,  244              ( "White" , "Solid" )      :  wx
. WHITE_BRUSH
,  245              ( "Black" , "Solid" )      :  wx
. BLACK_BRUSH
,  246              ( "Grey" , "Solid" )       :  wx
. GREY_BRUSH
,  247              ( "MediumGrey" , "Solid" ) :  wx
. MEDIUM_GREY_BRUSH
,  248              ( "LightGrey" , "Solid" )  :  wx
. LIGHT_GREY_BRUSH
,  249              ( "Cyan" , "Solid" )       :  wx
. CYAN_BRUSH
,  250              ( "Red" , "Solid" )        :  wx
. RED_BRUSH
 253              ( None , "Transparent" , 1 )   :  wx
. TRANSPARENT_PEN
,  254              ( "Green" , "Solid" , 1 )      :  wx
. GREEN_PEN
,  255              ( "White" , "Solid" , 1 )      :  wx
. WHITE_PEN
,  256              ( "Black" , "Solid" , 1 )      :  wx
. BLACK_PEN
,  257              ( "Grey" , "Solid" , 1 )       :  wx
. GREY_PEN
,  258              ( "MediumGrey" , "Solid" , 1 ) :  wx
. MEDIUM_GREY_PEN
,  259              ( "LightGrey" , "Solid" , 1 )  :  wx
. LIGHT_GREY_PEN
,  260              ( "Cyan" , "Solid" , 1 )       :  wx
. CYAN_PEN
,  261              ( "Red" , "Solid" , 1 )        :  wx
. RED_PEN
 265              "Transparent"     :  wx
. TRANSPARENT
,  267              "BiDiagonalHatch" :  wx
. BDIAGONAL_HATCH
,  268              "CrossDiagHatch"  :  wx
. CROSSDIAG_HATCH
,  269              "FDiagonal_Hatch" :  wx
. FDIAGONAL_HATCH
,  270              "CrossHatch"      :  wx
. CROSS_HATCH
,  271              "HorizontalHatch" :  wx
. HORIZONTAL_HATCH
,  272              "VerticalHatch"   :  wx
. VERTICAL_HATCH
 277              "Transparent" :  wx
. TRANSPARENT
,  279              "LongDash"    :  wx
. LONG_DASH
,  280              "ShortDash"   :  wx
. SHORT_DASH
,  281              "DotDash"     :  wx
. DOT_DASH
,  284      def  Bind ( self
,  Event
,  CallBackFun
):  285          self
. CallBackFuncs
[ Event
] =  CallBackFun
 287          self
._ Canvas
. UseHitTest 
=  True  288          if not  self
._ Canvas
._ HTdc
:  289              self
._ Canvas
. MakeNewHTdc ()  290          if not  self
. HitColor
:  291              if not  self
._ Canvas
. HitColorGenerator
:  292                  self
._ Canvas
. HitColorGenerator 
=  _colorGenerator ()  293                  self
._ Canvas
. HitColorGenerator
. next ()  # first call to prevent the background color from being used.  294              self
. HitColor 
=  self
._ Canvas
. HitColorGenerator
. next ()  295              self
. SetHitPen ( self
. HitColor
, self
. HitLineWidth
)  296              self
. SetHitBrush ( self
. HitColor
)  297          # put the object in the hit dict, indexed by it's color  298          if not  self
._ Canvas
. HitDict
:  299              self
._ Canvas
. MakeHitDict ()  300          self
._ Canvas
. HitDict
[ Event
][ self
. HitColor
] = ( self
)  # put the object in the hit dict, indexed by it's color  303          ## fixme: this only removes one from each list, there could be more.  304          if  self
._ Canvas
. HitDict
:  305              for  List 
in  self
._ Canvas
. HitDict
. itervalues ():  313      def  SetBrush ( self
, FillColor
, FillStyle
):  314          if  FillColor 
is None or  FillStyle 
is None :  315              self
. Brush 
=  wx
. TRANSPARENT_BRUSH
 316              self
. FillStyle 
=  "Transparent"  318              self
. Brush 
=  self
. BrushList
. setdefault ( ( FillColor
, FillStyle
),   wx
. Brush ( FillColor
, self
. FillStyleList
[ FillStyle
] ) )  320      def  SetPen ( self
, LineColor
, LineStyle
, LineWidth
):  321          if  ( LineColor 
is None )  or  ( LineStyle 
is None ):  322              self
. Pen 
=  wx
. TRANSPARENT_PEN
 323              self
. LineStyle 
=  'Transparent'  325               self
. Pen 
=  self
. PenList
. setdefault ( ( LineColor
, LineStyle
, LineWidth
),   wx
. Pen ( LineColor
, LineWidth
, self
. LineStyleList
[ LineStyle
]) )  327      def  SetHitBrush ( self
, HitColor
):  329              self
. HitBrush 
=  wx
. TRANSPARENT_BRUSH
 331              self
. HitBrush 
=  self
. BrushList
. setdefault ( ( HitColor
, "solid" ),   wx
. Brush ( HitColor
, self
. FillStyleList
[ "Solid" ] ) )  333      def  SetHitPen ( self
, HitColor
, LineWidth
):  335              self
. HitPen 
=  wx
. TRANSPARENT_PEN
 337              self
. HitPen 
=  self
. PenList
. setdefault ( ( HitColor
,  "solid" ,  self
. HitLineWidth
),   wx
. Pen ( HitColor
,  self
. HitLineWidth
,  self
. LineStyleList
[ "Solid" ]) )  339      def  PutInBackground ( self
):  340          if  self
._ Canvas 
and  self
. InForeground
:  341              self
._ Canvas
._ ForeDrawList
. remove ( self
)  342              self
._ Canvas
._ DrawList
. append ( self
)  343              self
._ Canvas
._ BackgroundDirty 
=  True  344              self
. InForeground 
=  False  346      def  PutInForeground ( self
):  347          if  self
._ Canvas 
and  ( not  self
. InForeground
):  348              self
._ Canvas
._ ForeDrawList
. append ( self
)  349              self
._ Canvas
._ DrawList
. remove ( self
)  350              self
._ Canvas
._ BackgroundDirty 
=  True  351              self
. InForeground 
=  True  353  class  ColorOnlyMixin
:  356      Mixin class for objects that have just one color, rather than a fill  361      def  SetColor ( self
,  Color
):  362          self
. SetPen ( Color
, "Solid" , 1 )  363          self
. SetBrush ( Color
, "Solid" )  365      SetFillColor 
=  SetColor 
# Just to provide a consistant interface   370      Mixin class for objects that have just one color, rather than a fill  375      def  SetLineColor ( self
,  LineColor
):  376          self
. LineColor 
=  LineColor
 377          self
. SetPen ( LineColor
, self
. LineStyle
, self
. LineWidth
)  379      def  SetLineStyle ( self
,  LineStyle
):  380          self
. LineStyle 
=  LineStyle
 381          self
. SetPen ( self
. LineColor
, LineStyle
, self
. LineWidth
)  383      def  SetLineWidth ( self
,  LineWidth
):  384          self
. LineWidth 
=  LineWidth
 385          self
. SetPen ( self
. LineColor
, self
. LineStyle
, LineWidth
)  387  class  LineAndFillMixin ( LineOnlyMixin
):  390      Mixin class for objects that have both a line and a fill color and  394      def  SetFillColor ( self
,  FillColor
):  395          self
. FillColor 
=  FillColor
 396          self
. SetBrush ( FillColor
, self
. FillStyle
)  398      def  SetFillStyle ( self
,  FillStyle
):  399          self
. FillStyle 
=  FillStyle
 400          self
. SetBrush ( self
. FillColor
, FillStyle
)  405      This is a mixin class that provides some methods suitable for use  406      with objects that have a single (x,y) coordinate pair.  410      def  Move ( self
,  Delta 
):  413          Move(Delta): moves the object by delta, where delta is a  414          (dx,dy) pair. Ideally a Numpy array of shape (2,)  418          Delta 
=  asarray ( Delta
,  Float
)  420          self
. BoundingBox 
=  self
. BoundingBox 
+  Delta
 422              self
._ Canvas
. BoundingBoxDirty 
=  True        424      def  SetXY ( self
,  x
,  y
):  425          self
. XY 
=  array ( ( x
,  y
),  Float
)  426          self
. CalcBoundingBox ()  428      def  CalcBoundingBox ( self
):  429          ## This may get overwritten in some subclasses  430          self
. BoundingBox 
=  array ( ( self
. XY
,  self
. XY
),  Float 
)  432      def  SetPoint ( self
,  xy
):  433          self
. XY 
=  array (  xy
,  Float
)  435          self
. CalcBoundingBox ()  437  class  PointsObjectMixin
:  440      This is a mixin class that provides some methods suitable for use  441      with objects that have a set of (x,y) coordinate pairs.  446  ## This is code for the PointsObjectMixin object, it needs to be adapted and tested.  447  ## Is the neccesary at all: you can always do:  448  ##    Object.SetPoints( Object.Points + delta, copy = False)      449  ##    def Move(self, Delta ):  452  ##        Move(Delta): moves the object by delta, where delta is an (dx,  453  ##        dy) pair. Ideally a Numpy array of shape (2,)  457  ##        Delta = array(Delta, Float)  459  ##        self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)  461  ##            self._Canvas.BoundingBoxDirty = True        463      def  CalcBoundingBox ( self
):  464          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]),  465                                     min ( self
. Points
[:, 1 ]) ),  466                                    ( max ( self
. Points
[:, 0 ]),  467                                     max ( self
. Points
[:, 1 ]) ) ),  Float 
)  469              self
._ Canvas
. BoundingBoxDirty 
=  True  471      def  SetPoints ( self
,  Points
,  copy 
=  True ):  473          Sets the coordinates of the points of the object to Points (NX2 array).  475          By default, a copy is made, if copy is set to False, a reference  476          is used, iff Points is a NumPy array of Floats. This allows you  477          to change some or all of the points without making any copies.  481          Points = Object.Points  482          Points += (5,10) # shifts the points 5 in the x dir, and 10 in the y dir.  483          Object.SetPoints(Points, False) # Sets the points to the same array as it was  487              self
. Points 
=  array ( Points
,  Float
)  488              self
. Points
. shape 
= (- 1 , 2 )  # Make sure it is a NX2 array, even if there is only one point  490              self
. Points 
=  asarray ( Points
,  Float
)  491          self
. CalcBoundingBox ()  494  class  Polygon ( DrawObject
, PointsObjectMixin
, LineAndFillMixin
):  498      The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of  499      point coordinates.  so that Points[N][0] is the x-coordinate of  500      point N and Points[N][1] is the y-coordinate or Points[N,0] is the  501      x-coordinate of point N and Points[N,1] is the y-coordinate for  504      The other parameters specify various properties of the Polygon, and  505      should be self explanatory.  515                   InForeground 
=  False ):  516          DrawObject
.__ init
__ ( self
, InForeground
)  517          self
. Points 
=  array ( Points
, Float
)  # this DOES need to make a copy  518          self
. CalcBoundingBox ()  520          self
. LineColor 
=  LineColor
 521          self
. LineStyle 
=  LineStyle
 522          self
. LineWidth 
=  LineWidth
 523          self
. FillColor 
=  FillColor
 524          self
. FillStyle 
=  FillStyle
 526          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  528          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  529          self
. SetBrush ( FillColor
, FillStyle
)  531      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel 
=  None ,  HTdc
= None ):  532          Points 
=  WorldToPixel ( self
. Points
)  534          dc
. SetBrush ( self
. Brush
)  535          dc
. DrawPolygon ( Points
)  536          if  HTdc 
and  self
. HitAble
:  537              HTdc
. SetPen ( self
. HitPen
)  538              HTdc
. SetBrush ( self
. HitBrush
)  539              HTdc
. DrawPolygon ( Points
)  541  ##class PolygonSet(DrawObject):  543  ##    The PolygonSet class takes a Geometry.Polygon object.  544  ##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!  546  ##    it creates a set of line segments, from (x1,y1) to (x2,y2)  550  ##    def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):  551  ##        DrawObject.__init__(self, InForeground)  553  ##        ##fixme: there should be some error checking for everything being the right length.  556  ##        self.Points = array(Points,Float)  557  ##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)  559  ##        self.LineColors = LineColors  560  ##        self.LineStyles = LineStyles  561  ##        self.LineWidths = LineWidths  562  ##        self.FillColors = FillColors  563  ##        self.FillStyles = FillStyles  565  ##        self.SetPens(LineColors,LineStyles,LineWidths)  567  ##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):  568  ##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):  569  ##        Points = WorldToPixel(self.Points)  570  ##        Points.shape = (-1,4)  571  ##        dc.DrawLineList(Points,self.Pens)  574  class  Line ( DrawObject
, PointsObjectMixin
, LineOnlyMixin
):  577      The Line class takes a list of 2-tuples, or a NX2 NumPy Float array  578      of point coordinates.  580      It will draw a straight line if there are two points, and a polyline  581      if there are more than two.  584      def  __init__ ( self
, Points
,  588                   InForeground 
=  False ):  589          DrawObject
.__ init
__ ( self
,  InForeground
)  592          self
. Points 
=  array ( Points
, Float
)  593          self
. CalcBoundingBox ()  595          self
. LineColor 
=  LineColor
 596          self
. LineStyle 
=  LineStyle
 597          self
. LineWidth 
=  LineWidth
 599          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  601          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  604      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  605          Points 
=  WorldToPixel ( self
. Points
)  608          if  HTdc 
and  self
. HitAble
:  609              HTdc
. SetPen ( self
. HitPen
)  610              HTdc
. DrawLines ( Points
)  612  class  Arrow ( DrawObject
, XYObjectMixin
, LineOnlyMixin
):  615      Arrow(XY, # coords of origin of arrow (x,y)  616            Length, # length of arrow in pixels  617            theta, # angle of arrow in degrees: zero is straight up  618                   # angle is to the right  624            InForeground = False):  626      It will draw an arrow , starting at the point, (X,Y) pointing in  637                   LineWidth    
=  2 ,  # pixels  638                   ArrowHeadSize 
=  8 ,  # pixels  639                   ArrowHeadAngle 
=  30 ,  # degrees  640                   InForeground 
=  False ):  642          DrawObject
.__ init
__ ( self
,  InForeground
)  644          self
. XY 
=  array ( XY
,  Float
)  645          self
. XY
. shape 
= ( 2 ,)  # Make sure it is a 1X2 array, even if there is only one point  647          self
. Direction 
=  float ( Direction
)  648          self
. ArrowHeadSize 
=  ArrowHeadSize 
 649          self
. ArrowHeadAngle 
=  float ( ArrowHeadAngle
)          651          self
. CalcArrowPoints ()  652          self
. CalcBoundingBox ()  654          self
. LineColor 
=  LineColor
 655          self
. LineStyle 
=  LineStyle
 656          self
. LineWidth 
=  LineWidth
 658          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  660          ##fixme: How should the HitTest be drawn?  661          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  663      def  SetDirection ( self
,  Direction
):  664          self
. Direction 
=  float ( Direction
)  665          self
. CalcArrowPoints ()  667      def  SetLength ( self
,  Length
):  669          self
. CalcArrowPoints ()  671      def  SetLengthDirection ( self
,  Length
,  Direction
):  672          self
. Direction 
=  float ( Direction
)  674          self
. CalcArrowPoints ()  676      def  SetLength ( self
,  Length
):  678          self
. CalcArrowPoints ()  680      def  CalcArrowPoints ( self
):  682          S 
=  self
. ArrowHeadSize
 683          phi 
=  self
. ArrowHeadAngle 
*  pi 
/  360  684          theta 
= ( self
. Direction
- 90.0 ) *  pi 
/  180  685          ArrowPoints 
=  array ( ( ( 0 ,  L
,  L 
-  S
* cos ( phi
), L
,  L 
-  S
* cos ( phi
) ),  686                                 ( 0 ,  0 ,  S
* sin ( phi
),     0 , - S
* sin ( phi
)    ) ),  688          RotationMatrix 
=  array ( ( (  cos ( theta
), - sin ( theta
) ),  689                                    (  sin ( theta
),  cos ( theta
) ) ),  692          ArrowPoints 
=  matrixmultiply ( RotationMatrix
,  ArrowPoints
)  693          self
. ArrowPoints 
=  transpose ( ArrowPoints
)  695      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  697          xy 
=  WorldToPixel ( self
. XY
)  698          ArrowPoints 
=  xy 
+  self
. ArrowPoints
 699          dc
. DrawLines ( ArrowPoints
)  700          if  HTdc 
and  self
. HitAble
:  701              HTdc
. SetPen ( self
. HitPen
)  702              HTdc
. DrawLines ( ArrowPoints
)  704  ##class LineSet(DrawObject, ObjectSetMixin):  706  ##    The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.  707  ##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!  709  ##    it creates a set of line segments, from (x1,y1) to (x2,y2)  713  ##    def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):  714  ##        DrawObject.__init__(self, InForeground)  716  ##        NumLines = len(Points) / 2  717  ##        ##fixme: there should be some error checking for everything being the right length.  720  ##        self.Points = array(Points,Float)  721  ##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)  723  ##        self.LineColors = LineColors  724  ##        self.LineStyles = LineStyles  725  ##        self.LineWidths = LineWidths  727  ##        self.SetPens(LineColors,LineStyles,LineWidths)  729  ##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):  730  ##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):  731  ##        Points = WorldToPixel(self.Points)  732  ##        Points.shape = (-1,4)  733  ##        dc.DrawLineList(Points,self.Pens)  735  class  PointSet ( DrawObject
, PointsObjectMixin
,  ColorOnlyMixin
):  738      The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of  741      If Points is a sequence of tuples: Points[N][0] is the x-coordinate of  742      point N and Points[N][1] is the y-coordinate.  744      If Points is a NumPy array: Points[N,0] is the x-coordinate of point  745      N and Points[N,1] is the y-coordinate for arrays.  747      Each point will be drawn the same color and Diameter. The Diameter  748      is in screen pixels, not world coordinates.  750      The hit-test code does not distingish between the points, you will  751      only know that one of the points got hit, not which one. You can use  752      PointSet.FindClosestPoint(WorldPoint) to find out which one  754      In the case of points, the HitLineWidth is used as diameter.  757      def  __init__ ( self
,  Points
,  Color 
=  "Black" ,  Diameter 
=   1 ,  InForeground 
=  False ):  758          DrawObject
.__ init
__ ( self
, InForeground
)  760          self
. Points 
=  array ( Points
, Float
)  761          self
. Points
. shape 
= (- 1 , 2 )  # Make sure it is a NX2 array, even if there is only one point  762          self
. CalcBoundingBox ()  763          self
. Diameter 
=  Diameter
 765          self
. HitLineWidth 
=  self
. MinHitLineWidth
 768      def  SetDiameter ( self
, Diameter
):  769              self
. Diameter 
=  Diameter
 772      def  FindClosestPoint ( self
,  XY
):  775          Returns the index of the closest point to the point, XY, given  776          in World coordinates. It's essentially random which you get if  777          there are more than one that are the same.  779          This can be used to figure out which point got hit in a mouse  780          binding callback, for instance. It's a lot faster that using a  781          lot of separate points.  784          ## kind of ugly to minimize data copying  786          d 
=  sum (  power ( d
, 2 , d
),  1  )  787          d 
=  absolute (  d
,  d 
)  # don't need the real distance, just which is smallest  788          #dist = sqrt( sum( (self.Points - XY)**2), 1) )  791      def  DrawD2 ( self
,  dc
,  Points
):  792          # A Little optimization for a diameter2 - point  793          dc
. DrawPointList ( Points
)  794          dc
. DrawPointList ( Points 
+ ( 1 , 0 ))  795          dc
. DrawPointList ( Points 
+ ( 0 , 1 ))  796          dc
. DrawPointList ( Points 
+ ( 1 , 1 ))  798      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  800          Points 
=  WorldToPixel ( self
. Points
)  801          if  self
. Diameter 
<=  1 :  802              dc
. DrawPointList ( Points
)  803          elif  self
. Diameter 
<=  2 :  804              self
. DrawD2 ( dc
,  Points
)  806              dc
. SetBrush ( self
. Brush
)  807              radius 
=  int ( round ( self
. Diameter
/ 2 ))  808              if  len ( Points
) >  100 :  810                  xywh 
=  concatenate (( xy
- radius
,  ones ( xy
. shape
) *  self
. Diameter 
),  1  )  811                  dc
. DrawEllipseList ( xywh
)  814                      dc
. DrawCircle ( xy
[ 0 ], xy
[ 1 ],  radius
)  815          if  HTdc 
and  self
. HitAble
:  816              HTdc
. SetPen ( self
. HitPen
)  817              HTdc
. SetBrush ( self
. HitBrush
)  818              if  self
. Diameter 
<=  1 :  819                  HTdc
. DrawPointList ( Points
)  820              elif  self
. Diameter 
<=  2 :  821                  self
. DrawD2 ( HTdc
,  Points
)  823                  if  len ( Points
) >  100 :  825                      xywh 
=  concatenate (( xy
- radius
,  ones ( xy
. shape
) *  self
. Diameter 
),  1  )  826                      HTdc
. DrawEllipseList ( xywh
)  829                          HTdc
. DrawCircle ( xy
[ 0 ], xy
[ 1 ],  radius
)  831  class  Point ( DrawObject
, XYObjectMixin
, ColorOnlyMixin
):  834      The Point class takes a 2-tuple, or a (2,) NumPy array of point  837      The Diameter is in screen points, not world coordinates, So the  838      Bounding box is just the point, and doesn't include the Diameter.  840      The HitLineWidth is used as diameter for the  844      def  __init__ ( self
,  XY
,  Color 
=  "Black" ,  Diameter 
=   1 ,  InForeground 
=  False ):  845          DrawObject
.__ init
__ ( self
,  InForeground
)  847          self
. XY 
=  array ( XY
,  Float
)  848          self
. XY
. shape 
= ( 2 ,)  # Make sure it is a 1X2 array, even if there is only one point  849          self
. CalcBoundingBox ()  851          self
. Diameter 
=  Diameter
 853          self
. HitLineWidth 
=  self
. MinHitLineWidth
 855      def  SetDiameter ( self
, Diameter
):  856              self
. Diameter 
=  Diameter
 859      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  861          xy 
=  WorldToPixel ( self
. XY
)  862          if  self
. Diameter 
<=  1 :  863              dc
. DrawPoint ( xy
[ 0 ],  xy
[ 1 ])  865              dc
. SetBrush ( self
. Brush
)  866              radius 
=  int ( round ( self
. Diameter
/ 2 ))  867              dc
. DrawCircle ( xy
[ 0 ], xy
[ 1 ],  radius
)  868          if  HTdc 
and  self
. HitAble
:  869              HTdc
. SetPen ( self
. HitPen
)  870              if  self
. Diameter 
<=  1 :  871                  HTdc
. DrawPoint ( xy
[ 0 ],  xy
[ 1 ])  873                  HTdc
. SetBrush ( self
. HitBrush
)  874                  HTdc
. DrawCircle ( xy
[ 0 ], xy
[ 1 ],  radius
)  876  class  RectEllipse ( DrawObject
,  XYObjectMixin
, LineAndFillMixin
):  877      def  __init__ ( self
, x
, y
, width
, height
,  883                   InForeground 
=  False ):  885          DrawObject
.__ init
__ ( self
, InForeground
)  887          self
. XY 
=  array ( ( x
,  y
),  Float
)  888          self
. WH 
=  array ( ( width
,  height
),  Float 
)  889          self
. BoundingBox 
=  array ((( x
, y
), ( self
. XY 
+  self
. WH
)),  Float
)  890          self
. LineColor 
=  LineColor
 891          self
. LineStyle 
=  LineStyle
 892          self
. LineWidth 
=  LineWidth
 893          self
. FillColor 
=  FillColor
 894          self
. FillStyle 
=  FillStyle
 896          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  898          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  899          self
. SetBrush ( FillColor
, FillStyle
)  901      def  SetShape ( self
, x
, y
, width
, height
):  902          self
. XY 
=  array ( ( x
,  y
),  Float
)  903          self
. WH 
=  array ( ( width
,  height
),  Float 
)  904          self
. CalcBoundingBox ()  907      def  SetUpDraw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
):  909          dc
. SetBrush ( self
. Brush
)  910          if  HTdc 
and  self
. HitAble
:  911              HTdc
. SetPen ( self
. HitPen
)  912              HTdc
. SetBrush ( self
. HitBrush
)  913          return  (  WorldToPixel ( self
. XY
),  914                   ScaleWorldToPixel ( self
. WH
) )  916      def  CalcBoundingBox ( self
):  917          self
. BoundingBox 
=  array (( self
. XY
, ( self
. XY 
+  self
. WH
) ),  Float
)  918          self
._ Canvas
. BoundingBoxDirty 
=  True  921  class  Rectangle ( RectEllipse
):  923      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  924          (  XY
,  WH 
) =  self
. SetUpDraw ( dc
,  928          dc
. DrawRectanglePointSize ( XY
,  WH
)  929          if  HTdc 
and  self
. HitAble
:  930              HTdc
. DrawRectanglePointSize ( XY
,  WH
)  932  class  Ellipse ( RectEllipse
):  933  #    def __init__(*args, **kwargs):  934  #        RectEllipse.__init__(*args, **kwargs)  936      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  937          (  XY
,  WH 
) =  self
. SetUpDraw ( dc
,  941          dc
. DrawEllipsePointSize ( XY
,  WH
)  942          if  HTdc 
and  self
. HitAble
:  943              HTdc
. DrawEllipsePointSize ( XY
,  WH
)  945  class  Circle ( Ellipse
):  946      def  __init__ ( self
,  x 
, y
,  Diameter
, ** kwargs
):  947          self
. Center 
=  array (( x
, y
), Float
)  948          RectEllipse
.__ init
__ ( self 
,  955      def  SetDiameter ( self
,  Diameter
):  956          x
, y 
=  self
. Center 
- ( Diameter
/ 2 .)  962  class  TextObjectMixin ( XYObjectMixin
):  965      A mix in class that holds attributes and methods that are needed by  970      ## I'm caching fonts, because on GTK, getting a new font can take a  971      ## while. However, it gets cleared after every full draw as hanging  972      ## on to a bunch of large fonts takes a massive amount of memory.  976      def  SetFont ( self
,  Size
,  Family
,  Style
,  Weight
,  Underline
,  FaceName
):  977          self
. Font 
=  self
. FontList
. setdefault ( ( Size
,  991      def  SetColor ( self
,  Color
):  994      def  SetBackgroundColor ( self
,  BackgroundColor
):  995          self
. BackgroundColor 
=  BackgroundColor
 997      ## store the function that shift the coords for drawing text. The  998      ## "c" parameter is the correction for world coordinates, rather  999      ## than pixel coords as the y axis is reversed 1000      ShiftFunDict 
= { 'tl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y
) , 1001                      'tc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y
) ,  1002                      'tr' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y
) ,  1003                      'cl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y 
-  h
/ 2  +  world
* h
) ,  1004                      'cc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y 
-  h
/ 2  +  world
* h
) ,  1005                      'cr' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y 
-  h
/ 2  +  world
* h
) , 1006                      'bl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y 
-  h 
+  2 * world
* h
) , 1007                      'bc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y 
-  h 
+  2 * world
* h
) ,  1008                      'br' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y 
-  h 
+  2 * world
* h
)} 1010  class  Text ( DrawObject
,  TextObjectMixin
): 1012      This class creates a text object, placed at the coordinates, 1013      x,y. the "Position" argument is a two charactor string, indicating 1014      where in relation to the coordinates the string should be oriented. 1016      The first letter is: t, c, or b, for top, center and bottom The 1017      second letter is: l, c, or r, for left, center and right The 1018      position refers to the position relative to the text itself. It 1019      defaults to "tl" (top left). 1021      Size is the size of the font in pixels, or in points for printing 1022      (if it ever gets implimented). Those will be the same, If you assume 1026          Font family, a generic way of referring to fonts without 1027          specifying actual facename. One of: 1028              wx.DEFAULT:  Chooses a default font.  1029              wx.DECORATIVE: A decorative font.  1030              wx.ROMAN: A formal, serif font.  1031              wx.SCRIPT: A handwriting font.  1032              wx.SWISS: A sans-serif font.  1033              wx.MODERN: A fixed pitch font. 1034          NOTE: these are only as good as the wxWindows defaults, which aren't so good. 1036          One of wx.NORMAL, wx.SLANT and wx.ITALIC. 1038          One of wx.NORMAL, wx.LIGHT and wx.BOLD. 1040          The value can be True or False. At present this may have an an 1041          effect on Windows only. 1043      Alternatively, you can set the kw arg: Font, to a wx.Font, and the above will be ignored. 1045      The size is fixed, and does not scale with the drawing. 1047      The hit-test is done on the entire text extent 1051      def  __init__ ( self
, String
, x
, y
, 1054                   BackgroundColor 
=  None , 1060                   InForeground 
=  False , 1063          DrawObject
.__ init
__ ( self
, InForeground
) 1065          self
. String 
=  String
1066          # Input size in in Pixels, compute points size from PPI info. 1067          # fixme: for printing, we'll have to do something a little different 1068          self
. Size 
=  int ( round ( 72.0  *  Size 
/  ScreenPPI
)) 1071          self
. BackgroundColor 
=  BackgroundColor
1076              FaceName           
=   Font
. GetFaceName ()            1077              Family             
=   Font
. GetFamily () 1078              Size               
=   Font
. GetPointSize ()           1079              Style              
=   Font
. GetStyle () 1080              Underlined         
=   Font
. GetUnderlined ()          1081              Weight             
=   Font
. GetWeight () 1082          self
. SetFont ( Size
,  Family
,  Style
,  Weight
,  Underline
,  FaceName
) 1084          self
. BoundingBox 
=  array ((( x
, y
),( x
, y
)), Float
) 1088          ( self
. TextWidth
,  self
. TextHeight
) = ( None ,  None ) 1089          self
. ShiftFun 
=  self
. ShiftFunDict
[ Position
] 1091      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ): 1092          XY 
=  WorldToPixel ( self
. XY
) 1093          dc
. SetFont ( self
. Font
) 1094          dc
. SetTextForeground ( self
. Color
) 1095          if  self
. BackgroundColor
: 1096              dc
. SetBackgroundMode ( wx
. SOLID
) 1097              dc
. SetTextBackground ( self
. BackgroundColor
) 1099              dc
. SetBackgroundMode ( wx
. TRANSPARENT
) 1100          if  self
. TextWidth 
is None or  self
. TextHeight 
is None : 1101              ( self
. TextWidth
,  self
. TextHeight
) =  dc
. GetTextExtent ( self
. String
) 1102          XY 
=  self
. ShiftFun ( XY
[ 0 ],  XY
[ 1 ],  self
. TextWidth
,  self
. TextHeight
) 1103          dc
. DrawTextPoint ( self
. String
,  XY
) 1104          if  HTdc 
and  self
. HitAble
: 1105              HTdc
. SetPen ( self
. HitPen
) 1106              HTdc
. SetBrush ( self
. HitBrush
) 1107              HTdc
. DrawRectanglePointSize ( XY
, ( self
. TextWidth
,  self
. TextHeight
) ) 1109  class  ScaledText ( DrawObject
,  TextObjectMixin
): 1111      This class creates a text object that is scaled when zoomed.  It is 1112      placed at the coordinates, x,y. the "Position" argument is a two 1113      charactor string, indicating where in relation to the coordinates 1114      the string should be oriented. 1116      The first letter is: t, c, or b, for top, center and bottom The 1117      second letter is: l, c, or r, for left, center and right The 1118      position refers to the position relative to the text itself. It 1119      defaults to "tl" (top left). 1121      Size is the size of the font in world coordinates. 1124          Font family, a generic way of referring to fonts without 1125          specifying actual facename. One of: 1126              wx.DEFAULT:  Chooses a default font.  1127              wx.DECORATI: A decorative font.  1128              wx.ROMAN: A formal, serif font.  1129              wx.SCRIPT: A handwriting font.  1130              wx.SWISS: A sans-serif font.  1131              wx.MODERN: A fixed pitch font. 1132          NOTE: these are only as good as the wxWindows defaults, which aren't so good. 1134          One of wx.NORMAL, wx.SLANT and wx.ITALIC. 1136          One of wx.NORMAL, wx.LIGHT and wx.BOLD. 1138          The value can be True or False. At present this may have an an 1139          effect on Windows only. 1141      Alternatively, you can set the kw arg: Font, to a wx.Font, and the 1142      above will be ignored. The size of the font you specify will be 1143      ignored, but the rest of it's attributes will be preserved. 1145      The size will scale as the drawing is zoomed. 1149      As fonts are scaled, the do end up a little different, so you don't 1150      get exactly the same picture as you scale up and doen, but it's 1153      On wxGTK1 on my Linux system, at least, using a font of over about 1154      3000 pts. brings the system to a halt. It's the Font Server using 1155      huge amounts of memory. My work around is to max the font size to 1156      3000 points, so it won't scale past there. GTK2 uses smarter font 1157      drawing, so that may not be an issue in future versions, so feel 1158      free to test. Another smarter way to do it would be to set a global 1159      zoom limit at that point. 1161      The hit-test is done on the entire text extent. This could be made 1162      optional, but I havn't gotten around to it. 1166      def  __init__ ( self
,  String
,  x
,  y 
,  Size
, 1168                   BackgroundColor 
=  None , 1175                   InForeground 
=  False ): 1177          DrawObject
.__ init
__ ( self
, InForeground
) 1179          self
. String 
=  String
1180          self
. XY 
=  array ( ( x
,  y
),  Float
) 1183          self
. BackgroundColor 
=  BackgroundColor
1184          self
. Family 
=  Family   
1186          self
. Weight 
=  Weight   
1187          self
. Underline 
=  Underline
1191              self
. FaceName           
=   Font
. GetFaceName ()            1192              self
. Family             
=   Font
. GetFamily ()     1193              self
. Style              
=   Font
. GetStyle ()      1194              self
. Underlined         
=   Font
. GetUnderlined ()          1195              self
. Weight             
=   Font
. GetWeight ()     1197          # Experimental max font size value on wxGTK2: this works OK on 1198          # my system. If it's a lot  larger, there is a crash, with the 1201          # The application 'FloatCanvasDemo.py' lost its 1202          # connection to the display :0.0; most likely the X server was 1203          # shut down or you killed/destroyed the application. 1205          # Windows and OS-X seem to be better behaved in this regard. 1206          # They may not draw it, but they don't crash either! 1207          self
. MaxFontSize 
=  1000 1209          self
. ShiftFun 
=  self
. ShiftFunDict
[ Position
] 1211          self
. CalcBoundingBox () 1214      def  CalcBoundingBox ( self
): 1215          ## this isn't exact, as fonts don't scale exactly. 1217          bitmap 
=  wx
. EmptyBitmap ( 1 ,  1 ) 1218          dc
. SelectObject ( bitmap
)  #wxMac needs a Bitmap selected for GetTextExtent to work. 1219          DrawingSize 
=  40  # pts This effectively determines the resolution that the BB is computed to. 1220          ScaleFactor 
=  float ( self
. Size
) /  DrawingSize
1221          dc
. SetFont ( self
. SetFont ( DrawingSize
,  self
. Family
,  self
. Style
,  self
. Weight
,  self
. Underline
,  self
. FaceName
) ) 1222          ( w
, h
) =  dc
. GetTextExtent ( self
. String
) 1225          x
,  y 
=  self
. ShiftFun ( self
. XY
[ 0 ],  self
. XY
[ 1 ],  w
,  h
,  world 
=  1 ) 1226          self
. BoundingBox 
=  array ((( x
,  y
- h 
),( x 
+  w
,  y
)), Float
) 1228      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ): 1229          ( X
, Y
) =  WorldToPixel ( ( self
. XY
) ) 1231          # compute the font size: 1232          Size 
=  abs (  ScaleWorldToPixel ( ( self
. Size
,  self
. Size
) )[ 1 ] )  # only need a y coordinate length 1233          ## Check to see if the font size is large enough to blow up the X font server 1234          ## If so, limit it. Would it be better just to not draw it? 1235          ## note that this limit is dependent on how much memory you have, etc. 1236          Size 
=  min ( Size
,  self
. MaxFontSize
) 1237          dc
. SetFont ( self
. SetFont ( Size
,  self
. Family
,  self
. Style
,  self
. Weight
,  self
. Underline
,  self
. FaceName
)) 1238          dc
. SetTextForeground ( self
. Color
) 1239          if  self
. BackgroundColor
: 1240              dc
. SetBackgroundMode ( wx
. SOLID
) 1241              dc
. SetTextBackground ( self
. BackgroundColor
) 1243              dc
. SetBackgroundMode ( wx
. TRANSPARENT
) 1244          ( w
, h
) =  dc
. GetTextExtent ( self
. String
) 1245          # compute the shift, and adjust the coordinates, if neccesary 1246          # This had to be put in here, because it changes with Zoom, as 1247          # fonts don't scale exactly. 1248          xy 
=  self
. ShiftFun ( X
,  Y
,  w
,  h
) 1250          dc
. DrawTextPoint ( self
. String
,  xy
) 1251          if  HTdc 
and  self
. HitAble
: 1252              HTdc
. SetPen ( self
. HitPen
) 1253              HTdc
. SetBrush ( self
. HitBrush
) 1254              HTdc
. DrawRectanglePointSize ( xy
, ( w
,  h
) ) 1257  #--------------------------------------------------------------------------- 1258  class  FloatCanvas ( wx
. Panel
): 1262      This is a high level window for drawing maps and anything else in an 1263      arbitrary coordinate system. 1265      The goal is to provide a convenient way to draw stuff on the screen 1266      without having to deal with handling OnPaint events, converting to pixel 1267      coordinates, knowing about wxWindows brushes, pens, and colors, etc. It 1268      also provides virtually unlimited zooming and scrolling 1270      I am using it for two things: 1271      1) general purpose drawing in floating point coordinates 1272      2) displaying map data in Lat-long coordinates 1274      If the projection is set to None, it will draw in general purpose 1275      floating point coordinates. If the projection is set to 'FlatEarth', it 1276      will draw a FlatEarth projection, centered on the part of the map that 1277      you are viewing. You can also pass in your own projection function. 1279      It is double buffered, so re-draws after the window is uncovered by something 1280      else are very quick. 1282      It relies on NumPy, which is needed for speed (maybe, I havn't profiled it) 1284      Bugs and Limitations: 1285          Lots: patches, fixes welcome 1287      For Map drawing: It ignores the fact that the world is, in fact, a 1288      sphere, so it will do strange things if you are looking at stuff near 1289      the poles or the date line. so far I don't have a need to do that, so I 1290      havn't bothered to add any checks for that yet. 1293      I have set no zoom limits. What this means is that if you zoom in really  1294      far, you can get integer overflows, and get wierd results. It 1295      doesn't seem to actually cause any problems other than wierd output, at 1296      least when I have run it. 1299      I have done a couple of things to improve speed in this app. The one 1300      thing I have done is used NumPy Arrays to store the coordinates of the 1301      points of the objects. This allowed me to use array oriented functions 1302      when doing transformations, and should provide some speed improvement 1303      for objects with a lot of points (big polygons, polylines, pointsets). 1305      The real slowdown comes when you have to draw a lot of objects, because 1306      you have to call the wx.DC.DrawSomething call each time. This is plenty 1307      fast for tens of objects, OK for hundreds of objects, but pretty darn 1308      slow for thousands of objects. 1310      The solution is to be able to pass some sort of object set to the DC 1311      directly. I've used DC.DrawPointList(Points), and it helped a lot with 1312      drawing lots of points. I havn't got a LineSet type object, so I havn't 1313      used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList() 1314      methods implimented, and then I'd also have a full set of Object sets 1315      that could take advantage of them. I hope to get to it some day. 1319      At this point, there are a full set of custom mouse events. They are 1320      just like the rebulsr mouse events, but include an extra attribute: 1321      Event.GetCoords(), that returns the (x,y) position in world 1322      coordinates, as a length-2 NumPy vector of Floats. 1324      Copyright: Christopher Barker 1326      License: Same as the version of wxPython you are using it with 1328      Please let me know if you're using this!!! 1332      Chris.Barker@noaa.gov 1336      def  __init__ ( self
,  parent
,  id  = - 1 , 1337                   size 
=  wx
. DefaultSize
, 1338                   ProjectionFun 
=  None , 1339                   BackgroundColor 
=  "WHITE" , 1342          wx
. Panel
.__ init
__ (  self
,  parent
,  id ,  wx
. DefaultPosition
,  size
) 1344          global  ScreenPPI 
## A global variable to hold the Pixels per inch that wxWindows thinks is in use. 1346          ScreenPPI 
=  dc
. GetPPI ()[ 0 ]  # Assume square pixels 1349          self
. HitColorGenerator 
=  None 1350          self
. UseHitTest 
=  None 1352          self
. NumBetweenBlits 
=  500 1354          self
. BackgroundBrush 
=  wx
. Brush ( BackgroundColor
, wx
. SOLID
) 1358          wx
. EVT_PAINT ( self
,  self
. OnPaint
) 1359          wx
. EVT_SIZE ( self
,  self
. OnSize
) 1361          wx
. EVT_LEFT_DOWN ( self
,  self
. LeftDownEvent 
)  1362          wx
. EVT_LEFT_UP ( self
,  self
. LeftUpEvent 
)  1363          wx
. EVT_LEFT_DCLICK ( self
,  self
. LeftDoubleClickEvent 
)  1364          wx
. EVT_MIDDLE_DOWN ( self
,  self
. MiddleDownEvent 
)  1365          wx
. EVT_MIDDLE_UP ( self
,  self
. MiddleUpEvent 
)  1366          wx
. EVT_MIDDLE_DCLICK ( self
,  self
. MiddleDoubleClickEvent 
)  1367          wx
. EVT_RIGHT_DOWN ( self
,  self
. RightDownEvent
) 1368          wx
. EVT_RIGHT_UP ( self
,  self
. RightUpEvent 
)  1369          wx
. EVT_RIGHT_DCLICK ( self
,  self
. RightDoubleCLickEvent 
)  1370          wx
. EVT_MOTION ( self
,  self
. MotionEvent 
)  1371          wx
. EVT_MOUSEWHEEL ( self
,  self
. WheelEvent 
)  1373          ## CHB: I'm leaving these out for now. 1374          #wx.EVT_ENTER_WINDOW(self, self. )  1375          #wx.EVT_LEAVE_WINDOW(self, self. )  1377          ## create the Hit Test Dicts: 1382          self
._ ForeDrawList 
= [] 1383          self
._ ForegroundBuffer 
=  None 1384          self
. BoundingBox 
=  None 1385          self
. BoundingBoxDirty 
=  False 1386          self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 1388          self
. SetProjectionFun ( ProjectionFun
) 1390          self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)  # No Projection to start! 1391          self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
)  # default Transformation 1396          self
. StartRBBox 
=  None 1397          self
. PrevRBBox 
=  None 1398          self
. StartMove 
=  None 1399          self
. PrevMoveXY 
=  None 1400          self
. ObjectUnderMouse 
=  None 1402          # called just to make sure everything is initialized 1407      def  SetProjectionFun ( self
, ProjectionFun
): 1408          if  ProjectionFun 
==  'FlatEarth' : 1409              self
. ProjectionFun 
=  self
. FlatEarthProjection 
1410          elif  type ( ProjectionFun
) ==  types
. FunctionType
: 1411              self
. ProjectionFun 
=  ProjectionFun 
1412          elif  ProjectionFun 
is None : 1413              self
. ProjectionFun 
=  lambda  x
= None :  array ( ( 1 , 1 ),  Float
) 1415              raise  FloatCanvasError ( 'Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector' ) 1417      def  FlatEarthProjection ( self
, CenterPoint
): 1418          return  array (( cos ( pi
* CenterPoint
[ 1 ]/ 180 ), 1 ), Float
) 1420      def  SetMode ( self
, Mode
): 1421          if  Mode 
in  [ "ZoomIn" , "ZoomOut" , "Move" , "Mouse" , None ]: 1424              raise  FloatCanvasError ( '" %s " is Not a valid Mode' % Mode
) 1426      def  MakeHitDict ( self
): 1427          ##fixme: Should this just be None if nothing has been bound?  1428          self
. HitDict 
=  {EVT_FC_LEFT_DOWN: {}
, 1430                          EVT_FC_LEFT_DCLICK
: {}, 1431                          EVT_FC_MIDDLE_DOWN
: {}, 1432                          EVT_FC_MIDDLE_UP
: {}, 1433                          EVT_FC_MIDDLE_DCLICK
: {}, 1434                          EVT_FC_RIGHT_DOWN
: {}, 1435                          EVT_FC_RIGHT_UP
: {}, 1436                          EVT_FC_RIGHT_DCLICK
: {}, 1437                          EVT_FC_ENTER_OBJECT
: {}, 1438                          EVT_FC_LEAVE_OBJECT
: {}, 1441      def  _RaiseMouseEvent ( self
,  Event
,  EventType
): 1443          This is called in various other places to raise a Mouse Event 1445          #print "in Raise Mouse Event", Event 1446          pt 
=  self
. PixelToWorld (  Event
. GetPosition () ) 1447          evt 
=  _MouseEvent ( EventType
,  Event
,  self
. GetId (),  pt
) 1448          self
. GetEventHandler (). ProcessEvent ( evt
)        1450      def  HitTest ( self
,  event
,  HitEvent
): 1452              # check if there are any objects in the dict for this event 1453              if  self
. HitDict
[  HitEvent 
]: 1454                  xy 
=  event
. GetPosition () 1455                  if  self
._ ForegroundHTdc
: 1456                      hitcolor 
=  self
._ ForegroundHTdc
. GetPixelPoint (  xy 
) 1458                      hitcolor 
=  self
._ HTdc
. GetPixelPoint (  xy 
) 1459                  color 
= (  hitcolor
. Red (),  hitcolor
. Green (),  hitcolor
. Blue () ) 1460                  if  color 
in  self
. HitDict
[  HitEvent 
]: 1461                      Object 
=  self
. HitDict
[  HitEvent 
][ color
] 1462                      ## Add the hit coords to the Object 1463                      Object
. HitCoords 
=  self
. PixelToWorld (  xy 
) 1464                      Object
. CallBackFuncs
[ HitEvent
]( Object
) 1468      def  MouseOverTest ( self
,  event
): 1469          ##fixme: Can this be cleaned up? 1471              xy 
=  event
. GetPosition () 1472              if  self
._ ForegroundHTdc
: 1473                  hitcolor 
=  self
._ ForegroundHTdc
. GetPixelPoint (  xy 
) 1475                  hitcolor 
=  self
._ HTdc
. GetPixelPoint (  xy 
) 1476              color 
= (  hitcolor
. Red (),  hitcolor
. Green (),  hitcolor
. Blue () ) 1477              OldObject 
=  self
. ObjectUnderMouse
1478              ObjectCallbackCalled 
=  False 1479              if  color 
in  self
. HitDict
[  EVT_FC_ENTER_OBJECT 
]: 1480                  Object 
=  self
. HitDict
[  EVT_FC_ENTER_OBJECT
][ color
] 1481                  if  ( OldObject 
is None ): 1483                          Object
. CallBackFuncs
[ EVT_FC_ENTER_OBJECT
]( Object
) 1484                          ObjectCallbackCalled 
=   True 1486                          pass  # this means the enter event isn't bound for that object 1487                  elif  OldObject 
==  Object
:  # the mouse is still on the same object 1489                      ## Is the mouse on a differnt object as it was... 1490                  elif not  ( Object 
==  OldObject
): 1491                      # call the leave object callback 1493                          OldObject
. CallBackFuncs
[ EVT_FC_LEAVE_OBJECT
]( OldObject
) 1494                          ObjectCallbackCalled 
=   True 1496                          pass  # this means the leave event isn't bound for that object 1498                          Object
. CallBackFuncs
[ EVT_FC_ENTER_OBJECT
]( Object
) 1499                          ObjectCallbackCalled 
=   True 1501                          pass  # this means the enter event isn't bound for that object 1502                      ## set the new object under mouse 1503                  self
. ObjectUnderMouse 
=  Object
1504              elif  color 
in  self
. HitDict
[  EVT_FC_LEAVE_OBJECT 
]: 1505                  Object 
=  self
. HitDict
[  EVT_FC_LEAVE_OBJECT
][ color
] 1506                  self
. ObjectUnderMouse 
=  Object
1508                  # no objects under mouse bound to mouse-over events 1509                  self
. ObjectUnderMouse 
=  None 1512                          OldObject
. CallBackFuncs
[ EVT_FC_LEAVE_OBJECT
]( OldObject
) 1513                          ObjectCallbackCalled 
=   True 1515                          pass  # this means the leave event isn't bound for that object 1516              return  ObjectCallbackCalled
1519      ## fixme: There is a lot of repeated code here 1520      ##        Is there a better way?             1521      def  LeftDoubleClickEvent ( self
, event
): 1522          if  self
. GUIMode 
==  "Mouse" : 1523              EventType 
=  EVT_FC_LEFT_DCLICK
1524              if not  self
. HitTest ( event
,  EventType
): 1525                  self
._ RaiseMouseEvent
( event
,  EventType
) 1528      def  MiddleDownEvent ( self
, event
): 1529          if  self
. GUIMode 
==  "Mouse" : 1530              EventType 
=  EVT_FC_MIDDLE_DOWN
1531              if not  self
. HitTest ( event
,  EventType
): 1532                  self
._ RaiseMouseEvent
( event
,  EventType
) 1534      def  MiddleUpEvent ( self
, event
): 1535          if  self
. GUIMode 
==  "Mouse" : 1536              EventType 
=  EVT_FC_MIDDLE_UP
1537              if not  self
. HitTest ( event
,  EventType
): 1538                  self
._ RaiseMouseEvent
( event
,  EventType
) 1540      def  MiddleDoubleClickEvent ( self
, event
): 1541          if  self
. GUIMode 
==  "Mouse" : 1542              EventType 
=  EVT_FC_MIDDLE_DCLICK
1543              if not  self
. HitTest ( event
,  EventType
): 1544                  self
._ RaiseMouseEvent
( event
,  EventType
) 1546      def  RightUpEvent ( self
, event
): 1547          if  self
. GUIMode 
==  "Mouse" : 1548              EventType 
=  EVT_FC_RIGHT_UP
1549              if not  self
. HitTest ( event
,  EventType
): 1550                  self
._ RaiseMouseEvent
( event
,  EventType
) 1552      def  RightDoubleCLickEvent ( self
, event
): 1553          if  self
. GUIMode 
==  "Mouse" : 1554              EventType 
=  EVT_FC_RIGHT_DCLICK
1555              if not  self
. HitTest ( event
,  EventType
): 1556                  self
._ RaiseMouseEvent
( event
,  EventType
) 1558      def  WheelEvent ( self
, event
): 1559          ##if self.GUIMode == "Mouse": 1560          ## Why not always raise this? 1561              self
._ RaiseMouseEvent
( event
,  EVT_FC_MOUSEWHEEL
) 1564      def  LeftDownEvent ( self
, event
): 1566              if  self
. GUIMode 
==  "ZoomIn" : 1567                  self
. StartRBBox 
=  array (  event
. GetPosition () ) 1568                  self
. PrevRBBox 
=  None 1570              elif  self
. GUIMode 
==  "ZoomOut" : 1571                  Center 
=  self
. PixelToWorld (  event
. GetPosition () ) 1572                  self
. Zoom ( 1 / 1.5 , Center
) 1573              elif  self
. GUIMode 
==  "Move" : 1574                  self
. StartMove 
=  array (  event
. GetPosition () ) 1575                  self
. PrevMoveXY 
= ( 0 , 0 ) 1576              elif  self
. GUIMode 
==  "Mouse" : 1578                  if not  self
. HitTest ( event
,  EVT_FC_LEFT_DOWN
): 1579                     self
._ RaiseMouseEvent
( event
, EVT_FC_LEFT_DOWN
) 1583      def  LeftUpEvent ( self
, event
): 1584          if  self
. HasCapture (): 1587              if  self
. GUIMode 
==  "ZoomIn" : 1588                  if  event
. LeftUp ()  and not  self
. StartRBBox 
is None : 1589                      self
. PrevRBBox 
=  None 1590                      EndRBBox 
=  event
. GetPosition () 1591                      StartRBBox 
=  self
. StartRBBox
1592                      # if mouse has moved less that ten pixels, don't use the box. 1593                      if  (  abs ( StartRBBox
[ 0 ] -  EndRBBox
[ 0 ]) >  10 1594                           and  abs ( StartRBBox
[ 1 ] -  EndRBBox
[ 1 ]) >  10  ): 1595                          EndRBBox 
=  self
. PixelToWorld ( EndRBBox
) 1596                          StartRBBox 
=  self
. PixelToWorld ( StartRBBox
) 1597                          BB 
=  array ((( min ( EndRBBox
[ 0 ], StartRBBox
[ 0 ]), 1598                                       min ( EndRBBox
[ 1 ], StartRBBox
[ 1 ])), 1599                                      ( max ( EndRBBox
[ 0 ], StartRBBox
[ 0 ]), 1600                                       max ( EndRBBox
[ 1 ], StartRBBox
[ 1 ]))), Float
) 1603                          Center 
=  self
. PixelToWorld ( StartRBBox
) 1604                          self
. Zoom ( 1.5 , Center
) 1605                      self
. StartRBBox 
=  None 1606              elif  self
. GUIMode 
==  "Move" : 1607                  if not  self
. StartMove 
is None : 1608                      StartMove 
=  self
. StartMove
1609                      EndMove 
=  array (( event
. GetX (), event
. GetY ())) 1610                      if  sum (( StartMove
- EndMove
)** 2 ) >  16 : 1611                          self
. MoveImage ( StartMove
- EndMove
, 'Pixel' ) 1612                      self
. StartMove 
=  None 1613              elif  self
. GUIMode 
==  "Mouse" : 1614                  EventType 
=  EVT_FC_LEFT_UP
1615                  if not  self
. HitTest ( event
,  EventType
): 1616                     self
._ RaiseMouseEvent
( event
,  EventType
) 1620      def  MotionEvent ( self
, event
): 1622              if  self
. GUIMode 
==  "ZoomIn" : 1623                  if  event
. Dragging ()  and  event
. LeftIsDown ()  and not  ( self
. StartRBBox 
is None ): 1624                      xy0 
=  self
. StartRBBox
1625                      xy1 
=  array (  event
. GetPosition () ) 1627                      wh
[ 0 ] =  max ( wh
[ 0 ],  int ( wh
[ 1 ]* self
. AspectRatio
)) 1628                      wh
[ 1 ] =  int ( wh
[ 0 ] /  self
. AspectRatio
) 1629                      xy_c 
= ( xy0 
+  xy1
) /  2 1630                      dc 
=  wx
. ClientDC ( self
) 1632                      dc
. SetPen ( wx
. Pen ( 'WHITE' ,  2 ,  wx
. SHORT_DASH
)) 1633                      dc
. SetBrush ( wx
. TRANSPARENT_BRUSH
) 1634                      dc
. SetLogicalFunction ( wx
. XOR
) 1636                          dc
. DrawRectanglePointSize (* self
. PrevRBBox
) 1637                      self
. PrevRBBox 
= (  xy_c 
-  wh
/ 2 ,  wh 
) 1638                      dc
. DrawRectanglePointSize ( * self
. PrevRBBox 
) 1640              elif  self
. GUIMode 
==  "Move" : 1641                  if  event
. Dragging ()  and  event
. LeftIsDown ()  and not  self
. StartMove 
is None : 1642                      xy1 
=  array (  event
. GetPosition () ) 1644                      xy_tl 
=  xy1 
-  self
. StartMove
1645                      dc 
=  wx
. ClientDC ( self
) 1647                      x1
, y1 
=  self
. PrevMoveXY
1649                      w
, h 
=  self
. PanelSize
1650                      if  x2 
>  x1 
and  y2 
>  y1
: 1657                      elif  x2 
>  x1 
and  y2 
<=  y1
: 1666                      elif  x2 
<=  x1 
and  y2 
>  y1
: 1675                      elif  x2 
<=  x1 
and  y2 
<=  y1
: 1685                      dc
. SetPen ( wx
. TRANSPARENT_PEN
) 1686                      dc
. SetBrush ( self
. BackgroundBrush
) 1687                      dc
. DrawRectangle ( xa
,  ya
,  wa
,  ha
) 1688                      dc
. DrawRectangle ( xb
,  yb
,  wb
,  hb
) 1689                      self
. PrevMoveXY 
=  xy_tl
1690                      if  self
._ ForegroundBuffer
: 1691                          dc
. DrawBitmapPoint ( self
._ ForegroundBuffer
, xy_tl
) 1693                          dc
. DrawBitmapPoint ( self
._ Buffer
, xy_tl
) 1695              elif  self
. GUIMode 
==  "Mouse" : 1696                  ## Only do something if there are mouse over events bound 1697                  if  self
. HitDict 
and  ( self
. HitDict
[  EVT_FC_ENTER_OBJECT 
]  or  self
. HitDict
[  EVT_FC_LEAVE_OBJECT 
] ): 1698                      if not  self
. MouseOverTest ( event
): 1699                          self
._ RaiseMouseEvent
( event
, EVT_FC_MOTION
) 1702              self
._ RaiseMouseEvent
( event
, EVT_FC_MOTION
) 1706      def  RightDownEvent ( self
, event
): 1708              if  self
. GUIMode 
==  "ZoomIn" : 1709                  Center 
=  self
. PixelToWorld (( event
. GetX (), event
. GetY ())) 1710                  self
. Zoom ( 1 / 1.5 , Center
) 1711              elif  self
. GUIMode 
==  "ZoomOut" : 1712                  Center 
=  self
. PixelToWorld (( event
. GetX (), event
. GetY ())) 1713                  self
. Zoom ( 1.5 , Center
) 1714              elif  self
. GUIMode 
==  "Mouse" : 1715                  EventType 
=  EVT_FC_RIGHT_DOWN
1716                  if not  self
. HitTest ( event
,  EventType
): 1717                     self
._ RaiseMouseEvent
( event
,  EventType
) 1721      def  MakeNewBuffers ( self
): 1722          self
._ BackgroundDirty 
=  True 1723          # Make new offscreen bitmap: 1724          self
._ Buffer 
=  wx
. EmptyBitmap (* self
. PanelSize
) 1726          #dc.SelectObject(self._Buffer) 1728          if  self
._ ForeDrawList
: 1729              self
._ ForegroundBuffer 
=  wx
. EmptyBitmap (* self
. PanelSize
) 1731              self
._ ForegroundBuffer 
=  None 1736              self
._ ForegroundHTdc 
=  None 1738      def  MakeNewHTdc ( self
): 1739          ## Note: While it's considered a "bad idea" to keep a 1740          ## MemoryDC around I'm doing it here because a wx.Bitmap 1741          ## doesn't have a GetPixel method so a DC is needed to do 1742          ## the hit-test. It didn't seem like a good idea to re-create 1743          ## a wx.MemoryDC on every single mouse event, so I keep it 1745          self
._ HTdc 
=  wx
. MemoryDC () 1746          self
._ HTBitmap 
=  wx
. EmptyBitmap (* self
. PanelSize
)  1747          self
._ HTdc
. SelectObject (  self
._ HTBitmap 
) 1748          self
._ HTdc
. SetBackground ( wx
. BLACK_BRUSH
) 1749          if  self
._ ForeDrawList
: 1750              self
._ ForegroundHTdc 
=  wx
. MemoryDC () 1751              self
._ ForegroundHTBitmap 
=  wx
. EmptyBitmap (* self
. PanelSize
)  1752              self
._ ForegroundHTdc
. SelectObject (  self
._ ForegroundHTBitmap 
) 1753              self
._ ForegroundHTdc
. SetBackground ( wx
. BLACK_BRUSH
) 1755             self
._ ForegroundHTdc 
=  None  1757      def  OnSize ( self
, event
): 1758          self
. PanelSize  
=  array ( self
. GetClientSizeTuple (), Int32
) 1759          self
. HalfPanelSize 
=  self
. PanelSize 
/  2  # lrk: added for speed in WorldToPixel 1760          if  self
. PanelSize
[ 0 ] ==  0  or  self
. PanelSize
[ 1 ] ==  0 : 1761              self
. AspectRatio 
=  1.0 1763              self
. AspectRatio 
=  float ( self
. PanelSize
[ 0 ]) /  self
. PanelSize
[ 1 ] 1764          self
. MakeNewBuffers () 1767      def  OnPaint ( self
,  event
): 1768          dc 
=  wx
. PaintDC ( self
) 1769          if  self
._ ForegroundBuffer
: 1770              dc
. DrawBitmap ( self
._ ForegroundBuffer
, 0 , 0 ) 1772              dc
. DrawBitmap ( self
._ Buffer
, 0 , 0 ) 1774      def  Draw ( self
,  Force
= False ): 1776          There is a main buffer set up to double buffer the screen, so 1777          you can get quick re-draws when the window gets uncovered. 1779          If there are any objects in self._ForeDrawList, then the 1780          background gets drawn to a new buffer, and the foreground 1781          objects get drawn on top of it. The final result if blitted to 1782          the screen, and stored for future Paint events.  This is done so 1783          that you can have a complicated background, but have something 1784          changing on the foreground, without having to wait for the 1785          background to get re-drawn. This can be used to support simple 1786          animation, for instance. 1789  #        print "in Draw", self.PanelSize 1790          if  sometrue ( self
. PanelSize 
<  1  ):  # it's possible for this to get called before being properly initialized. 1791  #        if self.PanelSize < (1,1): # it's possible for this to get called before being properly initialized. 1793          if  self
. Debug
:  start 
=  clock () 1794          ScreenDC 
=   wx
. ClientDC ( self
) 1795          ViewPortWorld 
= (  self
. PixelToWorld (( 0 , 0 )), 1796                            self
. PixelToWorld ( self
. PanelSize
) ) 1797          ViewPortBB 
=  array ( (  minimum
. reduce ( ViewPortWorld
), 1798                                maximum
. reduce ( ViewPortWorld
) ) ) 1800          dc
. SelectObject ( self
._ Buffer
) 1801          if  self
._ BackgroundDirty 
or  Force
: 1802              #print "Background is Dirty" 1803              dc
. SetBackground ( self
. BackgroundBrush
) 1807              self
._ DrawObjects
( dc
,  self
._ DrawList
,  ScreenDC
,  ViewPortBB
,  self
._ HTdc
) 1808              self
._ BackgroundDirty 
=  False 1810          if  self
._ ForeDrawList
: 1811              ## If an object was just added to the Foreground, there might not yet be a buffer 1812              if  self
._ ForegroundBuffer 
is None : 1813                  self
._ ForegroundBuffer 
=  wx
. EmptyBitmap ( self
. PanelSize
[ 0 ], 1816              dc 
=  wx
. MemoryDC ()  ## I got some strange errors (linewidths wrong) if I didn't make a new DC here 1817              dc
. SelectObject ( self
._ ForegroundBuffer
) 1818              dc
. DrawBitmap ( self
._ Buffer
, 0 , 0 ) 1819              if  self
._ ForegroundHTdc 
is None : 1820                  self
._ ForegroundHTdc 
=  wx
. MemoryDC () 1821                  self
._ ForegroundHTdc
. SelectObject (  wx
. EmptyBitmap ( 1823                                                     self
. PanelSize
[ 1 ]) ) 1825                  ## blit the background HT buffer to the foreground HT buffer 1826                  self
._ ForegroundHTdc
. Blit ( 0 ,  0 , 1827                                            self
. PanelSize
[ 0 ],  self
. PanelSize
[ 1 ], 1829              self
._ DrawObjects
( dc
, 1833                                self
._ ForegroundHTdc
) 1834          ScreenDC
. Blit ( 0 ,  0 ,  self
. PanelSize
[ 0 ], self
. PanelSize
[ 1 ],  dc
,  0 ,  0 ) 1835          # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn 1836          # This seeems out of place, but it works. 1838              ScreenDC
. SetPen ( wx
. Pen ( 'WHITE' ,  2 , wx
. SHORT_DASH
)) 1839              ScreenDC
. SetBrush ( wx
. TRANSPARENT_BRUSH
) 1840              ScreenDC
. SetLogicalFunction ( wx
. XOR
) 1841              ScreenDC
. DrawRectanglePointSize (* self
. PrevRBBox
) 1842          if  self
. Debug
:  print  "Drawing took  %f  seconds of CPU time" %( clock ()- start
) 1844          ## Clear the font cache 1845          ## IF you don't do this, the X font server starts to take up Massive amounts of memory 1846          ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in. 1847          DrawObject
. FontList 
= {} 1849      def  _ShouldRedraw ( DrawList
,  ViewPortBB
):  # lrk: adapted code from BBCheck 1850          # lrk: Returns the objects that should be redrawn 1854          for  Object 
in  DrawList
: 1855              BB1 
=  Object
. BoundingBox
1856              if  ( BB1
[ 1 , 0 ] >  BB2
[ 0 , 0 ]  and  BB1
[ 0 , 0 ] <  BB2
[ 1 , 0 ]  and 1857                   BB1
[ 1 , 1 ] >  BB2
[ 0 , 1 ]  and  BB1
[ 0 , 1 ] <  BB2
[ 1 , 1 ]): 1858                  redrawlist
. append ( Object
) 1860      _ShouldRedraw 
=  staticmethod ( _ShouldRedraw
) 1863  ##    def BBCheck(self, BB1, BB2): 1866  ##        BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise 1869  ##        if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and 1870  ##             (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ): 1875      def  MoveImage ( self
, shift
, CoordType
): 1877          move the image in the window. 1879          shift is an (x,y) tuple, specifying the amount to shift in each direction 1881          It can be in any of three coordinates: Panel, Pixel, World, 1882          specified by the CoordType parameter 1884          Panel coordinates means you want to shift the image by some 1885          fraction of the size of the displaed image 1887          Pixel coordinates means you want to shift the image by some number of pixels 1889          World coordinates mean you want to shift the image by an amount 1890          in Floating point world coordinates 1894          shift 
=  asarray ( shift
, Float
) 1895          #print "shifting by:", shift 1896          if  CoordType 
==  'Panel' : # convert from panel coordinates 1897              shift 
=  shift 
*  array ((- 1 , 1 ), Float
) * self
. PanelSize
/ self
. TransformVector
1898          elif  CoordType 
==  'Pixel' :  # convert from pixel coordinates 1899              shift 
=  shift
/ self
. TransformVector
1900          elif  CoordType 
==  'World' :  # No conversion 1903              raise  FloatCanvasError ( 'CoordType must be either "Panel", "Pixel", or "World"' ) 1905          #print "shifting by:", shift 1907          self
. ViewPortCenter 
=  self
. ViewPortCenter 
+  shift 
1908          self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1909          self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
) *  self
. MapProjectionVector
1910          self
._ BackgroundDirty 
=  True 1913      def  Zoom ( self
, factor
, center 
=  None ): 1916          Zoom(factor, center) changes the amount of zoom of the image by factor. 1917          If factor is greater than one, the image gets larger. 1918          If factor is less than one, the image gets smaller. 1920          Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming. 1921          If center is not given, the center will stay the same. 1924          self
. Scale 
=  self
. Scale
* factor
1925          if not  center 
is None : 1926              self
. ViewPortCenter 
=  array ( center
, Float
) 1927          self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1928          self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
) *  self
. MapProjectionVector
1929          self
._ BackgroundDirty 
=  True 1932      def  ZoomToBB ( self
,  NewBB 
=  None ,  DrawFlag 
=  True ): 1936          Zooms the image to the bounding box given, or to the bounding 1937          box of all the objects on the canvas, if none is given. 1941          if not   NewBB 
is None : 1944              if  self
. BoundingBoxDirty
: 1945                  self
._ ResetBoundingBox
() 1946              BoundingBox 
=  self
. BoundingBox
1947          if not  BoundingBox 
is None : 1948              self
. ViewPortCenter 
=  array ((( BoundingBox
[ 0 , 0 ]+ BoundingBox
[ 1 , 0 ])/ 2 , 1949                                           ( BoundingBox
[ 0 , 1 ]+ BoundingBox
[ 1 , 1 ])/ 2  ), Float
) 1950              self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1951              # Compute the new Scale 1952              BoundingBox 
=  BoundingBox 
*  self
. MapProjectionVector
1954                  self
. Scale 
=  min ( abs ( self
. PanelSize
[ 0 ] / ( BoundingBox
[ 1 , 0 ]- BoundingBox
[ 0 , 0 ])), 1955                                   abs ( self
. PanelSize
[ 1 ] / ( BoundingBox
[ 1 , 1 ]- BoundingBox
[ 0 , 1 ])) )* 0.95 1956              except  ZeroDivisionError :  # this will happen if the BB has zero width or height 1958                      self
. Scale 
= ( self
. PanelSize
[ 0 ]  / ( BoundingBox
[ 1 , 0 ]- BoundingBox
[ 0 , 0 ]))* 0.95 1959                  except  ZeroDivisionError : 1961                          self
. Scale 
= ( self
. PanelSize
[ 1 ]  / ( BoundingBox
[ 1 , 1 ]- BoundingBox
[ 0 , 1 ]))* 0.95 1962                      except  ZeroDivisionError :  #zero size! (must be a single point) 1965              self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
)*  self
. MapProjectionVector
1967                  self
._ BackgroundDirty 
=  True 1970              # Reset the shifting and scaling to defaults when there is no BB 1971              self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 1972              self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)  # No Projection to start! 1973              self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
)  # default Transformation 1976      def  RemoveObjects ( self
,  Objects
): 1977          for  Object 
in  Objects
: 1978              self
. RemoveObject ( Object
,  ResetBB 
=  False ) 1979          self
. BoundingBoxDirty 
=  True 1981      def  RemoveObject ( self
,  Object
,  ResetBB 
=  True ): 1982          ##fixme: Using the list.remove method is kind of slow 1983          if  Object
. InForeground
: 1984              self
._ ForeDrawList
. remove ( Object
) 1986              self
._ DrawList
. remove ( Object
) 1987              self
._ BackgroundDirty 
=  True 1989              self
. BoundingBoxDirty 
=  True 1991      def  ClearAll ( self
,  ResetBB 
=  True ): 1993          self
._ ForeDrawList 
= [] 1994          self
._ BackgroundDirty 
=  True 1995          self
. HitColorGenerator 
=  None 1996          self
. UseHitTest 
=  False 1998              self
._ ResetBoundingBox
() 1999          self
. MakeNewBuffers () 2003  ##    def _AddBoundingBox(self,NewBB): 2004  ##        if self.BoundingBox is None: 2005  ##            self.BoundingBox = NewBB 2006  ##            self.ZoomToBB(NewBB,DrawFlag = False) 2008  ##            self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]), 2009  ##                                         min(self.BoundingBox[0,1],NewBB[0,1])), 2010  ##                                        (max(self.BoundingBox[1,0],NewBB[1,0]), 2011  ##                                         max(self.BoundingBox[1,1],NewBB[1,1]))), 2014      def  _getboundingbox ( bboxarray
):  # lrk: added this 2016          upperleft 
=  minimum
. reduce ( bboxarray
[:, 0 ]) 2017          lowerright 
=  maximum
. reduce ( bboxarray
[:, 1 ]) 2018          return  array (( upperleft
,  lowerright
),  Float
) 2020      _getboundingbox 
=  staticmethod ( _getboundingbox
) 2022      def  _ResetBoundingBox ( self
): 2023          if  self
._ DrawList 
or  self
._ ForeDrawList
: 2024              bboxarray 
=  zeros (( len ( self
._ DrawList
)+ len ( self
._ ForeDrawList
),  2 ,  2 ), Float
)  2025              i 
= - 1  # just in case _DrawList is empty 2026              for  ( i
,  BB
)  in  enumerate ( self
._ DrawList
): 2027                  bboxarray
[ i
] =  BB
. BoundingBox
2028              for  ( j
,  BB
)  in  enumerate ( self
._ ForeDrawList
): 2029                  bboxarray
[ i
+ j
+ 1 ] =  BB
. BoundingBox
2030              self
. BoundingBox 
=  self
._ getboundingbox
( bboxarray
) 2032              self
. BoundingBox 
=  None 2033              self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 2034              self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
) 2035              self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)                      2037          self
. BoundingBoxDirty 
=  False 2039      def  PixelToWorld ( self
, Points
): 2041          Converts coordinates from Pixel coordinates to world coordinates. 2043          Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates. 2046          return   ((( asarray ( Points
, Float
) - ( self
. PanelSize
/ 2 ))/ self
. TransformVector
) +  self
. ViewPortCenter
) 2048      def  WorldToPixel ( self
, Coordinates
): 2050          This function will get passed to the drawing functions of the objects, 2051          to transform from world to pixel coordinates. 2052          Coordinates should be a NX2 array of (x,y) coordinates, or 2053          a 2-tuple, or sequence of 2-tuples. 2055          #Note: this can be called by users code for various reasons, so asarray is needed. 2056          return   ((( asarray ( Coordinates
, Float
) - 2057                     self
. ViewPortCenter
)* self
. TransformVector
)+ 2058                   ( self
. HalfPanelSize
)). astype ( 'i' ) 2060      def  ScaleWorldToPixel ( self
, Lengths
): 2062          This function will get passed to the drawing functions of the objects, 2063          to Change a length from world to pixel coordinates. 2065          Lengths should be a NX2 array of (x,y) coordinates, or 2066          a 2-tuple, or sequence of 2-tuples. 2068          return   ( ( asarray ( Lengths
, Float
)* self
. TransformVector
) ). astype ( 'i' ) 2070      def  ScalePixelToWorld ( self
, Lengths
): 2072          This function computes a pair of x.y lengths, 2073          to change then from pixel to world coordinates. 2075          Lengths should be a NX2 array of (x,y) coordinates, or 2076          a 2-tuple, or sequence of 2-tuples. 2079          return   ( asarray ( Lengths
, Float
) /  self
. TransformVector
) 2081      def  AddObject ( self
, obj
): 2082          # put in a reference to the Canvas, so remove and other stuff can work 2084          if   obj
. InForeground
: 2085              self
._ ForeDrawList
. append ( obj
) 2086              self
. UseForeground 
=  True 2088              self
._ DrawList
. append ( obj
) 2089              self
._ BackgroundDirty 
=  True 2090          self
. BoundingBoxDirty 
=  True 2093      def  _DrawObjects ( self
,  dc
,  DrawList
,  ScreenDC
,  ViewPortBB
,  HTdc 
=  None ): 2095          This is a convenience function; 2096          This function takes the list of objects and draws them to specified 2099          dc
. SetBackground ( self
. BackgroundBrush
) 2102          PanelSize0
,  PanelSize1 
=  self
. PanelSize 
# for speed 2103          WorldToPixel 
=  self
. WorldToPixel 
# for speed 2104          ScaleWorldToPixel 
=  self
. ScaleWorldToPixel 
# for speed 2105          Blit 
=  ScreenDC
. Blit 
# for speed 2106          NumBetweenBlits 
=  self
. NumBetweenBlits 
# for speed 2107          for  i
,  Object 
in  enumerate ( self
._ ShouldRedraw
( DrawList
,  ViewPortBB
)): 2108              Object
._ Draw
( dc
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
) 2109              if  i
+ 1  %  NumBetweenBlits 
==  0 : 2110                  Blit ( 0 ,  0 ,  PanelSize0
,  PanelSize1
,  dc
,  0 ,  0 ) 2113  ##    ## This is a way to automatically add a AddObject method for each 2114  ##    ## object type This code has been replaced by Leo's code above, so 2115  ##    ## that it happens at module init, rather than as needed. The 2116  ##    ## primary advantage of this is that dir(FloatCanvas) will have 2117  ##    ## them, and docstrings are preserved. Probably more useful 2118  ##    ## exceptions if there is a problem, as well. 2119  ##    def __getattr__(self, name): 2120  ##        if name[:3] == "Add": 2121  ##            func=globals()[name[3:]] 2122  ##            def AddFun(*args, **kwargs): 2123  ##                Object = func(*args, **kwargs) 2124  ##                self.AddObject(Object) 2126  ##            ## add it to FloatCanvas' dict for future calls. 2127  ##            self.__dict__[name] = AddFun 2130  ##            raise AttributeError("FloatCanvas has no attribute '%s'"%name) 2132  def  _makeFloatCanvasAddMethods ():  ## lrk's code for doing this in module __init__ 2133      classnames 
= [ "Circle" ,  "Ellipse" ,  "Rectangle" ,  "ScaledText" ,  "Polygon" , 2134                 "Line" ,  "Text" ,  "PointSet" , "Point" ,  "Arrow" ] 2135      for  classname 
in  classnames
: 2136          klass 
=  globals ()[ classname
] 2137          def  getaddshapemethod ( klass
= klass
): 2138              def  addshape ( self
, * args
, ** kwargs
): 2139                  Object 
=  klass (* args
, ** kwargs
) 2140                  self
. AddObject ( Object
) 2143          addshapemethod 
=  getaddshapemethod () 2144          methodname 
=  "Add"  +  classname
2145          setattr ( FloatCanvas
,  methodname
,  addshapemethod
) 2146          docstring 
=  "Creates  %s  and adds its reference to the canvas. \n "  %  classname
2147          docstring 
+=  "Argument protocol same as  %s  class"  %  classname
2149              docstring 
+=  ", whose docstring is: \n %s "  %  klass
.__ doc
__ 2150          FloatCanvas
.__ dict
__ [ methodname
] .__ doc
__  =  docstring
2152  _makeFloatCanvasAddMethods ()