]>
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         if not isinstance(colour
, wx
.Colour
): 
 205             colour 
= wx
.NamedColour(colour
) 
 206         pen 
= wx
.Pen(colour
, width
, style
) 
 207         pen
.SetCap(wx
.CAP_BUTT
) 
 210             dc
.DrawLines(self
.scaled
) 
 212             dc
.DrawLines(coord
) # draw legend line 
 214     def getSymExtent(self
, printerScale
): 
 215         """Width and Height of Marker""" 
 216         h
= self
.attributes
['width'] * printerScale
 
 221 class PolyMarker(PolyPoints
): 
 222     """Class to define marker type and style 
 223         - All methods except __init__ are private. 
 226     _attributes 
= {'colour': 'black', 
 230                    'fillstyle': wx
.SOLID
, 
 234     def __init__(self
, points
, **attr
): 
 235         """Creates PolyMarker object 
 236         points - sequence (array, tuple or list) of (x,y) points 
 237         **attr - key word attributes 
 239                 'colour'= 'black',          - wx.Pen Colour any wx.NamedColour 
 240                 'width'= 1,                 - Pen width 
 241                 'size'= 2,                  - Marker size 
 242                 'fillcolour'= same as colour,      - wx.Brush Colour any wx.NamedColour 
 243                 'fillstyle'= wx.SOLID,      - wx.Brush fill style (use wx.TRANSPARENT for no fill) 
 244                 'marker'= 'circle'          - Marker shape 
 245                 'legend'= ''                - Marker Legend to display 
 257         PolyPoints
.__init
__(self
, points
, attr
) 
 259     def draw(self
, dc
, printerScale
, coord
= None): 
 260         colour 
= self
.attributes
['colour'] 
 261         width 
= self
.attributes
['width'] * printerScale
 
 262         size 
= self
.attributes
['size'] * printerScale
 
 263         fillcolour 
= self
.attributes
['fillcolour'] 
 264         fillstyle 
= self
.attributes
['fillstyle'] 
 265         marker 
= self
.attributes
['marker'] 
 267         if colour 
and not isinstance(colour
, wx
.Colour
): 
 268             colour 
= wx
.NamedColour(colour
) 
 269         if fillcolour 
and not isinstance(fillcolour
, wx
.Colour
): 
 270             fillcolour 
= wx
.NamedColour(fillcolour
) 
 272         dc
.SetPen(wx
.Pen(colour
, width
)) 
 274             dc
.SetBrush(wx
.Brush(fillcolour
,fillstyle
)) 
 276             dc
.SetBrush(wx
.Brush(colour
, fillstyle
)) 
 278             self
._drawmarkers
(dc
, self
.scaled
, marker
, size
) 
 280             self
._drawmarkers
(dc
, coord
, marker
, size
) # draw legend marker 
 282     def getSymExtent(self
, printerScale
): 
 283         """Width and Height of Marker""" 
 284         s
= 5*self
.attributes
['size'] * printerScale
 
 287     def _drawmarkers(self
, dc
, coords
, marker
,size
=1): 
 288         f 
= eval('self._' +marker
) 
 291     def _circle(self
, dc
, coords
, size
=1): 
 294         rect
= _Numeric
.zeros((len(coords
),4),_Numeric
.Float
)+[0.0,0.0,wh
,wh
] 
 295         rect
[:,0:2]= coords
-[fact
,fact
] 
 296         dc
.DrawEllipseList(rect
.astype(_Numeric
.Int32
)) 
 298     def _dot(self
, dc
, coords
, size
=1): 
 299         dc
.DrawPointList(coords
) 
 301     def _square(self
, dc
, coords
, size
=1): 
 304         rect
= _Numeric
.zeros((len(coords
),4),_Numeric
.Float
)+[0.0,0.0,wh
,wh
] 
 305         rect
[:,0:2]= coords
-[fact
,fact
] 
 306         dc
.DrawRectangleList(rect
.astype(_Numeric
.Int32
)) 
 308     def _triangle(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 _triangle_down(self
, dc
, coords
, size
=1): 
 316         shape
= [(-2.5*size
,-1.44*size
), (2.5*size
,-1.44*size
), (0.0,2.88*size
)] 
 317         poly
= _Numeric
.repeat(coords
,3) 
 318         poly
.shape
= (len(coords
),3,2) 
 320         dc
.DrawPolygonList(poly
.astype(_Numeric
.Int32
)) 
 322     def _cross(self
, dc
, coords
, size
=1): 
 324         for f 
in [[-fact
,-fact
,fact
,fact
],[-fact
,fact
,fact
,-fact
]]: 
 325             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 326             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 328     def _plus(self
, dc
, coords
, size
=1): 
 330         for f 
in [[-fact
,0,fact
,0],[0,-fact
,0,fact
]]: 
 331             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 332             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 335     """Container to hold PolyXXX objects and graph labels 
 336         - All methods except __init__ are private. 
 339     def __init__(self
, objects
, title
='', xLabel
='', yLabel
= ''): 
 340         """Creates PlotGraphics object 
 341         objects - list of PolyXXX objects to make graph 
 342         title - title shown at top of graph 
 343         xLabel - label shown on x-axis 
 344         yLabel - label shown on y-axis 
 346         if type(objects
) not in [list,tuple]: 
 347             raise TypeError, "objects argument should be list or tuple" 
 348         self
.objects 
= objects
 
 353     def boundingBox(self
): 
 354         p1
, p2 
= self
.objects
[0].boundingBox() 
 355         for o 
in self
.objects
[1:]: 
 356             p1o
, p2o 
= o
.boundingBox() 
 357             p1 
= _Numeric
.minimum(p1
, p1o
) 
 358             p2 
= _Numeric
.maximum(p2
, p2o
) 
 361     def scaleAndShift(self
, scale
=(1,1), shift
=(0,0)): 
 362         for o 
in self
.objects
: 
 363             o
.scaleAndShift(scale
, shift
) 
 365     def setPrinterScale(self
, scale
): 
 366         """Thickens up lines and markers only for printing""" 
 367         self
.printerScale
= scale
 
 369     def setXLabel(self
, xLabel
= ''): 
 370         """Set the X axis label on the graph""" 
 373     def setYLabel(self
, yLabel
= ''): 
 374         """Set the Y axis label on the graph""" 
 377     def setTitle(self
, title
= ''): 
 378         """Set the title at the top of graph""" 
 382         """Get x axis label string""" 
 386         """Get y axis label string""" 
 389     def getTitle(self
, title
= ''): 
 390         """Get the title at the top of graph""" 
 394         for o 
in self
.objects
: 
 395             #t=_time.clock()          # profile info 
 396             o
.draw(dc
, self
.printerScale
) 
 398             #print o, "time=", dt 
 400     def getSymExtent(self
, printerScale
): 
 401         """Get max width and height of lines and markers symbols for legend""" 
 402         symExt 
= self
.objects
[0].getSymExtent(printerScale
) 
 403         for o 
in self
.objects
[1:]: 
 404             oSymExt 
= o
.getSymExtent(printerScale
) 
 405             symExt 
= _Numeric
.maximum(symExt
, oSymExt
) 
 408     def getLegendNames(self
): 
 409         """Returns list of legend names""" 
 410         lst 
= [None]*len(self
) 
 411         for i 
in range(len(self
)): 
 412             lst
[i
]= self
.objects
[i
].getLegend() 
 416         return len(self
.objects
) 
 418     def __getitem__(self
, item
): 
 419         return self
.objects
[item
] 
 422 #------------------------------------------------------------------------------- 
 423 # Main window that you will want to import into your application. 
 425 class PlotCanvas(wx
.Window
): 
 426     """Subclass of a wx.Window to allow simple general plotting 
 427     of data with zoom, labels, and automatic axis scaling.""" 
 429     def __init__(self
, parent
, id = -1, pos
=wx
.DefaultPosition
, 
 430             size
=wx
.DefaultSize
, style
= wx
.DEFAULT_FRAME_STYLE
, name
= ""): 
 431         """Constucts a window, which can be a child of a frame, dialog or 
 432         any other non-control window""" 
 434         wx
.Window
.__init
__(self
, parent
, id, pos
, size
, style
, name
) 
 437         self
.SetBackgroundColour("white") 
 439         # Create some mouse events for zooming 
 440         self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
 441         self
.Bind(wx
.EVT_LEFT_UP
, self
.OnMouseLeftUp
) 
 442         self
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
 443         self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnMouseDoubleClick
) 
 444         self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnMouseRightDown
) 
 446         # set curser as cross-hairs 
 447         self
.SetCursor(wx
.CROSS_CURSOR
) 
 449         # Things for printing 
 450         self
.print_data 
= wx
.PrintData() 
 451         self
.print_data
.SetPaperId(wx
.PAPER_LETTER
) 
 452         self
.print_data
.SetOrientation(wx
.LANDSCAPE
) 
 453         self
.pageSetupData
= wx
.PageSetupDialogData() 
 454         self
.pageSetupData
.SetMarginBottomRight((25,25)) 
 455         self
.pageSetupData
.SetMarginTopLeft((25,25)) 
 456         self
.pageSetupData
.SetPrintData(self
.print_data
) 
 457         self
.printerScale 
= 1 
 461         self
._zoomInFactor 
=  0.5 
 462         self
._zoomOutFactor 
= 2 
 463         self
._zoomCorner
1= _Numeric
.array([0.0, 0.0]) # left mouse down corner 
 464         self
._zoomCorner
2= _Numeric
.array([0.0, 0.0])   # left mouse up corner 
 465         self
._zoomEnabled
= False 
 466         self
._hasDragged
= False 
 469         self
.last_draw 
= None 
 474         self
._gridEnabled
= False 
 475         self
._legendEnabled
= False 
 479         self
._fontSizeAxis
= 10 
 480         self
._fontSizeTitle
= 15 
 481         self
._fontSizeLegend
= 7 
 484         self
._pointLabelEnabled
= False 
 485         self
.last_PointLabel
= None 
 486         self
._pointLabelFunc
= None 
 487         self
.Bind(wx
.EVT_LEAVE_WINDOW
, self
.OnLeave
) 
 489         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 490         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 491         # OnSize called to make sure the buffer is initialized. 
 492         # This might result in OnSize getting called twice on some 
 493         # platforms at initialization, but little harm done. 
 494         if wx
.Platform 
!= "__WXMAC__": 
 495             self
.OnSize(None) # sets the initial size based on client size 
 497         self
._gridColour 
= wx
.NamedColour('black') 
 499     def GetGridColour(self
): 
 500         return self
._gridColour
 
 502     def SetGridColour(self
, colour
): 
 503         if isinstance(colour
, wx
.Colour
): 
 504             self
._gridColour 
= colour
 
 506             self
._gridColour 
= wx
.NamedColour(colour
) 
 510     def SaveFile(self
, fileName
= ''): 
 511         """Saves the file to the type specified in the extension. If no file 
 512         name is specified a dialog box is provided.  Returns True if sucessful, 
 515         .bmp  Save a Windows bitmap file. 
 516         .xbm  Save an X bitmap file. 
 517         .xpm  Save an XPM bitmap file. 
 518         .png  Save a Portable Network Graphics file. 
 519         .jpg  Save a Joint Photographic Experts Group file. 
 521         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 522             dlg1 
= wx
.FileDialog( 
 524                     "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "", 
 525                     "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg", 
 526                     wx
.SAVE|wx
.OVERWRITE_PROMPT
 
 530                     if dlg1
.ShowModal() == wx
.ID_OK
: 
 531                         fileName 
= dlg1
.GetPath() 
 532                         # Check for proper exension 
 533                         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 534                             dlg2 
= wx
.MessageDialog(self
, 'File name extension\n' 
 536                             'bmp, xbm, xpm, png, or jpg', 
 537                               'File Name Error', wx
.OK | wx
.ICON_ERROR
) 
 543                             break # now save file 
 544                     else: # exit without saving 
 549         # File name has required extension 
 550         fType 
= _string
.lower(fileName
[-3:]) 
 552             tp
= wx
.BITMAP_TYPE_BMP       
# Save a Windows bitmap file. 
 554             tp
= wx
.BITMAP_TYPE_XBM       
# Save an X bitmap file. 
 556             tp
= wx
.BITMAP_TYPE_XPM       
# Save an XPM bitmap file. 
 558             tp
= wx
.BITMAP_TYPE_JPEG      
# Save a JPG file. 
 560             tp
= wx
.BITMAP_TYPE_PNG       
# Save a PNG file. 
 562         res
= self
._Buffer
.SaveFile(fileName
, tp
) 
 566         """Brings up the page setup dialog""" 
 567         data 
= self
.pageSetupData
 
 568         data
.SetPrintData(self
.print_data
) 
 569         dlg 
= wx
.PageSetupDialog(self
.parent
, data
) 
 571             if dlg
.ShowModal() == wx
.ID_OK
: 
 572                 data 
= dlg
.GetPageSetupData() # returns wx.PageSetupDialogData 
 573                 # updates page parameters from dialog 
 574                 self
.pageSetupData
.SetMarginBottomRight(data
.GetMarginBottomRight()) 
 575                 self
.pageSetupData
.SetMarginTopLeft(data
.GetMarginTopLeft()) 
 576                 self
.pageSetupData
.SetPrintData(data
.GetPrintData()) 
 577                 self
.print_data
=wx
.PrintData(data
.GetPrintData()) # updates print_data 
 581     def Printout(self
, paper
=None): 
 582         """Print current plot.""" 
 584             self
.print_data
.SetPaperId(paper
) 
 585         pdd 
= wx
.PrintDialogData(self
.print_data
) 
 586         printer 
= wx
.Printer(pdd
) 
 587         out 
= PlotPrintout(self
) 
 588         print_ok 
= printer
.Print(self
.parent
, out
) 
 590             self
.print_data 
= wx
.PrintData(printer
.GetPrintDialogData().GetPrintData()) 
 593     def PrintPreview(self
): 
 594         """Print-preview current plot.""" 
 595         printout 
= PlotPrintout(self
) 
 596         printout2 
= PlotPrintout(self
) 
 597         self
.preview 
= wx
.PrintPreview(printout
, printout2
, self
.print_data
) 
 598         if not self
.preview
.Ok(): 
 599             wx
.MessageDialog(self
, "Print Preview failed.\n" \
 
 600                                "Check that default printer is configured\n", \
 
 601                                "Print error", wx
.OK|wx
.CENTRE
).ShowModal() 
 602         self
.preview
.SetZoom(40) 
 603         # search up tree to find frame instance 
 605         while not isinstance(frameInst
, wx
.Frame
): 
 606             frameInst
= frameInst
.GetParent() 
 607         frame 
= wx
.PreviewFrame(self
.preview
, frameInst
, "Preview") 
 609         frame
.SetPosition(self
.GetPosition()) 
 610         frame
.SetSize((600,550)) 
 611         frame
.Centre(wx
.BOTH
) 
 614     def SetFontSizeAxis(self
, point
= 10): 
 615         """Set the tick and axis label font size (default is 10 point)""" 
 616         self
._fontSizeAxis
= point
 
 618     def GetFontSizeAxis(self
): 
 619         """Get current tick and axis label font size in points""" 
 620         return self
._fontSizeAxis
 
 622     def SetFontSizeTitle(self
, point
= 15): 
 623         """Set Title font size (default is 15 point)""" 
 624         self
._fontSizeTitle
= point
 
 626     def GetFontSizeTitle(self
): 
 627         """Get current Title font size in points""" 
 628         return self
._fontSizeTitle
 
 630     def SetFontSizeLegend(self
, point
= 7): 
 631         """Set Legend font size (default is 7 point)""" 
 632         self
._fontSizeLegend
= point
 
 634     def GetFontSizeLegend(self
): 
 635         """Get current Legend font size in points""" 
 636         return self
._fontSizeLegend
 
 638     def SetEnableZoom(self
, value
): 
 639         """Set True to enable zooming.""" 
 640         if value 
not in [True,False]: 
 641             raise TypeError, "Value should be True or False" 
 642         self
._zoomEnabled
= value
 
 644     def GetEnableZoom(self
): 
 645         """True if zooming enabled.""" 
 646         return self
._zoomEnabled
 
 648     def SetEnableGrid(self
, value
): 
 649         """Set True to enable grid.""" 
 650         if value 
not in [True,False,'Horizontal','Vertical']: 
 651             raise TypeError, "Value should be True, False, Horizontal or Vertical" 
 652         self
._gridEnabled
= value
 
 655     def GetEnableGrid(self
): 
 656         """True if grid enabled.""" 
 657         return self
._gridEnabled
 
 659     def SetEnableLegend(self
, value
): 
 660         """Set True to enable legend.""" 
 661         if value 
not in [True,False]: 
 662             raise TypeError, "Value should be True or False" 
 663         self
._legendEnabled
= value 
 
 666     def GetEnableLegend(self
): 
 667         """True if Legend enabled.""" 
 668         return self
._legendEnabled
 
 670     def SetEnablePointLabel(self
, value
): 
 671         """Set True to enable pointLabel.""" 
 672         if value 
not in [True,False]: 
 673             raise TypeError, "Value should be True or False" 
 674         self
._pointLabelEnabled
= value 
 
 675         self
.Redraw()  #will erase existing pointLabel if present 
 676         self
.last_PointLabel 
= None 
 678     def GetEnablePointLabel(self
): 
 679         """True if pointLabel enabled.""" 
 680         return self
._pointLabelEnabled
 
 682     def SetPointLabelFunc(self
, func
): 
 683         """Sets the function with custom code for pointLabel drawing 
 684             ******** more info needed *************** 
 686         self
._pointLabelFunc
= func
 
 688     def GetPointLabelFunc(self
): 
 689         """Returns pointLabel Drawing Function""" 
 690         return self
._pointLabelFunc
 
 693         """Unzoom the plot.""" 
 694         self
.last_PointLabel 
= None        #reset pointLabel 
 695         if self
.last_draw 
is not None: 
 696             self
.Draw(self
.last_draw
[0]) 
 698     def ScrollRight(self
, units
):           
 699         """Move view right number of axis units.""" 
 700         self
.last_PointLabel 
= None        #reset pointLabel 
 701         if self
.last_draw 
is not None: 
 702             graphics
, xAxis
, yAxis
= self
.last_draw
 
 703             xAxis
= (xAxis
[0]+units
, xAxis
[1]+units
) 
 704             self
.Draw(graphics
,xAxis
,yAxis
) 
 706     def ScrollUp(self
, units
): 
 707         """Move view up number of axis units.""" 
 708         self
.last_PointLabel 
= None        #reset pointLabel 
 709         if self
.last_draw 
is not None: 
 710              graphics
, xAxis
, yAxis
= self
.last_draw
 
 711              yAxis
= (yAxis
[0]+units
, yAxis
[1]+units
) 
 712              self
.Draw(graphics
,xAxis
,yAxis
) 
 715     def GetXY(self
,event
): 
 716         """Takes a mouse event and returns the XY user axis values.""" 
 717         x
,y
= self
.PositionScreenToUser(event
.GetPosition()) 
 720     def PositionUserToScreen(self
, pntXY
): 
 721         """Converts User position to Screen Coordinates""" 
 722         userPos
= _Numeric
.array(pntXY
) 
 723         x
,y
= userPos 
* self
._pointScale 
+ self
._pointShift
 
 726     def PositionScreenToUser(self
, pntXY
): 
 727         """Converts Screen position to User Coordinates""" 
 728         screenPos
= _Numeric
.array(pntXY
) 
 729         x
,y
= (screenPos
-self
._pointShift
)/self
._pointScale
 
 732     def SetXSpec(self
, type= 'auto'): 
 733         """xSpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 735             'none' - shows no axis or tick mark values 
 736             'min' - shows min bounding box values 
 737             'auto' - rounds axis range to sensible values 
 741     def SetYSpec(self
, type= 'auto'): 
 742         """ySpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 744             'none' - shows no axis or tick mark values 
 745             'min' - shows min bounding box values 
 746             'auto' - rounds axis range to sensible values 
 751         """Returns current XSpec for axis""" 
 755         """Returns current YSpec for axis""" 
 758     def GetXMaxRange(self
): 
 759         """Returns (minX, maxX) x-axis range for displayed graph""" 
 760         graphics
= self
.last_draw
[0] 
 761         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 762         xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 765     def GetYMaxRange(self
): 
 766         """Returns (minY, maxY) y-axis range for displayed graph""" 
 767         graphics
= self
.last_draw
[0] 
 768         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 769         yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 772     def GetXCurrentRange(self
): 
 773         """Returns (minX, maxX) x-axis for currently displayed portion of graph""" 
 774         return self
.last_draw
[1] 
 776     def GetYCurrentRange(self
): 
 777         """Returns (minY, maxY) y-axis for currently displayed portion of graph""" 
 778         return self
.last_draw
[2] 
 780     def Draw(self
, graphics
, xAxis 
= None, yAxis 
= None, dc 
= None): 
 781         """Draw objects in graphics with specified x and y axis. 
 782         graphics- instance of PlotGraphics with list of PolyXXX objects 
 783         xAxis - tuple with (min, max) axis range to view 
 784         yAxis - same as xAxis 
 785         dc - drawing context - doesn't have to be specified.     
 786         If it's not, the offscreen buffer is used 
 788         # check Axis is either tuple or none 
 789         if type(xAxis
) not in [type(None),tuple]: 
 790             raise TypeError, "xAxis should be None or (minX,maxX)" 
 791         if type(yAxis
) not in [type(None),tuple]: 
 792             raise TypeError, "yAxis should be None or (minY,maxY)" 
 794         # check case for axis = (a,b) where a==b caused by improper zooms 
 796             if xAxis
[0] == xAxis
[1]: 
 799             if yAxis
[0] == yAxis
[1]: 
 803             # sets new dc and clears it  
 804             dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
._Buffer
) 
 810         # set font size for every thing but title and legend 
 811         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 813         # sizes axis to axis type, create lower left and upper right corners of plot 
 814         if xAxis 
== None or yAxis 
== None: 
 815             # One or both axis not specified in Draw 
 816             p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 818                 xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 820                 yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 821             # Adjust bounding box for axis spec 
 822             p1
[0],p1
[1] = xAxis
[0], yAxis
[0]     # lower left corner user scale (xmin,ymin) 
 823             p2
[0],p2
[1] = xAxis
[1], yAxis
[1]     # upper right corner user scale (xmax,ymax) 
 825             # Both axis specified in Draw 
 826             p1
= _Numeric
.array([xAxis
[0], yAxis
[0]])    # lower left corner user scale (xmin,ymin) 
 827             p2
= _Numeric
.array([xAxis
[1], yAxis
[1]])     # upper right corner user scale (xmax,ymax) 
 829         self
.last_draw 
= (graphics
, xAxis
, yAxis
)       # saves most recient values 
 831         # Get ticks and textExtents for axis if required 
 832         if self
._xSpec 
is not 'none':         
 833             xticks 
= self
._ticks
(xAxis
[0], xAxis
[1]) 
 834             xTextExtent 
= dc
.GetTextExtent(xticks
[-1][1])# w h of x axis text last number on axis 
 837             xTextExtent
= (0,0) # No text for ticks 
 838         if self
._ySpec 
is not 'none': 
 839             yticks 
= self
._ticks
(yAxis
[0], yAxis
[1]) 
 840             yTextExtentBottom
= dc
.GetTextExtent(yticks
[0][1]) 
 841             yTextExtentTop   
= dc
.GetTextExtent(yticks
[-1][1]) 
 842             yTextExtent
= (max(yTextExtentBottom
[0],yTextExtentTop
[0]), 
 843                         max(yTextExtentBottom
[1],yTextExtentTop
[1])) 
 846             yTextExtent
= (0,0) # No text for ticks 
 848         # TextExtents for Title and Axis Labels 
 849         titleWH
, xLabelWH
, yLabelWH
= self
._titleLablesWH
(dc
, graphics
) 
 851         # TextExtents for Legend 
 852         legendBoxWH
, legendSymExt
, legendTextExt 
= self
._legendWH
(dc
, graphics
) 
 854         # room around graph area 
 855         rhsW
= max(xTextExtent
[0], legendBoxWH
[0]) # use larger of number width or legend width 
 856         lhsW
= yTextExtent
[0]+ yLabelWH
[1] 
 857         bottomH
= max(xTextExtent
[1], yTextExtent
[1]/2.)+ xLabelWH
[1] 
 858         topH
= yTextExtent
[1]/2. + titleWH
[1] 
 859         textSize_scale
= _Numeric
.array([rhsW
+lhsW
,bottomH
+topH
]) # make plot area smaller by text size 
 860         textSize_shift
= _Numeric
.array([lhsW
, bottomH
])          # shift plot area by this amount 
 862         # drawing title and labels text 
 863         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
 864         titlePos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- titleWH
[0]/2., 
 865                  self
.plotbox_origin
[1]- self
.plotbox_size
[1]) 
 866         dc
.DrawText(graphics
.getTitle(),titlePos
[0],titlePos
[1]) 
 867         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 868         xLabelPos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- xLabelWH
[0]/2., 
 869                  self
.plotbox_origin
[1]- xLabelWH
[1]) 
 870         dc
.DrawText(graphics
.getXLabel(),xLabelPos
[0],xLabelPos
[1]) 
 871         yLabelPos
= (self
.plotbox_origin
[0], 
 872                  self
.plotbox_origin
[1]- bottomH
- (self
.plotbox_size
[1]-bottomH
-topH
)/2.+ yLabelWH
[0]/2.) 
 873         if graphics
.getYLabel():  # bug fix for Linux 
 874             dc
.DrawRotatedText(graphics
.getYLabel(),yLabelPos
[0],yLabelPos
[1],90) 
 876         # drawing legend makers and text 
 877         if self
._legendEnabled
: 
 878             self
._drawLegend
(dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
) 
 880         # allow for scaling and shifting plotted points 
 881         scale 
= (self
.plotbox_size
-textSize_scale
) / (p2
-p1
)* _Numeric
.array((1,-1)) 
 882         shift 
= -p1
*scale 
+ self
.plotbox_origin 
+ textSize_shift 
* _Numeric
.array((1,-1)) 
 883         self
._pointScale
= scale  
# make available for mouse events 
 884         self
._pointShift
= shift        
 
 885         self
._drawAxes
(dc
, p1
, p2
, scale
, shift
, xticks
, yticks
) 
 887         graphics
.scaleAndShift(scale
, shift
) 
 888         graphics
.setPrinterScale(self
.printerScale
)  # thicken up lines and markers if printing 
 890         # set clipping area so drawing does not occur outside axis box 
 891         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(p1
, p2
) 
 892         dc
.SetClippingRegion(ptx
,pty
,rectWidth
,rectHeight
) 
 893         # Draw the lines and markers 
 894         #start = _time.clock() 
 896         # print "entire graphics drawing took: %f second"%(_time.clock() - start) 
 897         # remove the clipping region 
 898         dc
.DestroyClippingRegion() 
 901     def Redraw(self
, dc
= None): 
 902         """Redraw the existing plot.""" 
 903         if self
.last_draw 
is not None: 
 904             graphics
, xAxis
, yAxis
= self
.last_draw
 
 905             self
.Draw(graphics
,xAxis
,yAxis
,dc
) 
 908         """Erase the window.""" 
 909         self
.last_PointLabel 
= None        #reset pointLabel 
 910         dc 
= wx
.BufferedDC(wx
.ClientDC(self
), self
._Buffer
) 
 912         self
.last_draw 
= None 
 914     def Zoom(self
, Center
, Ratio
): 
 916             Centers on the X,Y coords given in Center 
 917             Zooms by the Ratio = (Xratio, Yratio) given 
 919         self
.last_PointLabel 
= None   #reset maker 
 921         if self
.last_draw 
!= None: 
 922             (graphics
, xAxis
, yAxis
) = self
.last_draw
 
 923             w 
= (xAxis
[1] - xAxis
[0]) * Ratio
[0] 
 924             h 
= (yAxis
[1] - yAxis
[0]) * Ratio
[1] 
 925             xAxis 
= ( x 
- w
/2, x 
+ w
/2 ) 
 926             yAxis 
= ( y 
- h
/2, y 
+ h
/2 ) 
 927             self
.Draw(graphics
, xAxis
, yAxis
) 
 929     def GetClosestPoints(self
, pntXY
, pointScaled
= True): 
 931             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 933             Returns [] if no curves are being plotted. 
 936             if pointScaled == True based on screen coords 
 937             if pointScaled == False based on user coords 
 939         if self
.last_draw 
== None: 
 942         graphics
, xAxis
, yAxis
= self
.last_draw
 
 944         for curveNum
,obj 
in enumerate(graphics
): 
 945             #check there are points in the curve 
 946             if len(obj
.points
) == 0: 
 947                 continue  #go to next obj 
 948             #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 949             cn 
= [curveNum
]+ [obj
.getLegend()]+ obj
.getClosestPoint( pntXY
, pointScaled
) 
 953     def GetClosetPoint(self
, pntXY
, pointScaled
= True): 
 955             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
 956             list for only the closest curve. 
 957             Returns [] if no curves are being plotted. 
 960             if pointScaled == True based on screen coords 
 961             if pointScaled == False based on user coords 
 963         #closest points on screen based on screen scaling (pointScaled= True) 
 964         #list [curveNumber, index, pointXY, scaledXY, distance] for each curve 
 965         closestPts
= self
.GetClosestPoints(pntXY
, pointScaled
) 
 967             return []  #no graph present 
 968         #find one with least distance 
 969         dists 
= [c
[-1] for c 
in closestPts
] 
 970         mdist 
= min(dists
)  #Min dist 
 971         i 
= dists
.index(mdist
)  #index for min dist 
 972         return closestPts
[i
]  #this is the closest point on closest curve 
 974     def UpdatePointLabel(self
, mDataDict
): 
 975         """Updates the pointLabel point on screen with data contained in 
 978             mDataDict will be passed to your function set by 
 979             SetPointLabelFunc.  It can contain anything you 
 980             want to display on the screen at the scaledXY point 
 983             This function can be called from parent window with onClick, 
 986         if self
.last_PointLabel 
!= None: 
 988             if mDataDict
["pointXY"] != self
.last_PointLabel
["pointXY"]: 
 990                 self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
 991                 self
._drawPointLabel
(mDataDict
) #plot new 
 993             #just plot new with no erase 
 994             self
._drawPointLabel
(mDataDict
) #plot new 
 996         self
.last_PointLabel 
= mDataDict
 
 998     # event handlers ********************************** 
 999     def OnMotion(self
, event
): 
1000         if self
._zoomEnabled 
and event
.LeftIsDown(): 
1001             if self
._hasDragged
: 
1002                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
1004                 self
._hasDragged
= True 
1005             self
._zoomCorner
2[0], self
._zoomCorner
2[1] = self
.GetXY(event
) 
1006             self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # add new 
1008     def OnMouseLeftDown(self
,event
): 
1009         self
._zoomCorner
1[0], self
._zoomCorner
1[1]= self
.GetXY(event
) 
1011     def OnMouseLeftUp(self
, event
): 
1012         if self
._zoomEnabled
: 
1013             if self
._hasDragged 
== True: 
1014                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
1015                 self
._zoomCorner
2[0], self
._zoomCorner
2[1]= self
.GetXY(event
) 
1016                 self
._hasDragged 
= False  # reset flag 
1017                 minX
, minY
= _Numeric
.minimum( self
._zoomCorner
1, self
._zoomCorner
2) 
1018                 maxX
, maxY
= _Numeric
.maximum( self
._zoomCorner
1, self
._zoomCorner
2) 
1019                 self
.last_PointLabel 
= None        #reset pointLabel 
1020                 if self
.last_draw 
!= None: 
1021                     self
.Draw(self
.last_draw
[0], xAxis 
= (minX
,maxX
), yAxis 
= (minY
,maxY
), dc 
= None) 
1022             #else: # A box has not been drawn, zoom in on a point 
1023             ## this interfered with the double click, so I've disables it. 
1024             #    X,Y = self.GetXY(event) 
1025             #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) ) 
1027     def OnMouseDoubleClick(self
,event
): 
1028         if self
._zoomEnabled
: 
1031     def OnMouseRightDown(self
,event
): 
1032         if self
._zoomEnabled
: 
1033             X
,Y 
= self
.GetXY(event
) 
1034             self
.Zoom( (X
,Y
), (self
._zoomOutFactor
, self
._zoomOutFactor
) ) 
1036     def OnPaint(self
, event
): 
1037         # All that is needed here is to draw the buffer to screen 
1038         if self
.last_PointLabel 
!= None: 
1039             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1040             self
.last_PointLabel 
= None 
1041         dc 
= wx
.BufferedPaintDC(self
, self
._Buffer
) 
1043     def OnSize(self
,event
): 
1044         # The Buffer init is done here, to make sure the buffer is always 
1045         # the same size as the Window 
1046         Size  
= self
.GetClientSize() 
1047         if Size
.width 
<= 0 or Size
.height 
<= 0: 
1050         # Make new offscreen bitmap: this bitmap will always have the 
1051         # current drawing in it, so it can be used to save the image to 
1052         # a file, or whatever. 
1053         self
._Buffer 
= wx
.EmptyBitmap(Size
[0],Size
[1]) 
1056         self
.last_PointLabel 
= None        #reset pointLabel 
1058         if self
.last_draw 
is None: 
1061             graphics
, xSpec
, ySpec 
= self
.last_draw
 
1062             self
.Draw(graphics
,xSpec
,ySpec
) 
1064     def OnLeave(self
, event
): 
1065         """Used to erase pointLabel when mouse outside window""" 
1066         if self
.last_PointLabel 
!= None: 
1067             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1068             self
.last_PointLabel 
= None 
1071     # Private Methods ************************************************** 
1072     def _setSize(self
, width
=None, height
=None): 
1073         """DC width and height.""" 
1075             (self
.width
,self
.height
) = self
.GetClientSize() 
1077             self
.width
, self
.height
= width
,height    
 
1078         self
.plotbox_size 
= 0.97*_Numeric
.array([self
.width
, self
.height
]) 
1079         xo 
= 0.5*(self
.width
-self
.plotbox_size
[0]) 
1080         yo 
= self
.height
-0.5*(self
.height
-self
.plotbox_size
[1]) 
1081         self
.plotbox_origin 
= _Numeric
.array([xo
, yo
]) 
1083     def _setPrinterScale(self
, scale
): 
1084         """Used to thicken lines and increase marker size for print out.""" 
1085         # line thickness on printer is very thin at 600 dot/in. Markers small 
1086         self
.printerScale
= scale
 
1088     def _printDraw(self
, printDC
): 
1089         """Used for printing.""" 
1090         if self
.last_draw 
!= None: 
1091             graphics
, xSpec
, ySpec
= self
.last_draw
 
1092             self
.Draw(graphics
,xSpec
,ySpec
,printDC
) 
1094     def _drawPointLabel(self
, mDataDict
): 
1095         """Draws and erases pointLabels""" 
1096         width 
= self
._Buffer
.GetWidth() 
1097         height 
= self
._Buffer
.GetHeight() 
1098         tmp_Buffer 
= wx
.EmptyBitmap(width
,height
) 
1100         dcs
.SelectObject(tmp_Buffer
) 
1103         self
._pointLabelFunc
(dcs
,mDataDict
)  #custom user pointLabel function 
1106         dc 
= wx
.ClientDC( self 
) 
1107         #this will erase if called twice 
1108         dc
.Blit(0, 0, width
, height
, dcs
, 0, 0, wx
.EQUIV
)  #(NOT src) XOR dst 
1111     def _drawLegend(self
,dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
): 
1112         """Draws legend symbols and text""" 
1113         # top right hand corner of graph box is ref corner 
1114         trhc
= self
.plotbox_origin
+ (self
.plotbox_size
-[rhsW
,topH
])*[1,-1] 
1115         legendLHS
= .091* legendBoxWH
[0]  # border space between legend sym and graph box 
1116         lineHeight
= max(legendSymExt
[1], legendTextExt
[1]) * 1.1 #1.1 used as space between lines 
1117         dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1118         for i 
in range(len(graphics
)): 
1121             if isinstance(o
,PolyMarker
): 
1122                 # draw marker with legend 
1123                 pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0]/2., trhc
[1]+s
+lineHeight
/2.) 
1124                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt
])) 
1125             elif isinstance(o
,PolyLine
): 
1126                 # draw line with legend 
1127                 pnt1
= (trhc
[0]+legendLHS
, trhc
[1]+s
+lineHeight
/2.) 
1128                 pnt2
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.) 
1129                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt1
,pnt2
])) 
1131                 raise TypeError, "object is neither PolyMarker or PolyLine instance" 
1133             pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.-legendTextExt
[1]/2) 
1134             dc
.DrawText(o
.getLegend(),pnt
[0],pnt
[1]) 
1135         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) # reset 
1137     def _titleLablesWH(self
, dc
, graphics
): 
1138         """Draws Title and labels and returns width and height for each""" 
1139         # TextExtents for Title and Axis Labels 
1140         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
1141         title
= graphics
.getTitle() 
1142         titleWH
= dc
.GetTextExtent(title
) 
1143         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1144         xLabel
, yLabel
= graphics
.getXLabel(),graphics
.getYLabel() 
1145         xLabelWH
= dc
.GetTextExtent(xLabel
) 
1146         yLabelWH
= dc
.GetTextExtent(yLabel
) 
1147         return titleWH
, xLabelWH
, yLabelWH
 
1149     def _legendWH(self
, dc
, graphics
): 
1150         """Returns the size in screen units for legend box""" 
1151         if self
._legendEnabled 
!= True: 
1152             legendBoxWH
= symExt
= txtExt
= (0,0) 
1154             # find max symbol size 
1155             symExt
= graphics
.getSymExtent(self
.printerScale
) 
1156             # find max legend text extent 
1157             dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1158             txtList
= graphics
.getLegendNames() 
1159             txtExt
= dc
.GetTextExtent(txtList
[0]) 
1160             for txt 
in graphics
.getLegendNames()[1:]: 
1161                 txtExt
= _Numeric
.maximum(txtExt
,dc
.GetTextExtent(txt
)) 
1162             maxW
= symExt
[0]+txtExt
[0]     
1163             maxH
= max(symExt
[1],txtExt
[1]) 
1164             # padding .1 for lhs of legend box and space between lines 
1166             maxH
= maxH
* 1.1 * len(txtList
) 
1167             dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1168             legendBoxWH
= (maxW
,maxH
) 
1169         return (legendBoxWH
, symExt
, txtExt
) 
1171     def _drawRubberBand(self
, corner1
, corner2
): 
1172         """Draws/erases rect box from corner1 to corner2""" 
1173         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(corner1
, corner2
) 
1175         dc 
= wx
.ClientDC( self 
) 
1177         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1178         dc
.SetBrush(wx
.Brush( wx
.WHITE
, wx
.TRANSPARENT 
) ) 
1179         dc
.SetLogicalFunction(wx
.INVERT
) 
1180         dc
.DrawRectangle( ptx
,pty
, rectWidth
,rectHeight
) 
1181         dc
.SetLogicalFunction(wx
.COPY
) 
1184     def _getFont(self
,size
): 
1185         """Take font size, adjusts if printing and returns wx.Font""" 
1186         s 
= size
*self
.printerScale
 
1188         # Linux speed up to get font from cache rather than X font server 
1189         key 
= (int(s
), of
.GetFamily (), of
.GetStyle (), of
.GetWeight ()) 
1190         font 
= self
._fontCache
.get (key
, None) 
1192             return font                 
# yeah! cache hit 
1194             font 
=  wx
.Font(int(s
), of
.GetFamily(), of
.GetStyle(), of
.GetWeight()) 
1195             self
._fontCache
[key
] = font
 
1199     def _point2ClientCoord(self
, corner1
, corner2
): 
1200         """Converts user point coords to client screen int coords x,y,width,height""" 
1201         c1
= _Numeric
.array(corner1
) 
1202         c2
= _Numeric
.array(corner2
) 
1203         # convert to screen coords 
1204         pt1
= c1
*self
._pointScale
+self
._pointShift
 
1205         pt2
= c2
*self
._pointScale
+self
._pointShift
 
1206         # make height and width positive 
1207         pul
= _Numeric
.minimum(pt1
,pt2
) # Upper left corner 
1208         plr
= _Numeric
.maximum(pt1
,pt2
) # Lower right corner 
1209         rectWidth
, rectHeight
= plr
-pul
 
1211         return ptx
, pty
, rectWidth
, rectHeight 
 
1213     def _axisInterval(self
, spec
, lower
, upper
): 
1214         """Returns sensible axis range for given spec""" 
1215         if spec 
== 'none' or spec 
== 'min': 
1217                 return lower
-0.5, upper
+0.5 
1220         elif spec 
== 'auto': 
1223                 return lower
-0.5, upper
+0.5 
1224             log 
= _Numeric
.log10(range) 
1225             power 
= _Numeric
.floor(log
) 
1226             fraction 
= log
-power
 
1227             if fraction 
<= 0.05: 
1230             lower 
= lower 
- lower 
% grid
 
1233                 upper 
= upper 
- mod 
+ grid
 
1235         elif type(spec
) == type(()): 
1242             raise ValueError, str(spec
) + ': illegal axis specification' 
1244     def _drawAxes(self
, dc
, p1
, p2
, scale
, shift
, xticks
, yticks
): 
1246         penWidth
= self
.printerScale        
# increases thickness for printing only 
1247         dc
.SetPen(wx
.Pen(self
._gridColour
, penWidth
)) 
1249         # set length of tick marks--long ones make grid 
1250         if self
._gridEnabled
: 
1251             x
,y
,width
,height
= self
._point
2ClientCoord
(p1
,p2
) 
1252             if self
._gridEnabled 
== 'Horizontal': 
1253                 yTickLength
= width
/2.0 +1 
1254                 xTickLength
= 3 * self
.printerScale
 
1255             elif self
._gridEnabled 
== 'Vertical': 
1256                 yTickLength
= 3 * self
.printerScale
 
1257                 xTickLength
= height
/2.0 +1 
1259                 yTickLength
= width
/2.0 +1 
1260                 xTickLength
= height
/2.0 +1 
1262             yTickLength
= 3 * self
.printerScale  
# lengthens lines for printing 
1263             xTickLength
= 3 * self
.printerScale
 
1265         if self
._xSpec 
is not 'none': 
1266             lower
, upper 
= p1
[0],p2
[0] 
1268             for y
, d 
in [(p1
[1], -xTickLength
), (p2
[1], xTickLength
)]:   # miny, maxy and tick lengths 
1269                 a1 
= scale
*_Numeric
.array([lower
, y
])+shift
 
1270                 a2 
= scale
*_Numeric
.array([upper
, y
])+shift
 
1271                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1])  # draws upper and lower axis line 
1272                 for x
, label 
in xticks
: 
1273                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1274                     dc
.DrawLine(pt
[0],pt
[1],pt
[0],pt
[1] + d
) # draws tick mark d units 
1276                         dc
.DrawText(label
,pt
[0],pt
[1]) 
1277                 text 
= 0  # axis values not drawn on top side 
1279         if self
._ySpec 
is not 'none': 
1280             lower
, upper 
= p1
[1],p2
[1] 
1282             h 
= dc
.GetCharHeight() 
1283             for x
, d 
in [(p1
[0], -yTickLength
), (p2
[0], yTickLength
)]: 
1284                 a1 
= scale
*_Numeric
.array([x
, lower
])+shift
 
1285                 a2 
= scale
*_Numeric
.array([x
, upper
])+shift
 
1286                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1]) 
1287                 for y
, label 
in yticks
: 
1288                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1289                     dc
.DrawLine(pt
[0],pt
[1],pt
[0]-d
,pt
[1]) 
1291                         dc
.DrawText(label
,pt
[0]-dc
.GetTextExtent(label
)[0], 
1293                 text 
= 0    # axis values not drawn on right side 
1295     def _ticks(self
, lower
, upper
): 
1296         ideal 
= (upper
-lower
)/7. 
1297         log 
= _Numeric
.log10(ideal
) 
1298         power 
= _Numeric
.floor(log
) 
1299         fraction 
= log
-power
 
1302         for f
, lf 
in self
._multiples
: 
1303             e 
= _Numeric
.fabs(fraction
-lf
) 
1307         grid 
= factor 
* 10.**power
 
1308         if power 
> 4 or power 
< -4: 
1311             digits 
= max(1, int(power
)) 
1312             format 
= '%' + `digits`
+'.0f' 
1314             digits 
= -int(power
) 
1315             format 
= '%'+`digits
+2`
+'.'+`digits`
+'f' 
1317         t 
= -grid
*_Numeric
.floor(-lower
/grid
) 
1319             ticks
.append( (t
, format 
% (t
,)) ) 
1323     _multiples 
= [(2., _Numeric
.log10(2.)), (5., _Numeric
.log10(5.))] 
1326 #------------------------------------------------------------------------------- 
1327 # Used to layout the printer page 
1329 class PlotPrintout(wx
.Printout
): 
1330     """Controls how the plot is made in printing and previewing""" 
1331     # Do not change method names in this class, 
1332     # we have to override wx.Printout methods here! 
1333     def __init__(self
, graph
): 
1334         """graph is instance of plotCanvas to be printed or previewed""" 
1335         wx
.Printout
.__init
__(self
) 
1338     def HasPage(self
, page
): 
1344     def GetPageInfo(self
): 
1345         return (1, 1, 1, 1)  # disable page numbers 
1347     def OnPrintPage(self
, page
): 
1348         dc 
= self
.GetDC()  # allows using floats for certain functions 
1349 ##        print "PPI Printer",self.GetPPIPrinter() 
1350 ##        print "PPI Screen", self.GetPPIScreen() 
1351 ##        print "DC GetSize", dc.GetSize() 
1352 ##        print "GetPageSizePixels", self.GetPageSizePixels() 
1353         # Note PPIScreen does not give the correct number 
1354         # Calulate everything for printer and then scale for preview 
1355         PPIPrinter
= self
.GetPPIPrinter()        # printer dots/inch (w,h) 
1356         #PPIScreen= self.GetPPIScreen()          # screen dots/inch (w,h) 
1357         dcSize
= dc
.GetSize()                    # DC size 
1358         pageSize
= self
.GetPageSizePixels() # page size in terms of pixcels 
1359         clientDcSize
= self
.graph
.GetClientSize() 
1361         # find what the margins are (mm) 
1362         margLeftSize
,margTopSize
= self
.graph
.pageSetupData
.GetMarginTopLeft() 
1363         margRightSize
, margBottomSize
= self
.graph
.pageSetupData
.GetMarginBottomRight() 
1365         # calculate offset and scale for dc 
1366         pixLeft
= margLeftSize
*PPIPrinter
[0]/25.4  # mm*(dots/in)/(mm/in) 
1367         pixRight
= margRightSize
*PPIPrinter
[0]/25.4     
1368         pixTop
= margTopSize
*PPIPrinter
[1]/25.4 
1369         pixBottom
= margBottomSize
*PPIPrinter
[1]/25.4 
1371         plotAreaW
= pageSize
[0]-(pixLeft
+pixRight
) 
1372         plotAreaH
= pageSize
[1]-(pixTop
+pixBottom
) 
1374         # ratio offset and scale to screen size if preview 
1375         if self
.IsPreview(): 
1376             ratioW
= float(dcSize
[0])/pageSize
[0] 
1377             ratioH
= float(dcSize
[1])/pageSize
[1] 
1383         # rescale plot to page or preview plot area 
1384         self
.graph
._setSize
(plotAreaW
,plotAreaH
) 
1386         # Set offset and scale 
1387         dc
.SetDeviceOrigin(pixLeft
,pixTop
) 
1389         # Thicken up pens and increase marker size for printing 
1390         ratioW
= float(plotAreaW
)/clientDcSize
[0] 
1391         ratioH
= float(plotAreaH
)/clientDcSize
[1] 
1392         aveScale
= (ratioW
+ratioH
)/2 
1393         self
.graph
._setPrinterScale
(aveScale
)  # tickens up pens for printing 
1395         self
.graph
._printDraw
(dc
) 
1396         # rescale back to original 
1397         self
.graph
._setSize
() 
1398         self
.graph
._setPrinterScale
(1) 
1399         self
.graph
.Redraw()     #to get point label scale and shift correct 
1406 #--------------------------------------------------------------------------- 
1407 # if running standalone... 
1409 #     ...a sample implementation using the above 
1412 def _draw1Objects(): 
1413     # 100 points sin function, plotted as green circles 
1414     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1415     data1
.shape 
= (100, 2) 
1416     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1417     markers1 
= PolyMarker(data1
, legend
='Green Markers', colour
='green', marker
='circle',size
=1) 
1419     # 50 points cos function, plotted as red line 
1420     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1421     data1
.shape 
= (50,2) 
1422     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1423     lines 
= PolyLine(data1
, legend
= 'Red Line', colour
='red') 
1425     # A few more points... 
1427     markers2 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1428                           (3.*pi
/4., -1)], legend
='Cross Legend', colour
='blue', 
1431     return PlotGraphics([markers1
, lines
, markers2
],"Graph Title", "X Axis", "Y Axis") 
1433 def _draw2Objects(): 
1434     # 100 points sin function, plotted as green dots 
1435     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1436     data1
.shape 
= (100, 2) 
1437     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1438     line1 
= PolyLine(data1
, legend
='Green Line', colour
='green', width
=6, style
=wx
.DOT
) 
1440     # 50 points cos function, plotted as red dot-dash 
1441     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1442     data1
.shape 
= (50,2) 
1443     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1444     line2 
= PolyLine(data1
, legend
='Red Line', colour
='red', width
=3, style
= wx
.DOT_DASH
) 
1446     # A few more points... 
1448     markers1 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1449                           (3.*pi
/4., -1)], legend
='Cross Hatch Square', colour
='blue', width
= 3, size
= 6, 
1450                           fillcolour
= 'red', fillstyle
= wx
.CROSSDIAG_HATCH
, 
1453     return PlotGraphics([markers1
, line1
, line2
], "Big Markers with Different Line Styles") 
1455 def _draw3Objects(): 
1456     markerList
= ['circle', 'dot', 'square', 'triangle', 'triangle_down', 
1457                 'cross', 'plus', 'circle'] 
1459     for i 
in range(len(markerList
)): 
1460         m
.append(PolyMarker([(2*i
+.5,i
+.5)], legend
=markerList
[i
], colour
='blue', 
1461                           marker
=markerList
[i
])) 
1462     return PlotGraphics(m
, "Selection of Markers", "Minimal Axis", "No Axis") 
1464 def _draw4Objects(): 
1466     data1 
= _Numeric
.arange(5e5
,1e6
,10) 
1467     data1
.shape 
= (25000, 2) 
1468     line1 
= PolyLine(data1
, legend
='Wide Line', colour
='green', width
=5) 
1470     # A few more points... 
1471     markers2 
= PolyMarker(data1
, legend
='Square', colour
='blue', 
1473     return PlotGraphics([line1
, markers2
], "25,000 Points", "Value X", "") 
1475 def _draw5Objects(): 
1476     # Empty graph with axis defined but no points/lines 
1478     line1 
= PolyLine(points
, legend
='Wide Line', colour
='green', width
=5) 
1479     return PlotGraphics([line1
], "Empty Plot With Just Axes", "Value X", "Value Y") 
1481 def _draw6Objects(): 
1483     points1
=[(1,0), (1,10)] 
1484     line1 
= PolyLine(points1
, colour
='green', legend
='Feb.', width
=10) 
1485     points1g
=[(2,0), (2,4)] 
1486     line1g 
= PolyLine(points1g
, colour
='red', legend
='Mar.', width
=10) 
1487     points1b
=[(3,0), (3,6)] 
1488     line1b 
= PolyLine(points1b
, colour
='blue', legend
='Apr.', width
=10) 
1490     points2
=[(4,0), (4,12)] 
1491     line2 
= PolyLine(points2
, colour
='Yellow', legend
='May', width
=10) 
1492     points2g
=[(5,0), (5,8)] 
1493     line2g 
= PolyLine(points2g
, colour
='orange', legend
='June', width
=10) 
1494     points2b
=[(6,0), (6,4)] 
1495     line2b 
= PolyLine(points2b
, colour
='brown', legend
='July', width
=10) 
1497     return PlotGraphics([line1
, line1g
, line1b
, line2
, line2g
, line2b
], 
1498                         "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students") 
1501 class TestFrame(wx
.Frame
): 
1502     def __init__(self
, parent
, id, title
): 
1503         wx
.Frame
.__init
__(self
, parent
, id, title
, 
1504                           wx
.DefaultPosition
, (600, 400)) 
1506         # Now Create the menu bar and items 
1507         self
.mainmenu 
= wx
.MenuBar() 
1510         menu
.Append(200, 'Page Setup...', 'Setup the printer page') 
1511         self
.Bind(wx
.EVT_MENU
, self
.OnFilePageSetup
, id=200) 
1513         menu
.Append(201, 'Print Preview...', 'Show the current plot on page') 
1514         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrintPreview
, id=201) 
1516         menu
.Append(202, 'Print...', 'Print the current plot') 
1517         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrint
, id=202) 
1519         menu
.Append(203, 'Save Plot...', 'Save current plot') 
1520         self
.Bind(wx
.EVT_MENU
, self
.OnSaveFile
, id=203) 
1522         menu
.Append(205, 'E&xit', 'Enough of this already!') 
1523         self
.Bind(wx
.EVT_MENU
, self
.OnFileExit
, id=205) 
1524         self
.mainmenu
.Append(menu
, '&File') 
1527         menu
.Append(206, 'Draw1', 'Draw plots1') 
1528         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw1
, id=206) 
1529         menu
.Append(207, 'Draw2', 'Draw plots2') 
1530         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw2
, id=207) 
1531         menu
.Append(208, 'Draw3', 'Draw plots3') 
1532         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw3
, id=208) 
1533         menu
.Append(209, 'Draw4', 'Draw plots4') 
1534         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw4
, id=209) 
1535         menu
.Append(210, 'Draw5', 'Draw plots5') 
1536         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw5
, id=210) 
1537         menu
.Append(260, 'Draw6', 'Draw plots6') 
1538         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw6
, id=260) 
1541         menu
.Append(211, '&Redraw', 'Redraw plots') 
1542         self
.Bind(wx
.EVT_MENU
,self
.OnPlotRedraw
, id=211) 
1543         menu
.Append(212, '&Clear', 'Clear canvas') 
1544         self
.Bind(wx
.EVT_MENU
,self
.OnPlotClear
, id=212) 
1545         menu
.Append(213, '&Scale', 'Scale canvas') 
1546         self
.Bind(wx
.EVT_MENU
,self
.OnPlotScale
, id=213)  
1547         menu
.Append(214, 'Enable &Zoom', 'Enable Mouse Zoom', kind
=wx
.ITEM_CHECK
) 
1548         self
.Bind(wx
.EVT_MENU
,self
.OnEnableZoom
, id=214)  
1549         menu
.Append(215, 'Enable &Grid', 'Turn on Grid', kind
=wx
.ITEM_CHECK
) 
1550         self
.Bind(wx
.EVT_MENU
,self
.OnEnableGrid
, id=215) 
1551         menu
.Append(220, 'Enable &Legend', 'Turn on Legend', kind
=wx
.ITEM_CHECK
) 
1552         self
.Bind(wx
.EVT_MENU
,self
.OnEnableLegend
, id=220) 
1553         menu
.Append(222, 'Enable &Point Label', 'Show Closest Point', kind
=wx
.ITEM_CHECK
) 
1554         self
.Bind(wx
.EVT_MENU
,self
.OnEnablePointLabel
, id=222) 
1556         menu
.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit') 
1557         self
.Bind(wx
.EVT_MENU
,self
.OnScrUp
, id=225)  
1558         menu
.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units') 
1559         self
.Bind(wx
.EVT_MENU
,self
.OnScrRt
, id=230) 
1560         menu
.Append(235, '&Plot Reset', 'Reset to original plot') 
1561         self
.Bind(wx
.EVT_MENU
,self
.OnReset
, id=235) 
1563         self
.mainmenu
.Append(menu
, '&Plot') 
1566         menu
.Append(300, '&About', 'About this thing...') 
1567         self
.Bind(wx
.EVT_MENU
, self
.OnHelpAbout
, id=300) 
1568         self
.mainmenu
.Append(menu
, '&Help') 
1570         self
.SetMenuBar(self
.mainmenu
) 
1572         # A status bar to tell people what's happening 
1573         self
.CreateStatusBar(1) 
1575         self
.client 
= PlotCanvas(self
) 
1576         #define the function for drawing pointLabels 
1577         self
.client
.SetPointLabelFunc(self
.DrawPointLabel
) 
1578         # Create mouse event for showing cursor coords in status bar 
1579         self
.client
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
1580         # Show closest point when enabled 
1581         self
.client
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
1585     def DrawPointLabel(self
, dc
, mDataDict
): 
1586         """This is the fuction that defines how the pointLabels are plotted 
1587             dc - DC that will be passed 
1588             mDataDict - Dictionary of data that you want to use for the pointLabel 
1590             As an example I have decided I want a box at the curve point 
1591             with some text information about the curve plotted below. 
1592             Any wxDC method can be used. 
1595         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1596         dc
.SetBrush(wx
.Brush( wx
.BLACK
, wx
.SOLID 
) ) 
1598         sx
, sy 
= mDataDict
["scaledXY"] #scaled x,y of closest point 
1599         dc
.DrawRectangle( sx
-5,sy
-5, 10, 10)  #10by10 square centered on point 
1600         px
,py 
= mDataDict
["pointXY"] 
1601         cNum 
= mDataDict
["curveNum"] 
1602         pntIn 
= mDataDict
["pIndex"] 
1603         legend 
= mDataDict
["legend"] 
1604         #make a string to display 
1605         s 
= "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum
, legend
, px
, py
, pntIn
) 
1606         dc
.DrawText(s
, sx 
, sy
+1) 
1609     def OnMouseLeftDown(self
,event
): 
1610         s
= "Left Mouse Down at Point: (%.4f, %.4f)" % self
.client
.GetXY(event
) 
1611         self
.SetStatusText(s
) 
1612         event
.Skip()            #allows plotCanvas OnMouseLeftDown to be called 
1614     def OnMotion(self
, event
): 
1615         #show closest point (when enbled) 
1616         if self
.client
.GetEnablePointLabel() == True: 
1617             #make up dict with info for the pointLabel 
1618             #I've decided to mark the closest point on the closest curve 
1619             dlst
= self
.client
.GetClosetPoint( self
.client
.GetXY(event
), pointScaled
= True) 
1620             if dlst 
!= []:    #returns [] if none 
1621                 curveNum
, legend
, pIndex
, pointXY
, scaledXY
, distance 
= dlst
 
1622                 #make up dictionary to pass to my user function (see DrawPointLabel)  
1623                 mDataDict
= {"curveNum":curveNum
, "legend":legend
, "pIndex":pIndex
,\
 
1624                             "pointXY":pointXY
, "scaledXY":scaledXY
} 
1625                 #pass dict to update the pointLabel 
1626                 self
.client
.UpdatePointLabel(mDataDict
) 
1627         event
.Skip()           #go to next handler 
1629     def OnFilePageSetup(self
, event
): 
1630         self
.client
.PageSetup() 
1632     def OnFilePrintPreview(self
, event
): 
1633         self
.client
.PrintPreview() 
1635     def OnFilePrint(self
, event
): 
1636         self
.client
.Printout() 
1638     def OnSaveFile(self
, event
): 
1639         self
.client
.SaveFile() 
1641     def OnFileExit(self
, event
): 
1644     def OnPlotDraw1(self
, event
): 
1645         self
.resetDefaults() 
1646         self
.client
.Draw(_draw1Objects()) 
1648     def OnPlotDraw2(self
, event
): 
1649         self
.resetDefaults() 
1650         self
.client
.Draw(_draw2Objects()) 
1652     def OnPlotDraw3(self
, event
): 
1653         self
.resetDefaults() 
1654         self
.client
.SetFont(wx
.Font(10,wx
.SCRIPT
,wx
.NORMAL
,wx
.NORMAL
)) 
1655         self
.client
.SetFontSizeAxis(20) 
1656         self
.client
.SetFontSizeLegend(12) 
1657         self
.client
.SetXSpec('min') 
1658         self
.client
.SetYSpec('none') 
1659         self
.client
.Draw(_draw3Objects()) 
1661     def OnPlotDraw4(self
, event
): 
1662         self
.resetDefaults() 
1663         drawObj
= _draw4Objects() 
1664         self
.client
.Draw(drawObj
) 
1666 ##        start = _time.clock()             
1667 ##        for x in range(10): 
1668 ##            self.client.Draw(drawObj) 
1669 ##        print "10 plots of Draw4 took: %f sec."%(_time.clock() - start) 
1672     def OnPlotDraw5(self
, event
): 
1673         # Empty plot with just axes 
1674         self
.resetDefaults() 
1675         drawObj
= _draw5Objects() 
1676         # make the axis X= (0,5), Y=(0,10) 
1677         # (default with None is X= (-1,1), Y= (-1,1)) 
1678         self
.client
.Draw(drawObj
, xAxis
= (0,5), yAxis
= (0,10)) 
1680     def OnPlotDraw6(self
, event
): 
1682         self
.resetDefaults() 
1683         #self.client.SetEnableLegend(True)   #turn on Legend 
1684         #self.client.SetEnableGrid(True)     #turn on Grid 
1685         self
.client
.SetXSpec('none')        #turns off x-axis scale 
1686         self
.client
.SetYSpec('auto') 
1687         self
.client
.Draw(_draw6Objects(), xAxis
= (0,7)) 
1689     def OnPlotRedraw(self
,event
): 
1690         self
.client
.Redraw() 
1692     def OnPlotClear(self
,event
): 
1695     def OnPlotScale(self
, event
): 
1696         if self
.client
.last_draw 
!= None: 
1697             graphics
, xAxis
, yAxis
= self
.client
.last_draw
 
1698             self
.client
.Draw(graphics
,(1,3.05),(0,1)) 
1700     def OnEnableZoom(self
, event
): 
1701         self
.client
.SetEnableZoom(event
.IsChecked()) 
1703     def OnEnableGrid(self
, event
): 
1704         self
.client
.SetEnableGrid(event
.IsChecked()) 
1706     def OnEnableLegend(self
, event
): 
1707         self
.client
.SetEnableLegend(event
.IsChecked()) 
1709     def OnEnablePointLabel(self
, event
): 
1710         self
.client
.SetEnablePointLabel(event
.IsChecked()) 
1712     def OnScrUp(self
, event
): 
1713         self
.client
.ScrollUp(1) 
1715     def OnScrRt(self
,event
): 
1716         self
.client
.ScrollRight(2) 
1718     def OnReset(self
,event
): 
1721     def OnHelpAbout(self
, event
): 
1722         from wx
.lib
.dialogs 
import ScrolledMessageDialog
 
1723         about 
= ScrolledMessageDialog(self
, __doc__
, "About...") 
1726     def resetDefaults(self
): 
1727         """Just to reset the fonts back to the PlotCanvas defaults""" 
1728         self
.client
.SetFont(wx
.Font(10,wx
.SWISS
,wx
.NORMAL
,wx
.NORMAL
)) 
1729         self
.client
.SetFontSizeAxis(10) 
1730         self
.client
.SetFontSizeLegend(7) 
1731         self
.client
.SetXSpec('auto') 
1732         self
.client
.SetYSpec('auto') 
1737     class MyApp(wx
.App
): 
1739             wx
.InitAllImageHandlers() 
1740             frame 
= TestFrame(None, -1, "PlotCanvas") 
1742             self
.SetTopWindow(frame
) 
1749 if __name__ 
== '__main__':