]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/floatcanvas/FloatCanvas.py 
83865fbc3475030f3d748d89cb59ba4da8614d99
   3      from  Numeric 
import  array
, asarray
, Float
, cos
, pi
, sum , minimum
, maximum
, Int32
, zeros
   5      from  numarray 
import  array
,  asarray
,  Float
,  cos
,  pi
,  sum ,  minimum
,  maximum
,  Int32
,  zeros
   7  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  FloatCanvasException ( Exception ):   26  ## All the mouse events   27  #EVT_FC_ENTER_WINDOW = wx.NewEventType()   28  #EVT_FC_LEAVE_WINDOW = wx.NewEventType()   29  EVT_FC_LEFT_DOWN 
=  wx
. NewEventType ()    30  EVT_FC_LEFT_UP  
=  wx
. NewEventType ()   31  EVT_FC_LEFT_DCLICK 
=  wx
. NewEventType ()    32  EVT_FC_MIDDLE_DOWN 
=  wx
. NewEventType ()    33  EVT_FC_MIDDLE_UP 
=  wx
. NewEventType ()    34  EVT_FC_MIDDLE_DCLICK 
=  wx
. NewEventType ()    35  EVT_FC_RIGHT_DOWN 
=  wx
. NewEventType ()    36  EVT_FC_RIGHT_UP 
=  wx
. NewEventType ()    37  EVT_FC_RIGHT_DCLICK 
=  wx
. NewEventType ()    38  EVT_FC_MOTION 
=  wx
. NewEventType ()    39  EVT_FC_MOUSEWHEEL 
=  wx
. NewEventType ()    40  ## these two are for the hit-test stuff, I never make them real Events   41  EVT_FC_ENTER_OBJECT 
=  wx
. NewEventType ()   42  EVT_FC_LEAVE_OBJECT 
=  wx
. NewEventType ()   44  #def EVT_ENTER_WINDOW( window, function ):   45  #    window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function )    46  #def EVT_LEAVE_WINDOW( window, function ):   47  #    window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function )    48  def  EVT_LEFT_DOWN (  window
,  function 
):     49      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_DOWN 
,  function 
)   50  def  EVT_LEFT_UP (  window
,  function 
):   51      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_UP 
,  function 
)   52  def  EVT_LEFT_DCLICK   (  window
,  function 
):   53      window
. Connect ( - 1 , - 1 , EVT_FC_LEFT_DCLICK 
,  function 
)   54  def  EVT_MIDDLE_DOWN   (  window
,  function 
):   55      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_DOWN 
,  function 
)   56  def  EVT_MIDDLE_UP   (  window
,  function 
):   57      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_UP 
,  function 
)   58  def  EVT_MIDDLE_DCLICK   (  window
,  function 
):   59      window
. Connect ( - 1 , - 1 , EVT_FC_MIDDLE_DCLICK 
,  function 
)   60  def  EVT_RIGHT_DOWN   (  window
,  function 
):   61      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_DOWN 
,  function 
)   62  def  EVT_RIGHT_UP (  window
,  function 
):   63      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_UP 
,  function 
)   64  def  EVT_RIGHT_DCLICK (  window
,  function 
):   65      window
. Connect ( - 1 , - 1 , EVT_FC_RIGHT_DCLICK 
,  function 
)   66  def  EVT_MOTION (  window
,  function 
):   67      window
. Connect ( - 1 , - 1 , EVT_FC_MOTION 
,  function 
)   68  def  EVT_MOUSEWHEEL (  window
,  function 
):   69      window
. Connect ( - 1 , - 1 , EVT_FC_MOUSEWHEEL 
,  function 
)   71  class  MouseEvent ( wx
. PyCommandEvent
):   75      This event class takes a regular wxWindows mouse event as a parameter,   76      and wraps it so that there is access to all the original methods. This   77      is similar to subclassing, but you can't subclass a wxWindows event   79      The goal is to be able to it just like a regular mouse event.   83      GetCoords() , which returns and (x,y) tuple in world coordinates.   85      Another differnce is that it is a CommandEvent, which propagates up   86      the window hierarchy until it is handled.   90      def  __init__ ( self
,  EventType
,  NativeEvent
,  WinID
,  Coords 
=  None ):   91          wx
. PyCommandEvent
.__ init
__ ( self
)   93          self
. SetEventType (  EventType 
)   94          self
._ NativeEvent 
=  NativeEvent
  97      def  SetCoords ( self
, Coords
):  103      def  __getattr__ ( self
,  name
):  104          #return eval(self.NativeEvent.__getattr__(name) )  105          return  getattr ( self
._ NativeEvent
,  name
)  107  #### ColorGEnerator class is now obsolete. I'm using a python generator function instead.  108  ##class ColorGenerator:  112  ##    An instance of this class generates a unique color each time  113  ##    GetNextColor() is called. Someday I will use a proper Python  114  ##    generator for this class.  116  ##    The point of this generator is for the hit-test bitmap, each object  117  ##    needs to be a unique color. Also, each system can be running a  118  ##    different number of colors, and it doesn't appear to be possible to  119  ##    have a wxMemDC with a different colordepth as the screen so this  120  ##    generates colors far enough apart that they can be distinguished on  121  ##    a 16bit screen. Anything less than 16bits won't work. It could, but  122  ##    I havn't written the code that way. You also wouldn't get many  127  ##    def __init__(self):  129  ##        ## figure out the color depth of the screen  130  ##        ## for some bizare reason, thisdoesn't work on OS-X  131  ##        if sys.platform == 'darwin':  134  ##            b = wx.EmptyBitmap(1,1)  135  ##            depth = b.GetDepth()  144  ##            raise FloatCanvasException("ColorGenerator does not work with depth = %s"%depth )  146  ##    def GetNextColor(self):  148  ##        ##r,g,b = self.r,self.g,self.b  157  ##                    ## fixme: this should be a derived exception  158  ##                    raise FloatCanvasException("Too many objects in colorgenerator for HitTest")  159  ##        return (self.r,self.g,self.b)  166  def  cycleidxs ( indexcount
,  maxvalue
,  step
):  170          for  idx 
in  xrange ( 0 ,  maxvalue
,  step
):  171              for  tail 
in  cycleidxs ( indexcount 
-  1 ,  maxvalue
,  step
):  174  def  colorGenerator ():  176      if  sys
. platform 
==  'darwin' :  179          b 
=  wx
. EmptyBitmap ( 1 , 1 )  186          raise  "ColorGenerator does not work with depth =  %s "  %  depth
 187      return  cycleidxs ( indexcount
= 3 ,  maxvalue
= 256 ,  step
= step
)  190  #### I don't know if the Set objects are useful, beyond the pointset object  191  #### The problem is that when zoomed in, the BB is checked to see whether to draw the object.  192  #### A Set object can defeat this  194  ##class ObjectSetMixin:  196  ##    A mix-in class for draw objects that are sets of objects  198  ##    It contains methods for setting lists of pens and brushes  201  ##    def SetPens(self,LineColors,LineStyles,LineWidths):  203  ##        This method used when an object could have a list of pens, rather than just one  204  ##        It is used for LineSet, and perhaps others in the future.  206  ##        fixme: this should be in a mixin  208  ##        fixme: this is really kludgy, there has got to be a better way!  213  ##        if type(LineColors) == types.ListType:  214  ##            length = len(LineColors)  216  ##            LineColors = [LineColors]  218  ##        if type(LineStyles) == types.ListType:  219  ##            length = len(LineStyles)  221  ##            LineStyles = [LineStyles]  223  ##        if type(LineWidths) == types.ListType:  224  ##            length = len(LineWidths)  226  ##            LineWidths = [LineWidths]  229  ##            if len(LineColors) == 1:  230  ##                LineColors = LineColors*length  231  ##            if len(LineStyles) == 1:  232  ##                LineStyles = LineStyles*length  233  ##            if len(LineWidths) == 1:  234  ##                LineWidths = LineWidths*length  237  ##        for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):  238  ##            if LineColor is None or LineStyle is None:  239  ##                self.Pens.append(wx.TRANSPARENT_PEN)  240  ##                # what's this for?> self.LineStyle = 'Transparent'  241  ##            if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):  242  ##                Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])  243  ##                self.Pens.append(Pen)  245  ##                self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])  247  ##            self.Pens = self.Pens[0]  253      This is the base class for all the objects that can be drawn.  257      def  __init__ ( self
, InForeground  
=  False ):  258          self
. InForeground 
=  InForeground
 263          self
. CallBackFuncs 
= {}  265          ## these are the defaults  269          self
. MinHitLineWidth 
=  3  270          self
. HitLineWidth 
=  3  ## this gets re-set by the subclasses if necessary  272      # I pre-define all these as class variables to provide an easier  273      # interface, and perhaps speed things up by caching all the Pens  274      # and Brushes, although that may not help, as I think wx now  275      # does that on it's own. Send me a note if you know!  278              (  None , "Transparent" )  :  wx
. TRANSPARENT_BRUSH
,  279              ( "Blue" , "Solid" )       :  wx
. BLUE_BRUSH
,  280              ( "Green" , "Solid" )      :  wx
. GREEN_BRUSH
,  281              ( "White" , "Solid" )      :  wx
. WHITE_BRUSH
,  282              ( "Black" , "Solid" )      :  wx
. BLACK_BRUSH
,  283              ( "Grey" , "Solid" )       :  wx
. GREY_BRUSH
,  284              ( "MediumGrey" , "Solid" ) :  wx
. MEDIUM_GREY_BRUSH
,  285              ( "LightGrey" , "Solid" )  :  wx
. LIGHT_GREY_BRUSH
,  286              ( "Cyan" , "Solid" )       :  wx
. CYAN_BRUSH
,  287              ( "Red" , "Solid" )        :  wx
. RED_BRUSH
 290              ( None , "Transparent" , 1 )   :  wx
. TRANSPARENT_PEN
,  291              ( "Green" , "Solid" , 1 )      :  wx
. GREEN_PEN
,  292              ( "White" , "Solid" , 1 )      :  wx
. WHITE_PEN
,  293              ( "Black" , "Solid" , 1 )      :  wx
. BLACK_PEN
,  294              ( "Grey" , "Solid" , 1 )       :  wx
. GREY_PEN
,  295              ( "MediumGrey" , "Solid" , 1 ) :  wx
. MEDIUM_GREY_PEN
,  296              ( "LightGrey" , "Solid" , 1 )  :  wx
. LIGHT_GREY_PEN
,  297              ( "Cyan" , "Solid" , 1 )       :  wx
. CYAN_PEN
,  298              ( "Red" , "Solid" , 1 )        :  wx
. RED_PEN
 302              "Transparent"     :  wx
. TRANSPARENT
,  304              "BiDiagonalHatch" :  wx
. BDIAGONAL_HATCH
,  305              "CrossDiagHatch"  :  wx
. CROSSDIAG_HATCH
,  306              "FDiagonal_Hatch" :  wx
. FDIAGONAL_HATCH
,  307              "CrossHatch"      :  wx
. CROSS_HATCH
,  308              "HorizontalHatch" :  wx
. HORIZONTAL_HATCH
,  309              "VerticalHatch"   :  wx
. VERTICAL_HATCH
 314              "Transparent" :  wx
. TRANSPARENT
,  316              "LongDash"    :  wx
. LONG_DASH
,  317              "ShortDash"   :  wx
. SHORT_DASH
,  318              "DotDash"     :  wx
. DOT_DASH
,  321      def  Bind ( self
,  Event
,  CallBackFun
):  322          self
. CallBackFuncs
[ Event
] =  CallBackFun
 324          self
._ Canvas
. UseHitTest 
=  True  325          if not  self
._ Canvas
._ HTdc
:  326              self
._ Canvas
. MakeNewHTdc ()  327          if not  self
. HitColor
:  328              if not  self
._ Canvas
. HitColorGenerator
:  329                  self
._ Canvas
. HitColorGenerator 
=  colorGenerator ()  330                  self
._ Canvas
. HitColorGenerator
. next ()  # first call to prevent the background color from being used.  331              self
. HitColor 
=  self
._ Canvas
. HitColorGenerator
. next ()  332              self
. SetHitPen ( self
. HitColor
, self
. HitLineWidth
)  333              self
. SetHitBrush ( self
. HitColor
)  334          # put the object in the hit dict, indexed by it's color  335          if not  self
._ Canvas
. HitDict
:  336              self
._ Canvas
. MakeHitDict ()  337          self
._ Canvas
. HitDict
[ Event
][ self
. HitColor
] = ( self
)  # put the object in the hit dict, indexed by it's color  341          ## fixme: this only removes one from each list, there could be more.  342          if  self
._ Canvas
. HitDict
:  343              for  List 
in  self
._ Canvas
. HitDict
. itervalues ():  350      def  SetBrush ( self
, FillColor
, FillStyle
):  351          if  FillColor 
is None or  FillStyle 
is None :  352              self
. Brush 
=  wx
. TRANSPARENT_BRUSH
 353              self
. FillStyle 
=  "Transparent"  355              self
. Brush 
=  self
. BrushList
. setdefault ( ( FillColor
, FillStyle
),   wx
. Brush ( FillColor
, self
. FillStyleList
[ FillStyle
] ) )  357      def  SetPen ( self
, LineColor
, LineStyle
, LineWidth
):  358          if  ( LineColor 
is None )  or  ( LineStyle 
is None ):  359              self
. Pen 
=  wx
. TRANSPARENT_PEN
 360              self
. LineStyle 
=  'Transparent'  362               self
. Pen 
=  self
. PenList
. setdefault ( ( LineColor
, LineStyle
, LineWidth
),   wx
. Pen ( LineColor
, LineWidth
, self
. LineStyleList
[ LineStyle
]) )  364      def  SetHitBrush ( self
, HitColor
):  366              self
. HitBrush 
=  wx
. TRANSPARENT_BRUSH
 368              self
. HitBrush 
=  self
. BrushList
. setdefault ( ( HitColor
, "solid" ),   wx
. Brush ( HitColor
, self
. FillStyleList
[ "Solid" ] ) )  370      def  SetHitPen ( self
, HitColor
, LineWidth
):  372              self
. HitPen 
=  wx
. TRANSPARENT_PEN
 374               self
. HitPen 
=  self
. PenList
. setdefault ( ( HitColor
,  "solid" ,  LineWidth
),   wx
. Pen ( HitColor
,  LineWidth
,  self
. LineStyleList
[ "Solid" ]) )  376      def  PutInBackground ( self
):  377          if  self
._ Canvas 
and  self
. InForeground
:  378              self
._ Canvas
._ ForeDrawList
. remove ( self
)  379              self
._ Canvas
._ DrawList
. append ( self
)  380              self
._ Canvas
._ BackgroundDirty 
=  True  381              self
. InForeground 
=  False  383      def  PutInForeground ( self
):  384          if  self
._ Canvas 
and  ( not  self
. InForeground
):  385              self
._ Canvas
._ ForeDrawList
. append ( self
)  386              self
._ Canvas
._ DrawList
. remove ( self
)  387              self
._ Canvas
._ BackgroundDirty 
=  True  388              self
. InForeground 
=  True  393      This is a mixin class that provides some methods suitable for use  394      with objects that have a single (x,y) coordinate pair.  398      def  Move ( self
,  Delta 
):  401          Move(Delta): moves the object by delta, where delta is a  402          (dx,dy) pair. Ideally a Numpy array of shape (2,)  406          Delta 
=  asarray ( Delta
,  Float
)  408          self
. BoundingBox 
=  self
. BoundingBox 
+  Delta
 410              self
._ Canvas
. BoundingBoxDirty 
=  True        412  class  PointsObjectMixin
:  415      This is a mixin class that provides some methods suitable for use  416      with objects that have a set of (x,y) coordinate pairs.  420  ## This is code for the XYMixin object, it needs to be adapeted and tested.  421  ##    def Move(self, Delta ):  424  ##        Move(Delta): moves the object by delta, where delta is an (dx,  425  ##        dy) pair. Ideally a Numpy array or shape (2,)  429  ##        Delta = array(Delta, Float)  431  ##        self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)  433  ##            self._Canvas.BoundingBoxDirty = True        435      def  SetPoints ( self
, Points
):  437          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]), min ( self
. Points
[:, 1 ])),( max ( self
. Points
[:, 0 ]), max ( self
. Points
[:, 1 ]))), Float
)  439              self
._ Canvas
. BoundingBoxDirty 
=  True  443  class  Polygon ( DrawObject
, PointsObjectMixin
):  447      The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of  448      point coordinates.  so that Points[N][0] is the x-coordinate of  449      point N and Points[N][1] is the y-coordinate or Points[N,0] is the  450      x-coordinate of point N and Points[N,1] is the y-coordinate for  461                   InForeground 
=  False ):  462          DrawObject
.__ init
__ ( self
, InForeground
)  463          self
. Points 
=  array ( Points
, Float
)  # this DOES need to make a copy  464          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]), min ( self
. Points
[:, 1 ])),( max ( self
. Points
[:, 0 ]), max ( self
. Points
[:, 1 ]))), Float
)  466          self
. LineColor 
=  LineColor
 467          self
. LineStyle 
=  LineStyle
 468          self
. LineWidth 
=  LineWidth
 469          self
. FillColor 
=  FillColor
 470          self
. FillStyle 
=  FillStyle
 472          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  474          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  475          self
. SetBrush ( FillColor
, FillStyle
)  477      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel 
=  None ,  HTdc
= None ):  478          Points 
=  WorldToPixel ( self
. Points
)  480          dc
. SetBrush ( self
. Brush
)  481          dc
. DrawPolygon ( Points
)  482          if  HTdc 
and  self
. HitAble
:  483              HTdc
. SetPen ( self
. HitPen
)  484              HTdc
. SetBrush ( self
. HitBrush
)  485              HTdc
. DrawPolygon ( Points
)  487  ##class PolygonSet(DrawObject):  489  ##    The PolygonSet class takes a Geometry.Polygon object.  490  ##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!  492  ##    it creates a set of line segments, from (x1,y1) to (x2,y2)  496  ##    def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):  497  ##        DrawObject.__init__(self, InForeground)  499  ##        ##fixme: there should be some error checking for everything being the right length.  502  ##        self.Points = array(Points,Float)  503  ##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)  505  ##        self.LineColors = LineColors  506  ##        self.LineStyles = LineStyles  507  ##        self.LineWidths = LineWidths  508  ##        self.FillColors = FillColors  509  ##        self.FillStyles = FillStyles  511  ##        self.SetPens(LineColors,LineStyles,LineWidths)  513  ##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):  514  ##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):  515  ##        Points = WorldToPixel(self.Points)  516  ##        Points.shape = (-1,4)  517  ##        dc.DrawLineList(Points,self.Pens)  520  class  Line ( DrawObject
, PointsObjectMixin
):  522      The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.  523      so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate  524      or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.  526      It will draw a straight line if there are two points, and a polyline if there are more than two.  529      def  __init__ ( self
, Points
,  533                   InForeground 
=  False ):  534          DrawObject
.__ init
__ ( self
,  InForeground
)  537          self
. Points 
=  array ( Points
, Float
)  538          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]), min ( self
. Points
[:, 1 ])),( max ( self
. Points
[:, 0 ]), max ( self
. Points
[:, 1 ]))), Float
)  540          self
. LineColor 
=  LineColor
 541          self
. LineStyle 
=  LineStyle
 542          self
. LineWidth 
=  LineWidth
 544          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  546          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  549      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  550          Points 
=  WorldToPixel ( self
. Points
)  553          if  HTdc 
and  self
. HitAble
:  554              HTdc
. SetPen ( self
. HitPen
)  555              HTdc
. DrawLines ( Points
)  557  ##class LineSet(DrawObject, ObjectSetMixin):  559  ##    The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.  560  ##    so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!  562  ##    it creates a set of line segments, from (x1,y1) to (x2,y2)  566  ##    def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):  567  ##        DrawObject.__init__(self, InForeground)  569  ##        NumLines = len(Points) / 2  570  ##        ##fixme: there should be some error checking for everything being the right length.  573  ##        self.Points = array(Points,Float)  574  ##        self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)  576  ##        self.LineColors = LineColors  577  ##        self.LineStyles = LineStyles  578  ##        self.LineWidths = LineWidths  580  ##        self.SetPens(LineColors,LineStyles,LineWidths)  582  ##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):  583  ##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):  584  ##        Points = WorldToPixel(self.Points)  585  ##        Points.shape = (-1,4)  586  ##        dc.DrawLineList(Points,self.Pens)  588  class  PointSet ( DrawObject
):  590      The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.  591      so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate  592      or  Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.  594      Each point will be drawn the same color and Diameter. The Diameter is in screen points,  595      not world coordinates.  597      At this point, the hit-test code does not distingish between the  598      points, you will only know that one of the poins got hit, not which  601      In the case of points, the HitLineWidth is used as diameter.  604      def  __init__ ( self
,  Points
,  Color 
=  "Black" ,  Diameter 
=   1 ,  InForeground 
=  False ):  605          DrawObject
.__ init
__ ( self
, InForeground
)  607          self
. Points 
=  array ( Points
, Float
)  608          self
. Points
. shape 
= (- 1 , 2 )  # Make sure it is a NX2 array, even if there is only one point  609          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]),  610                                     min ( self
. Points
[:, 1 ])),  611                                    ( max ( self
. Points
[:, 0 ]),  612                                     max ( self
. Points
[:, 1 ]))), Float
)  615          self
. Diameter 
=  Diameter
 617          self
. HitLineWidth 
=  self
. MinHitLineWidth
 618          self
. SetPen ( Color
, "Solid" , 1 )  619          self
. SetBrush ( Color
, "Solid" )  621      def  SetPoints ( self
, Points
):  622          self
. Points 
=  array ( Points
,  Float
)  623          self
. Points
. shape 
= (- 1 , 2 )  # Make sure it is a NX2 array, even if there is only one point  624          self
. BoundingBox 
=  array ((( min ( self
. Points
[:, 0 ]),  625                                     min ( self
. Points
[:, 1 ]) ),  626                                    ( max ( self
. Points
[:, 0 ]),  627                                     max ( self
. Points
[:, 1 ]) ) ) )  629              self
._ Canvas
. BoundingBoxDirty 
=  True  631      def  DrawD2 ( self
,  dc
,  Points
):  632          # A Little optimization for a diameter2 - point  633          dc
. DrawPointList ( Points
)  634          dc
. DrawPointList ( Points 
+ ( 1 , 0 ))  635          dc
. DrawPointList ( Points 
+ ( 0 , 1 ))  636          dc
. DrawPointList ( Points 
+ ( 1 , 1 ))  638      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  640          Points 
=  WorldToPixel ( self
. Points
)  641          if  self
. Diameter 
<=  1 :  642              dc
. DrawPointList ( Points
)  643          elif  self
. Diameter 
<=  2 :  644              self
. DrawD2 ( dc
,  Points
)  646              dc
. SetBrush ( self
. Brush
)  647              radius 
=  int ( round ( self
. Diameter
/ 2 ))  649                  dc
. DrawEllipsePointSize ( ( xy 
-  radius
), ( self
. Diameter
,  self
. Diameter
) )  650          if  HTdc 
and  self
. HitAble
:  651              HTdc
. SetPen ( self
. HitPen
)  652              if  self
. Diameter 
<=  1 :  653                  HTdc
. DrawPointList ( Points
)  654              elif  self
. Diameter 
<=  2 :  655                  self
. DrawD2 ( HTdc
,  Points
)  657                  HTdc
. SetBrush ( self
. HitBrush
)  658                  radius 
=  int ( round ( self
. Diameter
/ 2 ))  660                      HTdc
. DrawEllipsePointSize (  ( xy 
-  radius
), ( self
. Diameter
,  self
. Diameter
) )  662  #### Does anyone need this?                      663  ##class Dot(DrawObject):  665  ##    The Dot class takes an x.y coordinate pair, and the Diameter of the circle.  666  ##    The Diameter is in pixels, so it won't change with zoom.  668  ##    Also Fill and line data  671  ##    def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,InForeground = False):  672  ##        DrawObject.__init__(self,InForeground)  676  ##        self.Diameter = Diameter  677  ##        # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.  678  ##        # If this is  a problem, perhaps you should use a circle, instead!  679  ##        self.BoundingBox = array(((x,y),(x,y)),Float)  681  ##        self.LineColor = LineColor  682  ##        self.LineStyle = LineStyle  683  ##        self.LineWidth = LineWidth  684  ##        self.FillColor = FillColor  685  ##        self.FillStyle = FillStyle  687  ##        self.SetPen(LineColor,LineStyle,LineWidth)  688  ##        self.SetBrush(FillColor,FillStyle)  690  ##    def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):  691  ##    #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):  692  ##        dc.SetPen(self.Pen)  693  ##        dc.SetBrush(self.Brush)  694  ##        radius = int(round(self.Diameter/2))  695  ##        (X,Y) = WorldToPixel((self.X,self.Y))  696  ##        dc.DrawEllipse((X - radius), (Y - radius), self.Diameter, self.Diameter)  698  class  RectEllipse ( DrawObject
,  XYObjectMixin
):  699      def  __init__ ( self
, x
, y
, width
, height
,  705                   InForeground 
=  False ):  707          DrawObject
.__ init
__ ( self
, InForeground
)  709          self
. XY 
=  array ( ( x
,  y
),  Float
)  710          self
. WH 
=  array ( ( width
,  height
),  Float 
)  711          self
. BoundingBox 
=  array ((( x
, y
), ( self
. XY 
+  self
. WH
)),  Float
)  712          self
. LineColor 
=  LineColor
 713          self
. LineStyle 
=  LineStyle
 714          self
. LineWidth 
=  LineWidth
 715          self
. FillColor 
=  FillColor
 716          self
. FillStyle 
=  FillStyle
 718          self
. HitLineWidth 
=  max ( LineWidth
, self
. MinHitLineWidth
)  720          self
. SetPen ( LineColor
, LineStyle
, LineWidth
)  721          self
. SetBrush ( FillColor
, FillStyle
)  724      def  SetUpDraw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
):  726          dc
. SetBrush ( self
. Brush
)  727          if  HTdc 
and  self
. HitAble
:  728              HTdc
. SetPen ( self
. HitPen
)  729              HTdc
. SetBrush ( self
. HitBrush
)  730          return  (  WorldToPixel ( self
. XY
),  731                   ScaleWorldToPixel ( self
. WH
) )  733      def  SetXY ( self
,  x
,  y
):  734          self
. XY 
=  array ( ( x
,  y
),  Float
)  735          self
. BoundingBox 
=  array (( self
. XY
, ( self
. XY 
+  self
. WH
) ),  Float
)  737              self
._ Canvas
. BoundingBoxDirty 
=  True  740  class  Rectangle ( RectEllipse
):  741  #    def __init__(*args, **kwargs):  742  #        RectEllipse.__init__(*args, **kwargs)  745      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  746          (  XY
,  WH 
) =  self
. SetUpDraw ( dc
,  750          dc
. DrawRectanglePointSize ( XY
,  WH
)  751          if  HTdc 
and  self
. HitAble
:  752              HTdc
. DrawRectanglePointSize ( XY
,  WH
)  754  class  Ellipse ( RectEllipse
):  755  #    def __init__(*args, **kwargs):  756  #        RectEllipse.__init__(*args, **kwargs)  758      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  759          (  XY
,  WH 
) =  self
. SetUpDraw ( dc
,  763          dc
. DrawEllipsePointSize ( XY
,  WH
)  764          if  HTdc 
and  self
. HitAble
:  765              HTdc
. DrawEllipsePointSize ( XY
,  WH
)  767  class  Circle ( Ellipse
):  768      def  __init__ ( self
,  x 
, y
,  Diameter
, ** kwargs
):  769          RectEllipse
.__ init
__ ( self 
,  776  class  TextObjectMixin
:  779      A mix in class that holds attributes and methods that are needed by  784      ## I'm caching fonts, because on GTK, getting a new font can take a  785      ## while. However, it gets cleared after every full draw as hanging  786      ## on to a bunch of large fonts takes a massive amount of memory.  790      def  SetFont ( self
,  Size
,  Family
,  Style
,  Weight
,  Underline
,  FaceName
):  791          self
. Font 
=  self
. FontList
. setdefault ( ( Size
,  805      ## store the function that shift the coords for drawing text. The  806      ## "c" parameter is the correction for world coordinates, rather  807      ## than pixel coords as the y axis is reversed  808      ShiftFunDict 
= { 'tl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y
) ,  809                      'tc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y
) ,   810                      'tr' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y
) ,   811                      'cl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y 
-  h
/ 2  +  world
* h
) ,   812                      'cc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y 
-  h
/ 2  +  world
* h
) ,   813                      'cr' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y 
-  h
/ 2  +  world
* h
) ,  814                      'bl' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x
,  y 
-  h 
+  2 * world
* h
) ,  815                      'bc' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
/ 2 ,  y 
-  h 
+  2 * world
* h
) ,   816                      'br' :  lambda  x
,  y
,  w
,  h
,  world
= 0 : ( x 
-  w
,  y 
-  h 
+  2 * world
* h
)}  818  class  Text ( DrawObject
,  TextObjectMixin
):  820      This class creates a text object, placed at the coordinates,  821      x,y. the "Position" argument is a two charactor string, indicating  822      where in relation to the coordinates the string should be oriented.  824      The first letter is: t, c, or b, for top, center and bottom The  825      second letter is: l, c, or r, for left, center and right The  826      position refers to the position relative to the text itself. It  827      defaults to "tl" (top left).  829      Size is the size of the font in pixels, or in points for printing  830      (if it ever gets implimented). Those will be the same, If you assume  834          Font family, a generic way of referring to fonts without  835          specifying actual facename. One of:  836              wx.DEFAULT:  Chooses a default font.   837              wx.DECORATIVE: A decorative font.   838              wx.ROMAN: A formal, serif font.   839              wx.SCRIPT: A handwriting font.   840              wx.SWISS: A sans-serif font.   841              wx.MODERN: A fixed pitch font.  842          NOTE: these are only as good as the wxWindows defaults, which aren't so good.  844          One of wx.NORMAL, wx.SLANT and wx.ITALIC.  846          One of wx.NORMAL, wx.LIGHT and wx.BOLD.  848          The value can be True or False. At present this may have an an  849          effect on Windows only.  851      Alternatively, you can set the kw arg: Font, to a wx.Font, and the above will be ignored.  853      The size is fixed, and does not scale with the drawing.  855      The hit-test is done on the entire text extent  859      def  __init__ ( self
, String
, x
, y
,  862                   BackgroundColor 
=  None ,  868                   InForeground 
=  False ,  871          DrawObject
.__ init
__ ( self
, InForeground
)  874          # Input size in in Pixels, compute points size from PPI info.  875          # fixme: for printing, we'll have to do something a little different  876          self
. Size 
=  int ( round ( 72.0  *  Size 
/  ScreenPPI
))  879          self
. BackgroundColor 
=  BackgroundColor
 884              FaceName           
=   Font
. GetFaceName ()             885              Family             
=   Font
. GetFamily ()  886              Size               
=   Font
. GetPointSize ()            887              Style              
=   Font
. GetStyle ()  888              Underlined         
=   Font
. GetUnderlined ()           889              Weight             
=   Font
. GetWeight ()  890          self
. SetFont ( Size
,  Family
,  Style
,  Weight
,  Underline
,  FaceName
)  892          self
. BoundingBox 
=  array ((( x
, y
),( x
, y
)), Float
)  896          # use a memDC --  ScreenDC doesn't work with 2.5.1 and GTK2  898          #bitmap = wx.EmptyBitmap(1, 1)  899          #dc.SelectObject(bitmap)                  900          #dc.SetFont(self.Font)  901          #(self.TextWidth, self.TextHeight) = dc.GetTextExtent(self.String)  902          ( self
. TextWidth
,  self
. TextHeight
) = ( None ,  None )  903          self
. ShiftFun 
=  self
. ShiftFunDict
[ Position
]  905      def  SetXY ( self
,  x
,  y
):  907          self
. BoundingBox 
=  array (( self
. XY
,  self
. XY
), Float
)  909              self
._ Canvas
. BoundingBoxDirty 
=  True  911      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ):  912          XY 
=  WorldToPixel ( self
. XY
)  913          dc
. SetFont ( self
. Font
)  914          dc
. SetTextForeground ( self
. Color
)  915          if  self
. BackgroundColor
:  916              dc
. SetBackgroundMode ( wx
. SOLID
)  917              dc
. SetTextBackground ( self
. BackgroundColor
)  919              dc
. SetBackgroundMode ( wx
. TRANSPARENT
)  920          if  self
. TextWidth 
is None or  self
. TextHeight 
is None :  921              ( self
. TextWidth
,  self
. TextHeight
) =  dc
. GetTextExtent ( self
. String
)  922          XY 
=  self
. ShiftFun ( XY
[ 0 ],  XY
[ 1 ],  self
. TextWidth
,  self
. TextHeight
)  923          dc
. DrawTextPoint ( self
. String
,  XY
)  924          if  HTdc 
and  self
. HitAble
:  925              HTdc
. SetPen ( self
. HitPen
)  926              HTdc
. SetBrush ( self
. HitBrush
)  927              HTdc
. DrawRectanglePointSize ( XY
, ( self
. TextWidth
,  self
. TextHeight
) )  929  class  ScaledText ( DrawObject
,  TextObjectMixin
,  XYObjectMixin
):  931      This class creates a text object that is scaled when zoomed.  It is  932      placed at the coordinates, x,y. the "Position" argument is a two  933      charactor string, indicating where in relation to the coordinates  934      the string should be oriented.  936      The first letter is: t, c, or b, for top, center and bottom The  937      second letter is: l, c, or r, for left, center and right The  938      position refers to the position relative to the text itself. It  939      defaults to "tl" (top left).  941      Size is the size of the font in world coordinates.  944          Font family, a generic way of referring to fonts without  945          specifying actual facename. One of:  946              wx.DEFAULT:  Chooses a default font.   947              wx.DECORATI: A decorative font.   948              wx.ROMAN: A formal, serif font.   949              wx.SCRIPT: A handwriting font.   950              wx.SWISS: A sans-serif font.   951              wx.MODERN: A fixed pitch font.  952          NOTE: these are only as good as the wxWindows defaults, which aren't so good.  954          One of wx.NORMAL, wx.SLANT and wx.ITALIC.  956          One of wx.NORMAL, wx.LIGHT and wx.BOLD.  958          The value can be True or False. At present this may have an an  959          effect on Windows only.  961      Alternatively, you can set the kw arg: Font, to a wx.Font, and the  962      above will be ignored. The size of the font you specify will be  963      ignored, but the rest of it's attributes will be preserved.  965      The size will scale as the drawing is zoomed.  969      As fonts are scaled, the do end up a little different, so you don't  970      get exactly the same picture as you scale up and doen, but it's  973      On wxGTK1 on my Linux system, at least, using a font of over about  974      3000 pts. brings the system to a halt. It's the Font Server using  975      huge amounts of memory. My work around is to max the font size to  976      3000 points, so it won't scale past there. GTK2 uses smarter font  977      drawing, so that may not be an issue in future versions, so feel  978      free to test. Another smarter way to do it would be to set a global  979      zoom limit at that point.  981      The hit-test is done on the entire text extent. This could be made  982      optional, but I havn't gotten around to it.  986      def  __init__ ( self
,  String
,  x
,  y 
,  Size
,  988                   BackgroundColor 
=  None ,  995                   InForeground 
=  False ):  997          DrawObject
.__ init
__ ( self
, InForeground
) 1000          self
. XY 
=  array ( ( x
,  y
),  Float
) 1003          self
. BackgroundColor 
=  BackgroundColor
1004          self
. Family 
=  Family   
1006          self
. Weight 
=  Weight   
1007          self
. Underline 
=  Underline
1011              self
. FaceName           
=   Font
. GetFaceName ()            1012              self
. Family             
=   Font
. GetFamily ()     1013              self
. Style              
=   Font
. GetStyle ()      1014              self
. Underlined         
=   Font
. GetUnderlined ()          1015              self
. Weight             
=   Font
. GetWeight ()     1017          # Experimental max font size value on wxGTK2: this works OK on 1018          # my system If it's any larger, there is a crash, with the 1019          # message: The application 'FloatCanvasDemo.py' lost its 1020          # connection to the display :0.0; most likely the X server was 1021          # shut down or you killed/destroyed the application. 1024          self
. ShiftFun 
=  self
. ShiftFunDict
[ Position
] 1027          ## this isn't exact, as fonts don't scale exactly. 1029          bitmap 
=  wx
. EmptyBitmap ( 1 ,  1 ) 1030          dc
. SelectObject ( bitmap
)  #wxMac needs a Bitmap selected for GetTextExtent to work. 1031          DrawingSize 
=  40  # pts This effectively determines the resolution that the BB is computed to. 1032          ScaleFactor 
=  float ( Size
) /  DrawingSize
1033          dc
. SetFont ( self
. SetFont ( DrawingSize
,  self
. Family
,  self
. Style
,  self
. Weight
,  self
. Underline
,  self
. FaceName
) ) 1034          ( w
, h
) =  dc
. GetTextExtent ( self
. String
) 1037          x
,  y 
=  self
. ShiftFun ( x
,  y
,  w
,  h
,  world 
=  1 ) 1038          self
. BoundingBox 
=  array ((( x
,  y
- h 
),( x 
+  w
,  y
)), Float
) 1040          # the new coords are set to the corner of the BB: 1041          #self.X = self.BoundingBox[0,0] 1042          #self.Y = self.BoundingBox[1,1] 1043      def  _Draw ( self
,  dc 
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
= None ): 1044          ( X
, Y
) =  WorldToPixel ( ( self
. XY
) ) 1046          # compute the font size: 1047          Size 
=  abs (  ScaleWorldToPixel ( ( self
. Size
,  self
. Size
) )[ 1 ] )  # only need a y coordinate length 1048          ## Check to see if the font size is large enough to blow up the X font server 1049          ## If so, limit it. Would it be better just to not draw it? 1050          ## note that this limit is dependent on how much memory you have, etc. 1051          if  Size 
>  self
. MaxSize
: 1053          dc
. SetFont ( self
. SetFont ( Size
,  self
. Family
,  self
. Style
,  self
. Weight
,  self
. Underline
,  self
. FaceName
)) 1054          dc
. SetTextForeground ( self
. Color
) 1055          if  self
. BackgroundColor
: 1056              dc
. SetBackgroundMode ( wx
. SOLID
) 1057              dc
. SetTextBackground ( self
. BackgroundColor
) 1059              dc
. SetBackgroundMode ( wx
. TRANSPARENT
) 1060          ( w
, h
) =  dc
. GetTextExtent ( self
. String
) 1061          # compute the shift, and adjust the coordinates, if neccesary 1062          # This had to be put in here, because it changes with Zoom, as 1063          # fonts don't scale exactly. 1064          xy 
=  self
. ShiftFun ( X
,  Y
,  w
,  h
) 1066          dc
. DrawTextPoint ( self
. String
,  xy
) 1067          if  HTdc 
and  self
. HitAble
: 1068              HTdc
. SetPen ( self
. HitPen
) 1069              HTdc
. SetBrush ( self
. HitBrush
) 1070              HTdc
. DrawRectanglePointSize ( xy
, ( w
,  h
) ) 1073  #--------------------------------------------------------------------------- 1074  class  FloatCanvas ( wx
. Panel
): 1078      This is a high level window for drawing maps and anything else in an 1079      arbitrary coordinate system. 1081      The goal is to provide a convenient way to draw stuff on the screen 1082      without having to deal with handling OnPaint events, converting to pixel 1083      coordinates, knowing about wxWindows brushes, pens, and colors, etc. It 1084      also provides virtually unlimited zooming and scrolling 1086      I am using it for two things: 1087      1) general purpose drawing in floating point coordinates 1088      2) displaying map data in Lat-long coordinates 1090      If the projection is set to None, it will draw in general purpose 1091      floating point coordinates. If the projection is set to 'FlatEarth', it 1092      will draw a FlatEarth projection, centered on the part of the map that 1093      you are viewing. You can also pass in your own projection function. 1095      It is double buffered, so re-draws after the window is uncovered by something 1096      else are very quick. 1098      It relies on NumPy, which is needed for speed (maybe, I havn't profiled it) 1100      Bugs and Limitations: 1101          Lots: patches, fixes welcome 1103      For Map drawing: It ignores the fact that the world is, in fact, a 1104      sphere, so it will do strange things if you are looking at stuff near 1105      the poles or the date line. so far I don't have a need to do that, so I 1106      havn't bothered to add any checks for that yet. 1109      I have set no zoom limits. What this means is that if you zoom in really  1110      far, you can get integer overflows, and get wierd results. It 1111      doesn't seem to actually cause any problems other than wierd output, at 1112      least when I have run it. 1115      I have done a couple of things to improve speed in this app. The one 1116      thing I have done is used NumPy Arrays to store the coordinates of the 1117      points of the objects. This allowed me to use array oriented functions 1118      when doing transformations, and should provide some speed improvement 1119      for objects with a lot of points (big polygons, polylines, pointsets). 1121      The real slowdown comes when you have to draw a lot of objects, because 1122      you have to call the wx.DC.DrawSomething call each time. This is plenty 1123      fast for tens of objects, OK for hundreds of objects, but pretty darn 1124      slow for thousands of objects. 1126      The solution is to be able to pass some sort of object set to the DC 1127      directly. I've used DC.DrawPointList(Points), and it helped a lot with 1128      drawing lots of points. I havn't got a LineSet type object, so I havn't 1129      used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList() 1130      methods implimented, and then I'd also have a full set of Object sets 1131      that could take advantage of them. I hope to get to it some day. 1135      At this point, there are a full set of custom mouse events. They are 1136      just like the rebulsr mouse events, but include an extra attribute: 1137      Event.GetCoords(), that returns the (x,y) position in world 1138      coordinates, as a length-2 NumPy vector of Floats. 1140      Copyright: Christopher Barker 1142      License: Same as the version of wxPython you are using it with 1144      Please let me know if you're using this!!! 1148      Chris.Barker@noaa.gov 1152      def  __init__ ( self
,  parent
,  id  = - 1 , 1153                   size 
=  wx
. DefaultSize
, 1154                   ProjectionFun 
=  None , 1155                   BackgroundColor 
=  "WHITE" , 1158          wx
. Panel
.__ init
__ (  self
,  parent
,  id ,  wx
. DefaultPosition
,  size
) 1160          global  ScreenPPI 
## A global variable to hold the Pixels per inch that wxWindows thinks is in use. 1162          ScreenPPI 
=  dc
. GetPPI ()[ 0 ]  # Assume square pixels 1165          self
. HitColorGenerator 
=  None 1166          self
. UseHitTest 
=  None 1168          self
. NumBetweenBlits 
=  500 1170          self
. BackgroundBrush 
=  wx
. Brush ( BackgroundColor
, wx
. SOLID
) 1174          wx
. EVT_PAINT ( self
,  self
. OnPaint
) 1175          wx
. EVT_SIZE ( self
,  self
. OnSize
) 1177          wx
. EVT_LEFT_DOWN ( self
,  self
. LeftDownEvent 
)  1178          wx
. EVT_LEFT_UP ( self
,  self
. LeftUpEvent 
)  1179          wx
. EVT_LEFT_DCLICK ( self
,  self
. LeftDoubleClickEvent 
)  1180          wx
. EVT_MIDDLE_DOWN ( self
,  self
. MiddleDownEvent 
)  1181          wx
. EVT_MIDDLE_UP ( self
,  self
. MiddleUpEvent 
)  1182          wx
. EVT_MIDDLE_DCLICK ( self
,  self
. MiddleDoubleClickEvent 
)  1183          wx
. EVT_RIGHT_DOWN ( self
,  self
. RightDownEvent
) 1184          wx
. EVT_RIGHT_UP ( self
,  self
. RightUpEvent 
)  1185          wx
. EVT_RIGHT_DCLICK ( self
,  self
. RightDoubleCLickEvent 
)  1186          wx
. EVT_MOTION ( self
,  self
. MotionEvent 
)  1187          wx
. EVT_MOUSEWHEEL ( self
,  self
. WheelEvent 
)  1189          ## CHB: I'm leaving these out for now. 1190          #wx.EVT_ENTER_WINDOW(self, self. )  1191          #wx.EVT_LEAVE_WINDOW(self, self. )  1193          ## create the Hit Test Dicts: 1198          self
._ ForeDrawList 
= [] 1199          self
._ ForegroundBuffer 
=  None 1200          self
. BoundingBox 
=  None 1201          self
. BoundingBoxDirty 
=  False 1202          self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 1204          self
. SetProjectionFun ( ProjectionFun
) 1206          self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)  # No Projection to start! 1207          self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
)  # default Transformation 1212          self
. StartRBBox 
=  None 1213          self
. PrevRBBox 
=  None 1214          self
. StartMove 
=  None 1215          self
. PrevMoveXY 
=  None 1216          self
. ObjectUnderMouse 
=  None 1218          # called just to make sure everything is initialized 1223      def  SetProjectionFun ( self
, ProjectionFun
): 1224          if  ProjectionFun 
==  'FlatEarth' : 1225              self
. ProjectionFun 
=  self
. FlatEarthProjection 
1226          elif  type ( ProjectionFun
) ==  types
. FunctionType
: 1227              self
. ProjectionFun 
=  ProjectionFun 
1228          elif  ProjectionFun 
is None : 1229              self
. ProjectionFun 
=  lambda  x
= None :  array ( ( 1 , 1 ),  Float
) 1231              raise  FloatCanvasException ( 'Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector' ) 1233      def  FlatEarthProjection ( self
, CenterPoint
): 1234          return  array (( cos ( pi
* CenterPoint
[ 1 ]/ 180 ), 1 ), Float
) 1236      def  SetMode ( self
, Mode
): 1237          if  Mode 
in  [ "ZoomIn" , "ZoomOut" , "Move" , "Mouse" , None ]: 1240              raise  FloatCanvasException ( '" %s " is Not a valid Mode' % Mode
) 1242      def  MakeHitDict ( self
): 1243          ##fixme: Should this just be None if nothing has been bound?  1244          self
. HitDict 
=  {EVT_FC_LEFT_DOWN: {}
, 1246                          EVT_FC_LEFT_DCLICK
: {}, 1247                          EVT_FC_MIDDLE_DOWN
: {}, 1248                          EVT_FC_MIDDLE_UP
: {}, 1249                          EVT_FC_MIDDLE_DCLICK
: {}, 1250                          EVT_FC_RIGHT_DOWN
: {}, 1251                          EVT_FC_RIGHT_UP
: {}, 1252                          EVT_FC_RIGHT_DCLICK
: {}, 1253                          EVT_FC_ENTER_OBJECT
: {}, 1254                          EVT_FC_LEAVE_OBJECT
: {}, 1257      def  RaiseMouseEvent ( self
,  Event
,  EventType
): 1259          This is called in various other places to raise a Mouse Event 1261          #print "in Raise Mouse Event", Event 1262          pt 
=  self
. PixelToWorld (  Event
. GetPosition () ) 1263          evt 
=  MouseEvent ( EventType
,  Event
,  self
. GetId (),  pt
) 1264          self
. GetEventHandler (). ProcessEvent ( evt
)        1266      def  HitTest ( self
,  event
,  HitEvent
): 1268              # check if there are any objects in the dict for this event 1269              if  self
. HitDict
[  HitEvent 
]: 1270                  xy 
=  event
. GetPosition () 1271                  if  self
._ ForegroundHTdc
: 1272                      hitcolor 
=  self
._ ForegroundHTdc
. GetPixelPoint (  xy 
) 1274                      hitcolor 
=  self
._ HTdc
. GetPixelPoint (  xy 
) 1275                  color 
= (  hitcolor
. Red (),  hitcolor
. Green (),  hitcolor
. Blue () ) 1276                  if  color 
in  self
. HitDict
[  HitEvent 
]: 1277                      Object 
=  self
. HitDict
[  HitEvent 
][ color
] 1278                      ## Add the hit coords to the Object 1279                      Object
. HitCoords 
=  self
. PixelToWorld (  xy 
) 1280                      Object
. CallBackFuncs
[ HitEvent
]( Object
) 1284      def  MouseOverTest ( self
,  event
): 1285          ##fixme: Can this be cleaned up? 1287              xy 
=  event
. GetPosition () 1288              if  self
._ ForegroundHTdc
: 1289                  hitcolor 
=  self
._ ForegroundHTdc
. GetPixelPoint (  xy 
) 1291                  hitcolor 
=  self
._ HTdc
. GetPixelPoint (  xy 
) 1292              color 
= (  hitcolor
. Red (),  hitcolor
. Green (),  hitcolor
. Blue () ) 1293              OldObject 
=  self
. ObjectUnderMouse
1294              ObjectCallbackCalled 
=  False 1295              if  color 
in  self
. HitDict
[  EVT_FC_ENTER_OBJECT 
]: 1296                  Object 
=  self
. HitDict
[  EVT_FC_ENTER_OBJECT
][ color
] 1297                  if  ( OldObject 
is None ): 1299                          Object
. CallBackFuncs
[ EVT_FC_ENTER_OBJECT
]( Object
) 1300                          ObjectCallbackCalled 
=   True 1302                          pass  # this means the enter event isn't bound for that object 1303                  elif  OldObject 
==  Object
:  # the mouse is still on the same object 1305                      ## Is the mouse on a differnt object as it was... 1306                  elif not  ( Object 
==  OldObject
): 1307                      # call the leave object callback 1309                          OldObject
. CallBackFuncs
[ EVT_FC_LEAVE_OBJECT
]( OldObject
) 1310                          ObjectCallbackCalled 
=   True 1312                          pass  # this means the leave event isn't bound for that object 1314                          Object
. CallBackFuncs
[ EVT_FC_ENTER_OBJECT
]( Object
) 1315                          ObjectCallbackCalled 
=   True 1317                          pass  # this means the enter event isn't bound for that object 1318                      ## set the new object under mouse 1319                  self
. ObjectUnderMouse 
=  Object
1320              elif  color 
in  self
. HitDict
[  EVT_FC_LEAVE_OBJECT 
]: 1321                  Object 
=  self
. HitDict
[  EVT_FC_LEAVE_OBJECT
][ color
] 1322                  self
. ObjectUnderMouse 
=  Object
1324                  # no objects under mouse bound to mouse-over events 1325                  self
. ObjectUnderMouse 
=  None 1328                          OldObject
. CallBackFuncs
[ EVT_FC_LEAVE_OBJECT
]( OldObject
) 1329                          ObjectCallbackCalled 
=   True 1331                          pass  # this means the leave event isn't bound for that object 1332              return  ObjectCallbackCalled
1335      ## fixme: There is a lot of repeated code here 1336      ##        Is there a better way?             1337      def  LeftDoubleClickEvent ( self
, event
): 1338          if  self
. GUIMode 
==  "Mouse" : 1339              EventType 
=  EVT_FC_LEFT_DCLICK
1340              if not  self
. HitTest ( event
,  EventType
): 1341                  self
. RaiseMouseEvent ( event
,  EventType
) 1344      def  MiddleDownEvent ( self
, event
): 1345          if  self
. GUIMode 
==  "Mouse" : 1346              EventType 
=  EVT_FC_MIDDLE_DOWN
1347              if not  self
. HitTest ( event
,  EventType
): 1348                  self
. RaiseMouseEvent ( event
,  EventType
) 1350      def  MiddleUpEvent ( self
, event
): 1351          if  self
. GUIMode 
==  "Mouse" : 1352              EventType 
=  EVT_FC_MIDDLE_UP
1353              if not  self
. HitTest ( event
,  EventType
): 1354                  self
. RaiseMouseEvent ( event
,  EventType
) 1356      def  MiddleDoubleClickEvent ( self
, event
): 1357          if  self
. GUIMode 
==  "Mouse" : 1358              EventType 
=  EVT_FC_MIDDLE_DCLICK
1359              if not  self
. HitTest ( event
,  EventType
): 1360                  self
. RaiseMouseEvent ( event
,  EventType
) 1362      def  RightUpEvent ( self
, event
): 1363          if  self
. GUIMode 
==  "Mouse" : 1364              EventType 
=  EVT_FC_RIGHT_UP
1365              if not  self
. HitTest ( event
,  EventType
): 1366                  self
. RaiseMouseEvent ( event
,  EventType
) 1368      def  RightDoubleCLickEvent ( self
, event
): 1369          if  self
. GUIMode 
==  "Mouse" : 1370              EventType 
=  EVT_FC_RIGHT_DCLICK
1371              if not  self
. HitTest ( event
,  EventType
): 1372                  self
. RaiseMouseEvent ( event
,  EventType
) 1374      def  WheelEvent ( self
, event
): 1375          if  self
. GUIMode 
==  "Mouse" : 1376              self
. RaiseMouseEvent ( event
,  EVT_FC_MOUSEWHEEL
) 1379      def  LeftDownEvent ( self
, event
): 1381              if  self
. GUIMode 
==  "ZoomIn" : 1382                  self
. StartRBBox 
=  array (  event
. GetPosition () ) 1383                  self
. PrevRBBox 
=  None 1385              elif  self
. GUIMode 
==  "ZoomOut" : 1386                  Center 
=  self
. PixelToWorld (  event
. GetPosition () ) 1387                  self
. Zoom ( 1 / 1.5 , Center
) 1388              elif  self
. GUIMode 
==  "Move" : 1389                  self
. StartMove 
=  array (  event
. GetPosition () ) 1390                  self
. PrevMoveXY 
= ( 0 , 0 ) 1391              elif  self
. GUIMode 
==  "Mouse" : 1393                  if not  self
. HitTest ( event
,  EVT_FC_LEFT_DOWN
): 1394                     self
. RaiseMouseEvent ( event
, EVT_FC_LEFT_DOWN
) 1398      def  LeftUpEvent ( self
, event
): 1399          if  self
. HasCapture (): 1402              if  self
. GUIMode 
==  "ZoomIn" : 1403                  if  event
. LeftUp ()  and not  self
. StartRBBox 
is None : 1404                      self
. PrevRBBox 
=  None 1405                      EndRBBox 
=  event
. GetPosition () 1406                      StartRBBox 
=  self
. StartRBBox
1407                      # if mouse has moved less that ten pixels, don't use the box. 1408                      if  (  abs ( StartRBBox
[ 0 ] -  EndRBBox
[ 0 ]) >  10 1409                           and  abs ( StartRBBox
[ 1 ] -  EndRBBox
[ 1 ]) >  10  ): 1410                          EndRBBox 
=  self
. PixelToWorld ( EndRBBox
) 1411                          StartRBBox 
=  self
. PixelToWorld ( StartRBBox
) 1412                          BB 
=  array ((( min ( EndRBBox
[ 0 ], StartRBBox
[ 0 ]), 1413                                       min ( EndRBBox
[ 1 ], StartRBBox
[ 1 ])), 1414                                      ( max ( EndRBBox
[ 0 ], StartRBBox
[ 0 ]), 1415                                       max ( EndRBBox
[ 1 ], StartRBBox
[ 1 ]))), Float
) 1418                          Center 
=  self
. PixelToWorld ( StartRBBox
) 1419                          self
. Zoom ( 1.5 , Center
) 1420                      self
. StartRBBox 
=  None 1421              elif  self
. GUIMode 
==  "Move" : 1422                  if not  self
. StartMove 
is None : 1423                      StartMove 
=  self
. StartMove
1424                      EndMove 
=  array (( event
. GetX (), event
. GetY ())) 1425                      if  sum (( StartMove
- EndMove
)** 2 ) >  16 : 1426                          self
. Move ( StartMove
- EndMove
, 'Pixel' ) 1427                      self
. StartMove 
=  None 1428              elif  self
. GUIMode 
==  "Mouse" : 1429                  EventType 
=  EVT_FC_LEFT_UP
1430                  if not  self
. HitTest ( event
,  EventType
): 1431                     self
. RaiseMouseEvent ( event
,  EventType
) 1435      def  MotionEvent ( self
, event
): 1437              if  self
. GUIMode 
==  "ZoomIn" : 1438                  if  event
. Dragging ()  and  event
. LeftIsDown ()  and not  ( self
. StartRBBox 
is None ): 1439                      xy0 
=  self
. StartRBBox
1440                      xy1 
=  array (  event
. GetPosition () ) 1442                      wh
[ 0 ] =  max ( wh
[ 0 ],  int ( wh
[ 1 ]* self
. AspectRatio
)) 1443                      wh
[ 1 ] =  int ( wh
[ 0 ] /  self
. AspectRatio
) 1444                      xy_c 
= ( xy0 
+  xy1
) /  2 1445                      dc 
=  wx
. ClientDC ( self
) 1447                      dc
. SetPen ( wx
. Pen ( 'WHITE' ,  2 ,  wx
. SHORT_DASH
)) 1448                      dc
. SetBrush ( wx
. TRANSPARENT_BRUSH
) 1449                      dc
. SetLogicalFunction ( wx
. XOR
) 1451                          dc
. DrawRectanglePointSize (* self
. PrevRBBox
) 1452                      self
. PrevRBBox 
= (  xy_c 
-  wh
/ 2 ,  wh 
) 1453                      dc
. DrawRectanglePointSize ( * self
. PrevRBBox 
) 1455              elif  self
. GUIMode 
==  "Move" : 1456                  if  event
. Dragging ()  and  event
. LeftIsDown ()  and not  self
. StartMove 
is None : 1457                      xy1 
=  array (  event
. GetPosition () ) 1459                      xy_tl 
=  xy1 
-  self
. StartMove
1460                      dc 
=  wx
. ClientDC ( self
) 1462                      x1
, y1 
=  self
. PrevMoveXY
1464                      w
, h 
=  self
. PanelSize
1465                      if  x2 
>  x1 
and  y2 
>  y1
: 1472                      elif  x2 
>  x1 
and  y2 
<=  y1
: 1481                      elif  x2 
<=  x1 
and  y2 
>  y1
: 1490                      elif  x2 
<=  x1 
and  y2 
<=  y1
: 1500                      dc
. SetPen ( wx
. TRANSPARENT_PEN
) 1501                      dc
. SetBrush ( self
. BackgroundBrush
) 1502                      dc
. DrawRectangle ( xa
,  ya
,  wa
,  ha
) 1503                      dc
. DrawRectangle ( xb
,  yb
,  wb
,  hb
) 1504                      self
. PrevMoveXY 
=  xy_tl
1505                      if  self
._ ForegroundBuffer
: 1506                          dc
. DrawBitmapPoint ( self
._ ForegroundBuffer
, xy_tl
) 1508                          dc
. DrawBitmapPoint ( self
._ Buffer
, xy_tl
) 1510              elif  self
. GUIMode 
==  "Mouse" : 1511                  ## Only do something if there are mouse over events bound 1512                  if  self
. HitDict 
and  ( self
. HitDict
[  EVT_FC_ENTER_OBJECT 
]  or  self
. HitDict
[  EVT_FC_LEAVE_OBJECT 
] ): 1513                      if not  self
. MouseOverTest ( event
): 1514                          self
. RaiseMouseEvent ( event
, EVT_FC_MOTION
) 1517              self
. RaiseMouseEvent ( event
, EVT_FC_MOTION
) 1521      def  RightDownEvent ( self
, event
): 1523              if  self
. GUIMode 
==  "ZoomIn" : 1524                  Center 
=  self
. PixelToWorld (( event
. GetX (), event
. GetY ())) 1525                  self
. Zoom ( 1 / 1.5 , Center
) 1526              elif  self
. GUIMode 
==  "ZoomOut" : 1527                  Center 
=  self
. PixelToWorld (( event
. GetX (), event
. GetY ())) 1528                  self
. Zoom ( 1.5 , Center
) 1529              elif  self
. GUIMode 
==  "Mouse" : 1530                  EventType 
=  EVT_FC_RIGHT_DOWN
1531                  if not  self
. HitTest ( event
,  EventType
): 1532                     self
. RaiseMouseEvent ( event
,  EventType
) 1536      def  MakeNewBuffers ( self
): 1537          self
._ BackgroundDirty 
=  True 1538          # Make new offscreen bitmap: 1539          self
._ Buffer 
=  wx
. EmptyBitmap (* self
. PanelSize
) 1541          #dc.SelectObject(self._Buffer) 1543          if  self
._ ForeDrawList
: 1544              self
._ ForegroundBuffer 
=  wx
. EmptyBitmap (* self
. PanelSize
) 1546              self
._ ForegroundBuffer 
=  None 1551              self
._ ForegroundHTdc 
=  None 1553      def  MakeNewHTdc ( self
): 1554          ## Note: While it's considered a "bad idea" to keep a 1555          ## MemoryDC around I'm doing it here because a wx.Bitmap 1556          ## doesn't have a GetPixel method so a DC is needed to do 1557          ## the hit-test. It didn't seem like a good idea to re-create 1558          ## a wx.MemoryDC on every single mouse event, so I keep it 1560          self
._ HTdc 
=  wx
. MemoryDC () 1561          self
._ HTBitmap 
=  wx
. EmptyBitmap (* self
. PanelSize
)  1562          self
._ HTdc
. SelectObject (  self
._ HTBitmap 
) 1563          self
._ HTdc
. SetBackground ( wx
. BLACK_BRUSH
) 1564          if  self
._ ForeDrawList
: 1565              self
._ ForegroundHTdc 
=  wx
. MemoryDC () 1566              self
._ ForegroundHTBitmap 
=  wx
. EmptyBitmap (* self
. PanelSize
)  1567              self
._ ForegroundHTdc
. SelectObject (  self
._ ForegroundHTBitmap 
) 1568              self
._ ForegroundHTdc
. SetBackground ( wx
. BLACK_BRUSH
) 1570             self
._ ForegroundHTdc 
=  None  1572      def  OnSize ( self
, event
): 1573          self
. PanelSize  
=  array ( self
. GetClientSizeTuple (), Int32
) 1574          self
. HalfPanelSize 
=  self
. PanelSize 
/  2  # lrk: added for speed in WorldToPixel 1575          if  self
. PanelSize
[ 0 ] ==  0  or  self
. PanelSize
[ 1 ] ==  0 : 1576              self
. AspectRatio 
=  1.0 1578              self
. AspectRatio 
=  float ( self
. PanelSize
[ 0 ]) /  self
. PanelSize
[ 1 ] 1579          self
. MakeNewBuffers () 1582      def  OnPaint ( self
,  event
): 1583          dc 
=  wx
. PaintDC ( self
) 1584          if  self
._ ForegroundBuffer
: 1585              dc
. DrawBitmap ( self
._ ForegroundBuffer
, 0 , 0 ) 1587              dc
. DrawBitmap ( self
._ Buffer
, 0 , 0 ) 1589      def  Draw ( self
,  Force
= False ): 1591          There is a main buffer set up to double buffer the screen, so 1592          you can get quick re-draws when the window gets uncovered. 1594          If there are any objects in self._ForeDrawList, then the 1595          background gets drawn to a new buffer, and the foreground 1596          objects get drawn on top of it. The final result if blitted to 1597          the screen, and stored for future Paint events.  This is done so 1598          that you can have a complicated background, but have something 1599          changing on the foreground, without having to wait for the 1600          background to get re-drawn. This can be used to support simple 1601          animation, for instance. 1605          if  self
. Debug
:  start 
=  clock () 1606          ScreenDC 
=   wx
. ClientDC ( self
) 1607          ViewPortWorld 
= (  self
. PixelToWorld (( 0 , 0 )), 1608                            self
. PixelToWorld ( self
. PanelSize
) ) 1609          ViewPortBB 
=  array ( (  minimum
. reduce ( ViewPortWorld
), 1610                                maximum
. reduce ( ViewPortWorld
) ) ) 1612          dc
. SelectObject ( self
._ Buffer
) 1613          if  self
._ BackgroundDirty 
or  Force
: 1614              #print "Background is Dirty" 1615              dc
. SetBackground ( self
. BackgroundBrush
) 1619              self
._ DrawObjects
( dc
,  self
._ DrawList
,  ScreenDC
,  ViewPortBB
,  self
._ HTdc
) 1620              self
._ BackgroundDirty 
=  False 1622          if  self
._ ForeDrawList
: 1623              ## If an object was just added to the Foreground, there might not yet be a buffer 1624              if  self
._ ForegroundBuffer 
is None : 1625                  self
._ ForegroundBuffer 
=  wx
. EmptyBitmap ( self
. PanelSize
[ 0 ], 1628              dc 
=  wx
. MemoryDC ()  ## I got some strange errors (linewidths wrong) if I didn't make a new DC here 1629              dc
. SelectObject ( self
._ ForegroundBuffer
) 1630              dc
. DrawBitmap ( self
._ Buffer
, 0 , 0 ) 1631              if  self
._ ForegroundHTdc 
is None : 1632                  self
._ ForegroundHTdc 
=  wx
. MemoryDC () 1633                  self
._ ForegroundHTdc
. SelectObject (  wx
. EmptyBitmap ( 1635                                                     self
. PanelSize
[ 1 ]) ) 1637                  ## blit the background HT buffer to the foreground HT buffer 1638                  self
._ ForegroundHTdc
. Blit ( 0 ,  0 , 1639                                            self
. PanelSize
[ 0 ],  self
. PanelSize
[ 1 ], 1641              self
._ DrawObjects
( dc
, 1645                                self
._ ForegroundHTdc
) 1646          ScreenDC
. Blit ( 0 ,  0 ,  self
. PanelSize
[ 0 ], self
. PanelSize
[ 1 ],  dc
,  0 ,  0 ) 1647  ##        wx.GetApp().Yield(True) 1648          # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn 1649          # This seeems out of place, but it works. 1651              ScreenDC
. SetPen ( wx
. Pen ( 'WHITE' ,  2 , wx
. SHORT_DASH
)) 1652              ScreenDC
. SetBrush ( wx
. TRANSPARENT_BRUSH
) 1653              ScreenDC
. SetLogicalFunction ( wx
. XOR
) 1654              ScreenDC
. DrawRectanglePointSize (* self
. PrevRBBox
) 1655          if  self
. Debug
:  print  "Drawing took  %f  seconds of CPU time" %( clock ()- start
) 1657          ## Clear the font cache 1658          ## IF you don't do this, the X font server starts to take up Massive amounts of memory 1659          ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in. 1660          DrawObject
. FontList 
= {} 1662      def  _ShouldRedraw ( DrawList
,  ViewPortBB
):  # lrk: adapted code from BBCheck 1663          # lrk: Returns the objects that should be redrawn 1667          for  Object 
in  DrawList
: 1668              BB1 
=  Object
. BoundingBox
1669              if  ( BB1
[ 1 , 0 ] >  BB2
[ 0 , 0 ]  and  BB1
[ 0 , 0 ] <  BB2
[ 1 , 0 ]  and 1670                   BB1
[ 1 , 1 ] >  BB2
[ 0 , 1 ]  and  BB1
[ 0 , 1 ] <  BB2
[ 1 , 1 ]): 1671                  redrawlist
. append ( Object
) 1673      _ShouldRedraw 
=  staticmethod ( _ShouldRedraw
) 1676  ##    def BBCheck(self, BB1, BB2): 1679  ##        BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise 1682  ##        if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and 1683  ##             (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ): 1688      def  Move ( self
, shift
, CoordType
): 1690          move the image in the window. 1692          shift is an (x,y) tuple, specifying the amount to shift in each direction 1694          It can be in any of three coordinates: Panel, Pixel, World, 1695          specified by the CoordType parameter 1697          Panel coordinates means you want to shift the image by some 1698          fraction of the size of the displaed image 1700          Pixel coordinates means you want to shift the image by some number of pixels 1702          World coordinates mean you want to shift the image by an amount 1703          in Floating point world coordinates 1707          shift 
=  array ( shift
, Float
) 1708          if  CoordType 
==  'Panel' : # convert from panel coordinates 1709              shift 
=  shift 
*  array ((- 1 , 1 ), Float
) * self
. PanelSize
/ self
. TransformVector
1710          elif  CoordType 
==  'Pixel' :  # convert from pixel coordinates 1711              shift 
=  shift
/ self
. TransformVector
1712          elif  CoordType 
==  'World' :  # No conversion 1715              raise  FloatCanvasException ( 'CoordType must be either "Panel", "Pixel", or "World"' ) 1717          self
. ViewPortCenter 
=  self
. ViewPortCenter 
+  shift 
1718          self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1719          self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
) *  self
. MapProjectionVector
1720          self
._ BackgroundDirty 
=  True 1723      def  Zoom ( self
, factor
, center 
=  None ): 1726          Zoom(factor, center) changes the amount of zoom of the image by factor. 1727          If factor is greater than one, the image gets larger. 1728          If factor is less than one, the image gets smaller. 1730          Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming. 1731          If center is not given, the center will stay the same. 1734          self
. Scale 
=  self
. Scale
* factor
1735          if not  center 
is None : 1736              self
. ViewPortCenter 
=  array ( center
, Float
) 1737          self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1738          self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
) *  self
. MapProjectionVector
1739          self
._ BackgroundDirty 
=  True 1742      def  ZoomToBB ( self
,  NewBB 
=  None ,  DrawFlag 
=  True ): 1746          Zooms the image to the bounding box given, or to the bounding 1747          box of all the objects on the canvas, if none is given. 1751          if not   NewBB 
is None : 1754              if  self
. BoundingBoxDirty
: 1755                  self
._ ResetBoundingBox
() 1756              BoundingBox 
=  self
. BoundingBox
1757          if not  BoundingBox 
is None : 1758              self
. ViewPortCenter 
=  array ((( BoundingBox
[ 0 , 0 ]+ BoundingBox
[ 1 , 0 ])/ 2 , 1759                                           ( BoundingBox
[ 0 , 1 ]+ BoundingBox
[ 1 , 1 ])/ 2  ), Float
) 1760              self
. MapProjectionVector 
=  self
. ProjectionFun ( self
. ViewPortCenter
) 1761              # Compute the new Scale 1762              BoundingBox 
=  BoundingBox 
*  self
. MapProjectionVector
1764                  self
. Scale 
=  min ( abs ( self
. PanelSize
[ 0 ] / ( BoundingBox
[ 1 , 0 ]- BoundingBox
[ 0 , 0 ])), 1765                                   abs ( self
. PanelSize
[ 1 ] / ( BoundingBox
[ 1 , 1 ]- BoundingBox
[ 0 , 1 ])) )* 0.95 1766              except  ZeroDivisionError :  # this will happen if the BB has zero width or height 1768                      self
. Scale 
= ( self
. PanelSize
[ 0 ]  / ( BoundingBox
[ 1 , 0 ]- BoundingBox
[ 0 , 0 ]))* 0.95 1769                  except  ZeroDivisionError : 1771                          self
. Scale 
= ( self
. PanelSize
[ 1 ]  / ( BoundingBox
[ 1 , 1 ]- BoundingBox
[ 0 , 1 ]))* 0.95 1772                      except  ZeroDivisionError :  #zero size! (must be a single point) 1775              self
. TransformVector 
=  array (( self
. Scale
,- self
. Scale
), Float
)*  self
. MapProjectionVector
1777                  self
._ BackgroundDirty 
=  True 1780              # Reset the shifting and scaling to defaults when there is no BB 1781              self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 1782              self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)  # No Projection to start! 1783              self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
)  # default Transformation 1786      def  RemoveObjects ( self
,  Objects
): 1787          for  Object 
in  Objects
: 1788              self
. RemoveObject ( Object
,  ResetBB 
=  False ) 1789          self
. BoundingBoxDirty 
=  True 1791      def  RemoveObject ( self
,  Object
,  ResetBB 
=  True ): 1792          ##fixme: Using the list.remove method is kind of slow 1793          if  Object
. InForeground
: 1794              self
._ ForeDrawList
. remove ( Object
) 1796              self
._ DrawList
. remove ( Object
) 1797              self
._ BackgroundDirty 
=  True 1799              self
. BoundingBoxDirty 
=  True 1801      def  ClearAll ( self
,  ResetBB 
=  True ): 1803          self
._ ForeDrawList 
= [] 1804          self
._ BackgroundDirty 
=  True 1805          self
. HitColorGenerator 
=  None 1806          self
. UseHitTest 
=  False 1808              self
._ ResetBoundingBox
() 1809          self
. MakeNewBuffers () 1813  ##    def _AddBoundingBox(self,NewBB): 1814  ##        if self.BoundingBox is None: 1815  ##            self.BoundingBox = NewBB 1816  ##            self.ZoomToBB(NewBB,DrawFlag = False) 1818  ##            self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]), 1819  ##                                         min(self.BoundingBox[0,1],NewBB[0,1])), 1820  ##                                        (max(self.BoundingBox[1,0],NewBB[1,0]), 1821  ##                                         max(self.BoundingBox[1,1],NewBB[1,1]))), 1824      def  _getboundingbox ( bboxarray
):  # lrk: added this 1826          upperleft 
=  minimum
. reduce ( bboxarray
[:, 0 ]) 1827          lowerright 
=  maximum
. reduce ( bboxarray
[:, 1 ]) 1828          return  array (( upperleft
,  lowerright
),  Float
) 1830      _getboundingbox 
=  staticmethod ( _getboundingbox
) 1832      def  _ResetBoundingBox ( self
): 1833          if  self
._ DrawList 
or  self
._ ForeDrawList
: 1834              bboxarray 
=  zeros (( len ( self
._ DrawList
)+ len ( self
._ ForeDrawList
),  2 ,  2 ), Float
)  1835              i 
= - 1  # just in case _DrawList is empty 1836              for  ( i
,  BB
)  in  enumerate ( self
._ DrawList
): 1837                  bboxarray
[ i
] =  BB
. BoundingBox
1838              for  ( j
,  BB
)  in  enumerate ( self
._ ForeDrawList
): 1839                  bboxarray
[ i
+ j
+ 1 ] =  BB
. BoundingBox
1840              self
. BoundingBox 
=  self
._ getboundingbox
( bboxarray
) 1842              self
. BoundingBox 
=  None 1843              self
. ViewPortCenter
=  array ( ( 0 , 0 ),  Float
) 1844              self
. TransformVector 
=  array ( ( 1 ,- 1 ),  Float
) 1845              self
. MapProjectionVector 
=  array ( ( 1 , 1 ),  Float
)                      1847          self
. BoundingBoxDirty 
=  False 1849      def  PixelToWorld ( self
, Points
): 1851          Converts coordinates from Pixel coordinates to world coordinates. 1853          Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates. 1856          return   ((( asarray ( Points
, Float
) - ( self
. PanelSize
/ 2 ))/ self
. TransformVector
) +  self
. ViewPortCenter
) 1858      def  WorldToPixel ( self
, Coordinates
): 1860          This function will get passed to the drawing functions of the objects, 1861          to transform from world to pixel coordinates. 1862          Coordinates should be a NX2 array of (x,y) coordinates, or 1863          a 2-tuple, or sequence of 2-tuples. 1865          #Note: this can be called by users code for various reasons, so asarray is needed. 1866          return   ((( asarray ( Coordinates
, Float
) -  self
. ViewPortCenter
)* self
. TransformVector
)+( self
. HalfPanelSize
)). astype ( 'i' ) 1868      def  ScaleWorldToPixel ( self
, Lengths
): 1870          This function will get passed to the drawing functions of the objects, 1871          to Change a length from world to pixel coordinates. 1873          Lengths should be a NX2 array of (x,y) coordinates, or 1874          a 2-tuple, or sequence of 2-tuples. 1876          return   ( ( asarray ( Lengths
, Float
)* self
. TransformVector
) ). astype ( 'i' ) 1878      def  ScalePixelToWorld ( self
, Lengths
): 1880          This function computes a pair of x.y lengths, 1881          to change then from pixel to world coordinates. 1883          Lengths should be a NX2 array of (x,y) coordinates, or 1884          a 2-tuple, or sequence of 2-tuples. 1887          return   ( asarray ( Lengths
, Float
) /  self
. TransformVector
) 1889      def  AddObject ( self
, obj
): 1890          # put in a reference to the Canvas, so remove and other stuff can work 1892          if   obj
. InForeground
: 1893              self
._ ForeDrawList
. append ( obj
) 1894              self
. UseForeground 
=  True 1896              self
._ DrawList
. append ( obj
) 1897              self
._ BackgroundDirty 
=  True 1898          self
. BoundingBoxDirty 
=  True 1901      def  _DrawObjects ( self
,  dc
,  DrawList
,  ScreenDC
,  ViewPortBB
,  HTdc 
=  None ): 1903          This is a convenience function; 1904          This function takes the list of objects and draws them to specified 1907          dc
. SetBackground ( self
. BackgroundBrush
) 1910          PanelSize0
,  PanelSize1 
=  self
. PanelSize 
# for speed 1911          WorldToPixel 
=  self
. WorldToPixel 
# for speed 1912          ScaleWorldToPixel 
=  self
. ScaleWorldToPixel 
# for speed 1913          Blit 
=  ScreenDC
. Blit 
# for speed 1914          NumBetweenBlits 
=  self
. NumBetweenBlits 
# for speed 1915          for  i
,  Object 
in  enumerate ( self
._ ShouldRedraw
( DrawList
,  ViewPortBB
)): 1916              Object
._ Draw
( dc
,  WorldToPixel
,  ScaleWorldToPixel
,  HTdc
) 1917              if  i 
%  NumBetweenBlits 
==  0 : 1918                  Blit ( 0 ,  0 ,  PanelSize0
,  PanelSize1
,  dc
,  0 ,  0 ) 1921  ##    ## This is a way to automatically add a AddObject method for each 1922  ##    ## object type This code has been replaced by Leo's code above, so 1923  ##    ## that it happens at module init, rather than as needed. The 1924  ##    ## primary advantage of this is that dir(FloatCanvas) will have 1925  ##    ## them, and docstrings are preserved. Probably more useful 1926  ##    ## exceptions if there is a problem, as well. 1927  ##    def __getattr__(self, name): 1928  ##        if name[:3] == "Add": 1929  ##            func=globals()[name[3:]] 1930  ##            def AddFun(*args, **kwargs): 1931  ##                Object = func(*args, **kwargs) 1932  ##                self.AddObject(Object) 1934  ##            ## add it to FloatCanvas' dict for future calls. 1935  ##            self.__dict__[name] = AddFun 1938  ##            raise AttributeError("FloatCanvas has no attribute '%s'"%name) 1940  def  _makeFloatCanvasAddMethods ():  ## lrk's code for doing this in module __init__ 1941      classnames 
= [ "Circle" ,  "Ellipse" ,  "Rectangle" ,  "ScaledText" ,  "Polygon" , 1942                 "Line" ,  "Text" ,  "PointSet" ] 1943      for  classname 
in  classnames
: 1944          klass 
=  globals ()[ classname
] 1945          def  getaddshapemethod ( klass
= klass
): 1946              def  addshape ( self
, * args
, ** kwargs
): 1947                  Object 
=  klass (* args
, ** kwargs
) 1948                  self
. AddObject ( Object
) 1951          addshapemethod 
=  getaddshapemethod () 1952          methodname 
=  "Add"  +  classname
1953          setattr ( FloatCanvas
,  methodname
,  addshapemethod
) 1954          docstring 
=  "Creates  %s  and adds its reference to the canvas. \n "  %  classname
1955          docstring 
+=  "Argument protocol same as  %s  class"  %  classname
1957              docstring 
+=  ", whose docstring is: \n %s "  %  klass
.__ doc
__ 1958          FloatCanvas
.__ dict
__ [ methodname
] .__ doc
__  =  docstring
1960  _makeFloatCanvasAddMethods ()