Hi Chris,

On 9/18/2015 21:08, Chris Barker wrote:
Hey Werner,

THanks for working on all this!

note that very little was changed / added recently, so if stuff got lost on the way to Phoenix, it's probably not because it was added after the split...

Where is the source repo for wxPython 2.9.5?

hmm...

I think the wxWidgets SVN is the place to go for that:

http://svn.wxwidgets.org/svn/wx/wxPython/3rdParty/FloatCanvas/
Did a checkout of the above and then compared it to the this commit in git: 3f4fe92c4d81fc281910e638a097686f741523b1, which is from 26 April 2013 before the refactor and changes to make fc run on Phoenix where done.

Note that the original copy to the Phoenix repo was done on 13 August 2012.

Attached are the diffs for FloatCanvas and GUIMode, it looks like a few changes are Phoenix related but then there is some stuff, like the Group properties, which should be in Phoenix. I will try to sort this during next week and add changes to the existing PR (or should this be a separate one?).

Have a nice weekend
Werner

--- D:/devTools/Phoenix/wx/lib/floatcanvas/GUIMode.py   Sat Sep 19 10:52:55 2015
+++ D:/devTools/wxPython/trunk/wx/lib/floatcanvas/GUIMode.py    Sat Sep 19 
12:19:41 2015
@@ -5,7 +5,7 @@
 
 Note that this can only be imported after a wx.App() has been created.
 
-This approach was inpired by Christian Blouin, who also wrote the initial
+This approach was inspired by Christian Blouin, who also wrote the initial
 version of the code.
 
 """
@@ -104,6 +104,16 @@
         """
         pass
 
+## some mix-ins for use with the other modes:
+class ZoomWithMouseWheel():
+    def OnWheel(self, event):
+        point = event.Position
+        if event.GetWheelRotation() < 0:
+            self.Canvas.Zoom(0.9, point, centerCoords = "pixel", 
keepPointInPlace=True)
+        else:
+            self.Canvas.Zoom(1.1, point, centerCoords = "pixel", 
keepPointInPlace=True)
+
+
 class GUIMouse(GUIBase):
     """
 
@@ -166,11 +176,16 @@
 
     def OnMove(self, event):
         ## The Move event always gets raised, even if there is a hit-test
+        EventType = FloatCanvas.EVT_FC_MOTION
+        # process the object hit test for EVT_MOTION bindings
+        self.Canvas.HitTest(event, EventType)
+        # process enter and leave events
         self.Canvas.MouseOverTest(event)
-        self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+        # then raise the event on the canvas
+        self.Canvas._RaiseMouseEvent(event, EventType)
 
 
-class GUIMove(GUIBase):
+class GUIMove(ZoomWithMouseWheel, GUIBase):
     """
     Mode that moves the image (pans).
     It doesn't change any coordinates, it only changes what the viewport is
@@ -278,16 +293,7 @@
         dc.EndDrawing()
         #self.Canvas.Update()
 
-    def OnWheel(self, event):
-        """
-           By default, zoom in/out by a 0.1 factor per Wheel event.
-        """
-        if event.GetWheelRotation() < 0:
-            self.Canvas.Zoom(0.9)
-        else:
-            self.Canvas.Zoom(1.1)
-
-class GUIZoomIn(GUIBase):
+class GUIZoomIn(ZoomWithMouseWheel, GUIBase):
  
     def __init__(self, canvas=None):
         GUIBase.__init__(self, canvas)
@@ -353,13 +359,8 @@
     def OnRightDown(self, event):
         self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
 
-    def OnWheel(self, event):
-        if event.GetWheelRotation() < 0:
-            self.Canvas.Zoom(0.9)
-        else:
-            self.Canvas.Zoom(1.1)
 
-class GUIZoomOut(GUIBase):
+class GUIZoomOut(ZoomWithMouseWheel, GUIBase):
 
     def __init__(self, Canvas=None):
         GUIBase.__init__(self, Canvas)
@@ -370,12 +371,6 @@
 
     def OnRightDown(self, event):
         self.Canvas.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
-
-    def OnWheel(self, event):
-        if event.GetWheelRotation() < 0:
-            self.Canvas.Zoom(0.9)
-        else:
-            self.Canvas.Zoom(1.1)
 
     def OnMove(self, event):
         # Always raise the Move event.
--- D:/devTools/Phoenix/wx/lib/floatcanvas/FloatCanvas.py       Sat Sep 19 
10:52:55 2015
+++ D:/devTools/wxPython/trunk/wx/lib/floatcanvas/FloatCanvas.py        Sat Sep 
19 12:19:46 2015
@@ -96,7 +96,7 @@
 
 ## fixme: This should probably be re-factored into a class
 _testBitmap = None
-_testDC = None
+
 def _cycleidxs(indexcount, maxvalue, step):
 
     """
@@ -106,24 +106,18 @@
         """Return True if the color comes back from the bitmap identically."""
         if len(color) < 3:
             return True
-        global _testBitmap, _testDC
-        B = _testBitmap
-        if not mac:
-            dc = _testDC
-        if not B:
-            B = _testBitmap = wx.EmptyBitmap(1, 1)
-            if not mac:
-                dc = _testDC = wx.MemoryDC()
-        if mac:
-            dc = wx.MemoryDC()
-        dc.SelectObject(B)
+        global _testBitmap
+        dc = wx.MemoryDC()
+        if not _testBitmap:
+            _testBitmap = wx.EmptyBitmap(1, 1)
+        dc.SelectObject(_testBitmap)
         dc.SetBackground(wx.BLACK_BRUSH)
         dc.Clear()
         dc.SetPen(wx.Pen(wx.Colour(*color), 4))
         dc.DrawPoint(0,0)
-        if mac:
-            del dc
-            pdata = wx.AlphaPixelData(B)
+        if mac: # NOTE: can the Mac not jsut use the DC?
+            del dc # Mac can't work with bitmap when selected into a DC.
+            pdata = wx.AlphaPixelData(_testBitmap)
             pacc = pdata.GetPixels()
             pacc.MoveTo(pdata, 0, 0)
             outcolor = pacc.Get()[:3]
@@ -140,6 +134,7 @@
                 if not colormatch(color):
                     continue
                 yield color
+
 def _colorGenerator():
 
     """
@@ -148,14 +143,14 @@
     """
     return _cycleidxs(indexcount=3, maxvalue=256, step=1)
 
-class DrawObject:
+class DrawObject(object):
     """
     This is the base class for all the objects that can be drawn.
 
-    One must subclass from this (and an assortment of Mixins) to create
+    One must subclass from this (and an assortment of mixins) to create
     a new DrawObject.
 
-      \note This class contain a series of static dictionaries:
+      note: This class contain a series of static dictionaries:
 
       * BrushList
       * PenList
@@ -167,12 +162,14 @@
     """
 
     def __init__(self, InForeground  = False, IsVisible = True):
-        """! \param InForeground (bool)
-             \param IsVisible (Bool)
+        """
+        :param InForeground=False: whether you want the object in the 
foreground
+        :param param IsVisible = True: whther the object is visible
         """
         self.InForeground = InForeground
 
         self._Canvas = None
+        #self._Actual_Canvas = None
 
         self.HitColor = None
         self.CallBackFuncs = {}
@@ -195,6 +192,13 @@
     # interface, and perhaps speed things up by caching all the Pens
     # and Brushes, although that may not help, as I think wx now
     # does that on it's own. Send me a note if you know!
+    ## I'm caching fonts, because on GTK, getting a new font can take a
+    ## while. However, it gets cleared after every full draw as hanging
+    ## on to a bunch of large fonts takes a massive amount of memory.
+
+    ## a dict to cache Font Objects while drawing -- for those that need fonts
+    FontList = {}
+
 
     BrushList = {
             ( None,"Transparent")  : wx.TRANSPARENT_BRUSH,
@@ -240,6 +244,20 @@
             "DotDash"    : wx.DOT_DASH,
             }
 
+    # # made a property so sub-classes c
+    # @property
+    # def _Canvas(self):
+    #     """
+    #     getter for the _Canvas property
+    #     """
+    #     return self._Actual_Canvas
+    # @_Canvas.setter
+    # def _Canvas(self, canvas):
+    #     """
+    #     setter for Canvas property
+    #     """
+    #     self._Actual_Canvas = canvas
+
     def Bind(self, Event, CallBackFun):
         ##fixme: Way too much Canvas Manipulation here!
         self.CallBackFuncs[Event] = CallBackFun
@@ -275,12 +293,14 @@
 
     def SetBrush(self, FillColor, FillStyle):
         if FillColor is None or FillStyle is None:
-            self.Brush = wx.TRANSPARENT_BRUSH
+            #self.Brush = wx.TRANSPARENT_BRUSH
+            ## wx.TRANSPARENT_BRUSH seems to have a bug in Phoenix...
+            self.Brush = wx.Brush(wx.Colour(0,0,0),wx.TRANSPARENT)
             ##fixme: should I really re-set the style?
             self.FillStyle = "Transparent"
         else:
             self.Brush = self.BrushList.setdefault( (FillColor,FillStyle),  
wx.Brush(FillColor,self.FillStyleList[FillStyle] ) )
-            #print "Setting Brush, BrushList length:", len(self.BrushList)
+
     def SetPen(self,LineColor,LineStyle,LineWidth):
         if (LineColor is None) or (LineStyle is None):
             self.Pen = wx.TRANSPARENT_PEN
@@ -330,12 +350,14 @@
             self.InForeground = True
 
     def Hide(self):
-        """! \brief Make an object hidden.
+        """
+        Make an object hidden (not drawn)
         """
         self.Visible = False
 
     def Show(self):
-        """! \brief Make an object visible on the canvas.
+        """
+        Make an object visible on the canvas.
         """
         self.Visible = True
 
@@ -350,13 +372,38 @@
     """
 
     def __init__(self, ObjectList=[], InForeground  = False, IsVisible = True):
-        self.ObjectList = list(ObjectList)
+        self.ObjectList = []
+
         DrawObject.__init__(self, InForeground, IsVisible)
+
+        # this one uses a proprty for _Canvas...
+        self._Actual_Canvas = None
+
+        for obj in ObjectList:
+            self.AddObject(obj, calcBB = False)
         self.CalcBoundingBox()
 
-    def AddObject(self, obj):
+    ## re-define _Canvas property so that the sub-objects get set up right
+    @property
+    def _Canvas(self):
+        """
+        getter for the _Canvas property
+        """
+        return self._Actual_Canvas
+    @_Canvas.setter
+    def _Canvas(self, canvas):
+        """
+        setter for Canvas property
+        """
+        self._Actual_Canvas = canvas
+        for obj in self.ObjectList:
+            obj._Canvas = canvas
+
+    def AddObject(self, obj, calcBB=True):
         self.ObjectList.append(obj)
-        self.BoundingBox.Merge(obj.BoundingBox)
+        obj._Canvas = self._Canvas
+        if calcBB:
+            self.BoundingBox.Merge(obj.BoundingBox)
 
     def AddObjects(self, Objects):
         for o in Objects:
@@ -411,15 +458,24 @@
                 self._Canvas.HitColorGenerator.next() # first call to prevent 
the background color from being used.
             # Set all contained objects to the same Hit color:
             self.HitColor = self._Canvas.HitColorGenerator.next()
-        for obj in self.ObjectList:
-            obj.SetHitPen(self.HitColor, self.HitLineWidth)
-            obj.SetHitBrush(self.HitColor)
-            obj.HitAble = True
+        # for obj in self.ObjectList:
+        #     obj.SetHitPen(self.HitColor, self.HitLineWidth)
+        #     obj.SetHitBrush(self.HitColor)
+        #     obj.HitAble = True
         # put the object in the hit dict, indexed by it's color
+        self._ChangeChildrenHitColor(self.ObjectList)
         if not self._Canvas.HitDict:
             self._Canvas.MakeHitDict()
         self._Canvas.HitDict[Event][self.HitColor] = (self)
 
+    def _ChangeChildrenHitColor(self, objlist):
+        for obj in objlist:
+            obj.SetHitPen(self.HitColor, self.HitLineWidth)
+            obj.SetHitBrush(self.HitColor)
+            obj.HitAble = True
+            
+            if isinstance(obj, Group):
+                self._ChangeChildrenHitColor(obj.ObjectList)
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None):
         for obj in self.ObjectList:
@@ -1170,12 +1226,6 @@
 
     """
 
-    ## I'm caching fonts, because on GTK, getting a new font can take a
-    ## while. However, it gets cleared after every full draw as hanging
-    ## on to a bunch of large fonts takes a massive amount of memory.
-
-    FontList = {}
-
     LayoutFontSize = 16 # font size used for calculating layout
 
     def SetFont(self, Size, Family, Style, Weight, Underlined, FaceName):
@@ -1346,10 +1396,10 @@
     charactor string, indicating where in relation to the coordinates
     the string should be oriented.
 
-    The first letter is: t, c, or b, for top, center and bottom The
-    second letter is: l, c, or r, for left, center and right The
-    position refers to the position relative to the text itself. It
-    defaults to "tl" (top left).
+    The first letter is: t, c, or b, for top, center and bottom.
+    The second letter is: l, c, or r, for left, center and right.
+    The position refers to the position relative to the text itself.
+    It defaults to "tl" (top left).
 
     Size is the size of the font in world coordinates.
 
@@ -1357,7 +1407,7 @@
       specifying actual facename. One of:
       
             * wx.DEFAULT:  Chooses a default font.
-            * wx.DECORATI: A decorative font.
+            * wx.DECORATIVE: A decorative font.
             * wx.ROMAN: A formal, serif font.
             * wx.SCRIPT: A handwriting font.
             * wx.SWISS: A sans-serif font.
@@ -1367,7 +1417,7 @@
       
     * Style: One of wx.NORMAL, wx.SLANT and wx.ITALIC.
     * Weight: One of wx.NORMAL, wx.LIGHT and wx.BOLD.
-    * Underlined: The value can be True or False. At present this may have an 
an
+    * Underlined: The value can be True or False. At present this may have an
       effect on Windows only.
 
 
@@ -1379,8 +1429,8 @@
 
     Bugs/Limitations:
 
-    As fonts are scaled, the do end up a little different, so you don't
-    get exactly the same picture as you scale up and doen, but it's
+    As fonts are scaled, they do end up a little different, so you don't
+    get exactly the same picture as you scale up and down, but it's
     pretty darn close.
 
     On wxGTK1 on my Linux system, at least, using a font of over about
@@ -1589,7 +1639,6 @@
                  Font = None,
                  LineSpacing = 1.0,
                  InForeground = False):
-
         DrawObject.__init__(self,InForeground)
 
         self.XY = N.array(Point, N.float)
@@ -1787,7 +1836,6 @@
         ## If so, limit it. Would it be better just to not draw it?
         ## note that this limit is dependent on how much memory you have, etc.
         Size = min(Size, self.MaxFontSize)
-        
         Size = max(Size, self.MinFontSize) # smallest size you want - default 
to 1
 
         # Draw The Box
@@ -1802,7 +1850,10 @@
             dc.SetFont(self.Font)
             dc.SetTextForeground(self.Color)
             dc.SetBackgroundMode(wx.TRANSPARENT)
-            dc.DrawTextList(self.Words, Points)
+            ## NOTE: DrawTextList seems to have a memory leak if you call it 
with a numpy array.
+            #        This has probably been fixed in the wxPython source (as 
of 9/4/2013),
+            #        but for older versions it's this way for now.
+            dc.DrawTextList(self.Words, Points.tolist())
 
         # Draw the hit box.
         if HTdc and self.HitAble:
@@ -1844,6 +1895,10 @@
         (self.Width, self.Height) = self.Bitmap.GetWidth(), 
self.Bitmap.GetHeight()
         self.ShiftFun = self.ShiftFunDict[Position]
 
+        # no need for a line width with bitmaps
+        self.MinHitLineWidth = 0
+        self.HitLineWidth = 0
+
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         XY = WorldToPixel(self.XY)
         XY = self.ShiftFun(XY[0], XY[1], self.Width, self.Height)
@@ -1877,7 +1932,8 @@
                  XY,
                  Height,
                  Position = 'tl',
-                 InForeground = False):
+                 InForeground = False,
+                 Quality='normal'):
 
         DrawObject.__init__(self,InForeground)
 
@@ -1894,6 +1950,31 @@
         self.CalcBoundingBox()
         self.ScaledBitmap = None
         self.ScaledHeight = None
+        self.Quality = Quality
+
+        # no need for a line width with bitmaps
+        self.MinHitLineWidth = 0
+        self.HitLineWidth = 0
+
+
+    @property 
+    def Quality(self):
+        if self._scale_quality == wx.IMAGE_QUALITY_NORMAL:
+            return 'normal'
+        elif self._scale_quality == wx.IMAGE_QUALITY_HIGH:
+            return 'high'
+        else:
+            raise ValueError('the _scale_quality attribute should only be set 
to wx.IMAGE_QUALITY_NORMAL or wx.IMAGE_QUALITY_HIGH')
+
+    @Quality.setter
+    def Quality(self, qual):
+        if qual.lower() == 'normal':
+            self._scale_quality = wx.IMAGE_QUALITY_NORMAL
+        elif qual.lower() == 'high':
+            self._scale_quality = wx.IMAGE_QUALITY_HIGH
+        else:
+            raise ValueError('the Quality property can only be set to "normal" 
or "high"')
+
 
     def CalcBoundingBox(self):
         ## this isn't exact, as fonts don't scale exactly.
@@ -1904,19 +1985,22 @@
 
     def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
         XY = WorldToPixel(self.XY)
-        H = ScaleWorldToPixel(self.Height)[0]
-        W = H * (self.bmpWidth / self.bmpHeight)
-        if (self.ScaledBitmap is None) or (H <> self.ScaledHeight) :
-            self.ScaledHeight = H
-            Img = self.Image.Scale(W, H)
-            self.ScaledBitmap = wx.BitmapFromImage(Img)
+        H = int(round(ScaleWorldToPixel(self.Height)[0]))
+        W = int(round(H * (self.bmpWidth / self.bmpHeight)))
+        if W == 0 or H == 0: # nothign to draw
+            return
+        else:
+            if (self.ScaledBitmap is None) or (H <> self.ScaledHeight) :
+                self.ScaledHeight = H
+                Img = self.Image.Scale(W, H, quality=self._scale_quality)
+                self.ScaledBitmap = wx.BitmapFromImage(Img)
 
-        XY = self.ShiftFun(XY[0], XY[1], W, H)
-        dc.DrawBitmapPoint(self.ScaledBitmap, XY, True)
-        if HTdc and self.HitAble:
-            HTdc.SetPen(self.HitPen)
-            HTdc.SetBrush(self.HitBrush)
-            HTdc.DrawRectanglePointSize(XY, (W, H) )
+            XY = self.ShiftFun(XY[0], XY[1], W, H)
+            dc.DrawBitmapPoint(self.ScaledBitmap, XY, True)
+            if HTdc and self.HitAble:
+                HTdc.SetPen(self.HitPen)
+                HTdc.SetBrush(self.HitBrush)
+                HTdc.DrawRectanglePointSize(XY, (W, H) )
 
 class ScaledBitmap2(TextObjectMixin, DrawObject, ):
     """
@@ -1932,7 +2016,8 @@
                  Height,
                  Width=None,
                  Position = 'tl',
-                 InForeground = False):
+                 InForeground = False,
+                 Quality='normal'):
 
         DrawObject.__init__(self,InForeground)
 
@@ -1953,12 +2038,32 @@
         ##fixme: should this have a y = -1 to shift to y-up?
         self.BmpScale = self.bmpWH / self.WH
 
-        #print "bmpWH:", self.bmpWH
-        #print "Width, Height:", self.WH
-        #print "self.BmpScale", self.BmpScale
         self.ShiftFun = self.ShiftFunDict[Position]
         self.CalcBoundingBox()
         self.ScaledBitmap = None # cache of the last existing scaled bitmap
+        self.Quality = Quality
+
+        # no need for aline width with images.
+        self.MinHitLineWidth = 0
+        self.HitLineWidth = 0
+
+    @property 
+    def Quality(self):
+        if self._scale_quality == wx.IMAGE_QUALITY_NORMAL:
+            return 'normal'
+        elif self._scale_quality == wx.IMAGE_QUALITY_HIGH:
+            return 'high'
+        else:
+            raise ValueError('the _scale_quality attribute should only be set 
to wx.IMAGE_QUALITY_NORMAL or wx.IMAGE_QUALITY_HIGH')
+
+    @Quality.setter
+    def Quality(self, qual):
+        if qual.lower() == 'normal':
+            self._scale_quality = wx.IMAGE_QUALITY_NORMAL
+        elif qual.lower() == 'high':
+            self._scale_quality = wx.IMAGE_QUALITY_HIGH
+        else:
+            raise ValueError('the Quality property can only be set to "normal" 
or "high"')
 
     def CalcBoundingBox(self):
         ## this isn't exact, as fonts don't scale exactly.
@@ -1972,7 +2077,7 @@
         """
         delta = Pw - self.XY
         Pb = delta * self.BmpScale
-        Pb *= (1, -1) ##fixme: this may only works for Yup projection!
+        Pb *= (1, -1) ##fixme: this may only works for Y-up projection!
                       ##       and may only work for top left position
 
         return Pb.astype(N.int_)
@@ -1985,24 +2090,25 @@
 
         """
         XY = WorldToPixel(self.XY)
-        H = ScaleWorldToPixel(self.Height)[0]
-        W = H * (self.bmpWidth / self.bmpHeight)
-        if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (0, 0, 
self.bmpWidth, self.bmpHeight, W, H) ):
-        #if True: #fixme: (self.ScaledBitmap is None) or (H <> 
self.ScaledHeight) :
-            self.ScaledHeight = H
-            #print "Scaling to:", W, H
-            Img = self.Image.Scale(W, H)
-            bmp = wx.BitmapFromImage(Img)
-            self.ScaledBitmap = ((0, 0, self.bmpWidth, self.bmpHeight , W, H), 
bmp)# this defines the cached bitmap
+        H = int(round(ScaleWorldToPixel(self.Height)[0]))
+        W = int(round(H * (self.bmpWidth / self.bmpHeight)))
+        if W == 0 or H == 0: # nothing to draw
+            return
         else:
-            #print "Using Cached bitmap"
-            bmp = self.ScaledBitmap[1]
-        XY = self.ShiftFun(XY[0], XY[1], W, H)
-        dc.DrawBitmapPoint(bmp, XY, True)
-        if HTdc and self.HitAble:
-            HTdc.SetPen(self.HitPen)
-            HTdc.SetBrush(self.HitBrush)
-            HTdc.DrawRectanglePointSize(XY, (W, H) )
+            if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (0, 0, 
self.bmpWidth, self.bmpHeight, W, H) ):
+            #if True: #fixme: (self.ScaledBitmap is None) or (H <> 
self.ScaledHeight) :
+                self.ScaledHeight = H
+                Img = self.Image.Scale(W, H, quality=self._scale_quality)
+                bmp = wx.BitmapFromImage(Img)
+                self.ScaledBitmap = ((0, 0, self.bmpWidth, self.bmpHeight , W, 
H), bmp)# this defines the cached bitmap
+            else:
+                bmp = self.ScaledBitmap[1]
+            XY = self.ShiftFun(XY[0], XY[1], W, H)
+            dc.DrawBitmapPoint(bmp, XY, True)
+            if HTdc and self.HitAble:
+                HTdc.SetPen(self.HitPen)
+                HTdc.SetBrush(self.HitBrush)
+                HTdc.DrawRectanglePointSize(XY, (W, H) )
 
     def _DrawSubBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
         """
@@ -2057,14 +2163,12 @@
         Hs = int(scale * Hb + 0.5)
         if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (Xb, Yb, 
Wb, Hb, Ws, Ws) ):
             Img = self.Image.GetSubImage(wx.Rect(Xb, Yb, Wb, Hb))
-            print "rescaling with High quality"
-            Img.Rescale(Ws, Hs, quality=wx.IMAGE_QUALITY_HIGH)
+            Img.Rescale(Ws, Hs, quality=self._scale_quality)
             bmp = wx.BitmapFromImage(Img)
             self.ScaledBitmap = ((Xb, Yb, Wb, Hb, Ws, Ws), bmp)# this defines 
the cached bitmap
             #XY = self.ShiftFun(XY[0], XY[1], W, H)
             #fixme: get the shiftfun working!
         else:
-            #print "Using cached bitmap"
             ##fixme: The cached bitmap could be used if the one needed is the 
same scale, but
             ##       a subset of the cached one.
             bmp = self.ScaledBitmap[1]
@@ -2079,16 +2183,14 @@
         BBworld = BBox.asBBox(self._Canvas.ViewPortBB)
         ## first see if entire bitmap is displayed:
         if  BBworld.Inside(self.BoundingBox):
-            #print "Drawing entire bitmap with old code"
+            # use od code to draw entire bitmap
             self._DrawEntireBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
-            return None
+            return
         elif BBworld.Overlaps(self.BoundingBox):
-            #BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld))
-            #print "Drawing a sub-bitmap"
             self._DrawSubBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
         else:
-            #print "Not Drawing -- no part of image is showing"
-            pass
+            #Not Drawing -- no part of image is showing
+            return
 
 class DotGrid:
     """
@@ -2232,7 +2334,7 @@
         self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
 
         self.SetPen(LineColor, LineStyle, LineWidth)
-        self.SetBrush(FillColor, FillStyle)                  #Why isn't this 
working ???
+        self.SetBrush(FillColor, FillStyle)
 
     def Move(self, Delta ):
         """
@@ -2457,7 +2559,7 @@
         else:
             raise FloatCanvasError('Projectionfun must be either:'
                                    ' "FlatEarth", None, or a callable object '
-                                   '(function, for instance) that takes the '
+                                   '(function or callable object) that takes 
the '
                                    'ViewPortCenter and returns a 
MapProjectionVector')
 
     def FlatEarthProjection(self, CenterPoint):
@@ -2491,6 +2593,7 @@
                         EVT_FC_RIGHT_DCLICK: {},
                         EVT_FC_ENTER_OBJECT: {},
                         EVT_FC_LEAVE_OBJECT: {},
+                        EVT_FC_MOTION: {},
                         }
 
     def _RaiseMouseEvent(self, Event, EventType):
@@ -2499,11 +2602,11 @@
         """
         pt = self.PixelToWorld( Event.GetPosition() )
         evt = _MouseEvent(EventType, Event, self.GetId(), pt)
+        # called the Windows usual event handler
         self.GetEventHandler().ProcessEvent(evt)
 
     if wx.__version__ >= "2.8":
         HitTestBitmapDepth = 32
-        #print "Using hit test code for 2.8"
         def GetHitTestColor(self, xy):
             if self._ForegroundHTBitmap:
                 pdata = wx.AlphaPixelData(self._ForegroundHTBitmap)
@@ -2540,15 +2643,28 @@
         Object.CallBackFuncs[HitEvent](Object)
 
     def HitTest(self, event, HitEvent):
+        """
+        Does a hit test on objects that are "hit-able"
+
+        If an object is hit, its event handler is called,
+        and this method returns True
+
+        If no object is hit, this method returns False.
+
+        If the event is outside the Window, no object will be considered hit
+        """
         if self.HitDict:
             # check if there are any objects in the dict for this event
             if self.HitDict[ HitEvent ]:
                 xy = event.GetPosition()
-                color = self.GetHitTestColor( xy )
-                if color in self.HitDict[ HitEvent ]:
-                    Object = self.HitDict[ HitEvent ][color]
-                    self._CallHitCallback(Object, xy, HitEvent)
-                    return True
+                winsize = self.Size
+                if not (xy[0] < 0 or xy[1] < 0 or xy[0] > winsize[0] or xy[1] 
> winsize[1]):
+                    # The mouse event is in the Window
+                    color = self.GetHitTestColor( xy )
+                    if color in self.HitDict[ HitEvent ]:
+                        Object = self.HitDict[ HitEvent ][color]
+                        self._CallHitCallback(Object, xy, HitEvent)
+                        return True
             return False
 
 
@@ -2718,7 +2834,8 @@
 
     def OnSize(self, event=None):
         self.InitializePanel()
-        self.SizeTimer.Start(50, oneShot=True)
+        #self.SizeTimer.Start(50, oneShot=True)
+        self.OnSizeTimer()
 
     def OnSizeTimer(self, event=None):
         self.MakeNewBuffers()
@@ -2753,7 +2870,7 @@
         Note that the buffer will not be re-drawn unless something has
         changed. If you change a DrawObject directly, then the canvas
         will not know anything has changed. In this case, you can force
-        a re-draw by passing int True for the Force flag:
+        a re-draw by passing in True for the Force flag:
 
         Canvas.Draw(Force=True)
 
@@ -2762,7 +2879,7 @@
 
         If there are any objects in self._ForeDrawList, then the
         background gets drawn to a new buffer, and the foreground
-        objects get drawn on top of it. The final result if blitted to
+        objects get drawn on top of it. The final result is blitted to
         the screen, and stored for future Paint events.  This is done so
         that you can have a complicated background, but have something
         changing on the foreground, without having to wait for the
@@ -2840,6 +2957,8 @@
         ## starts to take up Massive amounts of memory This is mostly a
         ## problem with very large fonts, that you get with scaled text
         ## when zoomed in.
+        ## fixme -- this should probably be in the FLoatCanvas,
+        ##          rather than DrawObject, but it works here.
         DrawObject.FontList = {}
 
     def _ShouldRedraw(DrawList, ViewPortBB): 
@@ -2872,11 +2991,12 @@
 
         """
         shift = N.asarray(shift,N.float)
-        if CoordType == 'Panel':# convert from panel coordinates
+        CoordType = CoordType.lower()
+        if CoordType == 'panel':# convert from panel coordinates
             shift = shift * N.array((-1,1),N.float) 
*self.PanelSize/self.TransformVector
-        elif CoordType == 'Pixel': # convert from pixel coordinates
+        elif CoordType == 'pixel': # convert from pixel coordinates
             shift = shift/self.TransformVector
-        elif CoordType == 'World': # No conversion
+        elif CoordType == 'world': # No conversion
             pass
         else:
             raise FloatCanvasError('CoordType must be either "Panel", "Pixel", 
or "World"')
@@ -2888,36 +3008,68 @@
         if ReDraw:
             self.Draw()
 
-    def Zoom(self, factor, center = None, centerCoords="world"):
+    def Zoom(self, factor, center = None, centerCoords="world", 
keepPointInPlace=False):
 
         """
         Zoom(factor, center) changes the amount of zoom of the image by factor.
         If factor is greater than one, the image gets larger.
         If factor is less than one, the image gets smaller.
+        :param factor: amount to zoom in or out If factor is greater than one,
+                       the image gets larger. If factor is less than one, the
+                       image gets smaller.
+        :param center: a tuple of (x,y) coordinates of the center of the 
viewport,
+                       after zooming. If center is not given, the center will 
stay the same.
+
+        :param centerCoords: flag indicating whether the center given is in 
pixel or world 
+                             coords. Options are: "world" or "pixel"
+        :param keepPointInPlace: boolean flag. If False, the center point is 
what's given.
+                                 If True, the image is shifted so that the 
given center point
+                                 is kept in the same pixel space. This 
facilitates keeping the 
+                                 same point under the mouse when zooming with 
the scroll wheel.
+        """
+        if center is None:
+            center = self.ViewPortCenter
+            centerCoords = 'world' #override input if they don't give a center 
point.
 
-        center is a tuple of (x,y) coordinates of the center of the viewport, 
after zooming.
-        If center is not given, the center will stay the same.
+        if centerCoords == "pixel":
+            oldpoint = self.PixelToWorld( center )
+        else:
+            oldpoint = N.array(center, N.float)
 
-        centerCoords is a flag indicating whether the center given is in pixel 
or world 
-        coords. Options are: "world" or "pixel"
-        
-        """
         self.Scale = self.Scale*factor
-        if not center is None:
+        if keepPointInPlace:
+            self.SetToNewScale(False)
+    
             if centerCoords == "pixel":
-                center = self.PixelToWorld( center )
+                newpoint = self.PixelToWorld( center )
             else:
-                center = N.array(center,N.float)
-            self.ViewPortCenter = center
-        self.SetToNewScale()
+                newpoint = N.array(center, N.float)
+            delta = (newpoint - oldpoint)
+            self.MoveImage(-delta, 'world')       
+        else:
+            self.ViewPortCenter = oldpoint
+            self.SetToNewScale()         
 
-    def ZoomToBB(self, NewBB=None, DrawFlag=True):
+    def ZoomToBB(self, NewBB=None, DrawFlag=True, margin_adjust=0.95):
 
         """
 
         Zooms the image to the bounding box given, or to the bounding
         box of all the objects on the canvas, if none is given.
 
+        :param NewBB=None: the bounding box you want to zoom in to.
+                           If None, it will be calcuated from all the
+                           objects on the Canvas.
+        :type NewBB: floatcanvas.utilities.BBox.BBox object (or something 
compatible).
+
+        :param DrawFlag=True: If True, will force a re-draw, regardless of 
whether
+                              anythign has changed on the Canvas
+
+        :param margin_adjust=0.95: amount to adjust the scale so the BB will 
have a
+                                   bit of margin in the Canvas. 1.0 shoud be a 
tight fit.
+
+        :type margin_adjust: float
+
         """
         if NewBB is not None:
             BoundingBox = NewBB
@@ -2933,13 +3085,13 @@
             BoundingBox = BoundingBox*self.MapProjectionVector # this does 
need to make a copy!
             try:
                 self.Scale = min(abs(self.PanelSize[0] / 
(BoundingBox[1,0]-BoundingBox[0,0])),
-                                 abs(self.PanelSize[1] / 
(BoundingBox[1,1]-BoundingBox[0,1])) )*0.95
+                                 abs(self.PanelSize[1] / 
(BoundingBox[1,1]-BoundingBox[0,1])) )*margin_adjust
             except ZeroDivisionError: # this will happen if the BB has zero 
width or height
                 try: #width == 0
-                    self.Scale = (self.PanelSize[0]  / 
(BoundingBox[1,0]-BoundingBox[0,0]))*0.95
+                    self.Scale = (self.PanelSize[0]  / 
(BoundingBox[1,0]-BoundingBox[0,0]))*margin_adjust
                 except ZeroDivisionError:
                     try: # height == 0
-                        self.Scale = (self.PanelSize[1]  / 
(BoundingBox[1,1]-BoundingBox[0,1]))*0.95
+                        self.Scale = (self.PanelSize[1]  / 
(BoundingBox[1,1]-BoundingBox[0,1]))*margin_adjust
                     except ZeroDivisionError: #zero size! (must be a single 
point)
                         self.Scale = 1
 
@@ -3126,7 +3278,7 @@
 def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module 
__init__
     classnames = ["Circle", "Ellipse", "Arc", "Rectangle", "ScaledText", 
"Polygon",
                   "Line", "Text", "PointSet","Point", "Arrow", "ArrowLine", 
"ScaledTextBox",
-                  "SquarePoint","Bitmap", "ScaledBitmap", "Spline", "Group"]
+                  "SquarePoint","Bitmap", "ScaledBitmap", "ScaledBitmap2", 
"Spline", "Group"]
     for classname in classnames:
         klass = globals()[classname]
         def getaddshapemethod(klass=klass):
_______________________________________________
FloatCanvas mailing list
FloatCanvas@paulmcnett.com
http://mailman.paulmcnett.com/cgi-bin/mailman/listinfo/floatcanvas

Reply via email to