]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/lib/plot.py
wxSscanf() and friends are now Unicode+ANSI friendly wrappers instead of defines...
[wxWidgets.git] / wxPython / wx / lib / plot.py
index 2a5be751207b3d98a4d306000d5593fe0abe30da..96827b67c74c8efe2feced5bdcfa37b0fd3b9490 100644 (file)
@@ -94,7 +94,7 @@ import  wx
 
 # Needs Numeric or numarray or NumPy
 try:
 
 # Needs Numeric or numarray or NumPy
 try:
-    import numpy as _Numeric
+    import numpy.oldnumeric as _Numeric
 except:
     try:
         import numarray as _Numeric  #if numarray is used it is renamed Numeric
 except:
     try:
         import numarray as _Numeric  #if numarray is used it is renamed Numeric
@@ -121,7 +121,8 @@ class PolyPoints:
     """
 
     def __init__(self, points, attr):
     """
 
     def __init__(self, points, attr):
-        self.points = _Numeric.array(points)
+        self._points = _Numeric.array(points).astype(_Numeric.Float64)
+        self._logscale = (False, False)
         self.currentScale= (1,1)
         self.currentShift= (0,0)
         self.scaled = self.points
         self.currentScale= (1,1)
         self.currentShift= (0,0)
         self.scaled = self.points
@@ -132,12 +133,34 @@ class PolyPoints:
                 raise KeyError, "Style attribute incorrect. Should be one of %s" % self._attributes.keys()
             self.attributes[name] = value
         
                 raise KeyError, "Style attribute incorrect. Should be one of %s" % self._attributes.keys()
             self.attributes[name] = value
         
+    def setLogScale(self, logscale):
+        self._logscale = logscale
+    
+    def __getattr__(self, name):
+        if name == 'points':
+            if len(self._points)>0:
+                data = _Numeric.array(self._points,copy=True)
+                if self._logscale[0]:
+                    data = self.log10(data, 0)
+                if self._logscale[1]:
+                    data = self.log10(data, 1)
+                return data
+            else:
+                return self._points
+        else:
+            raise AttributeError, name
+            
+    def log10(self, data, ind):
+        data = _Numeric.compress(data[:,ind]>0,data,0)
+        data[:,ind] = _Numeric.log10(data[:,ind])
+        return data
+
     def boundingBox(self):
         if len(self.points) == 0:
             # no curves to draw
             # defaults to (-1,-1) and (1,1) but axis can be set in Draw
     def boundingBox(self):
         if len(self.points) == 0:
             # no curves to draw
             # defaults to (-1,-1) and (1,1) but axis can be set in Draw
-            minXY= _Numeric.array([-1,-1])
-            maxXY= _Numeric.array([ 1, 1])
+            minXY= _Numeric.array([-1.0,-1.0])
+            maxXY= _Numeric.array([ 1.0, 1.0])
         else:
             minXY= _Numeric.minimum.reduce(self.points)
             maxXY= _Numeric.maximum.reduce(self.points)
         else:
             minXY= _Numeric.minimum.reduce(self.points)
             maxXY= _Numeric.maximum.reduce(self.points)
@@ -353,6 +376,14 @@ class PlotGraphics:
         self.xLabel= xLabel
         self.yLabel= yLabel
 
         self.xLabel= xLabel
         self.yLabel= yLabel
 
+    def setLogScale(self, logscale):
+        if type(logscale) != tuple:
+            raise TypeError, 'logscale must be a tuple of bools, e.g. (False, False)'
+        if len(self.objects) == 0:
+            return
+        for o in self.objects:
+            o.setLogScale(logscale)
+        
     def boundingBox(self):
         p1, p2 = self.objects[0].boundingBox()
         for o in self.objects[1:]:
     def boundingBox(self):
         p1, p2 = self.objects[0].boundingBox()
         for o in self.objects[1:]:
@@ -431,15 +462,15 @@ class PlotCanvas(wx.Panel):
     plotting canvas (self.canvas). It allows for simple general plotting
     of data with zoom, labels, and automatic axis scaling."""
 
     plotting canvas (self.canvas). It allows for simple general plotting
     of data with zoom, labels, and automatic axis scaling."""
 
-    def __init__(self, parent):
+    def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, style=0, name="plotCanvas"):
         """Constructs a panel, which can be a child of a frame or
         any other non-control window"""
     
         """Constructs a panel, which can be a child of a frame or
         any other non-control window"""
     
-        wx.Panel.__init__(self, parent)
+        wx.Panel.__init__(self, parent, id, pos, size, style, name)
 
         sizer = wx.FlexGridSizer(2,2,0,0)
         self.canvas = wx.Window(self, -1)
 
         sizer = wx.FlexGridSizer(2,2,0,0)
         self.canvas = wx.Window(self, -1)
-        #self.canvas.SetMinSize((10,10))
         self.sb_vert = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL)
         self.sb_vert.SetScrollbar(0,1000,1000,1000)
         self.sb_hor = wx.ScrollBar(self, -1, style=wx.SB_HORIZONTAL)
         self.sb_vert = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL)
         self.sb_vert.SetScrollbar(0,1000,1000,1000)
         self.sb_hor = wx.ScrollBar(self, -1, style=wx.SB_HORIZONTAL)
@@ -449,17 +480,18 @@ class PlotCanvas(wx.Panel):
         sizer.Add(self.sb_vert, 0, wx.EXPAND)
         sizer.Add(self.sb_hor, 0, wx.EXPAND)
         sizer.Add((0,0))
         sizer.Add(self.sb_vert, 0, wx.EXPAND)
         sizer.Add(self.sb_hor, 0, wx.EXPAND)
         sizer.Add((0,0))
-        #corner = wx.Window(self)
-        #corner.SetMinSize((0,0))
-        #sizer.Add(corner, 0, wx.EXPAND)
         
         sizer.AddGrowableRow(0, 1)
         sizer.AddGrowableCol(0, 1)
         
         sizer.AddGrowableRow(0, 1)
         sizer.AddGrowableCol(0, 1)
+
         self.sb_vert.Show(False)
         self.sb_vert.Show(False)
-        self.sb_hor.Show(False)        
+        self.sb_hor.Show(False)
+        
         self.SetSizer(sizer)
         self.Fit()
 
         self.SetSizer(sizer)
         self.Fit()
 
+        self.border = (1,1)
+
         self.SetBackgroundColour("white")
         
         # Create some mouse events for zooming
         self.SetBackgroundColour("white")
         
         # Create some mouse events for zooming
@@ -504,6 +536,8 @@ class PlotCanvas(wx.Panel):
         self._dragEnabled = False
         self._screenCoordinates = _Numeric.array([0.0, 0.0])
         
         self._dragEnabled = False
         self._screenCoordinates = _Numeric.array([0.0, 0.0])
         
+        self._logscale = (False, False)
+
         # Zooming variables
         self._zoomInFactor =  0.5
         self._zoomOutFactor = 2
         # Zooming variables
         self._zoomInFactor =  0.5
         self._zoomOutFactor = 2
@@ -520,6 +554,7 @@ class PlotCanvas(wx.Panel):
         self._ySpec= 'auto'
         self._gridEnabled= False
         self._legendEnabled= False
         self._ySpec= 'auto'
         self._gridEnabled= False
         self._legendEnabled= False
+        self._titleEnabled= True
         
         # Fonts
         self._fontCache = {}
         
         # Fonts
         self._fontCache = {}
@@ -533,6 +568,8 @@ class PlotCanvas(wx.Panel):
         self._pointLabelFunc= None
         self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
 
         self._pointLabelFunc= None
         self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
 
+        self._useScientificNotation = False
+
         self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
         self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
         # OnSize called to make sure the buffer is initialized.
         self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
         self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
         # OnSize called to make sure the buffer is initialized.
@@ -660,6 +697,20 @@ class PlotCanvas(wx.Panel):
         frame.Centre(wx.BOTH)
         frame.Show(True)
 
         frame.Centre(wx.BOTH)
         frame.Show(True)
 
+    def setLogScale(self, logscale):
+        if type(logscale) != tuple:
+            raise TypeError, 'logscale must be a tuple of bools, e.g. (False, False)'
+        if self.last_draw is not None:
+            graphics, xAxis, yAxis= self.last_draw
+            graphics.setLogScale(logscale)
+            self.last_draw = (graphics, None, None)
+        self.SetXSpec('min')
+        self.SetYSpec('min')
+        self._logscale = logscale
+
+    def getLogScale(self):
+        return self._logscale
+    
     def SetFontSizeAxis(self, point= 10):
         """Set the tick and axis label font size (default is 10 point)"""
         self._fontSizeAxis= point
     def SetFontSizeAxis(self, point= 10):
         """Set the tick and axis label font size (default is 10 point)"""
         self._fontSizeAxis= point
@@ -698,6 +749,12 @@ class PlotCanvas(wx.Panel):
         """Set True to show scrollbars"""
         return self.sb_vert.IsShown()
 
         """Set True to show scrollbars"""
         return self.sb_vert.IsShown()
 
+    def SetUseScientificNotation(self, useScientificNotation):
+        self._useScientificNotation = useScientificNotation
+
+    def GetUseScientificNotation(self):
+        return self._useScientificNotation
+
     def SetEnableDrag(self, value):
         """Set True to enable drag."""
         if value not in [True,False]:
     def SetEnableDrag(self, value):
         """Set True to enable drag."""
         if value not in [True,False]:
@@ -751,6 +808,17 @@ class PlotCanvas(wx.Panel):
         """True if Legend enabled."""
         return self._legendEnabled
 
         """True if Legend enabled."""
         return self._legendEnabled
 
+    def SetEnableTitle(self, value):
+        """Set True to enable title."""
+        if value not in [True,False]:
+            raise TypeError, "Value should be True or False"
+        self._titleEnabled= value 
+        self.Redraw()
+
+    def GetEnableTitle(self):
+        """True if title enabled."""
+        return self._titleEnabled
+
     def SetEnablePointLabel(self, value):
         """Set True to enable pointLabel."""
         if value not in [True,False]:
     def SetEnablePointLabel(self, value):
         """Set True to enable pointLabel."""
         if value not in [True,False]:
@@ -777,7 +845,7 @@ class PlotCanvas(wx.Panel):
         """Unzoom the plot."""
         self.last_PointLabel = None        #reset pointLabel
         if self.last_draw is not None:
         """Unzoom the plot."""
         self.last_PointLabel = None        #reset pointLabel
         if self.last_draw is not None:
-            self.Draw(self.last_draw[0])
+            self._Draw(self.last_draw[0])
         
     def ScrollRight(self, units):          
         """Move view right number of axis units."""
         
     def ScrollRight(self, units):          
         """Move view right number of axis units."""
@@ -785,7 +853,7 @@ class PlotCanvas(wx.Panel):
         if self.last_draw is not None:
             graphics, xAxis, yAxis= self.last_draw
             xAxis= (xAxis[0]+units, xAxis[1]+units)
         if self.last_draw is not None:
             graphics, xAxis, yAxis= self.last_draw
             xAxis= (xAxis[0]+units, xAxis[1]+units)
-            self.Draw(graphics,xAxis,yAxis)
+            self._Draw(graphics,xAxis,yAxis)
 
     def ScrollUp(self, units):
         """Move view up number of axis units."""
 
     def ScrollUp(self, units):
         """Move view up number of axis units."""
@@ -793,10 +861,18 @@ class PlotCanvas(wx.Panel):
         if self.last_draw is not None:
              graphics, xAxis, yAxis= self.last_draw
              yAxis= (yAxis[0]+units, yAxis[1]+units)
         if self.last_draw is not None:
              graphics, xAxis, yAxis= self.last_draw
              yAxis= (yAxis[0]+units, yAxis[1]+units)
-             self.Draw(graphics,xAxis,yAxis)
-
+             self._Draw(graphics,xAxis,yAxis)
+
+    def GetXY(self, event):
+        """Wrapper around _getXY, which handles log scales"""
+        x,y = self._getXY(event)
+        if self.getLogScale()[0]:
+            x = _Numeric.power(10,x)
+        if self.getLogScale()[1]:
+            y = _Numeric.power(10,y)
+        return x,y
         
         
-    def GetXY(self,event):
+    def _getXY(self,event):
         """Takes a mouse event and returns the XY user axis values."""
         x,y= self.PositionScreenToUser(event.GetPosition())
         return x,y
         """Takes a mouse event and returns the XY user axis values."""
         x,y= self.PositionScreenToUser(event.GetPosition())
         return x,y
@@ -840,6 +916,12 @@ class PlotCanvas(wx.Panel):
         return self._ySpec
     
     def GetXMaxRange(self):
         return self._ySpec
     
     def GetXMaxRange(self):
+        xAxis = self._getXMaxRange()
+        if self.getLogScale()[0]:
+            xAxis = _Numeric.power(10,xAxis)
+        return xAxis
+
+    def _getXMaxRange(self):
         """Returns (minX, maxX) x-axis range for displayed graph"""
         graphics= self.last_draw[0]
         p1, p2 = graphics.boundingBox()     # min, max points of graphics
         """Returns (minX, maxX) x-axis range for displayed graph"""
         graphics= self.last_draw[0]
         p1, p2 = graphics.boundingBox()     # min, max points of graphics
@@ -847,6 +929,12 @@ class PlotCanvas(wx.Panel):
         return xAxis
 
     def GetYMaxRange(self):
         return xAxis
 
     def GetYMaxRange(self):
+        yAxis = self._getYMaxRange()
+        if self.getLogScale()[1]:
+            yAxis = _Numeric.power(10,yAxis)
+        return yAxis
+
+    def _getYMaxRange(self):
         """Returns (minY, maxY) y-axis range for displayed graph"""
         graphics= self.last_draw[0]
         p1, p2 = graphics.boundingBox()     # min, max points of graphics
         """Returns (minY, maxY) y-axis range for displayed graph"""
         graphics= self.last_draw[0]
         p1, p2 = graphics.boundingBox()     # min, max points of graphics
@@ -854,35 +942,59 @@ class PlotCanvas(wx.Panel):
         return yAxis
 
     def GetXCurrentRange(self):
         return yAxis
 
     def GetXCurrentRange(self):
+        xAxis = self._getXCurrentRange()
+        if self.getLogScale()[0]:
+            xAxis = _Numeric.power(10,xAxis)
+        return xAxis
+
+    def _getXCurrentRange(self):
         """Returns (minX, maxX) x-axis for currently displayed portion of graph"""
         return self.last_draw[1]
     
     def GetYCurrentRange(self):
         """Returns (minX, maxX) x-axis for currently displayed portion of graph"""
         return self.last_draw[1]
     
     def GetYCurrentRange(self):
+        yAxis = self._getYCurrentRange()
+        if self.getLogScale()[1]:
+            yAxis = _Numeric.power(10,yAxis)
+        return yAxis
+
+    def _getYCurrentRange(self):
         """Returns (minY, maxY) y-axis for currently displayed portion of graph"""
         return self.last_draw[2]
         """Returns (minY, maxY) y-axis for currently displayed portion of graph"""
         return self.last_draw[2]
-        
+
     def Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
     def Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
-        """Draw objects in graphics with specified x and y axis.
-        graphics- instance of PlotGraphics with list of PolyXXX objects
-        xAxis - tuple with (min, max) axis range to view
-        yAxis - same as xAxis
-        dc - drawing context - doesn't have to be specified.    
-        If it's not, the offscreen buffer is used
-        """
+        """Wrapper around _Draw, which handles log axes"""
+        
+        graphics.setLogScale(self.getLogScale())
+        
         # check Axis is either tuple or none
         if type(xAxis) not in [type(None),tuple]:
         # check Axis is either tuple or none
         if type(xAxis) not in [type(None),tuple]:
-            raise TypeError, "xAxis should be None or (minX,maxX)"
+            raise TypeError, "xAxis should be None or (minX,maxX)"+str(type(xAxis))
         if type(yAxis) not in [type(None),tuple]:
         if type(yAxis) not in [type(None),tuple]:
-            raise TypeError, "yAxis should be None or (minY,maxY)"
+            raise TypeError, "yAxis should be None or (minY,maxY)"+str(type(xAxis))
              
         # check case for axis = (a,b) where a==b caused by improper zooms
         if xAxis != None:
             if xAxis[0] == xAxis[1]:
                 return
              
         # check case for axis = (a,b) where a==b caused by improper zooms
         if xAxis != None:
             if xAxis[0] == xAxis[1]:
                 return
+            if self.getLogScale()[0]:
+                xAxis = _Numeric.log10(xAxis)
         if yAxis != None:
             if yAxis[0] == yAxis[1]:
                 return
         if yAxis != None:
             if yAxis[0] == yAxis[1]:
                 return
-            
+            if self.getLogScale()[1]:
+                yAxis = _Numeric.log10(yAxis)
+        self._Draw(graphics, xAxis, yAxis, dc)
+        
+    def _Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
+        """\
+        Draw objects in graphics with specified x and y axis.
+        graphics- instance of PlotGraphics with list of PolyXXX objects
+        xAxis - tuple with (min, max) axis range to view
+        yAxis - same as xAxis
+        dc - drawing context - doesn't have to be specified.    
+        If it's not, the offscreen buffer is used
+        """
+
         if dc == None:
             # sets new dc and clears it 
             dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
         if dc == None:
             # sets new dc and clears it 
             dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
@@ -910,21 +1022,24 @@ class PlotCanvas(wx.Panel):
             p1= _Numeric.array([xAxis[0], yAxis[0]])    # lower left corner user scale (xmin,ymin)
             p2= _Numeric.array([xAxis[1], yAxis[1]])     # upper right corner user scale (xmax,ymax)
 
             p1= _Numeric.array([xAxis[0], yAxis[0]])    # lower left corner user scale (xmin,ymin)
             p2= _Numeric.array([xAxis[1], yAxis[1]])     # upper right corner user scale (xmax,ymax)
 
-        self.last_draw = (graphics, xAxis, yAxis)       # saves most recient values
+        self.last_draw = (graphics, _Numeric.array(xAxis), _Numeric.array(yAxis))       # saves most recient values
 
         # Get ticks and textExtents for axis if required
         if self._xSpec is not 'none':        
 
         # Get ticks and textExtents for axis if required
         if self._xSpec is not 'none':        
-            xticks = self._ticks(xAxis[0], xAxis[1])
+            xticks = self._xticks(xAxis[0], xAxis[1])
             xTextExtent = dc.GetTextExtent(xticks[-1][1])# w h of x axis text last number on axis
         else:
             xticks = None
             xTextExtent= (0,0) # No text for ticks
         if self._ySpec is not 'none':
             xTextExtent = dc.GetTextExtent(xticks[-1][1])# w h of x axis text last number on axis
         else:
             xticks = None
             xTextExtent= (0,0) # No text for ticks
         if self._ySpec is not 'none':
-            yticks = self._ticks(yAxis[0], yAxis[1])
-            yTextExtentBottom= dc.GetTextExtent(yticks[0][1])
-            yTextExtentTop   = dc.GetTextExtent(yticks[-1][1])
-            yTextExtent= (max(yTextExtentBottom[0],yTextExtentTop[0]),
-                        max(yTextExtentBottom[1],yTextExtentTop[1]))
+            yticks = self._yticks(yAxis[0], yAxis[1])
+            if self.getLogScale()[1]:
+                yTextExtent = dc.GetTextExtent('-2e-2')
+            else:
+                yTextExtentBottom = dc.GetTextExtent(yticks[0][1])
+                yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
+                yTextExtent= (max(yTextExtentBottom[0],yTextExtentTop[0]),
+                              max(yTextExtentBottom[1],yTextExtentTop[1]))
         else:
             yticks = None
             yTextExtent= (0,0) # No text for ticks
         else:
             yticks = None
             yTextExtent= (0,0) # No text for ticks
@@ -943,11 +1058,14 @@ class PlotCanvas(wx.Panel):
         textSize_scale= _Numeric.array([rhsW+lhsW,bottomH+topH]) # make plot area smaller by text size
         textSize_shift= _Numeric.array([lhsW, bottomH])          # shift plot area by this amount
 
         textSize_scale= _Numeric.array([rhsW+lhsW,bottomH+topH]) # make plot area smaller by text size
         textSize_shift= _Numeric.array([lhsW, bottomH])          # shift plot area by this amount
 
-        # drawing title and labels text
-        dc.SetFont(self._getFont(self._fontSizeTitle))
-        titlePos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- titleWH[0]/2.,
-                 self.plotbox_origin[1]- self.plotbox_size[1])
-        dc.DrawText(graphics.getTitle(),titlePos[0],titlePos[1])
+        # draw title if requested
+        if self._titleEnabled:
+            dc.SetFont(self._getFont(self._fontSizeTitle))
+            titlePos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- titleWH[0]/2.,
+                       self.plotbox_origin[1]- self.plotbox_size[1])
+            dc.DrawText(graphics.getTitle(),titlePos[0],titlePos[1])
+
+        # draw label text
         dc.SetFont(self._getFont(self._fontSizeAxis))
         xLabelPos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- xLabelWH[0]/2.,
                  self.plotbox_origin[1]- xLabelWH[1])
         dc.SetFont(self._getFont(self._fontSizeAxis))
         xLabelPos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- xLabelWH[0]/2.,
                  self.plotbox_origin[1]- xLabelWH[1])
@@ -981,13 +1099,14 @@ class PlotCanvas(wx.Panel):
         # remove the clipping region
         dc.DestroyClippingRegion()
         dc.EndDrawing()
         # remove the clipping region
         dc.DestroyClippingRegion()
         dc.EndDrawing()
+
         self._adjustScrollbars()
         
         self._adjustScrollbars()
         
-    def Redraw(self, dc= None):
+    def Redraw(self, dc=None):
         """Redraw the existing plot."""
         if self.last_draw is not None:
             graphics, xAxis, yAxis= self.last_draw
         """Redraw the existing plot."""
         if self.last_draw is not None:
             graphics, xAxis, yAxis= self.last_draw
-            self.Draw(graphics,xAxis,yAxis,dc)
+            self._Draw(graphics,xAxis,yAxis,dc)
 
     def Clear(self):
         """Erase the window."""
 
     def Clear(self):
         """Erase the window."""
@@ -1009,7 +1128,7 @@ class PlotCanvas(wx.Panel):
             h = (yAxis[1] - yAxis[0]) * Ratio[1]
             xAxis = ( x - w/2, x + w/2 )
             yAxis = ( y - h/2, y + h/2 )
             h = (yAxis[1] - yAxis[0]) * Ratio[1]
             xAxis = ( x - w/2, x + w/2 )
             yAxis = ( y - h/2, y + h/2 )
-            self.Draw(graphics, xAxis, yAxis)
+            self._Draw(graphics, xAxis, yAxis)
         
     def GetClosestPoints(self, pntXY, pointScaled= True):
         """Returns list with
         
     def GetClosestPoints(self, pntXY, pointScaled= True):
         """Returns list with
@@ -1035,7 +1154,7 @@ class PlotCanvas(wx.Panel):
             l.append(cn)
         return l
 
             l.append(cn)
         return l
 
-    def GetClosetPoint(self, pntXY, pointScaled= True):
+    def GetClosestPoint(self, pntXY, pointScaled= True):
         """Returns list with
             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
             list for only the closest curve.
         """Returns list with
             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
             list for only the closest curve.
@@ -1055,6 +1174,8 @@ class PlotCanvas(wx.Panel):
         mdist = min(dists)  #Min dist
         i = dists.index(mdist)  #index for min dist
         return closestPts[i]  #this is the closest point on closest curve
         mdist = min(dists)  #Min dist
         i = dists.index(mdist)  #index for min dist
         return closestPts[i]  #this is the closest point on closest curve
+    
+    GetClosetPoint = GetClosestPoint
 
     def UpdatePointLabel(self, mDataDict):
         """Updates the pointLabel point on screen with data contained in
 
     def UpdatePointLabel(self, mDataDict):
         """Updates the pointLabel point on screen with data contained in
@@ -1070,7 +1191,7 @@ class PlotCanvas(wx.Panel):
         """
         if self.last_PointLabel != None:
             #compare pointXY
         """
         if self.last_PointLabel != None:
             #compare pointXY
-            if mDataDict["pointXY"] != self.last_PointLabel["pointXY"]:
+            if _Numeric.sometrue(mDataDict["pointXY"] != self.last_PointLabel["pointXY"]):
                 #closest changed
                 self._drawPointLabel(self.last_PointLabel) #erase old
                 self._drawPointLabel(mDataDict) #plot new
                 #closest changed
                 self._drawPointLabel(self.last_PointLabel) #erase old
                 self._drawPointLabel(mDataDict) #plot new
@@ -1087,7 +1208,7 @@ class PlotCanvas(wx.Panel):
                 self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
             else:
                 self._hasDragged= True
                 self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
             else:
                 self._hasDragged= True
-            self._zoomCorner2[0], self._zoomCorner2[1] = self.GetXY(event)
+            self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
             self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # add new
         elif self._dragEnabled and event.LeftIsDown():
             coordinates = event.GetPosition()
             self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # add new
         elif self._dragEnabled and event.LeftIsDown():
             coordinates = event.GetPosition()
@@ -1095,11 +1216,14 @@ class PlotCanvas(wx.Panel):
             dist = newpos-oldpos
             self._screenCoordinates = coordinates
 
             dist = newpos-oldpos
             self._screenCoordinates = coordinates
 
-            self.ScrollUp(-dist[1])
-            self.ScrollRight(-dist[0])
+            if self.last_draw is not None:
+                graphics, xAxis, yAxis= self.last_draw
+                yAxis -= dist[1]
+                xAxis -= dist[0]
+                self._Draw(graphics,xAxis,yAxis)
             
     def OnMouseLeftDown(self,event):
             
     def OnMouseLeftDown(self,event):
-        self._zoomCorner1[0], self._zoomCorner1[1]= self.GetXY(event)
+        self._zoomCorner1[0], self._zoomCorner1[1]= self._getXY(event)
         self._screenCoordinates = _Numeric.array(event.GetPosition())
         if self._dragEnabled:
             self.SetCursor(self.GrabHandCursor)
         self._screenCoordinates = _Numeric.array(event.GetPosition())
         if self._dragEnabled:
             self.SetCursor(self.GrabHandCursor)
@@ -1109,16 +1233,16 @@ class PlotCanvas(wx.Panel):
         if self._zoomEnabled:
             if self._hasDragged == True:
                 self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
         if self._zoomEnabled:
             if self._hasDragged == True:
                 self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
-                self._zoomCorner2[0], self._zoomCorner2[1]= self.GetXY(event)
+                self._zoomCorner2[0], self._zoomCorner2[1]= self._getXY(event)
                 self._hasDragged = False  # reset flag
                 minX, minY= _Numeric.minimum( self._zoomCorner1, self._zoomCorner2)
                 maxX, maxY= _Numeric.maximum( self._zoomCorner1, self._zoomCorner2)
                 self.last_PointLabel = None        #reset pointLabel
                 if self.last_draw != None:
                 self._hasDragged = False  # reset flag
                 minX, minY= _Numeric.minimum( self._zoomCorner1, self._zoomCorner2)
                 maxX, maxY= _Numeric.maximum( self._zoomCorner1, self._zoomCorner2)
                 self.last_PointLabel = None        #reset pointLabel
                 if self.last_draw != None:
-                    self.Draw(self.last_draw[0], xAxis = (minX,maxX), yAxis = (minY,maxY), dc = None)
+                    self._Draw(self.last_draw[0], xAxis = (minX,maxX), yAxis = (minY,maxY), dc = None)
             #else: # A box has not been drawn, zoom in on a point
             ## this interfered with the double click, so I've disables it.
             #else: # A box has not been drawn, zoom in on a point
             ## this interfered with the double click, so I've disables it.
-            #    X,Y = self.GetXY(event)
+            #    X,Y = self._getXY(event)
             #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
         if self._dragEnabled:
             self.SetCursor(self.HandCursor)
             #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
         if self._dragEnabled:
             self.SetCursor(self.HandCursor)
@@ -1134,7 +1258,7 @@ class PlotCanvas(wx.Panel):
         
     def OnMouseRightDown(self,event):
         if self._zoomEnabled:
         
     def OnMouseRightDown(self,event):
         if self._zoomEnabled:
-            X,Y = self.GetXY(event)
+            X,Y = self._getXY(event)
             self.Zoom( (X,Y), (self._zoomOutFactor, self._zoomOutFactor) )
 
     def OnPaint(self, event):
             self.Zoom( (X,Y), (self._zoomOutFactor, self._zoomOutFactor) )
 
     def OnPaint(self, event):
@@ -1148,13 +1272,13 @@ class PlotCanvas(wx.Panel):
         # The Buffer init is done here, to make sure the buffer is always
         # the same size as the Window
         Size  = self.canvas.GetClientSize()
         # The Buffer init is done here, to make sure the buffer is always
         # the same size as the Window
         Size  = self.canvas.GetClientSize()
-        if Size.width <= 0 or Size.height <= 0:
-            return
+        Size.width = max(1, Size.width)
+        Size.height = max(1, Size.height)
         
         # Make new offscreen bitmap: this bitmap will always have the
         # current drawing in it, so it can be used to save the image to
         # a file, or whatever.
         
         # Make new offscreen bitmap: this bitmap will always have the
         # current drawing in it, so it can be used to save the image to
         # a file, or whatever.
-        self._Buffer = wx.EmptyBitmap(Size[0],Size[1])
+        self._Buffer = wx.EmptyBitmap(Size.width, Size.height)
         self._setSize()
 
         self.last_PointLabel = None        #reset pointLabel
         self._setSize()
 
         self.last_PointLabel = None        #reset pointLabel
@@ -1163,7 +1287,7 @@ class PlotCanvas(wx.Panel):
             self.Clear()
         else:
             graphics, xSpec, ySpec = self.last_draw
             self.Clear()
         else:
             graphics, xSpec, ySpec = self.last_draw
-            self.Draw(graphics,xSpec,ySpec)
+            self._Draw(graphics,xSpec,ySpec)
 
     def OnLeave(self, event):
         """Used to erase pointLabel when mouse outside window"""
 
     def OnLeave(self, event):
         """Used to erase pointLabel when mouse outside window"""
@@ -1179,11 +1303,11 @@ class PlotCanvas(wx.Panel):
             if evt.GetOrientation() == wx.VERTICAL:
                 fullrange,pagesize = self.sb_vert.GetRange(),self.sb_vert.GetPageSize()
                 sbpos = fullrange-pagesize-sbpos
             if evt.GetOrientation() == wx.VERTICAL:
                 fullrange,pagesize = self.sb_vert.GetRange(),self.sb_vert.GetPageSize()
                 sbpos = fullrange-pagesize-sbpos
-                dist = sbpos*self._sb_yunit-(self.GetYCurrentRange()[0]-self._sb_yfullrange[0])
+                dist = sbpos*self._sb_yunit-(self._getYCurrentRange()[0]-self._sb_yfullrange[0])
                 self.ScrollUp(dist)
             
             if evt.GetOrientation() == wx.HORIZONTAL:
                 self.ScrollUp(dist)
             
             if evt.GetOrientation() == wx.HORIZONTAL:
-                dist = sbpos*self._sb_xunit-(self.GetXCurrentRange()[0]-self._sb_xfullrange[0])
+                dist = sbpos*self._sb_xunit-(self._getXCurrentRange()[0]-self._sb_xfullrange[0])
                 self.ScrollRight(dist)
                
     # Private Methods **************************************************
                 self.ScrollRight(dist)
                
     # Private Methods **************************************************
@@ -1207,7 +1331,7 @@ class PlotCanvas(wx.Panel):
         """Used for printing."""
         if self.last_draw != None:
             graphics, xSpec, ySpec= self.last_draw
         """Used for printing."""
         if self.last_draw != None:
             graphics, xSpec, ySpec= self.last_draw
-            self.Draw(graphics,xSpec,ySpec,printDC)
+            self._Draw(graphics,xSpec,ySpec,printDC)
 
     def _drawPointLabel(self, mDataDict):
         """Draws and erases pointLabels"""
 
     def _drawPointLabel(self, mDataDict):
         """Draws and erases pointLabels"""
@@ -1256,8 +1380,11 @@ class PlotCanvas(wx.Panel):
         """Draws Title and labels and returns width and height for each"""
         # TextExtents for Title and Axis Labels
         dc.SetFont(self._getFont(self._fontSizeTitle))
         """Draws Title and labels and returns width and height for each"""
         # TextExtents for Title and Axis Labels
         dc.SetFont(self._getFont(self._fontSizeTitle))
-        title= graphics.getTitle()
-        titleWH= dc.GetTextExtent(title)
+        if self._titleEnabled:
+            title= graphics.getTitle()
+            titleWH= dc.GetTextExtent(title)
+        else:
+            titleWH= (0,0)
         dc.SetFont(self._getFont(self._fontSizeAxis))
         xLabel, yLabel= graphics.getXLabel(),graphics.getYLabel()
         xLabelWH= dc.GetTextExtent(xLabel)
         dc.SetFont(self._getFont(self._fontSizeAxis))
         xLabel, yLabel= graphics.getXLabel(),graphics.getYLabel()
         xLabelWH= dc.GetTextExtent(xLabel)
@@ -1410,6 +1537,49 @@ class PlotCanvas(wx.Panel):
                                     pt[1]-0.5*h)
                 text = 0    # axis values not drawn on right side
 
                                     pt[1]-0.5*h)
                 text = 0    # axis values not drawn on right side
 
+    def _xticks(self, *args):
+        if self._logscale[0]:
+            return self._logticks(*args)
+        else:
+            return self._ticks(*args)
+    
+    def _yticks(self, *args):
+        if self._logscale[1]:
+            return self._logticks(*args)
+        else:
+            return self._ticks(*args)
+        
+    def _logticks(self, lower, upper):
+        #lower,upper = map(_Numeric.log10,[lower,upper])
+        #print 'logticks',lower,upper
+        ticks = []
+        mag = _Numeric.power(10,_Numeric.floor(lower))
+        if upper-lower > 6:
+            t = _Numeric.power(10,_Numeric.ceil(lower))
+            base = _Numeric.power(10,_Numeric.floor((upper-lower)/6))
+            def inc(t):
+                return t*base-t
+        else:
+            t = _Numeric.ceil(_Numeric.power(10,lower)/mag)*mag
+            def inc(t):
+                return 10**int(_Numeric.floor(_Numeric.log10(t)+1e-16))
+        majortick = int(_Numeric.log10(mag))
+        while t <= pow(10,upper):
+            if majortick != int(_Numeric.floor(_Numeric.log10(t)+1e-16)):
+                majortick = int(_Numeric.floor(_Numeric.log10(t)+1e-16))
+                ticklabel = '1e%d'%majortick
+            else:
+                if upper-lower < 2:
+                    minortick = int(t/pow(10,majortick)+.5)
+                    ticklabel = '%de%d'%(minortick,majortick)
+                else:
+                    ticklabel = ''
+            ticks.append((_Numeric.log10(t), ticklabel))
+            t += inc(t)
+        if len(ticks) == 0:
+            ticks = [(0,'')]
+        return ticks
+    
     def _ticks(self, lower, upper):
         ideal = (upper-lower)/7.
         log = _Numeric.log10(ideal)
     def _ticks(self, lower, upper):
         ideal = (upper-lower)/7.
         log = _Numeric.log10(ideal)
@@ -1423,7 +1593,7 @@ class PlotCanvas(wx.Panel):
                 error = e
                 factor = f
         grid = factor * 10.**power
                 error = e
                 factor = f
         grid = factor * 10.**power
-        if power > 4 or power < -4:
+        if self._useScientificNotation and (power > 4 or power < -4):
             format = '%+7.1e'        
         elif power >= 0:
             digits = max(1, int(power))
             format = '%+7.1e'        
         elif power >= 0:
             digits = max(1, int(power))
@@ -1450,8 +1620,8 @@ class PlotCanvas(wx.Panel):
         needScrollbars = False
         
         # horizontal scrollbar
         needScrollbars = False
         
         # horizontal scrollbar
-        r_current = self.GetXCurrentRange()
-        r_max = list(self.GetXMaxRange())
+        r_current = self._getXCurrentRange()
+        r_max = list(self._getXMaxRange())
         sbfullrange = float(self.sb_hor.GetRange())
 
         r_max[0] = min(r_max[0],r_current[0])
         sbfullrange = float(self.sb_hor.GetRange())
 
         r_max[0] = min(r_max[0],r_current[0])
@@ -1472,8 +1642,8 @@ class PlotCanvas(wx.Panel):
             self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
 
         # vertical scrollbar
             self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
 
         # vertical scrollbar
-        r_current = self.GetYCurrentRange()
-        r_max = list(self.GetYMaxRange())
+        r_current = self._getYCurrentRange()
+        r_max = list(self._getYMaxRange())
         sbfullrange = float(self.sb_vert.GetRange())
 
         r_max[0] = min(r_max[0],r_current[0])
         sbfullrange = float(self.sb_vert.GetRange())
 
         r_max[0] = min(r_max[0],r_current[0])
@@ -1746,6 +1916,16 @@ def _draw6Objects():
 
     return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
                         "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
 
     return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
                         "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
+def _draw7Objects():
+    # Empty graph with axis defined but no points/lines
+    x = _Numeric.arange(1,1000,1)
+    y1 = 4.5*x**2
+    y2 = 2.2*x**3
+    points1 = _Numeric.transpose([x,y1])
+    points2 = _Numeric.transpose([x,y2])
+    line1 = PolyLine(points1, legend='quadratic', colour='blue', width=1)
+    line2 = PolyLine(points2, legend='cubic', colour='red', width=1)
+    return PlotGraphics([line1,line2], "double log plot", "Value X", "Value Y")
 
 
 class TestFrame(wx.Frame):
 
 
 class TestFrame(wx.Frame):
@@ -1786,7 +1966,8 @@ class TestFrame(wx.Frame):
         self.Bind(wx.EVT_MENU,self.OnPlotDraw5, id=210)
         menu.Append(260, 'Draw6', 'Draw plots6')
         self.Bind(wx.EVT_MENU,self.OnPlotDraw6, id=260)
         self.Bind(wx.EVT_MENU,self.OnPlotDraw5, id=210)
         menu.Append(260, 'Draw6', 'Draw plots6')
         self.Bind(wx.EVT_MENU,self.OnPlotDraw6, id=260)
-       
+        menu.Append(261, 'Draw7', 'Draw plots7')
+        self.Bind(wx.EVT_MENU,self.OnPlotDraw7, id=261)
 
         menu.Append(211, '&Redraw', 'Redraw plots')
         self.Bind(wx.EVT_MENU,self.OnPlotRedraw, id=211)
 
         menu.Append(211, '&Redraw', 'Redraw plots')
         self.Bind(wx.EVT_MENU,self.OnPlotRedraw, id=211)
@@ -1828,12 +2009,11 @@ class TestFrame(wx.Frame):
         #define the function for drawing pointLabels
         self.client.SetPointLabelFunc(self.DrawPointLabel)
         # Create mouse event for showing cursor coords in status bar
         #define the function for drawing pointLabels
         self.client.SetPointLabelFunc(self.DrawPointLabel)
         # Create mouse event for showing cursor coords in status bar
-        self.client.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
+        self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
         # Show closest point when enabled
         # Show closest point when enabled
-        self.client.Bind(wx.EVT_MOTION, self.OnMotion)
-
-        self.Show()
+        self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
 
 
+        self.Show(True)
 
     def DrawPointLabel(self, dc, mDataDict):
         """This is the fuction that defines how the pointLabels are plotted
 
     def DrawPointLabel(self, dc, mDataDict):
         """This is the fuction that defines how the pointLabels are plotted
@@ -1860,7 +2040,7 @@ class TestFrame(wx.Frame):
         # -----------
 
     def OnMouseLeftDown(self,event):
         # -----------
 
     def OnMouseLeftDown(self,event):
-        s= "Left Mouse Down at Point: (%.4f, %.4f)" % self.client.GetXY(event)
+        s= "Left Mouse Down at Point: (%.4f, %.4f)" % self.client._getXY(event)
         self.SetStatusText(s)
         event.Skip()            #allows plotCanvas OnMouseLeftDown to be called
 
         self.SetStatusText(s)
         event.Skip()            #allows plotCanvas OnMouseLeftDown to be called
 
@@ -1869,7 +2049,7 @@ class TestFrame(wx.Frame):
         if self.client.GetEnablePointLabel() == True:
             #make up dict with info for the pointLabel
             #I've decided to mark the closest point on the closest curve
         if self.client.GetEnablePointLabel() == True:
             #make up dict with info for the pointLabel
             #I've decided to mark the closest point on the closest curve
-            dlst= self.client.GetClosetPoint( self.client.GetXY(event), pointScaled= True)
+            dlst= self.client.GetClosestPoint( self.client._getXY(event), pointScaled= True)
             if dlst != []:    #returns [] if none
                 curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
                 #make up dictionary to pass to my user function (see DrawPointLabel) 
             if dlst != []:    #returns [] if none
                 curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
                 #make up dictionary to pass to my user function (see DrawPointLabel) 
@@ -1939,6 +2119,12 @@ class TestFrame(wx.Frame):
         self.client.SetYSpec('auto')
         self.client.Draw(_draw6Objects(), xAxis= (0,7))
 
         self.client.SetYSpec('auto')
         self.client.Draw(_draw6Objects(), xAxis= (0,7))
 
+    def OnPlotDraw7(self, event):
+        #log scale example
+        self.resetDefaults()
+        self.client.setLogScale((True,True))
+        self.client.Draw(_draw7Objects())
+
     def OnPlotRedraw(self,event):
         self.client.Redraw()
 
     def OnPlotRedraw(self,event):
         self.client.Redraw()
 
@@ -1986,9 +2172,11 @@ class TestFrame(wx.Frame):
         self.client.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
         self.client.SetFontSizeAxis(10)
         self.client.SetFontSizeLegend(7)
         self.client.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
         self.client.SetFontSizeAxis(10)
         self.client.SetFontSizeLegend(7)
+        self.client.setLogScale((False,False))
         self.client.SetXSpec('auto')
         self.client.SetYSpec('auto')
         
         self.client.SetXSpec('auto')
         self.client.SetYSpec('auto')
         
+        
 
 def __test():
 
 
 def __test():