]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/plot.py
   1 #----------------------------------------------------------------------------- 
   3 # Purpose:     Line, Bar and Scatter Graphs 
   5 # Author:      Gordon Williams 
  10 # Licence:     Use as you wish. 
  11 #----------------------------------------------------------------------------- 
  12 # 12/15/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  14 # o 2.5 compatability update. 
  15 # o Renamed to plot.py in the wx.lib directory. 
  16 # o Reworked test frame to work with wx demo framework. This saves a bit 
  17 #   of tedious cut and paste, and the test app is excellent. 
  19 # 12/18/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
  21 # o wxScrolledMessageDialog -> ScrolledMessageDialog 
  23 # Oct 6, 2004  Gordon Williams (g_will@cyberus.ca) 
  24 #   - Added bar graph demo 
  25 #   - Modified line end shape from round to square. 
  26 #   - Removed FloatDCWrapper for conversion to ints and ints in arguments 
  28 # Oct 15, 2004  Gordon Williams (g_will@cyberus.ca) 
  29 #   - Imported modules given leading underscore to name. 
  30 #   - Added Cursor Line Tracking and User Point Labels.  
  31 #   - Demo for Cursor Line Tracking and Point Labels. 
  32 #   - Size of plot preview frame adjusted to show page better. 
  33 #   - Added helper functions PositionUserToScreen and PositionScreenToUser in PlotCanvas. 
  34 #   - Added functions GetClosestPoints (all curves) and GetClosestPoint (only closest curve) 
  35 #       can be in either user coords or screen coords. 
  40 This is a simple light weight plotting module that can be used with 
  41 Boa or easily integrated into your own wxPython application.  The 
  42 emphasis is on small size and fast plotting for large data sets.  It 
  43 has a reasonable number of features to do line and scatter graphs 
  44 easily as well as simple bar graphs.  It is not as sophisticated or  
  45 as powerful as SciPy Plt or Chaco.  Both of these are great packages  
  46 but consume huge amounts of computer resources for simple plots. 
  47 They can be found at http://scipy.com 
  49 This file contains two parts; first the re-usable library stuff, then, 
  50 after a "if __name__=='__main__'" test, a simple frame and a few default 
  51 plots for examples and testing. 
  54 Written by K.Hinsen, R. Srinivasan; 
  55 Ported to wxPython Harm van der Heijden, feb 1999 
  57 Major Additions Gordon Williams Feb. 2003 (g_will@cyberus.ca) 
  59     -Zooming using mouse "rubber band" 
  62     -Printing, preview, and page set up (margins) 
  63     -Axis and title labels 
  64     -Cursor xy axis values 
  65     -Doc strings and lots of comments 
  66     -Optimizations for large number of points 
  69 Did a lot of work here to speed markers up. Only a factor of 4 
  70 improvement though. Lines are much faster than markers, especially 
  71 filled markers.  Stay away from circles and triangles unless you 
  72 only have a few thousand points. 
  74 Times for 25,000 points 
  81 triangle, triangle_down -  0.90 
  83 Thanks to Chris Barker for getting this version working on Linux. 
  85 Zooming controls with mouse (when enabled): 
  86     Left mouse drag - Zoom box. 
  87     Left mouse double click - reset zoom. 
  88     Right mouse click - zoom out centred on click location. 
  91 import  string 
as _string
 
  95 # Needs Numeric or numarray 
  97     import Numeric 
as _Numeric
 
 100         import numarray 
as _Numeric  
#if numarray is used it is renamed Numeric 
 103         This module requires the Numeric or numarray module, 
 104         which could not be imported.  It probably is not installed 
 105         (it's not part of the standard Python distribution). See the 
 106         Python site (http://www.python.org) for information on 
 107         downloading source or binaries.""" 
 108         raise ImportError, "Numeric or numarray not found. \n" + msg
 
 113 # Plotting classes... 
 116     """Base Class for lines and markers 
 117         - All methods are private. 
 120     def __init__(self
, points
, attr
): 
 121         self
.points 
= _Numeric
.array(points
) 
 122         self
.currentScale
= (1,1) 
 123         self
.currentShift
= (0,0) 
 124         self
.scaled 
= self
.points
 
 126         self
.attributes
.update(self
._attributes
) 
 127         for name
, value 
in attr
.items():    
 128             if name 
not in self
._attributes
.keys(): 
 129                 raise KeyError, "Style attribute incorrect. Should be one of %s" % self
._attributes
.keys() 
 130             self
.attributes
[name
] = value
 
 132     def boundingBox(self
): 
 133         if len(self
.points
) == 0: 
 135             # defaults to (-1,-1) and (1,1) but axis can be set in Draw 
 136             minXY
= _Numeric
.array([-1,-1]) 
 137             maxXY
= _Numeric
.array([ 1, 1]) 
 139             minXY
= _Numeric
.minimum
.reduce(self
.points
) 
 140             maxXY
= _Numeric
.maximum
.reduce(self
.points
) 
 143     def scaleAndShift(self
, scale
=(1,1), shift
=(0,0)): 
 144         if len(self
.points
) == 0: 
 147         if (scale 
is not self
.currentScale
) or (shift 
is not self
.currentShift
): 
 148             # update point scaling 
 149             self
.scaled 
= scale
*self
.points
+shift
 
 150             self
.currentScale
= scale
 
 151             self
.currentShift
= shift
 
 152         # else unchanged use the current scaling 
 155         return self
.attributes
['legend'] 
 157     def getClosestPoint(self
, pntXY
, pointScaled
= True): 
 158         """Returns the index of closest point on the curve, pointXY, scaledXY, distance 
 160             if pointScaled == True based on screen coords 
 161             if pointScaled == False based on user coords 
 163         if pointScaled 
== True: 
 166             pxy 
= self
.currentScale 
* _Numeric
.array(pntXY
)+ self
.currentShift
 
 170             pxy 
= _Numeric
.array(pntXY
) 
 171         #determine distance for each point 
 172         d
= _Numeric
.sqrt(_Numeric
.add
.reduce((p
-pxy
)**2,1)) #sqrt(dx^2+dy^2) 
 173         pntIndex 
= _Numeric
.argmin(d
) 
 175         return [pntIndex
, self
.points
[pntIndex
], self
.scaled
[pntIndex
], dist
] 
 178 class PolyLine(PolyPoints
): 
 179     """Class to define line type and style 
 180         - All methods except __init__ are private. 
 183     _attributes 
= {'colour': 'black', 
 188     def __init__(self
, points
, **attr
): 
 189         """Creates PolyLine object 
 190             points - sequence (array, tuple or list) of (x,y) points making up line 
 191             **attr - key word attributes 
 193                     'colour'= 'black',          - wx.Pen Colour any wx.NamedColour 
 194                     'width'= 1,                 - Pen width 
 195                     'style'= wx.SOLID,          - wx.Pen style 
 196                     'legend'= ''                - Line Legend to display 
 198         PolyPoints
.__init
__(self
, points
, attr
) 
 200     def draw(self
, dc
, printerScale
, coord
= None): 
 201         colour 
= self
.attributes
['colour'] 
 202         width 
= self
.attributes
['width'] * printerScale
 
 203         style
= self
.attributes
['style'] 
 204         pen 
= wx
.Pen(wx
.NamedColour(colour
), width
, style
) 
 205         pen
.SetCap(wx
.CAP_BUTT
) 
 208             dc
.DrawLines(self
.scaled
) 
 210             dc
.DrawLines(coord
) # draw legend line 
 212     def getSymExtent(self
, printerScale
): 
 213         """Width and Height of Marker""" 
 214         h
= self
.attributes
['width'] * printerScale
 
 219 class PolyMarker(PolyPoints
): 
 220     """Class to define marker type and style 
 221         - All methods except __init__ are private. 
 224     _attributes 
= {'colour': 'black', 
 228                    'fillstyle': wx
.SOLID
, 
 232     def __init__(self
, points
, **attr
): 
 233         """Creates PolyMarker object 
 234         points - sequence (array, tuple or list) of (x,y) points 
 235         **attr - key word attributes 
 237                 'colour'= 'black',          - wx.Pen Colour any wx.NamedColour 
 238                 'width'= 1,                 - Pen width 
 239                 'size'= 2,                  - Marker size 
 240                 'fillcolour'= same as colour,      - wx.Brush Colour any wx.NamedColour 
 241                 'fillstyle'= wx.SOLID,      - wx.Brush fill style (use wx.TRANSPARENT for no fill) 
 242                 'marker'= 'circle'          - Marker shape 
 243                 'legend'= ''                - Marker Legend to display 
 255         PolyPoints
.__init
__(self
, points
, attr
) 
 257     def draw(self
, dc
, printerScale
, coord
= None): 
 258         colour 
= self
.attributes
['colour'] 
 259         width 
= self
.attributes
['width'] * printerScale
 
 260         size 
= self
.attributes
['size'] * printerScale
 
 261         fillcolour 
= self
.attributes
['fillcolour'] 
 262         fillstyle 
= self
.attributes
['fillstyle'] 
 263         marker 
= self
.attributes
['marker'] 
 265         dc
.SetPen(wx
.Pen(wx
.NamedColour(colour
), width
)) 
 267             dc
.SetBrush(wx
.Brush(wx
.NamedColour(fillcolour
),fillstyle
)) 
 269             dc
.SetBrush(wx
.Brush(wx
.NamedColour(colour
), fillstyle
)) 
 271             self
._drawmarkers
(dc
, self
.scaled
, marker
, size
) 
 273             self
._drawmarkers
(dc
, coord
, marker
, size
) # draw legend marker 
 275     def getSymExtent(self
, printerScale
): 
 276         """Width and Height of Marker""" 
 277         s
= 5*self
.attributes
['size'] * printerScale
 
 280     def _drawmarkers(self
, dc
, coords
, marker
,size
=1): 
 281         f 
= eval('self._' +marker
) 
 284     def _circle(self
, dc
, coords
, size
=1): 
 287         rect
= _Numeric
.zeros((len(coords
),4),_Numeric
.Float
)+[0.0,0.0,wh
,wh
] 
 288         rect
[:,0:2]= coords
-[fact
,fact
] 
 289         dc
.DrawEllipseList(rect
.astype(_Numeric
.Int32
)) 
 291     def _dot(self
, dc
, coords
, size
=1): 
 292         dc
.DrawPointList(coords
) 
 294     def _square(self
, dc
, coords
, size
=1): 
 297         rect
= _Numeric
.zeros((len(coords
),4),_Numeric
.Float
)+[0.0,0.0,wh
,wh
] 
 298         rect
[:,0:2]= coords
-[fact
,fact
] 
 299         dc
.DrawRectangleList(rect
.astype(_Numeric
.Int32
)) 
 301     def _triangle(self
, dc
, coords
, size
=1): 
 302         shape
= [(-2.5*size
,1.44*size
), (2.5*size
,1.44*size
), (0.0,-2.88*size
)] 
 303         poly
= _Numeric
.repeat(coords
,3) 
 304         poly
.shape
= (len(coords
),3,2) 
 306         dc
.DrawPolygonList(poly
.astype(_Numeric
.Int32
)) 
 308     def _triangle_down(self
, dc
, coords
, size
=1): 
 309         shape
= [(-2.5*size
,-1.44*size
), (2.5*size
,-1.44*size
), (0.0,2.88*size
)] 
 310         poly
= _Numeric
.repeat(coords
,3) 
 311         poly
.shape
= (len(coords
),3,2) 
 313         dc
.DrawPolygonList(poly
.astype(_Numeric
.Int32
)) 
 315     def _cross(self
, dc
, coords
, size
=1): 
 317         for f 
in [[-fact
,-fact
,fact
,fact
],[-fact
,fact
,fact
,-fact
]]: 
 318             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 319             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 321     def _plus(self
, dc
, coords
, size
=1): 
 323         for f 
in [[-fact
,0,fact
,0],[0,-fact
,0,fact
]]: 
 324             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 325             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 328     """Container to hold PolyXXX objects and graph labels 
 329         - All methods except __init__ are private. 
 332     def __init__(self
, objects
, title
='', xLabel
='', yLabel
= ''): 
 333         """Creates PlotGraphics object 
 334         objects - list of PolyXXX objects to make graph 
 335         title - title shown at top of graph 
 336         xLabel - label shown on x-axis 
 337         yLabel - label shown on y-axis 
 339         if type(objects
) not in [list,tuple]: 
 340             raise TypeError, "objects argument should be list or tuple" 
 341         self
.objects 
= objects
 
 346     def boundingBox(self
): 
 347         p1
, p2 
= self
.objects
[0].boundingBox() 
 348         for o 
in self
.objects
[1:]: 
 349             p1o
, p2o 
= o
.boundingBox() 
 350             p1 
= _Numeric
.minimum(p1
, p1o
) 
 351             p2 
= _Numeric
.maximum(p2
, p2o
) 
 354     def scaleAndShift(self
, scale
=(1,1), shift
=(0,0)): 
 355         for o 
in self
.objects
: 
 356             o
.scaleAndShift(scale
, shift
) 
 358     def setPrinterScale(self
, scale
): 
 359         """Thickens up lines and markers only for printing""" 
 360         self
.printerScale
= scale
 
 362     def setXLabel(self
, xLabel
= ''): 
 363         """Set the X axis label on the graph""" 
 366     def setYLabel(self
, yLabel
= ''): 
 367         """Set the Y axis label on the graph""" 
 370     def setTitle(self
, title
= ''): 
 371         """Set the title at the top of graph""" 
 375         """Get x axis label string""" 
 379         """Get y axis label string""" 
 382     def getTitle(self
, title
= ''): 
 383         """Get the title at the top of graph""" 
 387         for o 
in self
.objects
: 
 388             #t=_time.clock()          # profile info 
 389             o
.draw(dc
, self
.printerScale
) 
 391             #print o, "time=", dt 
 393     def getSymExtent(self
, printerScale
): 
 394         """Get max width and height of lines and markers symbols for legend""" 
 395         symExt 
= self
.objects
[0].getSymExtent(printerScale
) 
 396         for o 
in self
.objects
[1:]: 
 397             oSymExt 
= o
.getSymExtent(printerScale
) 
 398             symExt 
= _Numeric
.maximum(symExt
, oSymExt
) 
 401     def getLegendNames(self
): 
 402         """Returns list of legend names""" 
 403         lst 
= [None]*len(self
) 
 404         for i 
in range(len(self
)): 
 405             lst
[i
]= self
.objects
[i
].getLegend() 
 409         return len(self
.objects
) 
 411     def __getitem__(self
, item
): 
 412         return self
.objects
[item
] 
 415 #------------------------------------------------------------------------------- 
 416 # Main window that you will want to import into your application. 
 418 class PlotCanvas(wx
.Window
): 
 419     """Subclass of a wx.Window to allow simple general plotting 
 420     of data with zoom, labels, and automatic axis scaling.""" 
 422     def __init__(self
, parent
, id = -1, pos
=wx
.DefaultPosition
, 
 423             size
=wx
.DefaultSize
, style
= wx
.DEFAULT_FRAME_STYLE
, name
= ""): 
 424         """Constucts a window, which can be a child of a frame, dialog or 
 425         any other non-control window""" 
 427         wx
.Window
.__init
__(self
, parent
, id, pos
, size
, style
, name
) 
 430         self
.SetBackgroundColour("white") 
 432         # Create some mouse events for zooming 
 433         self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
 434         self
.Bind(wx
.EVT_LEFT_UP
, self
.OnMouseLeftUp
) 
 435         self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
 436         self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnMouseDoubleClick
) 
 437         self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnMouseRightDown
) 
 439         # set curser as cross-hairs 
 440         self
.SetCursor(wx
.CROSS_CURSOR
) 
 442         # Things for printing 
 443         self
.print_data 
= wx
.PrintData() 
 444         self
.print_data
.SetPaperId(wx
.PAPER_LETTER
) 
 445         self
.print_data
.SetOrientation(wx
.LANDSCAPE
) 
 446         self
.pageSetupData
= wx
.PageSetupDialogData() 
 447         self
.pageSetupData
.SetMarginBottomRight((25,25)) 
 448         self
.pageSetupData
.SetMarginTopLeft((25,25)) 
 449         self
.pageSetupData
.SetPrintData(self
.print_data
) 
 450         self
.printerScale 
= 1 
 454         self
._zoomInFactor 
=  0.5 
 455         self
._zoomOutFactor 
= 2 
 456         self
._zoomCorner
1= _Numeric
.array([0.0, 0.0]) # left mouse down corner 
 457         self
._zoomCorner
2= _Numeric
.array([0.0, 0.0])   # left mouse up corner 
 458         self
._zoomEnabled
= False 
 459         self
._hasDragged
= False 
 462         self
.last_draw 
= None 
 467         self
._gridEnabled
= False 
 468         self
._legendEnabled
= False 
 472         self
._fontSizeAxis
= 10 
 473         self
._fontSizeTitle
= 15 
 474         self
._fontSizeLegend
= 7 
 477         self
._pointLabelEnabled
= False 
 478         self
.last_PointLabel
= None 
 479         self
._pointLabelFunc
= None 
 480         self
.Bind(wx
.EVT_LEAVE_WINDOW
, self
.OnLeave
) 
 482         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 483         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 484         # OnSize called to make sure the buffer is initialized. 
 485         # This might result in OnSize getting called twice on some 
 486         # platforms at initialization, but little harm done. 
 487         if wx
.Platform 
!= "__WXMAC__": 
 488             self
.OnSize(None) # sets the initial size based on client size 
 492     def SaveFile(self
, fileName
= ''): 
 493         """Saves the file to the type specified in the extension. If no file 
 494         name is specified a dialog box is provided.  Returns True if sucessful, 
 497         .bmp  Save a Windows bitmap file. 
 498         .xbm  Save an X bitmap file. 
 499         .xpm  Save an XPM bitmap file. 
 500         .png  Save a Portable Network Graphics file. 
 501         .jpg  Save a Joint Photographic Experts Group file. 
 503         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 504             dlg1 
= wx
.FileDialog( 
 506                     "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "", 
 507                     "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg", 
 508                     wx
.SAVE|wx
.OVERWRITE_PROMPT
 
 512                     if dlg1
.ShowModal() == wx
.ID_OK
: 
 513                         fileName 
= dlg1
.GetPath() 
 514                         # Check for proper exension 
 515                         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 516                             dlg2 
= wx
.MessageDialog(self
, 'File name extension\n' 
 518                             'bmp, xbm, xpm, png, or jpg', 
 519                               'File Name Error', wx
.OK | wx
.ICON_ERROR
) 
 525                             break # now save file 
 526                     else: # exit without saving 
 531         # File name has required extension 
 532         fType 
= _string
.lower(fileName
[-3:]) 
 534             tp
= wx
.BITMAP_TYPE_BMP       
# Save a Windows bitmap file. 
 536             tp
= wx
.BITMAP_TYPE_XBM       
# Save an X bitmap file. 
 538             tp
= wx
.BITMAP_TYPE_XPM       
# Save an XPM bitmap file. 
 540             tp
= wx
.BITMAP_TYPE_JPEG      
# Save a JPG file. 
 542             tp
= wx
.BITMAP_TYPE_PNG       
# Save a PNG file. 
 544         res
= self
._Buffer
.SaveFile(fileName
, tp
) 
 548         """Brings up the page setup dialog""" 
 549         data 
= self
.pageSetupData
 
 550         data
.SetPrintData(self
.print_data
) 
 551         dlg 
= wx
.PageSetupDialog(self
.parent
, data
) 
 553             if dlg
.ShowModal() == wx
.ID_OK
: 
 554                 data 
= dlg
.GetPageSetupData() # returns wx.PageSetupDialogData 
 555                 # updates page parameters from dialog 
 556                 self
.pageSetupData
.SetMarginBottomRight(data
.GetMarginBottomRight()) 
 557                 self
.pageSetupData
.SetMarginTopLeft(data
.GetMarginTopLeft()) 
 558                 self
.pageSetupData
.SetPrintData(data
.GetPrintData()) 
 559                 self
.print_data
=wx
.PrintData(data
.GetPrintData()) # updates print_data 
 563     def Printout(self
, paper
=None): 
 564         """Print current plot.""" 
 566             self
.print_data
.SetPaperId(paper
) 
 567         pdd 
= wx
.PrintDialogData(self
.print_data
) 
 568         printer 
= wx
.Printer(pdd
) 
 569         out 
= PlotPrintout(self
) 
 570         print_ok 
= printer
.Print(self
.parent
, out
) 
 572             self
.print_data 
= wx
.PrintData(printer
.GetPrintDialogData().GetPrintData()) 
 575     def PrintPreview(self
): 
 576         """Print-preview current plot.""" 
 577         printout 
= PlotPrintout(self
) 
 578         printout2 
= PlotPrintout(self
) 
 579         self
.preview 
= wx
.PrintPreview(printout
, printout2
, self
.print_data
) 
 580         if not self
.preview
.Ok(): 
 581             wx
.MessageDialog(self
, "Print Preview failed.\n" \
 
 582                                "Check that default printer is configured\n", \
 
 583                                "Print error", wx
.OK|wx
.CENTRE
).ShowModal() 
 584         self
.preview
.SetZoom(40) 
 585         # search up tree to find frame instance 
 587         while not isinstance(frameInst
, wx
.Frame
): 
 588             frameInst
= frameInst
.GetParent() 
 589         frame 
= wx
.PreviewFrame(self
.preview
, frameInst
, "Preview") 
 591         frame
.SetPosition(self
.GetPosition()) 
 592         frame
.SetSize((600,550)) 
 593         frame
.Centre(wx
.BOTH
) 
 596     def SetFontSizeAxis(self
, point
= 10): 
 597         """Set the tick and axis label font size (default is 10 point)""" 
 598         self
._fontSizeAxis
= point
 
 600     def GetFontSizeAxis(self
): 
 601         """Get current tick and axis label font size in points""" 
 602         return self
._fontSizeAxis
 
 604     def SetFontSizeTitle(self
, point
= 15): 
 605         """Set Title font size (default is 15 point)""" 
 606         self
._fontSizeTitle
= point
 
 608     def GetFontSizeTitle(self
): 
 609         """Get current Title font size in points""" 
 610         return self
._fontSizeTitle
 
 612     def SetFontSizeLegend(self
, point
= 7): 
 613         """Set Legend font size (default is 7 point)""" 
 614         self
._fontSizeLegend
= point
 
 616     def GetFontSizeLegend(self
): 
 617         """Get current Legend font size in points""" 
 618         return self
._fontSizeLegend
 
 620     def SetEnableZoom(self
, value
): 
 621         """Set True to enable zooming.""" 
 622         if value 
not in [True,False]: 
 623             raise TypeError, "Value should be True or False" 
 624         self
._zoomEnabled
= value
 
 626     def GetEnableZoom(self
): 
 627         """True if zooming enabled.""" 
 628         return self
._zoomEnabled
 
 630     def SetEnableGrid(self
, value
): 
 631         """Set True to enable grid.""" 
 632         if value 
not in [True,False,'Horizontal','Vertical']: 
 633             raise TypeError, "Value should be True, False, Horizontal or Vertical" 
 634         self
._gridEnabled
= value
 
 637     def GetEnableGrid(self
): 
 638         """True if grid enabled.""" 
 639         return self
._gridEnabled
 
 641     def SetEnableLegend(self
, value
): 
 642         """Set True to enable legend.""" 
 643         if value 
not in [True,False]: 
 644             raise TypeError, "Value should be True or False" 
 645         self
._legendEnabled
= value 
 
 648     def GetEnableLegend(self
): 
 649         """True if Legend enabled.""" 
 650         return self
._legendEnabled
 
 652     def SetEnablePointLabel(self
, value
): 
 653         """Set True to enable pointLabel.""" 
 654         if value 
not in [True,False]: 
 655             raise TypeError, "Value should be True or False" 
 656         self
._pointLabelEnabled
= value 
 
 657         self
.Redraw()  #will erase existing pointLabel if present 
 658         self
.last_PointLabel 
= None 
 660     def GetEnablePointLabel(self
): 
 661         """True if pointLabel enabled.""" 
 662         return self
._pointLabelEnabled
 
 664     def SetPointLabelFunc(self
, func
): 
 665         """Sets the function with custom code for pointLabel drawing 
 666             ******** more info needed *************** 
 668         self
._pointLabelFunc
= func
 
 670     def GetPointLabelFunc(self
): 
 671         """Returns pointLabel Drawing Function""" 
 672         return self
._pointLabelFunc
 
 675         """Unzoom the plot.""" 
 676         self
.last_PointLabel 
= None        #reset pointLabel 
 677         if self
.last_draw 
is not None: 
 678             self
.Draw(self
.last_draw
[0]) 
 680     def ScrollRight(self
, units
):           
 681         """Move view right number of axis units.""" 
 682         self
.last_PointLabel 
= None        #reset pointLabel 
 683         if self
.last_draw 
is not None: 
 684             graphics
, xAxis
, yAxis
= self
.last_draw
 
 685             xAxis
= (xAxis
[0]+units
, xAxis
[1]+units
) 
 686             self
.Draw(graphics
,xAxis
,yAxis
) 
 688     def ScrollUp(self
, units
): 
 689         """Move view up number of axis units.""" 
 690         self
.last_PointLabel 
= None        #reset pointLabel 
 691         if self
.last_draw 
is not None:   
 692              graphics
, xAxis
, yAxis
= self
.last_draw
 
 693              yAxis
= (yAxis
[0]+units
, yAxis
[1]+units
) 
 694              self
.Draw(graphics
,xAxis
,yAxis
) 
 697     def GetXY(self
,event
): 
 698         """Takes a mouse event and returns the XY user axis values.""" 
 699         x
,y
= self
.PositionScreenToUser(event
.GetPosition()) 
 702     def PositionUserToScreen(self
, pntXY
): 
 703         """Converts User position to Screen Coordinates""" 
 704         userPos
= _Numeric
.array(pntXY
) 
 705         x
,y
= userPos 
* self
._pointScale 
+ self
._pointShift
 
 708     def PositionScreenToUser(self
, pntXY
): 
 709         """Converts Screen position to User Coordinates""" 
 710         screenPos
= _Numeric
.array(pntXY
) 
 711         x
,y
= (screenPos
-self
._pointShift
)/self
._pointScale
 
 714     def SetXSpec(self
, type= 'auto'): 
 715         """xSpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 717             'none' - shows no axis or tick mark values 
 718             'min' - shows min bounding box values 
 719             'auto' - rounds axis range to sensible values 
 723     def SetYSpec(self
, type= 'auto'): 
 724         """ySpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 726             'none' - shows no axis or tick mark values 
 727             'min' - shows min bounding box values 
 728             'auto' - rounds axis range to sensible values 
 733         """Returns current XSpec for axis""" 
 737         """Returns current YSpec for axis""" 
 740     def GetXMaxRange(self
): 
 741         """Returns (minX, maxX) x-axis range for displayed graph""" 
 742         graphics
= self
.last_draw
[0] 
 743         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 744         xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 747     def GetYMaxRange(self
): 
 748         """Returns (minY, maxY) y-axis range for displayed graph""" 
 749         graphics
= self
.last_draw
[0] 
 750         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 751         yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 754     def GetXCurrentRange(self
): 
 755         """Returns (minX, maxX) x-axis for currently displayed portion of graph""" 
 756         return self
.last_draw
[1] 
 758     def GetYCurrentRange(self
): 
 759         """Returns (minY, maxY) y-axis for currently displayed portion of graph""" 
 760         return self
.last_draw
[2] 
 762     def Draw(self
, graphics
, xAxis 
= None, yAxis 
= None, dc 
= None): 
 763         """Draw objects in graphics with specified x and y axis. 
 764         graphics- instance of PlotGraphics with list of PolyXXX objects 
 765         xAxis - tuple with (min, max) axis range to view 
 766         yAxis - same as xAxis 
 767         dc - drawing context - doesn't have to be specified.     
 768         If it's not, the offscreen buffer is used 
 770         # check Axis is either tuple or none 
 771         if type(xAxis
) not in [type(None),tuple]: 
 772             raise TypeError, "xAxis should be None or (minX,maxX)" 
 773         if type(yAxis
) not in [type(None),tuple]: 
 774             raise TypeError, "yAxis should be None or (minY,maxY)" 
 776         # check case for axis = (a,b) where a==b caused by improper zooms 
 778             if xAxis
[0] == xAxis
[1]: 
 781             if yAxis
[0] == yAxis
[1]: 
 785             # sets new dc and clears it  
 786             dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
._Buffer
) 
 792         # set font size for every thing but title and legend 
 793         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 795         # sizes axis to axis type, create lower left and upper right corners of plot 
 796         if xAxis 
== None or yAxis 
== None: 
 797             # One or both axis not specified in Draw 
 798             p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 800                 xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 802                 yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 803             # Adjust bounding box for axis spec 
 804             p1
[0],p1
[1] = xAxis
[0], yAxis
[0]     # lower left corner user scale (xmin,ymin) 
 805             p2
[0],p2
[1] = xAxis
[1], yAxis
[1]     # upper right corner user scale (xmax,ymax) 
 807             # Both axis specified in Draw 
 808             p1
= _Numeric
.array([xAxis
[0], yAxis
[0]])    # lower left corner user scale (xmin,ymin) 
 809             p2
= _Numeric
.array([xAxis
[1], yAxis
[1]])     # upper right corner user scale (xmax,ymax) 
 811         self
.last_draw 
= (graphics
, xAxis
, yAxis
)       # saves most recient values 
 813         # Get ticks and textExtents for axis if required 
 814         if self
._xSpec 
is not 'none':         
 815             xticks 
= self
._ticks
(xAxis
[0], xAxis
[1]) 
 816             xTextExtent 
= dc
.GetTextExtent(xticks
[-1][1])# w h of x axis text last number on axis 
 819             xTextExtent
= (0,0) # No text for ticks 
 820         if self
._ySpec 
is not 'none': 
 821             yticks 
= self
._ticks
(yAxis
[0], yAxis
[1]) 
 822             yTextExtentBottom
= dc
.GetTextExtent(yticks
[0][1]) 
 823             yTextExtentTop   
= dc
.GetTextExtent(yticks
[-1][1]) 
 824             yTextExtent
= (max(yTextExtentBottom
[0],yTextExtentTop
[0]), 
 825                         max(yTextExtentBottom
[1],yTextExtentTop
[1])) 
 828             yTextExtent
= (0,0) # No text for ticks 
 830         # TextExtents for Title and Axis Labels 
 831         titleWH
, xLabelWH
, yLabelWH
= self
._titleLablesWH
(dc
, graphics
) 
 833         # TextExtents for Legend 
 834         legendBoxWH
, legendSymExt
, legendTextExt 
= self
._legendWH
(dc
, graphics
) 
 836         # room around graph area 
 837         rhsW
= max(xTextExtent
[0], legendBoxWH
[0]) # use larger of number width or legend width 
 838         lhsW
= yTextExtent
[0]+ yLabelWH
[1] 
 839         bottomH
= max(xTextExtent
[1], yTextExtent
[1]/2.)+ xLabelWH
[1] 
 840         topH
= yTextExtent
[1]/2. + titleWH
[1] 
 841         textSize_scale
= _Numeric
.array([rhsW
+lhsW
,bottomH
+topH
]) # make plot area smaller by text size 
 842         textSize_shift
= _Numeric
.array([lhsW
, bottomH
])          # shift plot area by this amount 
 844         # drawing title and labels text 
 845         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
 846         titlePos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- titleWH
[0]/2., 
 847                  self
.plotbox_origin
[1]- self
.plotbox_size
[1]) 
 848         dc
.DrawText(graphics
.getTitle(),titlePos
[0],titlePos
[1]) 
 849         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 850         xLabelPos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- xLabelWH
[0]/2., 
 851                  self
.plotbox_origin
[1]- xLabelWH
[1]) 
 852         dc
.DrawText(graphics
.getXLabel(),xLabelPos
[0],xLabelPos
[1]) 
 853         yLabelPos
= (self
.plotbox_origin
[0], 
 854                  self
.plotbox_origin
[1]- bottomH
- (self
.plotbox_size
[1]-bottomH
-topH
)/2.+ yLabelWH
[0]/2.) 
 855         if graphics
.getYLabel():  # bug fix for Linux 
 856             dc
.DrawRotatedText(graphics
.getYLabel(),yLabelPos
[0],yLabelPos
[1],90) 
 858         # drawing legend makers and text 
 859         if self
._legendEnabled
: 
 860             self
._drawLegend
(dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
) 
 862         # allow for scaling and shifting plotted points 
 863         scale 
= (self
.plotbox_size
-textSize_scale
) / (p2
-p1
)* _Numeric
.array((1,-1)) 
 864         shift 
= -p1
*scale 
+ self
.plotbox_origin 
+ textSize_shift 
* _Numeric
.array((1,-1)) 
 865         self
._pointScale
= scale  
# make available for mouse events 
 866         self
._pointShift
= shift        
 
 867         self
._drawAxes
(dc
, p1
, p2
, scale
, shift
, xticks
, yticks
) 
 869         graphics
.scaleAndShift(scale
, shift
) 
 870         graphics
.setPrinterScale(self
.printerScale
)  # thicken up lines and markers if printing 
 872         # set clipping area so drawing does not occur outside axis box 
 873         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(p1
, p2
) 
 874         dc
.SetClippingRegion(ptx
,pty
,rectWidth
,rectHeight
) 
 875         # Draw the lines and markers 
 876         #start = _time.clock() 
 878         # print "entire graphics drawing took: %f second"%(_time.clock() - start) 
 879         # remove the clipping region 
 880         dc
.DestroyClippingRegion() 
 883     def Redraw(self
, dc
= None): 
 884         """Redraw the existing plot.""" 
 885         if self
.last_draw 
is not None: 
 886             graphics
, xAxis
, yAxis
= self
.last_draw
 
 887             self
.Draw(graphics
,xAxis
,yAxis
,dc
) 
 890         """Erase the window.""" 
 891         self
.last_PointLabel 
= None        #reset pointLabel 
 892         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
._Buffer
) 
 894         self
.last_draw 
= None 
 896     def Zoom(self
, Center
, Ratio
): 
 898             Centers on the X,Y coords given in Center 
 899             Zooms by the Ratio = (Xratio, Yratio) given 
 901         self
.last_PointLabel 
= None   #reset maker 
 903         if self
.last_draw 
!= None: 
 904             (graphics
, xAxis
, yAxis
) = self
.last_draw
 
 905             w 
= (xAxis
[1] - xAxis
[0]) * Ratio
[0] 
 906             h 
= (yAxis
[1] - yAxis
[0]) * Ratio
[1] 
 907             xAxis 
= ( x 
- w
/2, x 
+ w
/2 ) 
 908             yAxis 
= ( y 
- h
/2, y 
+ h
/2 ) 
 909             self
.Draw(graphics
, xAxis
, yAxis
) 
 911     def GetClosestPoints(self
, pntXY
, pointScaled
= True): 
 913             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 915             Returns [] if no curves are being plotted. 
 918             if pointScaled == True based on screen coords 
 919             if pointScaled == False based on user coords 
 921         if self
.last_draw 
== None: 
 924         graphics
, xAxis
, yAxis
= self
.last_draw
 
 926         for curveNum
,obj 
in enumerate(graphics
): 
 927             #check there are points in the curve 
 928             if len(obj
.points
) == 0: 
 929                 continue  #go to next obj 
 930             #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 931             cn 
= [curveNum
]+ [obj
.getLegend()]+ obj
.getClosestPoint( pntXY
, pointScaled
) 
 935     def GetClosetPoint(self
, pntXY
, pointScaled
= True): 
 937             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 938             list for only the closest curve. 
 939             Returns [] if no curves are being plotted. 
 942             if pointScaled == True based on screen coords 
 943             if pointScaled == False based on user coords 
 945         #closest points on screen based on screen scaling (pointScaled= True) 
 946         #list [curveNumber, index, pointXY, scaledXY, distance] for each curve 
 947         closestPts
= self
.GetClosestPoints(pntXY
, pointScaled
) 
 949             return []  #no graph present 
 950         #find one with least distance 
 951         dists 
= [c
[-1] for c 
in closestPts
] 
 952         mdist 
= min(dists
)  #Min dist 
 953         i 
= dists
.index(mdist
)  #index for min dist 
 954         return closestPts
[i
]  #this is the closest point on closest curve 
 956     def UpdatePointLabel(self
, mDataDict
): 
 957         """Updates the pointLabel point on screen with data contained in 
 960             mDataDict will be passed to your function set by 
 961             SetPointLabelFunc.  It can contain anything you 
 962             want to display on the screen at the scaledXY point 
 965             This function can be called from parent window with onClick, 
 968         if self
.last_PointLabel 
!= None: 
 970             if mDataDict
["pointXY"] != self
.last_PointLabel
["pointXY"]: 
 972                 self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
 973                 self
._drawPointLabel
(mDataDict
) #plot new 
 975             #just plot new with no erase 
 976             self
._drawPointLabel
(mDataDict
) #plot new 
 978         self
.last_PointLabel 
= mDataDict
 
 980     # event handlers ********************************** 
 981     def OnMotion(self
, event
): 
 982         if self
._zoomEnabled 
and event
.LeftIsDown(): 
 984                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
 986                 self
._hasDragged
= True 
 987             self
._zoomCorner
2[0], self
._zoomCorner
2[1] = self
.GetXY(event
) 
 988             self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # add new 
 990     def OnMouseLeftDown(self
,event
): 
 991         self
._zoomCorner
1[0], self
._zoomCorner
1[1]= self
.GetXY(event
) 
 993     def OnMouseLeftUp(self
, event
): 
 994         if self
._zoomEnabled
: 
 995             if self
._hasDragged 
== True: 
 996                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
 997                 self
._zoomCorner
2[0], self
._zoomCorner
2[1]= self
.GetXY(event
) 
 998                 self
._hasDragged 
= False  # reset flag 
 999                 minX
, minY
= _Numeric
.minimum( self
._zoomCorner
1, self
._zoomCorner
2) 
1000                 maxX
, maxY
= _Numeric
.maximum( self
._zoomCorner
1, self
._zoomCorner
2) 
1001                 self
.last_PointLabel 
= None        #reset pointLabel 
1002                 if self
.last_draw 
!= None: 
1003                     self
.Draw(self
.last_draw
[0], xAxis 
= (minX
,maxX
), yAxis 
= (minY
,maxY
), dc 
= None) 
1004             #else: # A box has not been drawn, zoom in on a point 
1005             ## this interfered with the double click, so I've disables it. 
1006             #    X,Y = self.GetXY(event) 
1007             #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) ) 
1009     def OnMouseDoubleClick(self
,event
): 
1010         if self
._zoomEnabled
: 
1013     def OnMouseRightDown(self
,event
): 
1014         if self
._zoomEnabled
: 
1015             X
,Y 
= self
.GetXY(event
) 
1016             self
.Zoom( (X
,Y
), (self
._zoomOutFactor
, self
._zoomOutFactor
) ) 
1018     def OnPaint(self
, event
): 
1019         # All that is needed here is to draw the buffer to screen 
1020         if self
.last_PointLabel 
!= None: 
1021             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1022             self
.last_PointLabel 
= None 
1023         dc 
= wx
.BufferedPaintDC(self
, self
._Buffer
) 
1025     def OnSize(self
,event
): 
1026         # The Buffer init is done here, to make sure the buffer is always 
1027         # the same size as the Window 
1028         Size  
= self
.GetClientSize() 
1029         if Size
.width 
<= 0 or Size
.height 
<= 0: 
1032         # Make new offscreen bitmap: this bitmap will always have the 
1033         # current drawing in it, so it can be used to save the image to 
1034         # a file, or whatever. 
1035         self
._Buffer 
= wx
.EmptyBitmap(Size
[0],Size
[1]) 
1038         self
.last_PointLabel 
= None        #reset pointLabel 
1040         if self
.last_draw 
is None: 
1043             graphics
, xSpec
, ySpec 
= self
.last_draw
 
1044             self
.Draw(graphics
,xSpec
,ySpec
) 
1046     def OnLeave(self
, event
): 
1047         """Used to erase pointLabel when mouse outside window""" 
1048         if self
.last_PointLabel 
!= None: 
1049             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1050             self
.last_PointLabel 
= None 
1053     # Private Methods ************************************************** 
1054     def _setSize(self
, width
=None, height
=None): 
1055         """DC width and height.""" 
1057             (self
.width
,self
.height
) = self
.GetClientSize() 
1059             self
.width
, self
.height
= width
,height    
 
1060         self
.plotbox_size 
= 0.97*_Numeric
.array([self
.width
, self
.height
]) 
1061         xo 
= 0.5*(self
.width
-self
.plotbox_size
[0]) 
1062         yo 
= self
.height
-0.5*(self
.height
-self
.plotbox_size
[1]) 
1063         self
.plotbox_origin 
= _Numeric
.array([xo
, yo
]) 
1065     def _setPrinterScale(self
, scale
): 
1066         """Used to thicken lines and increase marker size for print out.""" 
1067         # line thickness on printer is very thin at 600 dot/in. Markers small 
1068         self
.printerScale
= scale
 
1070     def _printDraw(self
, printDC
): 
1071         """Used for printing.""" 
1072         if self
.last_draw 
!= None: 
1073             graphics
, xSpec
, ySpec
= self
.last_draw
 
1074             self
.Draw(graphics
,xSpec
,ySpec
,printDC
) 
1076     def _drawPointLabel(self
, mDataDict
): 
1077         """Draws and erases pointLabels""" 
1078         width 
= self
._Buffer
.GetWidth() 
1079         height 
= self
._Buffer
.GetHeight() 
1080         tmp_Buffer 
= wx
.EmptyBitmap(width
,height
) 
1082         dcs
.SelectObject(tmp_Buffer
) 
1085         self
._pointLabelFunc
(dcs
,mDataDict
)  #custom user pointLabel function 
1088         dc 
= wx
.ClientDC( self 
) 
1089         #this will erase if called twice 
1090         dc
.Blit(0, 0, width
, height
, dcs
, 0, 0, wx
.EQUIV
)  #(NOT src) XOR dst 
1093     def _drawLegend(self
,dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
): 
1094         """Draws legend symbols and text""" 
1095         # top right hand corner of graph box is ref corner 
1096         trhc
= self
.plotbox_origin
+ (self
.plotbox_size
-[rhsW
,topH
])*[1,-1] 
1097         legendLHS
= .091* legendBoxWH
[0]  # border space between legend sym and graph box 
1098         lineHeight
= max(legendSymExt
[1], legendTextExt
[1]) * 1.1 #1.1 used as space between lines 
1099         dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1100         for i 
in range(len(graphics
)): 
1103             if isinstance(o
,PolyMarker
): 
1104                 # draw marker with legend 
1105                 pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0]/2., trhc
[1]+s
+lineHeight
/2.) 
1106                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt
])) 
1107             elif isinstance(o
,PolyLine
): 
1108                 # draw line with legend 
1109                 pnt1
= (trhc
[0]+legendLHS
, trhc
[1]+s
+lineHeight
/2.) 
1110                 pnt2
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.) 
1111                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt1
,pnt2
])) 
1113                 raise TypeError, "object is neither PolyMarker or PolyLine instance" 
1115             pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.-legendTextExt
[1]/2) 
1116             dc
.DrawText(o
.getLegend(),pnt
[0],pnt
[1]) 
1117         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) # reset 
1119     def _titleLablesWH(self
, dc
, graphics
): 
1120         """Draws Title and labels and returns width and height for each""" 
1121         # TextExtents for Title and Axis Labels 
1122         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
1123         title
= graphics
.getTitle() 
1124         titleWH
= dc
.GetTextExtent(title
) 
1125         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1126         xLabel
, yLabel
= graphics
.getXLabel(),graphics
.getYLabel() 
1127         xLabelWH
= dc
.GetTextExtent(xLabel
) 
1128         yLabelWH
= dc
.GetTextExtent(yLabel
) 
1129         return titleWH
, xLabelWH
, yLabelWH
 
1131     def _legendWH(self
, dc
, graphics
): 
1132         """Returns the size in screen units for legend box""" 
1133         if self
._legendEnabled 
!= True: 
1134             legendBoxWH
= symExt
= txtExt
= (0,0) 
1136             # find max symbol size 
1137             symExt
= graphics
.getSymExtent(self
.printerScale
) 
1138             # find max legend text extent 
1139             dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1140             txtList
= graphics
.getLegendNames() 
1141             txtExt
= dc
.GetTextExtent(txtList
[0]) 
1142             for txt 
in graphics
.getLegendNames()[1:]: 
1143                 txtExt
= _Numeric
.maximum(txtExt
,dc
.GetTextExtent(txt
)) 
1144             maxW
= symExt
[0]+txtExt
[0]     
1145             maxH
= max(symExt
[1],txtExt
[1]) 
1146             # padding .1 for lhs of legend box and space between lines 
1148             maxH
= maxH
* 1.1 * len(txtList
) 
1149             dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1150             legendBoxWH
= (maxW
,maxH
) 
1151         return (legendBoxWH
, symExt
, txtExt
) 
1153     def _drawRubberBand(self
, corner1
, corner2
): 
1154         """Draws/erases rect box from corner1 to corner2""" 
1155         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(corner1
, corner2
) 
1157         dc 
= wx
.ClientDC( self 
) 
1159         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1160         dc
.SetBrush(wx
.Brush( wx
.WHITE
, wx
.TRANSPARENT 
) ) 
1161         dc
.SetLogicalFunction(wx
.INVERT
) 
1162         dc
.DrawRectangle( ptx
,pty
, rectWidth
,rectHeight
) 
1163         dc
.SetLogicalFunction(wx
.COPY
) 
1166     def _getFont(self
,size
): 
1167         """Take font size, adjusts if printing and returns wx.Font""" 
1168         s 
= size
*self
.printerScale
 
1170         # Linux speed up to get font from cache rather than X font server 
1171         key 
= (int(s
), of
.GetFamily (), of
.GetStyle (), of
.GetWeight ()) 
1172         font 
= self
._fontCache
.get (key
, None) 
1174             return font                 
# yeah! cache hit 
1176             font 
=  wx
.Font(int(s
), of
.GetFamily(), of
.GetStyle(), of
.GetWeight()) 
1177             self
._fontCache
[key
] = font
 
1181     def _point2ClientCoord(self
, corner1
, corner2
): 
1182         """Converts user point coords to client screen int coords x,y,width,height""" 
1183         c1
= _Numeric
.array(corner1
) 
1184         c2
= _Numeric
.array(corner2
) 
1185         # convert to screen coords 
1186         pt1
= c1
*self
._pointScale
+self
._pointShift
 
1187         pt2
= c2
*self
._pointScale
+self
._pointShift
 
1188         # make height and width positive 
1189         pul
= _Numeric
.minimum(pt1
,pt2
) # Upper left corner 
1190         plr
= _Numeric
.maximum(pt1
,pt2
) # Lower right corner 
1191         rectWidth
, rectHeight
= plr
-pul
 
1193         return ptx
, pty
, rectWidth
, rectHeight 
 
1195     def _axisInterval(self
, spec
, lower
, upper
): 
1196         """Returns sensible axis range for given spec""" 
1197         if spec 
== 'none' or spec 
== 'min': 
1199                 return lower
-0.5, upper
+0.5 
1202         elif spec 
== 'auto': 
1205                 return lower
-0.5, upper
+0.5 
1206             log 
= _Numeric
.log10(range) 
1207             power 
= _Numeric
.floor(log
) 
1208             fraction 
= log
-power
 
1209             if fraction 
<= 0.05: 
1212             lower 
= lower 
- lower 
% grid
 
1215                 upper 
= upper 
- mod 
+ grid
 
1217         elif type(spec
) == type(()): 
1224             raise ValueError, str(spec
) + ': illegal axis specification' 
1226     def _drawAxes(self
, dc
, p1
, p2
, scale
, shift
, xticks
, yticks
): 
1228         penWidth
= self
.printerScale        
# increases thickness for printing only 
1229         dc
.SetPen(wx
.Pen(wx
.NamedColour('BLACK'), penWidth
)) 
1231         # set length of tick marks--long ones make grid 
1232         if self
._gridEnabled
: 
1233             x
,y
,width
,height
= self
._point
2ClientCoord
(p1
,p2
) 
1234             if self
._gridEnabled 
== 'Horizontal': 
1235                 yTickLength
= width
/2.0 +1 
1236                 xTickLength
= 3 * self
.printerScale
 
1237             elif self
._gridEnabled 
== 'Vertical': 
1238                 yTickLength
= 3 * self
.printerScale
 
1239                 xTickLength
= height
/2.0 +1 
1241                 yTickLength
= width
/2.0 +1 
1242                 xTickLength
= height
/2.0 +1 
1244             yTickLength
= 3 * self
.printerScale  
# lengthens lines for printing 
1245             xTickLength
= 3 * self
.printerScale
 
1247         if self
._xSpec 
is not 'none': 
1248             lower
, upper 
= p1
[0],p2
[0] 
1250             for y
, d 
in [(p1
[1], -xTickLength
), (p2
[1], xTickLength
)]:   # miny, maxy and tick lengths 
1251                 a1 
= scale
*_Numeric
.array([lower
, y
])+shift
 
1252                 a2 
= scale
*_Numeric
.array([upper
, y
])+shift
 
1253                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1])  # draws upper and lower axis line 
1254                 for x
, label 
in xticks
: 
1255                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1256                     dc
.DrawLine(pt
[0],pt
[1],pt
[0],pt
[1] + d
) # draws tick mark d units 
1258                         dc
.DrawText(label
,pt
[0],pt
[1]) 
1259                 text 
= 0  # axis values not drawn on top side 
1261         if self
._ySpec 
is not 'none': 
1262             lower
, upper 
= p1
[1],p2
[1] 
1264             h 
= dc
.GetCharHeight() 
1265             for x
, d 
in [(p1
[0], -yTickLength
), (p2
[0], yTickLength
)]: 
1266                 a1 
= scale
*_Numeric
.array([x
, lower
])+shift
 
1267                 a2 
= scale
*_Numeric
.array([x
, upper
])+shift
 
1268                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1]) 
1269                 for y
, label 
in yticks
: 
1270                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1271                     dc
.DrawLine(pt
[0],pt
[1],pt
[0]-d
,pt
[1]) 
1273                         dc
.DrawText(label
,pt
[0]-dc
.GetTextExtent(label
)[0], 
1275                 text 
= 0    # axis values not drawn on right side 
1277     def _ticks(self
, lower
, upper
): 
1278         ideal 
= (upper
-lower
)/7. 
1279         log 
= _Numeric
.log10(ideal
) 
1280         power 
= _Numeric
.floor(log
) 
1281         fraction 
= log
-power
 
1284         for f
, lf 
in self
._multiples
: 
1285             e 
= _Numeric
.fabs(fraction
-lf
) 
1289         grid 
= factor 
* 10.**power
 
1290         if power 
> 4 or power 
< -4: 
1293             digits 
= max(1, int(power
)) 
1294             format 
= '%' + `digits`
+'.0f' 
1296             digits 
= -int(power
) 
1297             format 
= '%'+`digits
+2`
+'.'+`digits`
+'f' 
1299         t 
= -grid
*_Numeric
.floor(-lower
/grid
) 
1301             ticks
.append( (t
, format 
% (t
,)) ) 
1305     _multiples 
= [(2., _Numeric
.log10(2.)), (5., _Numeric
.log10(5.))] 
1308 #------------------------------------------------------------------------------- 
1309 # Used to layout the printer page 
1311 class PlotPrintout(wx
.Printout
): 
1312     """Controls how the plot is made in printing and previewing""" 
1313     # Do not change method names in this class, 
1314     # we have to override wx.Printout methods here! 
1315     def __init__(self
, graph
): 
1316         """graph is instance of plotCanvas to be printed or previewed""" 
1317         wx
.Printout
.__init
__(self
) 
1320     def HasPage(self
, page
): 
1326     def GetPageInfo(self
): 
1327         return (1, 1, 1, 1)  # disable page numbers 
1329     def OnPrintPage(self
, page
): 
1330         dc 
= self
.GetDC()  # allows using floats for certain functions 
1331 ##        print "PPI Printer",self.GetPPIPrinter() 
1332 ##        print "PPI Screen", self.GetPPIScreen() 
1333 ##        print "DC GetSize", dc.GetSize() 
1334 ##        print "GetPageSizePixels", self.GetPageSizePixels() 
1335         # Note PPIScreen does not give the correct number 
1336         # Calulate everything for printer and then scale for preview 
1337         PPIPrinter
= self
.GetPPIPrinter()        # printer dots/inch (w,h) 
1338         #PPIScreen= self.GetPPIScreen()          # screen dots/inch (w,h) 
1339         dcSize
= dc
.GetSize()                    # DC size 
1340         pageSize
= self
.GetPageSizePixels() # page size in terms of pixcels 
1341         clientDcSize
= self
.graph
.GetClientSize() 
1343         # find what the margins are (mm) 
1344         margLeftSize
,margTopSize
= self
.graph
.pageSetupData
.GetMarginTopLeft() 
1345         margRightSize
, margBottomSize
= self
.graph
.pageSetupData
.GetMarginBottomRight() 
1347         # calculate offset and scale for dc 
1348         pixLeft
= margLeftSize
*PPIPrinter
[0]/25.4  # mm*(dots/in)/(mm/in) 
1349         pixRight
= margRightSize
*PPIPrinter
[0]/25.4     
1350         pixTop
= margTopSize
*PPIPrinter
[1]/25.4 
1351         pixBottom
= margBottomSize
*PPIPrinter
[1]/25.4 
1353         plotAreaW
= pageSize
[0]-(pixLeft
+pixRight
) 
1354         plotAreaH
= pageSize
[1]-(pixTop
+pixBottom
) 
1356         # ratio offset and scale to screen size if preview 
1357         if self
.IsPreview(): 
1358             ratioW
= float(dcSize
[0])/pageSize
[0] 
1359             ratioH
= float(dcSize
[1])/pageSize
[1] 
1365         # rescale plot to page or preview plot area 
1366         self
.graph
._setSize
(plotAreaW
,plotAreaH
) 
1368         # Set offset and scale 
1369         dc
.SetDeviceOrigin(pixLeft
,pixTop
) 
1371         # Thicken up pens and increase marker size for printing 
1372         ratioW
= float(plotAreaW
)/clientDcSize
[0] 
1373         ratioH
= float(plotAreaH
)/clientDcSize
[1] 
1374         aveScale
= (ratioW
+ratioH
)/2 
1375         self
.graph
._setPrinterScale
(aveScale
)  # tickens up pens for printing 
1377         self
.graph
._printDraw
(dc
) 
1378         # rescale back to original 
1379         self
.graph
._setSize
() 
1380         self
.graph
._setPrinterScale
(1) 
1381         self
.graph
.Redraw()     #to get point label scale and shift correct 
1388 #--------------------------------------------------------------------------- 
1389 # if running standalone... 
1391 #     ...a sample implementation using the above 
1394 def _draw1Objects(): 
1395     # 100 points sin function, plotted as green circles 
1396     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1397     data1
.shape 
= (100, 2) 
1398     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1399     markers1 
= PolyMarker(data1
, legend
='Green Markers', colour
='green', marker
='circle',size
=1) 
1401     # 50 points cos function, plotted as red line 
1402     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1403     data1
.shape 
= (50,2) 
1404     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1405     lines 
= PolyLine(data1
, legend
= 'Red Line', colour
='red') 
1407     # A few more points... 
1409     markers2 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1410                           (3.*pi
/4., -1)], legend
='Cross Legend', colour
='blue', 
1413     return PlotGraphics([markers1
, lines
, markers2
],"Graph Title", "X Axis", "Y Axis") 
1415 def _draw2Objects(): 
1416     # 100 points sin function, plotted as green dots 
1417     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1418     data1
.shape 
= (100, 2) 
1419     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1420     line1 
= PolyLine(data1
, legend
='Green Line', colour
='green', width
=6, style
=wx
.DOT
) 
1422     # 50 points cos function, plotted as red dot-dash 
1423     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1424     data1
.shape 
= (50,2) 
1425     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1426     line2 
= PolyLine(data1
, legend
='Red Line', colour
='red', width
=3, style
= wx
.DOT_DASH
) 
1428     # A few more points... 
1430     markers1 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1431                           (3.*pi
/4., -1)], legend
='Cross Hatch Square', colour
='blue', width
= 3, size
= 6, 
1432                           fillcolour
= 'red', fillstyle
= wx
.CROSSDIAG_HATCH
, 
1435     return PlotGraphics([markers1
, line1
, line2
], "Big Markers with Different Line Styles") 
1437 def _draw3Objects(): 
1438     markerList
= ['circle', 'dot', 'square', 'triangle', 'triangle_down', 
1439                 'cross', 'plus', 'circle'] 
1441     for i 
in range(len(markerList
)): 
1442         m
.append(PolyMarker([(2*i
+.5,i
+.5)], legend
=markerList
[i
], colour
='blue', 
1443                           marker
=markerList
[i
])) 
1444     return PlotGraphics(m
, "Selection of Markers", "Minimal Axis", "No Axis") 
1446 def _draw4Objects(): 
1448     data1 
= _Numeric
.arange(5e5
,1e6
,10) 
1449     data1
.shape 
= (25000, 2) 
1450     line1 
= PolyLine(data1
, legend
='Wide Line', colour
='green', width
=5) 
1452     # A few more points... 
1453     markers2 
= PolyMarker(data1
, legend
='Square', colour
='blue', 
1455     return PlotGraphics([line1
, markers2
], "25,000 Points", "Value X", "") 
1457 def _draw5Objects(): 
1458     # Empty graph with axis defined but no points/lines 
1460     line1 
= PolyLine(points
, legend
='Wide Line', colour
='green', width
=5) 
1461     return PlotGraphics([line1
], "Empty Plot With Just Axes", "Value X", "Value Y") 
1463 def _draw6Objects(): 
1465     points1
=[(1,0), (1,10)] 
1466     line1 
= PolyLine(points1
, colour
='green', legend
='Feb.', width
=10) 
1467     points1g
=[(2,0), (2,4)] 
1468     line1g 
= PolyLine(points1g
, colour
='red', legend
='Mar.', width
=10) 
1469     points1b
=[(3,0), (3,6)] 
1470     line1b 
= PolyLine(points1b
, colour
='blue', legend
='Apr.', width
=10) 
1472     points2
=[(4,0), (4,12)] 
1473     line2 
= PolyLine(points2
, colour
='Yellow', legend
='May', width
=10) 
1474     points2g
=[(5,0), (5,8)] 
1475     line2g 
= PolyLine(points2g
, colour
='orange', legend
='June', width
=10) 
1476     points2b
=[(6,0), (6,4)] 
1477     line2b 
= PolyLine(points2b
, colour
='brown', legend
='July', width
=10) 
1479     return PlotGraphics([line1
, line1g
, line1b
, line2
, line2g
, line2b
], 
1480                         "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students") 
1483 class TestFrame(wx
.Frame
): 
1484     def __init__(self
, parent
, id, title
): 
1485         wx
.Frame
.__init
__(self
, parent
, id, title
, 
1486                           wx
.DefaultPosition
, (600, 400)) 
1488         # Now Create the menu bar and items 
1489         self
.mainmenu 
= wx
.MenuBar() 
1492         menu
.Append(200, 'Page Setup...', 'Setup the printer page') 
1493         self
.Bind(wx
.EVT_MENU
, self
.OnFilePageSetup
, id=200) 
1495         menu
.Append(201, 'Print Preview...', 'Show the current plot on page') 
1496         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrintPreview
, id=201) 
1498         menu
.Append(202, 'Print...', 'Print the current plot') 
1499         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrint
, id=202) 
1501         menu
.Append(203, 'Save Plot...', 'Save current plot') 
1502         self
.Bind(wx
.EVT_MENU
, self
.OnSaveFile
, id=203) 
1504         menu
.Append(205, 'E&xit', 'Enough of this already!') 
1505         self
.Bind(wx
.EVT_MENU
, self
.OnFileExit
, id=205) 
1506         self
.mainmenu
.Append(menu
, '&File') 
1509         menu
.Append(206, 'Draw1', 'Draw plots1') 
1510         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw1
, id=206) 
1511         menu
.Append(207, 'Draw2', 'Draw plots2') 
1512         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw2
, id=207) 
1513         menu
.Append(208, 'Draw3', 'Draw plots3') 
1514         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw3
, id=208) 
1515         menu
.Append(209, 'Draw4', 'Draw plots4') 
1516         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw4
, id=209) 
1517         menu
.Append(210, 'Draw5', 'Draw plots5') 
1518         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw5
, id=210) 
1519         menu
.Append(260, 'Draw6', 'Draw plots6') 
1520         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw6
, id=260) 
1523         menu
.Append(211, '&Redraw', 'Redraw plots') 
1524         self
.Bind(wx
.EVT_MENU
,self
.OnPlotRedraw
, id=211) 
1525         menu
.Append(212, '&Clear', 'Clear canvas') 
1526         self
.Bind(wx
.EVT_MENU
,self
.OnPlotClear
, id=212) 
1527         menu
.Append(213, '&Scale', 'Scale canvas') 
1528         self
.Bind(wx
.EVT_MENU
,self
.OnPlotScale
, id=213)  
1529         menu
.Append(214, 'Enable &Zoom', 'Enable Mouse Zoom', kind
=wx
.ITEM_CHECK
) 
1530         self
.Bind(wx
.EVT_MENU
,self
.OnEnableZoom
, id=214)  
1531         menu
.Append(215, 'Enable &Grid', 'Turn on Grid', kind
=wx
.ITEM_CHECK
) 
1532         self
.Bind(wx
.EVT_MENU
,self
.OnEnableGrid
, id=215) 
1533         menu
.Append(220, 'Enable &Legend', 'Turn on Legend', kind
=wx
.ITEM_CHECK
) 
1534         self
.Bind(wx
.EVT_MENU
,self
.OnEnableLegend
, id=220) 
1535         menu
.Append(222, 'Enable &Point Label', 'Show Closest Point', kind
=wx
.ITEM_CHECK
) 
1536         self
.Bind(wx
.EVT_MENU
,self
.OnEnablePointLabel
, id=222) 
1538         menu
.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit') 
1539         self
.Bind(wx
.EVT_MENU
,self
.OnScrUp
, id=225)  
1540         menu
.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units') 
1541         self
.Bind(wx
.EVT_MENU
,self
.OnScrRt
, id=230) 
1542         menu
.Append(235, '&Plot Reset', 'Reset to original plot') 
1543         self
.Bind(wx
.EVT_MENU
,self
.OnReset
, id=235) 
1545         self
.mainmenu
.Append(menu
, '&Plot') 
1548         menu
.Append(300, '&About', 'About this thing...') 
1549         self
.Bind(wx
.EVT_MENU
, self
.OnHelpAbout
, id=300) 
1550         self
.mainmenu
.Append(menu
, '&Help') 
1552         self
.SetMenuBar(self
.mainmenu
) 
1554         # A status bar to tell people what's happening 
1555         self
.CreateStatusBar(1) 
1557         self
.client 
= PlotCanvas(self
) 
1558         #define the function for drawing pointLabels 
1559         self
.client
.SetPointLabelFunc(self
.DrawPointLabel
) 
1560         # Create mouse event for showing cursor coords in status bar 
1561         self
.client
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
1562         # Show closest point when enabled 
1563         self
.client
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
1567     def DrawPointLabel(self
, dc
, mDataDict
): 
1568         """This is the fuction that defines how the pointLabels are plotted 
1569             dc - DC that will be passed 
1570             mDataDict - Dictionary of data that you want to use for the pointLabel 
1572             As an example I have decided I want a box at the curve point 
1573             with some text information about the curve plotted below. 
1574             Any wxDC method can be used. 
1577         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1578         dc
.SetBrush(wx
.Brush( wx
.BLACK
, wx
.SOLID 
) ) 
1580         sx
, sy 
= mDataDict
["scaledXY"] #scaled x,y of closest point 
1581         dc
.DrawRectangle( sx
-5,sy
-5, 10, 10)  #10by10 square centered on point 
1582         px
,py 
= mDataDict
["pointXY"] 
1583         cNum 
= mDataDict
["curveNum"] 
1584         pntIn 
= mDataDict
["pIndex"] 
1585         legend 
= mDataDict
["legend"] 
1586         #make a string to display 
1587         s 
= "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum
, legend
, px
, py
, pntIn
) 
1588         dc
.DrawText(s
, sx 
, sy
+1) 
1591     def OnMouseLeftDown(self
,event
): 
1592         s
= "Left Mouse Down at Point: (%.4f, %.4f)" % self
.client
.GetXY(event
) 
1593         self
.SetStatusText(s
) 
1594         event
.Skip()            #allows plotCanvas OnMouseLeftDown to be called 
1596     def OnMotion(self
, event
): 
1597         #show closest point (when enbled) 
1598         if self
.client
.GetEnablePointLabel() == True: 
1599             #make up dict with info for the pointLabel 
1600             #I've decided to mark the closest point on the closest curve 
1601             dlst
= self
.client
.GetClosetPoint( self
.client
.GetXY(event
), pointScaled
= True) 
1602             if dlst 
!= []:    #returns [] if none 
1603                 curveNum
, legend
, pIndex
, pointXY
, scaledXY
, distance 
= dlst
 
1604                 #make up dictionary to pass to my user function (see DrawPointLabel)  
1605                 mDataDict
= {"curveNum":curveNum
, "legend":legend
, "pIndex":pIndex
,\
 
1606                             "pointXY":pointXY
, "scaledXY":scaledXY
} 
1607                 #pass dict to update the pointLabel 
1608                 self
.client
.UpdatePointLabel(mDataDict
) 
1609         event
.Skip()           #go to next handler 
1611     def OnFilePageSetup(self
, event
): 
1612         self
.client
.PageSetup() 
1614     def OnFilePrintPreview(self
, event
): 
1615         self
.client
.PrintPreview() 
1617     def OnFilePrint(self
, event
): 
1618         self
.client
.Printout() 
1620     def OnSaveFile(self
, event
): 
1621         self
.client
.SaveFile() 
1623     def OnFileExit(self
, event
): 
1626     def OnPlotDraw1(self
, event
): 
1627         self
.resetDefaults() 
1628         self
.client
.Draw(_draw1Objects()) 
1630     def OnPlotDraw2(self
, event
): 
1631         self
.resetDefaults() 
1632         self
.client
.Draw(_draw2Objects()) 
1634     def OnPlotDraw3(self
, event
): 
1635         self
.resetDefaults() 
1636         self
.client
.SetFont(wx
.Font(10,wx
.SCRIPT
,wx
.NORMAL
,wx
.NORMAL
)) 
1637         self
.client
.SetFontSizeAxis(20) 
1638         self
.client
.SetFontSizeLegend(12) 
1639         self
.client
.SetXSpec('min') 
1640         self
.client
.SetYSpec('none') 
1641         self
.client
.Draw(_draw3Objects()) 
1643     def OnPlotDraw4(self
, event
): 
1644         self
.resetDefaults() 
1645         drawObj
= _draw4Objects() 
1646         self
.client
.Draw(drawObj
) 
1648 ##        start = _time.clock()             
1649 ##        for x in range(10): 
1650 ##            self.client.Draw(drawObj) 
1651 ##        print "10 plots of Draw4 took: %f sec."%(_time.clock() - start) 
1654     def OnPlotDraw5(self
, event
): 
1655         # Empty plot with just axes 
1656         self
.resetDefaults() 
1657         drawObj
= _draw5Objects() 
1658         # make the axis X= (0,5), Y=(0,10) 
1659         # (default with None is X= (-1,1), Y= (-1,1)) 
1660         self
.client
.Draw(drawObj
, xAxis
= (0,5), yAxis
= (0,10)) 
1662     def OnPlotDraw6(self
, event
): 
1664         self
.resetDefaults() 
1665         #self.client.SetEnableLegend(True)   #turn on Legend 
1666         #self.client.SetEnableGrid(True)     #turn on Grid 
1667         self
.client
.SetXSpec('none')        #turns off x-axis scale 
1668         self
.client
.SetYSpec('auto') 
1669         self
.client
.Draw(_draw6Objects(), xAxis
= (0,7)) 
1671     def OnPlotRedraw(self
,event
): 
1672         self
.client
.Redraw() 
1674     def OnPlotClear(self
,event
): 
1677     def OnPlotScale(self
, event
): 
1678         if self
.client
.last_draw 
!= None: 
1679             graphics
, xAxis
, yAxis
= self
.client
.last_draw
 
1680             self
.client
.Draw(graphics
,(1,3.05),(0,1)) 
1682     def OnEnableZoom(self
, event
): 
1683         self
.client
.SetEnableZoom(event
.IsChecked()) 
1685     def OnEnableGrid(self
, event
): 
1686         self
.client
.SetEnableGrid(event
.IsChecked()) 
1688     def OnEnableLegend(self
, event
): 
1689         self
.client
.SetEnableLegend(event
.IsChecked()) 
1691     def OnEnablePointLabel(self
, event
): 
1692         self
.client
.SetEnablePointLabel(event
.IsChecked()) 
1694     def OnScrUp(self
, event
): 
1695         self
.client
.ScrollUp(1) 
1697     def OnScrRt(self
,event
): 
1698         self
.client
.ScrollRight(2) 
1700     def OnReset(self
,event
): 
1703     def OnHelpAbout(self
, event
): 
1704         from wx
.lib
.dialogs 
import ScrolledMessageDialog
 
1705         about 
= ScrolledMessageDialog(self
, __doc__
, "About...") 
1708     def resetDefaults(self
): 
1709         """Just to reset the fonts back to the PlotCanvas defaults""" 
1710         self
.client
.SetFont(wx
.Font(10,wx
.SWISS
,wx
.NORMAL
,wx
.NORMAL
)) 
1711         self
.client
.SetFontSizeAxis(10) 
1712         self
.client
.SetFontSizeLegend(7) 
1713         self
.client
.SetXSpec('auto') 
1714         self
.client
.SetYSpec('auto') 
1719     class MyApp(wx
.App
): 
1721             wx
.InitAllImageHandlers() 
1722             frame 
= TestFrame(None, -1, "PlotCanvas") 
1724             self
.SetTopWindow(frame
) 
1731 if __name__ 
== '__main__':