]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/plot.py
2a5be751207b3d98a4d306000d5593fe0abe30da
   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 or NumPy 
  97     import numpy 
as _Numeric
 
 100         import numarray 
as _Numeric  
#if numarray is used it is renamed Numeric 
 103             import Numeric 
as _Numeric
 
 106             This module requires the Numeric/numarray or NumPy module, 
 107             which could not be imported.  It probably is not installed 
 108             (it's not part of the standard Python distribution). See the 
 109             Numeric Python site (http://numpy.scipy.org) for information on 
 110             downloading source or binaries.""" 
 111             raise ImportError, "Numeric,numarray or NumPy not found. \n" + msg
 
 116 # Plotting classes... 
 119     """Base Class for lines and markers 
 120         - All methods are private. 
 123     def __init__(self
, points
, attr
): 
 124         self
.points 
= _Numeric
.array(points
) 
 125         self
.currentScale
= (1,1) 
 126         self
.currentShift
= (0,0) 
 127         self
.scaled 
= self
.points
 
 129         self
.attributes
.update(self
._attributes
) 
 130         for name
, value 
in attr
.items():    
 131             if name 
not in self
._attributes
.keys(): 
 132                 raise KeyError, "Style attribute incorrect. Should be one of %s" % self
._attributes
.keys() 
 133             self
.attributes
[name
] = value
 
 135     def boundingBox(self
): 
 136         if len(self
.points
) == 0: 
 138             # defaults to (-1,-1) and (1,1) but axis can be set in Draw 
 139             minXY
= _Numeric
.array([-1,-1]) 
 140             maxXY
= _Numeric
.array([ 1, 1]) 
 142             minXY
= _Numeric
.minimum
.reduce(self
.points
) 
 143             maxXY
= _Numeric
.maximum
.reduce(self
.points
) 
 146     def scaleAndShift(self
, scale
=(1,1), shift
=(0,0)): 
 147         if len(self
.points
) == 0: 
 150         if (scale 
is not self
.currentScale
) or (shift 
is not self
.currentShift
): 
 151             # update point scaling 
 152             self
.scaled 
= scale
*self
.points
+shift
 
 153             self
.currentScale
= scale
 
 154             self
.currentShift
= shift
 
 155         # else unchanged use the current scaling 
 158         return self
.attributes
['legend'] 
 160     def getClosestPoint(self
, pntXY
, pointScaled
= True): 
 161         """Returns the index of closest point on the curve, pointXY, scaledXY, distance 
 163             if pointScaled == True based on screen coords 
 164             if pointScaled == False based on user coords 
 166         if pointScaled 
== True: 
 169             pxy 
= self
.currentScale 
* _Numeric
.array(pntXY
)+ self
.currentShift
 
 173             pxy 
= _Numeric
.array(pntXY
) 
 174         #determine distance for each point 
 175         d
= _Numeric
.sqrt(_Numeric
.add
.reduce((p
-pxy
)**2,1)) #sqrt(dx^2+dy^2) 
 176         pntIndex 
= _Numeric
.argmin(d
) 
 178         return [pntIndex
, self
.points
[pntIndex
], self
.scaled
[pntIndex
], dist
] 
 181 class PolyLine(PolyPoints
): 
 182     """Class to define line type and style 
 183         - All methods except __init__ are private. 
 186     _attributes 
= {'colour': 'black', 
 191     def __init__(self
, points
, **attr
): 
 192         """Creates PolyLine object 
 193             points - sequence (array, tuple or list) of (x,y) points making up line 
 194             **attr - key word attributes 
 196                     'colour'= 'black',          - wx.Pen Colour any wx.NamedColour 
 197                     'width'= 1,                 - Pen width 
 198                     'style'= wx.SOLID,          - wx.Pen style 
 199                     'legend'= ''                - Line Legend to display 
 201         PolyPoints
.__init
__(self
, points
, attr
) 
 203     def draw(self
, dc
, printerScale
, coord
= None): 
 204         colour 
= self
.attributes
['colour'] 
 205         width 
= self
.attributes
['width'] * printerScale
 
 206         style
= self
.attributes
['style'] 
 207         if not isinstance(colour
, wx
.Colour
): 
 208             colour 
= wx
.NamedColour(colour
) 
 209         pen 
= wx
.Pen(colour
, width
, style
) 
 210         pen
.SetCap(wx
.CAP_BUTT
) 
 213             dc
.DrawLines(self
.scaled
) 
 215             dc
.DrawLines(coord
) # draw legend line 
 217     def getSymExtent(self
, printerScale
): 
 218         """Width and Height of Marker""" 
 219         h
= self
.attributes
['width'] * printerScale
 
 224 class PolyMarker(PolyPoints
): 
 225     """Class to define marker type and style 
 226         - All methods except __init__ are private. 
 229     _attributes 
= {'colour': 'black', 
 233                    'fillstyle': wx
.SOLID
, 
 237     def __init__(self
, points
, **attr
): 
 238         """Creates PolyMarker object 
 239         points - sequence (array, tuple or list) of (x,y) points 
 240         **attr - key word attributes 
 242                 'colour'= 'black',          - wx.Pen Colour any wx.NamedColour 
 243                 'width'= 1,                 - Pen width 
 244                 'size'= 2,                  - Marker size 
 245                 'fillcolour'= same as colour,      - wx.Brush Colour any wx.NamedColour 
 246                 'fillstyle'= wx.SOLID,      - wx.Brush fill style (use wx.TRANSPARENT for no fill) 
 247                 'marker'= 'circle'          - Marker shape 
 248                 'legend'= ''                - Marker Legend to display 
 260         PolyPoints
.__init
__(self
, points
, attr
) 
 262     def draw(self
, dc
, printerScale
, coord
= None): 
 263         colour 
= self
.attributes
['colour'] 
 264         width 
= self
.attributes
['width'] * printerScale
 
 265         size 
= self
.attributes
['size'] * printerScale
 
 266         fillcolour 
= self
.attributes
['fillcolour'] 
 267         fillstyle 
= self
.attributes
['fillstyle'] 
 268         marker 
= self
.attributes
['marker'] 
 270         if colour 
and not isinstance(colour
, wx
.Colour
): 
 271             colour 
= wx
.NamedColour(colour
) 
 272         if fillcolour 
and not isinstance(fillcolour
, wx
.Colour
): 
 273             fillcolour 
= wx
.NamedColour(fillcolour
) 
 275         dc
.SetPen(wx
.Pen(colour
, width
)) 
 277             dc
.SetBrush(wx
.Brush(fillcolour
,fillstyle
)) 
 279             dc
.SetBrush(wx
.Brush(colour
, fillstyle
)) 
 281             self
._drawmarkers
(dc
, self
.scaled
, marker
, size
) 
 283             self
._drawmarkers
(dc
, coord
, marker
, size
) # draw legend marker 
 285     def getSymExtent(self
, printerScale
): 
 286         """Width and Height of Marker""" 
 287         s
= 5*self
.attributes
['size'] * printerScale
 
 290     def _drawmarkers(self
, dc
, coords
, marker
,size
=1): 
 291         f 
= eval('self._' +marker
) 
 294     def _circle(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
.DrawEllipseList(rect
.astype(_Numeric
.Int32
)) 
 301     def _dot(self
, dc
, coords
, size
=1): 
 302         dc
.DrawPointList(coords
) 
 304     def _square(self
, dc
, coords
, size
=1): 
 307         rect
= _Numeric
.zeros((len(coords
),4),_Numeric
.Float
)+[0.0,0.0,wh
,wh
] 
 308         rect
[:,0:2]= coords
-[fact
,fact
] 
 309         dc
.DrawRectangleList(rect
.astype(_Numeric
.Int32
)) 
 311     def _triangle(self
, dc
, coords
, size
=1): 
 312         shape
= [(-2.5*size
,1.44*size
), (2.5*size
,1.44*size
), (0.0,-2.88*size
)] 
 313         poly
= _Numeric
.repeat(coords
,3) 
 314         poly
.shape
= (len(coords
),3,2) 
 316         dc
.DrawPolygonList(poly
.astype(_Numeric
.Int32
)) 
 318     def _triangle_down(self
, dc
, coords
, size
=1): 
 319         shape
= [(-2.5*size
,-1.44*size
), (2.5*size
,-1.44*size
), (0.0,2.88*size
)] 
 320         poly
= _Numeric
.repeat(coords
,3) 
 321         poly
.shape
= (len(coords
),3,2) 
 323         dc
.DrawPolygonList(poly
.astype(_Numeric
.Int32
)) 
 325     def _cross(self
, dc
, coords
, size
=1): 
 327         for f 
in [[-fact
,-fact
,fact
,fact
],[-fact
,fact
,fact
,-fact
]]: 
 328             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 329             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 331     def _plus(self
, dc
, coords
, size
=1): 
 333         for f 
in [[-fact
,0,fact
,0],[0,-fact
,0,fact
]]: 
 334             lines
= _Numeric
.concatenate((coords
,coords
),axis
=1)+f
 
 335             dc
.DrawLineList(lines
.astype(_Numeric
.Int32
)) 
 338     """Container to hold PolyXXX objects and graph labels 
 339         - All methods except __init__ are private. 
 342     def __init__(self
, objects
, title
='', xLabel
='', yLabel
= ''): 
 343         """Creates PlotGraphics object 
 344         objects - list of PolyXXX objects to make graph 
 345         title - title shown at top of graph 
 346         xLabel - label shown on x-axis 
 347         yLabel - label shown on y-axis 
 349         if type(objects
) not in [list,tuple]: 
 350             raise TypeError, "objects argument should be list or tuple" 
 351         self
.objects 
= objects
 
 356     def boundingBox(self
): 
 357         p1
, p2 
= self
.objects
[0].boundingBox() 
 358         for o 
in self
.objects
[1:]: 
 359             p1o
, p2o 
= o
.boundingBox() 
 360             p1 
= _Numeric
.minimum(p1
, p1o
) 
 361             p2 
= _Numeric
.maximum(p2
, p2o
) 
 364     def scaleAndShift(self
, scale
=(1,1), shift
=(0,0)): 
 365         for o 
in self
.objects
: 
 366             o
.scaleAndShift(scale
, shift
) 
 368     def setPrinterScale(self
, scale
): 
 369         """Thickens up lines and markers only for printing""" 
 370         self
.printerScale
= scale
 
 372     def setXLabel(self
, xLabel
= ''): 
 373         """Set the X axis label on the graph""" 
 376     def setYLabel(self
, yLabel
= ''): 
 377         """Set the Y axis label on the graph""" 
 380     def setTitle(self
, title
= ''): 
 381         """Set the title at the top of graph""" 
 385         """Get x axis label string""" 
 389         """Get y axis label string""" 
 392     def getTitle(self
, title
= ''): 
 393         """Get the title at the top of graph""" 
 397         for o 
in self
.objects
: 
 398             #t=_time.clock()          # profile info 
 399             o
.draw(dc
, self
.printerScale
) 
 401             #print o, "time=", dt 
 403     def getSymExtent(self
, printerScale
): 
 404         """Get max width and height of lines and markers symbols for legend""" 
 405         symExt 
= self
.objects
[0].getSymExtent(printerScale
) 
 406         for o 
in self
.objects
[1:]: 
 407             oSymExt 
= o
.getSymExtent(printerScale
) 
 408             symExt 
= _Numeric
.maximum(symExt
, oSymExt
) 
 411     def getLegendNames(self
): 
 412         """Returns list of legend names""" 
 413         lst 
= [None]*len(self
) 
 414         for i 
in range(len(self
)): 
 415             lst
[i
]= self
.objects
[i
].getLegend() 
 419         return len(self
.objects
) 
 421     def __getitem__(self
, item
): 
 422         return self
.objects
[item
] 
 425 #------------------------------------------------------------------------------- 
 426 # Main window that you will want to import into your application. 
 428 class PlotCanvas(wx
.Panel
): 
 430     Subclass of a wx.Panel which holds two scrollbars and the actual 
 431     plotting canvas (self.canvas). It allows for simple general plotting 
 432     of data with zoom, labels, and automatic axis scaling.""" 
 434     def __init__(self
, parent
): 
 435         """Constructs a panel, which can be a child of a frame or 
 436         any other non-control window""" 
 438         wx
.Panel
.__init
__(self
, parent
) 
 440         sizer 
= wx
.FlexGridSizer(2,2,0,0) 
 441         self
.canvas 
= wx
.Window(self
, -1) 
 442         #self.canvas.SetMinSize((10,10)) 
 443         self
.sb_vert 
= wx
.ScrollBar(self
, -1, style
=wx
.SB_VERTICAL
) 
 444         self
.sb_vert
.SetScrollbar(0,1000,1000,1000) 
 445         self
.sb_hor 
= wx
.ScrollBar(self
, -1, style
=wx
.SB_HORIZONTAL
) 
 446         self
.sb_hor
.SetScrollbar(0,1000,1000,1000) 
 448         sizer
.Add(self
.canvas
, 1, wx
.EXPAND
) 
 449         sizer
.Add(self
.sb_vert
, 0, wx
.EXPAND
) 
 450         sizer
.Add(self
.sb_hor
, 0, wx
.EXPAND
) 
 452         #corner = wx.Window(self) 
 453         #corner.SetMinSize((0,0)) 
 454         #sizer.Add(corner, 0, wx.EXPAND) 
 456         sizer
.AddGrowableRow(0, 1) 
 457         sizer
.AddGrowableCol(0, 1) 
 458         self
.sb_vert
.Show(False) 
 459         self
.sb_hor
.Show(False)         
 463         self
.SetBackgroundColour("white") 
 465         # Create some mouse events for zooming 
 466         self
.canvas
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
 467         self
.canvas
.Bind(wx
.EVT_LEFT_UP
, self
.OnMouseLeftUp
) 
 468         self
.canvas
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
 469         self
.canvas
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnMouseDoubleClick
) 
 470         self
.canvas
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnMouseRightDown
) 
 473         self
.Bind(wx
.EVT_SCROLL_THUMBTRACK
, self
.OnScroll
) 
 474         self
.Bind(wx
.EVT_SCROLL_PAGEUP
, self
.OnScroll
) 
 475         self
.Bind(wx
.EVT_SCROLL_PAGEDOWN
, self
.OnScroll
) 
 476         self
.Bind(wx
.EVT_SCROLL_LINEUP
, self
.OnScroll
) 
 477         self
.Bind(wx
.EVT_SCROLL_LINEDOWN
, self
.OnScroll
) 
 479         # set curser as cross-hairs 
 480         self
.canvas
.SetCursor(wx
.CROSS_CURSOR
) 
 481         self
.HandCursor 
= wx
.CursorFromImage(getHandImage()) 
 482         self
.GrabHandCursor 
= wx
.CursorFromImage(getGrabHandImage()) 
 483         self
.MagCursor 
= wx
.CursorFromImage(getMagPlusImage()) 
 485         # Things for printing 
 486         self
.print_data 
= wx
.PrintData() 
 487         self
.print_data
.SetPaperId(wx
.PAPER_LETTER
) 
 488         self
.print_data
.SetOrientation(wx
.LANDSCAPE
) 
 489         self
.pageSetupData
= wx
.PageSetupDialogData() 
 490         self
.pageSetupData
.SetMarginBottomRight((25,25)) 
 491         self
.pageSetupData
.SetMarginTopLeft((25,25)) 
 492         self
.pageSetupData
.SetPrintData(self
.print_data
) 
 493         self
.printerScale 
= 1 
 496         # scrollbar variables 
 497         self
._sb
_ignore 
= False 
 498         self
._adjustingSB 
= False 
 499         self
._sb
_xfullrange 
= 0 
 500         self
._sb
_yfullrange 
= 0 
 504         self
._dragEnabled 
= False 
 505         self
._screenCoordinates 
= _Numeric
.array([0.0, 0.0]) 
 508         self
._zoomInFactor 
=  0.5 
 509         self
._zoomOutFactor 
= 2 
 510         self
._zoomCorner
1= _Numeric
.array([0.0, 0.0]) # left mouse down corner 
 511         self
._zoomCorner
2= _Numeric
.array([0.0, 0.0])   # left mouse up corner 
 512         self
._zoomEnabled
= False 
 513         self
._hasDragged
= False 
 516         self
.last_draw 
= None 
 521         self
._gridEnabled
= False 
 522         self
._legendEnabled
= False 
 526         self
._fontSizeAxis
= 10 
 527         self
._fontSizeTitle
= 15 
 528         self
._fontSizeLegend
= 7 
 531         self
._pointLabelEnabled
= False 
 532         self
.last_PointLabel
= None 
 533         self
._pointLabelFunc
= None 
 534         self
.canvas
.Bind(wx
.EVT_LEAVE_WINDOW
, self
.OnLeave
) 
 536         self
.canvas
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 537         self
.canvas
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 538         # OnSize called to make sure the buffer is initialized. 
 539         # This might result in OnSize getting called twice on some 
 540         # platforms at initialization, but little harm done. 
 541         self
.OnSize(None) # sets the initial size based on client size 
 543         self
._gridColour 
= wx
.NamedColour('black') 
 545     def SetCursor(self
, cursor
): 
 546         self
.canvas
.SetCursor(cursor
) 
 548     def GetGridColour(self
): 
 549         return self
._gridColour
 
 551     def SetGridColour(self
, colour
): 
 552         if isinstance(colour
, wx
.Colour
): 
 553             self
._gridColour 
= colour
 
 555             self
._gridColour 
= wx
.NamedColour(colour
) 
 559     def SaveFile(self
, fileName
= ''): 
 560         """Saves the file to the type specified in the extension. If no file 
 561         name is specified a dialog box is provided.  Returns True if sucessful, 
 564         .bmp  Save a Windows bitmap file. 
 565         .xbm  Save an X bitmap file. 
 566         .xpm  Save an XPM bitmap file. 
 567         .png  Save a Portable Network Graphics file. 
 568         .jpg  Save a Joint Photographic Experts Group file. 
 570         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 571             dlg1 
= wx
.FileDialog( 
 573                     "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "", 
 574                     "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg", 
 575                     wx
.SAVE|wx
.OVERWRITE_PROMPT
 
 579                     if dlg1
.ShowModal() == wx
.ID_OK
: 
 580                         fileName 
= dlg1
.GetPath() 
 581                         # Check for proper exension 
 582                         if _string
.lower(fileName
[-3:]) not in ['bmp','xbm','xpm','png','jpg']: 
 583                             dlg2 
= wx
.MessageDialog(self
, 'File name extension\n' 
 585                             'bmp, xbm, xpm, png, or jpg', 
 586                               'File Name Error', wx
.OK | wx
.ICON_ERROR
) 
 592                             break # now save file 
 593                     else: # exit without saving 
 598         # File name has required extension 
 599         fType 
= _string
.lower(fileName
[-3:]) 
 601             tp
= wx
.BITMAP_TYPE_BMP       
# Save a Windows bitmap file. 
 603             tp
= wx
.BITMAP_TYPE_XBM       
# Save an X bitmap file. 
 605             tp
= wx
.BITMAP_TYPE_XPM       
# Save an XPM bitmap file. 
 607             tp
= wx
.BITMAP_TYPE_JPEG      
# Save a JPG file. 
 609             tp
= wx
.BITMAP_TYPE_PNG       
# Save a PNG file. 
 611         res
= self
._Buffer
.SaveFile(fileName
, tp
) 
 615         """Brings up the page setup dialog""" 
 616         data 
= self
.pageSetupData
 
 617         data
.SetPrintData(self
.print_data
) 
 618         dlg 
= wx
.PageSetupDialog(self
.parent
, data
) 
 620             if dlg
.ShowModal() == wx
.ID_OK
: 
 621                 data 
= dlg
.GetPageSetupData() # returns wx.PageSetupDialogData 
 622                 # updates page parameters from dialog 
 623                 self
.pageSetupData
.SetMarginBottomRight(data
.GetMarginBottomRight()) 
 624                 self
.pageSetupData
.SetMarginTopLeft(data
.GetMarginTopLeft()) 
 625                 self
.pageSetupData
.SetPrintData(data
.GetPrintData()) 
 626                 self
.print_data
=wx
.PrintData(data
.GetPrintData()) # updates print_data 
 630     def Printout(self
, paper
=None): 
 631         """Print current plot.""" 
 633             self
.print_data
.SetPaperId(paper
) 
 634         pdd 
= wx
.PrintDialogData(self
.print_data
) 
 635         printer 
= wx
.Printer(pdd
) 
 636         out 
= PlotPrintout(self
) 
 637         print_ok 
= printer
.Print(self
.parent
, out
) 
 639             self
.print_data 
= wx
.PrintData(printer
.GetPrintDialogData().GetPrintData()) 
 642     def PrintPreview(self
): 
 643         """Print-preview current plot.""" 
 644         printout 
= PlotPrintout(self
) 
 645         printout2 
= PlotPrintout(self
) 
 646         self
.preview 
= wx
.PrintPreview(printout
, printout2
, self
.print_data
) 
 647         if not self
.preview
.Ok(): 
 648             wx
.MessageDialog(self
, "Print Preview failed.\n" \
 
 649                                "Check that default printer is configured\n", \
 
 650                                "Print error", wx
.OK|wx
.CENTRE
).ShowModal() 
 651         self
.preview
.SetZoom(40) 
 652         # search up tree to find frame instance 
 654         while not isinstance(frameInst
, wx
.Frame
): 
 655             frameInst
= frameInst
.GetParent() 
 656         frame 
= wx
.PreviewFrame(self
.preview
, frameInst
, "Preview") 
 658         frame
.SetPosition(self
.GetPosition()) 
 659         frame
.SetSize((600,550)) 
 660         frame
.Centre(wx
.BOTH
) 
 663     def SetFontSizeAxis(self
, point
= 10): 
 664         """Set the tick and axis label font size (default is 10 point)""" 
 665         self
._fontSizeAxis
= point
 
 667     def GetFontSizeAxis(self
): 
 668         """Get current tick and axis label font size in points""" 
 669         return self
._fontSizeAxis
 
 671     def SetFontSizeTitle(self
, point
= 15): 
 672         """Set Title font size (default is 15 point)""" 
 673         self
._fontSizeTitle
= point
 
 675     def GetFontSizeTitle(self
): 
 676         """Get current Title font size in points""" 
 677         return self
._fontSizeTitle
 
 679     def SetFontSizeLegend(self
, point
= 7): 
 680         """Set Legend font size (default is 7 point)""" 
 681         self
._fontSizeLegend
= point
 
 683     def GetFontSizeLegend(self
): 
 684         """Get current Legend font size in points""" 
 685         return self
._fontSizeLegend
 
 687     def SetShowScrollbars(self
, value
): 
 688         """Set True to show scrollbars""" 
 689         if value 
not in [True,False]: 
 690             raise TypeError, "Value should be True or False" 
 691         if value 
== self
.GetShowScrollbars(): 
 693         self
.sb_vert
.Show(value
) 
 694         self
.sb_hor
.Show(value
) 
 695         wx
.CallAfter(self
.Layout
) 
 697     def GetShowScrollbars(self
): 
 698         """Set True to show scrollbars""" 
 699         return self
.sb_vert
.IsShown() 
 701     def SetEnableDrag(self
, value
): 
 702         """Set True to enable drag.""" 
 703         if value 
not in [True,False]: 
 704             raise TypeError, "Value should be True or False" 
 706             if self
.GetEnableZoom(): 
 707                 self
.SetEnableZoom(False) 
 708             self
.SetCursor(self
.HandCursor
) 
 710             self
.SetCursor(wx
.CROSS_CURSOR
) 
 711         self
._dragEnabled 
= value
 
 713     def GetEnableDrag(self
): 
 714         return self
._dragEnabled
 
 716     def SetEnableZoom(self
, value
): 
 717         """Set True to enable zooming.""" 
 718         if value 
not in [True,False]: 
 719             raise TypeError, "Value should be True or False" 
 721             if self
.GetEnableDrag(): 
 722                 self
.SetEnableDrag(False) 
 723             self
.SetCursor(self
.MagCursor
) 
 725             self
.SetCursor(wx
.CROSS_CURSOR
) 
 726         self
._zoomEnabled
= value
 
 728     def GetEnableZoom(self
): 
 729         """True if zooming enabled.""" 
 730         return self
._zoomEnabled
 
 732     def SetEnableGrid(self
, value
): 
 733         """Set True to enable grid.""" 
 734         if value 
not in [True,False,'Horizontal','Vertical']: 
 735             raise TypeError, "Value should be True, False, Horizontal or Vertical" 
 736         self
._gridEnabled
= value
 
 739     def GetEnableGrid(self
): 
 740         """True if grid enabled.""" 
 741         return self
._gridEnabled
 
 743     def SetEnableLegend(self
, value
): 
 744         """Set True to enable legend.""" 
 745         if value 
not in [True,False]: 
 746             raise TypeError, "Value should be True or False" 
 747         self
._legendEnabled
= value 
 
 750     def GetEnableLegend(self
): 
 751         """True if Legend enabled.""" 
 752         return self
._legendEnabled
 
 754     def SetEnablePointLabel(self
, value
): 
 755         """Set True to enable pointLabel.""" 
 756         if value 
not in [True,False]: 
 757             raise TypeError, "Value should be True or False" 
 758         self
._pointLabelEnabled
= value 
 
 759         self
.Redraw()  #will erase existing pointLabel if present 
 760         self
.last_PointLabel 
= None 
 762     def GetEnablePointLabel(self
): 
 763         """True if pointLabel enabled.""" 
 764         return self
._pointLabelEnabled
 
 766     def SetPointLabelFunc(self
, func
): 
 767         """Sets the function with custom code for pointLabel drawing 
 768             ******** more info needed *************** 
 770         self
._pointLabelFunc
= func
 
 772     def GetPointLabelFunc(self
): 
 773         """Returns pointLabel Drawing Function""" 
 774         return self
._pointLabelFunc
 
 777         """Unzoom the plot.""" 
 778         self
.last_PointLabel 
= None        #reset pointLabel 
 779         if self
.last_draw 
is not None: 
 780             self
.Draw(self
.last_draw
[0]) 
 782     def ScrollRight(self
, units
):           
 783         """Move view right number of axis units.""" 
 784         self
.last_PointLabel 
= None        #reset pointLabel 
 785         if self
.last_draw 
is not None: 
 786             graphics
, xAxis
, yAxis
= self
.last_draw
 
 787             xAxis
= (xAxis
[0]+units
, xAxis
[1]+units
) 
 788             self
.Draw(graphics
,xAxis
,yAxis
) 
 790     def ScrollUp(self
, units
): 
 791         """Move view up number of axis units.""" 
 792         self
.last_PointLabel 
= None        #reset pointLabel 
 793         if self
.last_draw 
is not None: 
 794              graphics
, xAxis
, yAxis
= self
.last_draw
 
 795              yAxis
= (yAxis
[0]+units
, yAxis
[1]+units
) 
 796              self
.Draw(graphics
,xAxis
,yAxis
) 
 799     def GetXY(self
,event
): 
 800         """Takes a mouse event and returns the XY user axis values.""" 
 801         x
,y
= self
.PositionScreenToUser(event
.GetPosition()) 
 804     def PositionUserToScreen(self
, pntXY
): 
 805         """Converts User position to Screen Coordinates""" 
 806         userPos
= _Numeric
.array(pntXY
) 
 807         x
,y
= userPos 
* self
._pointScale 
+ self
._pointShift
 
 810     def PositionScreenToUser(self
, pntXY
): 
 811         """Converts Screen position to User Coordinates""" 
 812         screenPos
= _Numeric
.array(pntXY
) 
 813         x
,y
= (screenPos
-self
._pointShift
)/self
._pointScale
 
 816     def SetXSpec(self
, type= 'auto'): 
 817         """xSpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 819             'none' - shows no axis or tick mark values 
 820             'min' - shows min bounding box values 
 821             'auto' - rounds axis range to sensible values 
 825     def SetYSpec(self
, type= 'auto'): 
 826         """ySpec- defines x axis type. Can be 'none', 'min' or 'auto' 
 828             'none' - shows no axis or tick mark values 
 829             'min' - shows min bounding box values 
 830             'auto' - rounds axis range to sensible values 
 835         """Returns current XSpec for axis""" 
 839         """Returns current YSpec for axis""" 
 842     def GetXMaxRange(self
): 
 843         """Returns (minX, maxX) x-axis range for displayed graph""" 
 844         graphics
= self
.last_draw
[0] 
 845         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 846         xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 849     def GetYMaxRange(self
): 
 850         """Returns (minY, maxY) y-axis range for displayed graph""" 
 851         graphics
= self
.last_draw
[0] 
 852         p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 853         yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 856     def GetXCurrentRange(self
): 
 857         """Returns (minX, maxX) x-axis for currently displayed portion of graph""" 
 858         return self
.last_draw
[1] 
 860     def GetYCurrentRange(self
): 
 861         """Returns (minY, maxY) y-axis for currently displayed portion of graph""" 
 862         return self
.last_draw
[2] 
 864     def Draw(self
, graphics
, xAxis 
= None, yAxis 
= None, dc 
= None): 
 865         """Draw objects in graphics with specified x and y axis. 
 866         graphics- instance of PlotGraphics with list of PolyXXX objects 
 867         xAxis - tuple with (min, max) axis range to view 
 868         yAxis - same as xAxis 
 869         dc - drawing context - doesn't have to be specified.     
 870         If it's not, the offscreen buffer is used 
 872         # check Axis is either tuple or none 
 873         if type(xAxis
) not in [type(None),tuple]: 
 874             raise TypeError, "xAxis should be None or (minX,maxX)" 
 875         if type(yAxis
) not in [type(None),tuple]: 
 876             raise TypeError, "yAxis should be None or (minY,maxY)" 
 878         # check case for axis = (a,b) where a==b caused by improper zooms 
 880             if xAxis
[0] == xAxis
[1]: 
 883             if yAxis
[0] == yAxis
[1]: 
 887             # sets new dc and clears it  
 888             dc 
= wx
.BufferedDC(wx
.ClientDC(self
.canvas
), self
._Buffer
) 
 894         # set font size for every thing but title and legend 
 895         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 897         # sizes axis to axis type, create lower left and upper right corners of plot 
 898         if xAxis 
== None or yAxis 
== None: 
 899             # One or both axis not specified in Draw 
 900             p1
, p2 
= graphics
.boundingBox()     # min, max points of graphics 
 902                 xAxis 
= self
._axisInterval
(self
._xSpec
, p1
[0], p2
[0]) # in user units 
 904                 yAxis 
= self
._axisInterval
(self
._ySpec
, p1
[1], p2
[1]) 
 905             # Adjust bounding box for axis spec 
 906             p1
[0],p1
[1] = xAxis
[0], yAxis
[0]     # lower left corner user scale (xmin,ymin) 
 907             p2
[0],p2
[1] = xAxis
[1], yAxis
[1]     # upper right corner user scale (xmax,ymax) 
 909             # Both axis specified in Draw 
 910             p1
= _Numeric
.array([xAxis
[0], yAxis
[0]])    # lower left corner user scale (xmin,ymin) 
 911             p2
= _Numeric
.array([xAxis
[1], yAxis
[1]])     # upper right corner user scale (xmax,ymax) 
 913         self
.last_draw 
= (graphics
, xAxis
, yAxis
)       # saves most recient values 
 915         # Get ticks and textExtents for axis if required 
 916         if self
._xSpec 
is not 'none':         
 917             xticks 
= self
._ticks
(xAxis
[0], xAxis
[1]) 
 918             xTextExtent 
= dc
.GetTextExtent(xticks
[-1][1])# w h of x axis text last number on axis 
 921             xTextExtent
= (0,0) # No text for ticks 
 922         if self
._ySpec 
is not 'none': 
 923             yticks 
= self
._ticks
(yAxis
[0], yAxis
[1]) 
 924             yTextExtentBottom
= dc
.GetTextExtent(yticks
[0][1]) 
 925             yTextExtentTop   
= dc
.GetTextExtent(yticks
[-1][1]) 
 926             yTextExtent
= (max(yTextExtentBottom
[0],yTextExtentTop
[0]), 
 927                         max(yTextExtentBottom
[1],yTextExtentTop
[1])) 
 930             yTextExtent
= (0,0) # No text for ticks 
 932         # TextExtents for Title and Axis Labels 
 933         titleWH
, xLabelWH
, yLabelWH
= self
._titleLablesWH
(dc
, graphics
) 
 935         # TextExtents for Legend 
 936         legendBoxWH
, legendSymExt
, legendTextExt 
= self
._legendWH
(dc
, graphics
) 
 938         # room around graph area 
 939         rhsW
= max(xTextExtent
[0], legendBoxWH
[0]) # use larger of number width or legend width 
 940         lhsW
= yTextExtent
[0]+ yLabelWH
[1] 
 941         bottomH
= max(xTextExtent
[1], yTextExtent
[1]/2.)+ xLabelWH
[1] 
 942         topH
= yTextExtent
[1]/2. + titleWH
[1] 
 943         textSize_scale
= _Numeric
.array([rhsW
+lhsW
,bottomH
+topH
]) # make plot area smaller by text size 
 944         textSize_shift
= _Numeric
.array([lhsW
, bottomH
])          # shift plot area by this amount 
 946         # drawing title and labels text 
 947         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
 948         titlePos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- titleWH
[0]/2., 
 949                  self
.plotbox_origin
[1]- self
.plotbox_size
[1]) 
 950         dc
.DrawText(graphics
.getTitle(),titlePos
[0],titlePos
[1]) 
 951         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
 952         xLabelPos
= (self
.plotbox_origin
[0]+ lhsW 
+ (self
.plotbox_size
[0]-lhsW
-rhsW
)/2.- xLabelWH
[0]/2., 
 953                  self
.plotbox_origin
[1]- xLabelWH
[1]) 
 954         dc
.DrawText(graphics
.getXLabel(),xLabelPos
[0],xLabelPos
[1]) 
 955         yLabelPos
= (self
.plotbox_origin
[0], 
 956                  self
.plotbox_origin
[1]- bottomH
- (self
.plotbox_size
[1]-bottomH
-topH
)/2.+ yLabelWH
[0]/2.) 
 957         if graphics
.getYLabel():  # bug fix for Linux 
 958             dc
.DrawRotatedText(graphics
.getYLabel(),yLabelPos
[0],yLabelPos
[1],90) 
 960         # drawing legend makers and text 
 961         if self
._legendEnabled
: 
 962             self
._drawLegend
(dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
) 
 964         # allow for scaling and shifting plotted points 
 965         scale 
= (self
.plotbox_size
-textSize_scale
) / (p2
-p1
)* _Numeric
.array((1,-1)) 
 966         shift 
= -p1
*scale 
+ self
.plotbox_origin 
+ textSize_shift 
* _Numeric
.array((1,-1)) 
 967         self
._pointScale
= scale  
# make available for mouse events 
 968         self
._pointShift
= shift        
 
 969         self
._drawAxes
(dc
, p1
, p2
, scale
, shift
, xticks
, yticks
) 
 971         graphics
.scaleAndShift(scale
, shift
) 
 972         graphics
.setPrinterScale(self
.printerScale
)  # thicken up lines and markers if printing 
 974         # set clipping area so drawing does not occur outside axis box 
 975         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(p1
, p2
) 
 976         dc
.SetClippingRegion(ptx
,pty
,rectWidth
,rectHeight
) 
 977         # Draw the lines and markers 
 978         #start = _time.clock() 
 980         # print "entire graphics drawing took: %f second"%(_time.clock() - start) 
 981         # remove the clipping region 
 982         dc
.DestroyClippingRegion() 
 984         self
._adjustScrollbars
() 
 986     def Redraw(self
, dc
= None): 
 987         """Redraw the existing plot.""" 
 988         if self
.last_draw 
is not None: 
 989             graphics
, xAxis
, yAxis
= self
.last_draw
 
 990             self
.Draw(graphics
,xAxis
,yAxis
,dc
) 
 993         """Erase the window.""" 
 994         self
.last_PointLabel 
= None        #reset pointLabel 
 995         dc 
= wx
.BufferedDC(wx
.ClientDC(self
.canvas
), self
._Buffer
) 
 997         self
.last_draw 
= None 
 999     def Zoom(self
, Center
, Ratio
): 
1000         """ Zoom on the plot 
1001             Centers on the X,Y coords given in Center 
1002             Zooms by the Ratio = (Xratio, Yratio) given 
1004         self
.last_PointLabel 
= None   #reset maker 
1006         if self
.last_draw 
!= None: 
1007             (graphics
, xAxis
, yAxis
) = self
.last_draw
 
1008             w 
= (xAxis
[1] - xAxis
[0]) * Ratio
[0] 
1009             h 
= (yAxis
[1] - yAxis
[0]) * Ratio
[1] 
1010             xAxis 
= ( x 
- w
/2, x 
+ w
/2 ) 
1011             yAxis 
= ( y 
- h
/2, y 
+ h
/2 ) 
1012             self
.Draw(graphics
, xAxis
, yAxis
) 
1014     def GetClosestPoints(self
, pntXY
, pointScaled
= True): 
1015         """Returns list with 
1016             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
1017             list for each curve. 
1018             Returns [] if no curves are being plotted. 
1021             if pointScaled == True based on screen coords 
1022             if pointScaled == False based on user coords 
1024         if self
.last_draw 
== None: 
1027         graphics
, xAxis
, yAxis
= self
.last_draw
 
1029         for curveNum
,obj 
in enumerate(graphics
): 
1030             #check there are points in the curve 
1031             if len(obj
.points
) == 0: 
1032                 continue  #go to next obj 
1033             #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
1034             cn 
= [curveNum
]+ [obj
.getLegend()]+ obj
.getClosestPoint( pntXY
, pointScaled
) 
1038     def GetClosetPoint(self
, pntXY
, pointScaled
= True): 
1039         """Returns list with 
1040             [curveNumber, legend, index of closest point, pointXY, scaledXY, distance] 
1041             list for only the closest curve. 
1042             Returns [] if no curves are being plotted. 
1045             if pointScaled == True based on screen coords 
1046             if pointScaled == False based on user coords 
1048         #closest points on screen based on screen scaling (pointScaled= True) 
1049         #list [curveNumber, index, pointXY, scaledXY, distance] for each curve 
1050         closestPts
= self
.GetClosestPoints(pntXY
, pointScaled
) 
1051         if closestPts 
== []: 
1052             return []  #no graph present 
1053         #find one with least distance 
1054         dists 
= [c
[-1] for c 
in closestPts
] 
1055         mdist 
= min(dists
)  #Min dist 
1056         i 
= dists
.index(mdist
)  #index for min dist 
1057         return closestPts
[i
]  #this is the closest point on closest curve 
1059     def UpdatePointLabel(self
, mDataDict
): 
1060         """Updates the pointLabel point on screen with data contained in 
1063             mDataDict will be passed to your function set by 
1064             SetPointLabelFunc.  It can contain anything you 
1065             want to display on the screen at the scaledXY point 
1068             This function can be called from parent window with onClick, 
1069             onMotion events etc.             
1071         if self
.last_PointLabel 
!= None: 
1073             if mDataDict
["pointXY"] != self
.last_PointLabel
["pointXY"]: 
1075                 self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1076                 self
._drawPointLabel
(mDataDict
) #plot new 
1078             #just plot new with no erase 
1079             self
._drawPointLabel
(mDataDict
) #plot new 
1080         #save for next erase 
1081         self
.last_PointLabel 
= mDataDict
 
1083     # event handlers ********************************** 
1084     def OnMotion(self
, event
): 
1085         if self
._zoomEnabled 
and event
.LeftIsDown(): 
1086             if self
._hasDragged
: 
1087                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
1089                 self
._hasDragged
= True 
1090             self
._zoomCorner
2[0], self
._zoomCorner
2[1] = self
.GetXY(event
) 
1091             self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # add new 
1092         elif self
._dragEnabled 
and event
.LeftIsDown(): 
1093             coordinates 
= event
.GetPosition() 
1094             newpos
, oldpos 
= map(_Numeric
.array
, map(self
.PositionScreenToUser
, [coordinates
, self
._screenCoordinates
])) 
1095             dist 
= newpos
-oldpos
 
1096             self
._screenCoordinates 
= coordinates
 
1098             self
.ScrollUp(-dist
[1]) 
1099             self
.ScrollRight(-dist
[0]) 
1101     def OnMouseLeftDown(self
,event
): 
1102         self
._zoomCorner
1[0], self
._zoomCorner
1[1]= self
.GetXY(event
) 
1103         self
._screenCoordinates 
= _Numeric
.array(event
.GetPosition()) 
1104         if self
._dragEnabled
: 
1105             self
.SetCursor(self
.GrabHandCursor
) 
1106             self
.canvas
.CaptureMouse() 
1108     def OnMouseLeftUp(self
, event
): 
1109         if self
._zoomEnabled
: 
1110             if self
._hasDragged 
== True: 
1111                 self
._drawRubberBand
(self
._zoomCorner
1, self
._zoomCorner
2) # remove old 
1112                 self
._zoomCorner
2[0], self
._zoomCorner
2[1]= self
.GetXY(event
) 
1113                 self
._hasDragged 
= False  # reset flag 
1114                 minX
, minY
= _Numeric
.minimum( self
._zoomCorner
1, self
._zoomCorner
2) 
1115                 maxX
, maxY
= _Numeric
.maximum( self
._zoomCorner
1, self
._zoomCorner
2) 
1116                 self
.last_PointLabel 
= None        #reset pointLabel 
1117                 if self
.last_draw 
!= None: 
1118                     self
.Draw(self
.last_draw
[0], xAxis 
= (minX
,maxX
), yAxis 
= (minY
,maxY
), dc 
= None) 
1119             #else: # A box has not been drawn, zoom in on a point 
1120             ## this interfered with the double click, so I've disables it. 
1121             #    X,Y = self.GetXY(event) 
1122             #    self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) ) 
1123         if self
._dragEnabled
: 
1124             self
.SetCursor(self
.HandCursor
) 
1125             if self
.canvas
.HasCapture(): 
1126                 self
.canvas
.ReleaseMouse() 
1128     def OnMouseDoubleClick(self
,event
): 
1129         if self
._zoomEnabled
: 
1130             # Give a little time for the click to be totally finished 
1131             # before (possibly) removing the scrollbars and trigering 
1133             wx
.FutureCall(200,self
.Reset
) 
1135     def OnMouseRightDown(self
,event
): 
1136         if self
._zoomEnabled
: 
1137             X
,Y 
= self
.GetXY(event
) 
1138             self
.Zoom( (X
,Y
), (self
._zoomOutFactor
, self
._zoomOutFactor
) ) 
1140     def OnPaint(self
, event
): 
1141         # All that is needed here is to draw the buffer to screen 
1142         if self
.last_PointLabel 
!= None: 
1143             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1144             self
.last_PointLabel 
= None 
1145         dc 
= wx
.BufferedPaintDC(self
.canvas
, self
._Buffer
) 
1147     def OnSize(self
,event
): 
1148         # The Buffer init is done here, to make sure the buffer is always 
1149         # the same size as the Window 
1150         Size  
= self
.canvas
.GetClientSize() 
1151         if Size
.width 
<= 0 or Size
.height 
<= 0: 
1154         # Make new offscreen bitmap: this bitmap will always have the 
1155         # current drawing in it, so it can be used to save the image to 
1156         # a file, or whatever. 
1157         self
._Buffer 
= wx
.EmptyBitmap(Size
[0],Size
[1]) 
1160         self
.last_PointLabel 
= None        #reset pointLabel 
1162         if self
.last_draw 
is None: 
1165             graphics
, xSpec
, ySpec 
= self
.last_draw
 
1166             self
.Draw(graphics
,xSpec
,ySpec
) 
1168     def OnLeave(self
, event
): 
1169         """Used to erase pointLabel when mouse outside window""" 
1170         if self
.last_PointLabel 
!= None: 
1171             self
._drawPointLabel
(self
.last_PointLabel
) #erase old 
1172             self
.last_PointLabel 
= None 
1174     def OnScroll(self
, evt
): 
1175         if not self
._adjustingSB
: 
1176             self
._sb
_ignore 
= True 
1177             sbpos 
= evt
.GetPosition() 
1179             if evt
.GetOrientation() == wx
.VERTICAL
: 
1180                 fullrange
,pagesize 
= self
.sb_vert
.GetRange(),self
.sb_vert
.GetPageSize() 
1181                 sbpos 
= fullrange
-pagesize
-sbpos
 
1182                 dist 
= sbpos
*self
._sb
_yunit
-(self
.GetYCurrentRange()[0]-self
._sb
_yfullrange
[0]) 
1185             if evt
.GetOrientation() == wx
.HORIZONTAL
: 
1186                 dist 
= sbpos
*self
._sb
_xunit
-(self
.GetXCurrentRange()[0]-self
._sb
_xfullrange
[0]) 
1187                 self
.ScrollRight(dist
) 
1189     # Private Methods ************************************************** 
1190     def _setSize(self
, width
=None, height
=None): 
1191         """DC width and height.""" 
1193             (self
.width
,self
.height
) = self
.canvas
.GetClientSize() 
1195             self
.width
, self
.height
= width
,height    
 
1196         self
.plotbox_size 
= 0.97*_Numeric
.array([self
.width
, self
.height
]) 
1197         xo 
= 0.5*(self
.width
-self
.plotbox_size
[0]) 
1198         yo 
= self
.height
-0.5*(self
.height
-self
.plotbox_size
[1]) 
1199         self
.plotbox_origin 
= _Numeric
.array([xo
, yo
]) 
1201     def _setPrinterScale(self
, scale
): 
1202         """Used to thicken lines and increase marker size for print out.""" 
1203         # line thickness on printer is very thin at 600 dot/in. Markers small 
1204         self
.printerScale
= scale
 
1206     def _printDraw(self
, printDC
): 
1207         """Used for printing.""" 
1208         if self
.last_draw 
!= None: 
1209             graphics
, xSpec
, ySpec
= self
.last_draw
 
1210             self
.Draw(graphics
,xSpec
,ySpec
,printDC
) 
1212     def _drawPointLabel(self
, mDataDict
): 
1213         """Draws and erases pointLabels""" 
1214         width 
= self
._Buffer
.GetWidth() 
1215         height 
= self
._Buffer
.GetHeight() 
1216         tmp_Buffer 
= wx
.EmptyBitmap(width
,height
) 
1218         dcs
.SelectObject(tmp_Buffer
) 
1221         self
._pointLabelFunc
(dcs
,mDataDict
)  #custom user pointLabel function 
1224         dc 
= wx
.ClientDC( self
.canvas 
) 
1225         #this will erase if called twice 
1226         dc
.Blit(0, 0, width
, height
, dcs
, 0, 0, wx
.EQUIV
)  #(NOT src) XOR dst 
1229     def _drawLegend(self
,dc
,graphics
,rhsW
,topH
,legendBoxWH
, legendSymExt
, legendTextExt
): 
1230         """Draws legend symbols and text""" 
1231         # top right hand corner of graph box is ref corner 
1232         trhc
= self
.plotbox_origin
+ (self
.plotbox_size
-[rhsW
,topH
])*[1,-1] 
1233         legendLHS
= .091* legendBoxWH
[0]  # border space between legend sym and graph box 
1234         lineHeight
= max(legendSymExt
[1], legendTextExt
[1]) * 1.1 #1.1 used as space between lines 
1235         dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1236         for i 
in range(len(graphics
)): 
1239             if isinstance(o
,PolyMarker
): 
1240                 # draw marker with legend 
1241                 pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0]/2., trhc
[1]+s
+lineHeight
/2.) 
1242                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt
])) 
1243             elif isinstance(o
,PolyLine
): 
1244                 # draw line with legend 
1245                 pnt1
= (trhc
[0]+legendLHS
, trhc
[1]+s
+lineHeight
/2.) 
1246                 pnt2
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.) 
1247                 o
.draw(dc
, self
.printerScale
, coord
= _Numeric
.array([pnt1
,pnt2
])) 
1249                 raise TypeError, "object is neither PolyMarker or PolyLine instance" 
1251             pnt
= (trhc
[0]+legendLHS
+legendSymExt
[0], trhc
[1]+s
+lineHeight
/2.-legendTextExt
[1]/2) 
1252             dc
.DrawText(o
.getLegend(),pnt
[0],pnt
[1]) 
1253         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) # reset 
1255     def _titleLablesWH(self
, dc
, graphics
): 
1256         """Draws Title and labels and returns width and height for each""" 
1257         # TextExtents for Title and Axis Labels 
1258         dc
.SetFont(self
._getFont
(self
._fontSizeTitle
)) 
1259         title
= graphics
.getTitle() 
1260         titleWH
= dc
.GetTextExtent(title
) 
1261         dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1262         xLabel
, yLabel
= graphics
.getXLabel(),graphics
.getYLabel() 
1263         xLabelWH
= dc
.GetTextExtent(xLabel
) 
1264         yLabelWH
= dc
.GetTextExtent(yLabel
) 
1265         return titleWH
, xLabelWH
, yLabelWH
 
1267     def _legendWH(self
, dc
, graphics
): 
1268         """Returns the size in screen units for legend box""" 
1269         if self
._legendEnabled 
!= True: 
1270             legendBoxWH
= symExt
= txtExt
= (0,0) 
1272             # find max symbol size 
1273             symExt
= graphics
.getSymExtent(self
.printerScale
) 
1274             # find max legend text extent 
1275             dc
.SetFont(self
._getFont
(self
._fontSizeLegend
)) 
1276             txtList
= graphics
.getLegendNames() 
1277             txtExt
= dc
.GetTextExtent(txtList
[0]) 
1278             for txt 
in graphics
.getLegendNames()[1:]: 
1279                 txtExt
= _Numeric
.maximum(txtExt
,dc
.GetTextExtent(txt
)) 
1280             maxW
= symExt
[0]+txtExt
[0]     
1281             maxH
= max(symExt
[1],txtExt
[1]) 
1282             # padding .1 for lhs of legend box and space between lines 
1284             maxH
= maxH
* 1.1 * len(txtList
) 
1285             dc
.SetFont(self
._getFont
(self
._fontSizeAxis
)) 
1286             legendBoxWH
= (maxW
,maxH
) 
1287         return (legendBoxWH
, symExt
, txtExt
) 
1289     def _drawRubberBand(self
, corner1
, corner2
): 
1290         """Draws/erases rect box from corner1 to corner2""" 
1291         ptx
,pty
,rectWidth
,rectHeight
= self
._point
2ClientCoord
(corner1
, corner2
) 
1293         dc 
= wx
.ClientDC( self
.canvas 
) 
1295         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1296         dc
.SetBrush(wx
.Brush( wx
.WHITE
, wx
.TRANSPARENT 
) ) 
1297         dc
.SetLogicalFunction(wx
.INVERT
) 
1298         dc
.DrawRectangle( ptx
,pty
, rectWidth
,rectHeight
) 
1299         dc
.SetLogicalFunction(wx
.COPY
) 
1302     def _getFont(self
,size
): 
1303         """Take font size, adjusts if printing and returns wx.Font""" 
1304         s 
= size
*self
.printerScale
 
1306         # Linux speed up to get font from cache rather than X font server 
1307         key 
= (int(s
), of
.GetFamily (), of
.GetStyle (), of
.GetWeight ()) 
1308         font 
= self
._fontCache
.get (key
, None) 
1310             return font                 
# yeah! cache hit 
1312             font 
=  wx
.Font(int(s
), of
.GetFamily(), of
.GetStyle(), of
.GetWeight()) 
1313             self
._fontCache
[key
] = font
 
1317     def _point2ClientCoord(self
, corner1
, corner2
): 
1318         """Converts user point coords to client screen int coords x,y,width,height""" 
1319         c1
= _Numeric
.array(corner1
) 
1320         c2
= _Numeric
.array(corner2
) 
1321         # convert to screen coords 
1322         pt1
= c1
*self
._pointScale
+self
._pointShift
 
1323         pt2
= c2
*self
._pointScale
+self
._pointShift
 
1324         # make height and width positive 
1325         pul
= _Numeric
.minimum(pt1
,pt2
) # Upper left corner 
1326         plr
= _Numeric
.maximum(pt1
,pt2
) # Lower right corner 
1327         rectWidth
, rectHeight
= plr
-pul
 
1329         return ptx
, pty
, rectWidth
, rectHeight 
 
1331     def _axisInterval(self
, spec
, lower
, upper
): 
1332         """Returns sensible axis range for given spec""" 
1333         if spec 
== 'none' or spec 
== 'min': 
1335                 return lower
-0.5, upper
+0.5 
1338         elif spec 
== 'auto': 
1341                 return lower
-0.5, upper
+0.5 
1342             log 
= _Numeric
.log10(range) 
1343             power 
= _Numeric
.floor(log
) 
1344             fraction 
= log
-power
 
1345             if fraction 
<= 0.05: 
1348             lower 
= lower 
- lower 
% grid
 
1351                 upper 
= upper 
- mod 
+ grid
 
1353         elif type(spec
) == type(()): 
1360             raise ValueError, str(spec
) + ': illegal axis specification' 
1362     def _drawAxes(self
, dc
, p1
, p2
, scale
, shift
, xticks
, yticks
): 
1364         penWidth
= self
.printerScale        
# increases thickness for printing only 
1365         dc
.SetPen(wx
.Pen(self
._gridColour
, penWidth
)) 
1367         # set length of tick marks--long ones make grid 
1368         if self
._gridEnabled
: 
1369             x
,y
,width
,height
= self
._point
2ClientCoord
(p1
,p2
) 
1370             if self
._gridEnabled 
== 'Horizontal': 
1371                 yTickLength
= width
/2.0 +1 
1372                 xTickLength
= 3 * self
.printerScale
 
1373             elif self
._gridEnabled 
== 'Vertical': 
1374                 yTickLength
= 3 * self
.printerScale
 
1375                 xTickLength
= height
/2.0 +1 
1377                 yTickLength
= width
/2.0 +1 
1378                 xTickLength
= height
/2.0 +1 
1380             yTickLength
= 3 * self
.printerScale  
# lengthens lines for printing 
1381             xTickLength
= 3 * self
.printerScale
 
1383         if self
._xSpec 
is not 'none': 
1384             lower
, upper 
= p1
[0],p2
[0] 
1386             for y
, d 
in [(p1
[1], -xTickLength
), (p2
[1], xTickLength
)]:   # miny, maxy and tick lengths 
1387                 a1 
= scale
*_Numeric
.array([lower
, y
])+shift
 
1388                 a2 
= scale
*_Numeric
.array([upper
, y
])+shift
 
1389                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1])  # draws upper and lower axis line 
1390                 for x
, label 
in xticks
: 
1391                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1392                     dc
.DrawLine(pt
[0],pt
[1],pt
[0],pt
[1] + d
) # draws tick mark d units 
1394                         dc
.DrawText(label
,pt
[0],pt
[1]) 
1395                 text 
= 0  # axis values not drawn on top side 
1397         if self
._ySpec 
is not 'none': 
1398             lower
, upper 
= p1
[1],p2
[1] 
1400             h 
= dc
.GetCharHeight() 
1401             for x
, d 
in [(p1
[0], -yTickLength
), (p2
[0], yTickLength
)]: 
1402                 a1 
= scale
*_Numeric
.array([x
, lower
])+shift
 
1403                 a2 
= scale
*_Numeric
.array([x
, upper
])+shift
 
1404                 dc
.DrawLine(a1
[0],a1
[1],a2
[0],a2
[1]) 
1405                 for y
, label 
in yticks
: 
1406                     pt 
= scale
*_Numeric
.array([x
, y
])+shift
 
1407                     dc
.DrawLine(pt
[0],pt
[1],pt
[0]-d
,pt
[1]) 
1409                         dc
.DrawText(label
,pt
[0]-dc
.GetTextExtent(label
)[0], 
1411                 text 
= 0    # axis values not drawn on right side 
1413     def _ticks(self
, lower
, upper
): 
1414         ideal 
= (upper
-lower
)/7. 
1415         log 
= _Numeric
.log10(ideal
) 
1416         power 
= _Numeric
.floor(log
) 
1417         fraction 
= log
-power
 
1420         for f
, lf 
in self
._multiples
: 
1421             e 
= _Numeric
.fabs(fraction
-lf
) 
1425         grid 
= factor 
* 10.**power
 
1426         if power 
> 4 or power 
< -4: 
1429             digits 
= max(1, int(power
)) 
1430             format 
= '%' + `digits`
+'.0f' 
1432             digits 
= -int(power
) 
1433             format 
= '%'+`digits
+2`
+'.'+`digits`
+'f' 
1435         t 
= -grid
*_Numeric
.floor(-lower
/grid
) 
1437             ticks
.append( (t
, format 
% (t
,)) ) 
1441     _multiples 
= [(2., _Numeric
.log10(2.)), (5., _Numeric
.log10(5.))] 
1444     def _adjustScrollbars(self
): 
1446             self
._sb
_ignore 
= False 
1449         self
._adjustingSB 
= True 
1450         needScrollbars 
= False 
1452         # horizontal scrollbar 
1453         r_current 
= self
.GetXCurrentRange() 
1454         r_max 
= list(self
.GetXMaxRange()) 
1455         sbfullrange 
= float(self
.sb_hor
.GetRange()) 
1457         r_max
[0] = min(r_max
[0],r_current
[0]) 
1458         r_max
[1] = max(r_max
[1],r_current
[1]) 
1460         self
._sb
_xfullrange 
= r_max
 
1462         unit 
= (r_max
[1]-r_max
[0])/float(self
.sb_hor
.GetRange()) 
1463         pos 
= int((r_current
[0]-r_max
[0])/unit
) 
1466             pagesize 
= int((r_current
[1]-r_current
[0])/unit
) 
1468             self
.sb_hor
.SetScrollbar(pos
, pagesize
, sbfullrange
, pagesize
) 
1469             self
._sb
_xunit 
= unit
 
1470             needScrollbars 
= needScrollbars 
or (pagesize 
!= sbfullrange
) 
1472             self
.sb_hor
.SetScrollbar(0, 1000, 1000, 1000) 
1474         # vertical scrollbar 
1475         r_current 
= self
.GetYCurrentRange() 
1476         r_max 
= list(self
.GetYMaxRange()) 
1477         sbfullrange 
= float(self
.sb_vert
.GetRange()) 
1479         r_max
[0] = min(r_max
[0],r_current
[0]) 
1480         r_max
[1] = max(r_max
[1],r_current
[1]) 
1482         self
._sb
_yfullrange 
= r_max
 
1484         unit 
= (r_max
[1]-r_max
[0])/sbfullrange
 
1485         pos 
= int((r_current
[0]-r_max
[0])/unit
) 
1488             pagesize 
= int((r_current
[1]-r_current
[0])/unit
) 
1489             pos 
= (sbfullrange
-1-pos
-pagesize
) 
1490             self
.sb_vert
.SetScrollbar(pos
, pagesize
, sbfullrange
, pagesize
) 
1491             self
._sb
_yunit 
= unit
 
1492             needScrollbars 
= needScrollbars 
or (pagesize 
!= sbfullrange
) 
1494             self
.sb_vert
.SetScrollbar(0, 1000, 1000, 1000) 
1496         self
.SetShowScrollbars(needScrollbars
) 
1497         self
._adjustingSB 
= False 
1499 #------------------------------------------------------------------------------- 
1500 # Used to layout the printer page 
1502 class PlotPrintout(wx
.Printout
): 
1503     """Controls how the plot is made in printing and previewing""" 
1504     # Do not change method names in this class, 
1505     # we have to override wx.Printout methods here! 
1506     def __init__(self
, graph
): 
1507         """graph is instance of plotCanvas to be printed or previewed""" 
1508         wx
.Printout
.__init
__(self
) 
1511     def HasPage(self
, page
): 
1517     def GetPageInfo(self
): 
1518         return (1, 1, 1, 1)  # disable page numbers 
1520     def OnPrintPage(self
, page
): 
1521         dc 
= self
.GetDC()  # allows using floats for certain functions 
1522 ##        print "PPI Printer",self.GetPPIPrinter() 
1523 ##        print "PPI Screen", self.GetPPIScreen() 
1524 ##        print "DC GetSize", dc.GetSize() 
1525 ##        print "GetPageSizePixels", self.GetPageSizePixels() 
1526         # Note PPIScreen does not give the correct number 
1527         # Calulate everything for printer and then scale for preview 
1528         PPIPrinter
= self
.GetPPIPrinter()        # printer dots/inch (w,h) 
1529         #PPIScreen= self.GetPPIScreen()          # screen dots/inch (w,h) 
1530         dcSize
= dc
.GetSize()                    # DC size 
1531         pageSize
= self
.GetPageSizePixels() # page size in terms of pixcels 
1532         clientDcSize
= self
.graph
.GetClientSize() 
1534         # find what the margins are (mm) 
1535         margLeftSize
,margTopSize
= self
.graph
.pageSetupData
.GetMarginTopLeft() 
1536         margRightSize
, margBottomSize
= self
.graph
.pageSetupData
.GetMarginBottomRight() 
1538         # calculate offset and scale for dc 
1539         pixLeft
= margLeftSize
*PPIPrinter
[0]/25.4  # mm*(dots/in)/(mm/in) 
1540         pixRight
= margRightSize
*PPIPrinter
[0]/25.4     
1541         pixTop
= margTopSize
*PPIPrinter
[1]/25.4 
1542         pixBottom
= margBottomSize
*PPIPrinter
[1]/25.4 
1544         plotAreaW
= pageSize
[0]-(pixLeft
+pixRight
) 
1545         plotAreaH
= pageSize
[1]-(pixTop
+pixBottom
) 
1547         # ratio offset and scale to screen size if preview 
1548         if self
.IsPreview(): 
1549             ratioW
= float(dcSize
[0])/pageSize
[0] 
1550             ratioH
= float(dcSize
[1])/pageSize
[1] 
1556         # rescale plot to page or preview plot area 
1557         self
.graph
._setSize
(plotAreaW
,plotAreaH
) 
1559         # Set offset and scale 
1560         dc
.SetDeviceOrigin(pixLeft
,pixTop
) 
1562         # Thicken up pens and increase marker size for printing 
1563         ratioW
= float(plotAreaW
)/clientDcSize
[0] 
1564         ratioH
= float(plotAreaH
)/clientDcSize
[1] 
1565         aveScale
= (ratioW
+ratioH
)/2 
1566         self
.graph
._setPrinterScale
(aveScale
)  # tickens up pens for printing 
1568         self
.graph
._printDraw
(dc
) 
1569         # rescale back to original 
1570         self
.graph
._setSize
() 
1571         self
.graph
._setPrinterScale
(1) 
1572         self
.graph
.Redraw()     #to get point label scale and shift correct 
1577 #---------------------------------------------------------------------- 
1578 from wx 
import ImageFromStream
, BitmapFromImage
 
1579 import cStringIO
, zlib
 
1582 def getMagPlusData(): 
1583     return zlib
.decompress( 
1584 'x\xda\x01*\x01\xd5\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\ 
1585 \x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\ 
1586 \x08\x08|\x08d\x88\x00\x00\x00\xe1IDATx\x9c\xb5U\xd1\x0e\xc4 \x08\xa3n\xff\ 
1587 \xff\xc5\xdb\xb8\xa7\xee<\x04\x86gFb\xb2\x88\xb6\x14\x90\x01m\x937m\x8f\x1c\ 
1588 \xd7yh\xe4k\xdb\x8e*\x01<\x05\x04\x07F\x1cU\x9d"\x19\x14\\\xe7\xa1\x1e\xf07"\ 
1589 \x90H+$?\x04\x16\x9c\xd1z\x04\x00J$m\x06\xdc\xee\x03Hku\x13\xd8C\x16\x84+"O\ 
1590 \x1b\xa2\x07\xca"\xb7\xc6sY\xbdD\x926\xf5.\xce\x06!\xd2)x\xcb^\'\x08S\xe4\ 
1591 \xe5x&5\xb4[A\xb5h\xb4j=\x9a\xc8\xf8\xecm\xd4\\\x9e\xdf\xbb?\x10\xf0P\x06\ 
1592 \x12\xed?=\xb6a\xd8=\xcd\xa2\xc8T\xd5U2t\x11\x95d\xa3"\x9aQ\x9e\x12\xb7M\x19\ 
1593 I\x9f\xff\x1e\xd8\xa63#q\xff\x07U\x8b\xd2\xd9\xa7k\xe9\xa1U\x94,\xbf\xe4\x88\ 
1594 \xe4\xf6\xaf\x12x$}\x8a\xc2Q\xf1\'\x89\xf2\x9b\xfbKE\xae\xd8\x07+\xd2\xa7c\ 
1595 \xdf\x0e\xc3D\x00\x00\x00\x00IEND\xaeB`\x82\xe2ovy' ) 
1597 def getMagPlusBitmap(): 
1598     return BitmapFromImage(getMagPlusImage()) 
1600 def getMagPlusImage(): 
1601     stream 
= cStringIO
.StringIO(getMagPlusData()) 
1602     return ImageFromStream(stream
) 
1604 #---------------------------------------------------------------------- 
1605 def getGrabHandData(): 
1606     return zlib
.decompress( 
1607 'x\xda\x01Z\x01\xa5\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\ 
1608 \x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\ 
1609 \x08\x08|\x08d\x88\x00\x00\x01\x11IDATx\x9c\xb5U\xd1\x12\x830\x08Kh\xff\xff\ 
1610 \x8b7\xb3\x97\xd1C\xa4Zw\x93;\x1fJ1\t\x98VJ\x92\xb5N<\x14\x04 I\x00\x80H\xb4\ 
1611 \xbd_\x8a9_{\\\x89\xf2z\x02\x18/J\x82\xb5\xce\xed\xfd\x12\xc9\x91\x03\x00_\ 
1612 \xc7\xda\x8al\x00{\xfdW\xfex\xf2zeO\x92h\xed\x80\x05@\xa45D\xc5\xb3\x98u\x12\ 
1613 \xf7\xab.\xa9\xd0k\x1eK\x95\xbb\x1a]&0\x92\xf0\'\xc6]gI\xda\tsr\xab\x8aI\x1e\ 
1614 \\\xe3\xa4\x0e\xb4*`7"\x07\x8f\xaa"x\x05\xe0\xdfo6B\xf3\x17\xe3\x98r\xf1\xaf\ 
1615 \x07\xd1Z\'%\x95\x0erW\xac\x8c\xe3\xe0\xfd\xd8AN\xae\xb8\xa3R\x9as>\x11\x8bl\ 
1616 yD\xab\x1f\xf3\xec\x1cY\x06\x89$\xbf\x80\xfb\x14\\dw\x90x\x12\xa3+\xeeD\x16%\ 
1617 I\xe3\x1c\xb8\xc7c\'\xd5Y8S\x9f\xc3Zg\xcf\x89\xe8\xaao\'\xbbk{U\xfd\xc0\xacX\ 
1618 \xab\xbb\xe8\xae\xfa)AEr\x15g\x86(\t\xfe\x19\xa4\xb5\xe9f\xfem\xde\xdd\xbf$\ 
1619 \xf8G<>\xa2\xc7\t>\tE\xfc\x8a\xf6\x8dqc\x00\x00\x00\x00IEND\xaeB`\x82\xdb\ 
1622 def getGrabHandBitmap(): 
1623     return BitmapFromImage(getGrabHandImage()) 
1625 def getGrabHandImage(): 
1626     stream 
= cStringIO
.StringIO(getGrabHandData()) 
1627     return ImageFromStream(stream
) 
1629 #---------------------------------------------------------------------- 
1631     return zlib
.decompress( 
1632 'x\xda\x01Y\x01\xa6\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\ 
1633 \x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\ 
1634 \x08\x08|\x08d\x88\x00\x00\x01\x10IDATx\x9c\xad\x96\xe1\x02\xc2 \x08\x849\ 
1635 \xf5\xfd\x9fx\xdb\xf5\'\x8c!\xa8\xab\xee\x975\xe5\x83\x0b\\@\xa9\xb2\xab\xeb\ 
1636 <\xa8\xebR\x1bv\xce\xb4\'\xc1\x81OL\x92\xdc\x81\x0c\x00\x1b\x88\xa4\x94\xda\ 
1637 \xe0\x83\x8b\x88\x00\x10\x92\xcb\x8a\xca,K\x1fT\xa1\x1e\x04\xe0f_\n\x88\x02\ 
1638 \xf1:\xc3\x83>\x81\x0c\x92\x02v\xe5+\xba\xce\x83\xb7f\xb8\xd1\x9c\x8fz8\xb2*\ 
1639 \x93\xb7l\xa8\xe0\x9b\xa06\xb8]_\xe7\xc1\x01\x10U\xe1m\x98\xc9\xefm"ck\xea\ 
1640 \x1a\x80\xa0Th\xb9\xfd\x877{V*Qk\xda,\xb4\x8b\xf4;[\xa1\xcf6\xaa4\x9cd\x85X\ 
1641 \xb0\r\\j\x83\x9dd\x92\xc3 \xf6\xbd\xab\x0c2\x05\xc0p\x9a\xa7]\xf4\x14\x18]3\ 
1642 7\x80}h?\xff\xa2\xa2\xe5e\x90\xact\xaf\xe8B\x14y[4\x83|\x13\xdc\x9e\xeb\x16e\ 
1643 \x90\xa7\xf2I\rw\x91\x87d\xd7p\x96\xbd\xd70\x07\xda\xe3v\x9a\xf5\xc5\xb2\xb2\ 
1644 +\xb24\xbc\xaew\xedZe\x9f\x02"\xc8J\xdb\x83\xf6oa\xf5\xb7\xa5\xbf8\x12\xffW\ 
1645 \xcf_\xbd;\xe4\x8c\x03\x10\xdb^\x00\x00\x00\x00IEND\xaeB`\x82\xd1>\x97B' ) 
1647 def getHandBitmap(): 
1648     return BitmapFromImage(getHandImage()) 
1651     stream 
= cStringIO
.StringIO(getHandData()) 
1652     return ImageFromStream(stream
) 
1656 #--------------------------------------------------------------------------- 
1657 # if running standalone... 
1659 #     ...a sample implementation using the above 
1662 def _draw1Objects(): 
1663     # 100 points sin function, plotted as green circles 
1664     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1665     data1
.shape 
= (100, 2) 
1666     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1667     markers1 
= PolyMarker(data1
, legend
='Green Markers', colour
='green', marker
='circle',size
=1) 
1669     # 50 points cos function, plotted as red line 
1670     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1671     data1
.shape 
= (50,2) 
1672     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1673     lines 
= PolyLine(data1
, legend
= 'Red Line', colour
='red') 
1675     # A few more points... 
1677     markers2 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1678                           (3.*pi
/4., -1)], legend
='Cross Legend', colour
='blue', 
1681     return PlotGraphics([markers1
, lines
, markers2
],"Graph Title", "X Axis", "Y Axis") 
1683 def _draw2Objects(): 
1684     # 100 points sin function, plotted as green dots 
1685     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(200)/200. 
1686     data1
.shape 
= (100, 2) 
1687     data1
[:,1] = _Numeric
.sin(data1
[:,0]) 
1688     line1 
= PolyLine(data1
, legend
='Green Line', colour
='green', width
=6, style
=wx
.DOT
) 
1690     # 50 points cos function, plotted as red dot-dash 
1691     data1 
= 2.*_Numeric
.pi
*_Numeric
.arange(100)/100. 
1692     data1
.shape 
= (50,2) 
1693     data1
[:,1] = _Numeric
.cos(data1
[:,0]) 
1694     line2 
= PolyLine(data1
, legend
='Red Line', colour
='red', width
=3, style
= wx
.DOT_DASH
) 
1696     # A few more points... 
1698     markers1 
= PolyMarker([(0., 0.), (pi
/4., 1.), (pi
/2, 0.), 
1699                           (3.*pi
/4., -1)], legend
='Cross Hatch Square', colour
='blue', width
= 3, size
= 6, 
1700                           fillcolour
= 'red', fillstyle
= wx
.CROSSDIAG_HATCH
, 
1703     return PlotGraphics([markers1
, line1
, line2
], "Big Markers with Different Line Styles") 
1705 def _draw3Objects(): 
1706     markerList
= ['circle', 'dot', 'square', 'triangle', 'triangle_down', 
1707                 'cross', 'plus', 'circle'] 
1709     for i 
in range(len(markerList
)): 
1710         m
.append(PolyMarker([(2*i
+.5,i
+.5)], legend
=markerList
[i
], colour
='blue', 
1711                           marker
=markerList
[i
])) 
1712     return PlotGraphics(m
, "Selection of Markers", "Minimal Axis", "No Axis") 
1714 def _draw4Objects(): 
1716     data1 
= _Numeric
.arange(5e5
,1e6
,10) 
1717     data1
.shape 
= (25000, 2) 
1718     line1 
= PolyLine(data1
, legend
='Wide Line', colour
='green', width
=5) 
1720     # A few more points... 
1721     markers2 
= PolyMarker(data1
, legend
='Square', colour
='blue', 
1723     return PlotGraphics([line1
, markers2
], "25,000 Points", "Value X", "") 
1725 def _draw5Objects(): 
1726     # Empty graph with axis defined but no points/lines 
1728     line1 
= PolyLine(points
, legend
='Wide Line', colour
='green', width
=5) 
1729     return PlotGraphics([line1
], "Empty Plot With Just Axes", "Value X", "Value Y") 
1731 def _draw6Objects(): 
1733     points1
=[(1,0), (1,10)] 
1734     line1 
= PolyLine(points1
, colour
='green', legend
='Feb.', width
=10) 
1735     points1g
=[(2,0), (2,4)] 
1736     line1g 
= PolyLine(points1g
, colour
='red', legend
='Mar.', width
=10) 
1737     points1b
=[(3,0), (3,6)] 
1738     line1b 
= PolyLine(points1b
, colour
='blue', legend
='Apr.', width
=10) 
1740     points2
=[(4,0), (4,12)] 
1741     line2 
= PolyLine(points2
, colour
='Yellow', legend
='May', width
=10) 
1742     points2g
=[(5,0), (5,8)] 
1743     line2g 
= PolyLine(points2g
, colour
='orange', legend
='June', width
=10) 
1744     points2b
=[(6,0), (6,4)] 
1745     line2b 
= PolyLine(points2b
, colour
='brown', legend
='July', width
=10) 
1747     return PlotGraphics([line1
, line1g
, line1b
, line2
, line2g
, line2b
], 
1748                         "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students") 
1751 class TestFrame(wx
.Frame
): 
1752     def __init__(self
, parent
, id, title
): 
1753         wx
.Frame
.__init
__(self
, parent
, id, title
, 
1754                           wx
.DefaultPosition
, (600, 400)) 
1756         # Now Create the menu bar and items 
1757         self
.mainmenu 
= wx
.MenuBar() 
1760         menu
.Append(200, 'Page Setup...', 'Setup the printer page') 
1761         self
.Bind(wx
.EVT_MENU
, self
.OnFilePageSetup
, id=200) 
1763         menu
.Append(201, 'Print Preview...', 'Show the current plot on page') 
1764         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrintPreview
, id=201) 
1766         menu
.Append(202, 'Print...', 'Print the current plot') 
1767         self
.Bind(wx
.EVT_MENU
, self
.OnFilePrint
, id=202) 
1769         menu
.Append(203, 'Save Plot...', 'Save current plot') 
1770         self
.Bind(wx
.EVT_MENU
, self
.OnSaveFile
, id=203) 
1772         menu
.Append(205, 'E&xit', 'Enough of this already!') 
1773         self
.Bind(wx
.EVT_MENU
, self
.OnFileExit
, id=205) 
1774         self
.mainmenu
.Append(menu
, '&File') 
1777         menu
.Append(206, 'Draw1', 'Draw plots1') 
1778         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw1
, id=206) 
1779         menu
.Append(207, 'Draw2', 'Draw plots2') 
1780         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw2
, id=207) 
1781         menu
.Append(208, 'Draw3', 'Draw plots3') 
1782         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw3
, id=208) 
1783         menu
.Append(209, 'Draw4', 'Draw plots4') 
1784         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw4
, id=209) 
1785         menu
.Append(210, 'Draw5', 'Draw plots5') 
1786         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw5
, id=210) 
1787         menu
.Append(260, 'Draw6', 'Draw plots6') 
1788         self
.Bind(wx
.EVT_MENU
,self
.OnPlotDraw6
, id=260) 
1791         menu
.Append(211, '&Redraw', 'Redraw plots') 
1792         self
.Bind(wx
.EVT_MENU
,self
.OnPlotRedraw
, id=211) 
1793         menu
.Append(212, '&Clear', 'Clear canvas') 
1794         self
.Bind(wx
.EVT_MENU
,self
.OnPlotClear
, id=212) 
1795         menu
.Append(213, '&Scale', 'Scale canvas') 
1796         self
.Bind(wx
.EVT_MENU
,self
.OnPlotScale
, id=213)  
1797         menu
.Append(214, 'Enable &Zoom', 'Enable Mouse Zoom', kind
=wx
.ITEM_CHECK
) 
1798         self
.Bind(wx
.EVT_MENU
,self
.OnEnableZoom
, id=214)  
1799         menu
.Append(215, 'Enable &Grid', 'Turn on Grid', kind
=wx
.ITEM_CHECK
) 
1800         self
.Bind(wx
.EVT_MENU
,self
.OnEnableGrid
, id=215) 
1801         menu
.Append(217, 'Enable &Drag', 'Activates dragging mode', kind
=wx
.ITEM_CHECK
) 
1802         self
.Bind(wx
.EVT_MENU
,self
.OnEnableDrag
, id=217) 
1803         menu
.Append(220, 'Enable &Legend', 'Turn on Legend', kind
=wx
.ITEM_CHECK
) 
1804         self
.Bind(wx
.EVT_MENU
,self
.OnEnableLegend
, id=220) 
1805         menu
.Append(222, 'Enable &Point Label', 'Show Closest Point', kind
=wx
.ITEM_CHECK
) 
1806         self
.Bind(wx
.EVT_MENU
,self
.OnEnablePointLabel
, id=222) 
1808         menu
.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit') 
1809         self
.Bind(wx
.EVT_MENU
,self
.OnScrUp
, id=225)  
1810         menu
.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units') 
1811         self
.Bind(wx
.EVT_MENU
,self
.OnScrRt
, id=230) 
1812         menu
.Append(235, '&Plot Reset', 'Reset to original plot') 
1813         self
.Bind(wx
.EVT_MENU
,self
.OnReset
, id=235) 
1815         self
.mainmenu
.Append(menu
, '&Plot') 
1818         menu
.Append(300, '&About', 'About this thing...') 
1819         self
.Bind(wx
.EVT_MENU
, self
.OnHelpAbout
, id=300) 
1820         self
.mainmenu
.Append(menu
, '&Help') 
1822         self
.SetMenuBar(self
.mainmenu
) 
1824         # A status bar to tell people what's happening 
1825         self
.CreateStatusBar(1) 
1827         self
.client 
= PlotCanvas(self
) 
1828         #define the function for drawing pointLabels 
1829         self
.client
.SetPointLabelFunc(self
.DrawPointLabel
) 
1830         # Create mouse event for showing cursor coords in status bar 
1831         self
.client
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnMouseLeftDown
) 
1832         # Show closest point when enabled 
1833         self
.client
.Bind(wx
.EVT_MOTION
, self
.OnMotion
) 
1838     def DrawPointLabel(self
, dc
, mDataDict
): 
1839         """This is the fuction that defines how the pointLabels are plotted 
1840             dc - DC that will be passed 
1841             mDataDict - Dictionary of data that you want to use for the pointLabel 
1843             As an example I have decided I want a box at the curve point 
1844             with some text information about the curve plotted below. 
1845             Any wxDC method can be used. 
1848         dc
.SetPen(wx
.Pen(wx
.BLACK
)) 
1849         dc
.SetBrush(wx
.Brush( wx
.BLACK
, wx
.SOLID 
) ) 
1851         sx
, sy 
= mDataDict
["scaledXY"] #scaled x,y of closest point 
1852         dc
.DrawRectangle( sx
-5,sy
-5, 10, 10)  #10by10 square centered on point 
1853         px
,py 
= mDataDict
["pointXY"] 
1854         cNum 
= mDataDict
["curveNum"] 
1855         pntIn 
= mDataDict
["pIndex"] 
1856         legend 
= mDataDict
["legend"] 
1857         #make a string to display 
1858         s 
= "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum
, legend
, px
, py
, pntIn
) 
1859         dc
.DrawText(s
, sx 
, sy
+1) 
1862     def OnMouseLeftDown(self
,event
): 
1863         s
= "Left Mouse Down at Point: (%.4f, %.4f)" % self
.client
.GetXY(event
) 
1864         self
.SetStatusText(s
) 
1865         event
.Skip()            #allows plotCanvas OnMouseLeftDown to be called 
1867     def OnMotion(self
, event
): 
1868         #show closest point (when enbled) 
1869         if self
.client
.GetEnablePointLabel() == True: 
1870             #make up dict with info for the pointLabel 
1871             #I've decided to mark the closest point on the closest curve 
1872             dlst
= self
.client
.GetClosetPoint( self
.client
.GetXY(event
), pointScaled
= True) 
1873             if dlst 
!= []:    #returns [] if none 
1874                 curveNum
, legend
, pIndex
, pointXY
, scaledXY
, distance 
= dlst
 
1875                 #make up dictionary to pass to my user function (see DrawPointLabel)  
1876                 mDataDict
= {"curveNum":curveNum
, "legend":legend
, "pIndex":pIndex
,\
 
1877                             "pointXY":pointXY
, "scaledXY":scaledXY
} 
1878                 #pass dict to update the pointLabel 
1879                 self
.client
.UpdatePointLabel(mDataDict
) 
1880         event
.Skip()           #go to next handler 
1882     def OnFilePageSetup(self
, event
): 
1883         self
.client
.PageSetup() 
1885     def OnFilePrintPreview(self
, event
): 
1886         self
.client
.PrintPreview() 
1888     def OnFilePrint(self
, event
): 
1889         self
.client
.Printout() 
1891     def OnSaveFile(self
, event
): 
1892         self
.client
.SaveFile() 
1894     def OnFileExit(self
, event
): 
1897     def OnPlotDraw1(self
, event
): 
1898         self
.resetDefaults() 
1899         self
.client
.Draw(_draw1Objects()) 
1901     def OnPlotDraw2(self
, event
): 
1902         self
.resetDefaults() 
1903         self
.client
.Draw(_draw2Objects()) 
1905     def OnPlotDraw3(self
, event
): 
1906         self
.resetDefaults() 
1907         self
.client
.SetFont(wx
.Font(10,wx
.SCRIPT
,wx
.NORMAL
,wx
.NORMAL
)) 
1908         self
.client
.SetFontSizeAxis(20) 
1909         self
.client
.SetFontSizeLegend(12) 
1910         self
.client
.SetXSpec('min') 
1911         self
.client
.SetYSpec('none') 
1912         self
.client
.Draw(_draw3Objects()) 
1914     def OnPlotDraw4(self
, event
): 
1915         self
.resetDefaults() 
1916         drawObj
= _draw4Objects() 
1917         self
.client
.Draw(drawObj
) 
1919 ##        start = _time.clock()             
1920 ##        for x in range(10): 
1921 ##            self.client.Draw(drawObj) 
1922 ##        print "10 plots of Draw4 took: %f sec."%(_time.clock() - start) 
1925     def OnPlotDraw5(self
, event
): 
1926         # Empty plot with just axes 
1927         self
.resetDefaults() 
1928         drawObj
= _draw5Objects() 
1929         # make the axis X= (0,5), Y=(0,10) 
1930         # (default with None is X= (-1,1), Y= (-1,1)) 
1931         self
.client
.Draw(drawObj
, xAxis
= (0,5), yAxis
= (0,10)) 
1933     def OnPlotDraw6(self
, event
): 
1935         self
.resetDefaults() 
1936         #self.client.SetEnableLegend(True)   #turn on Legend 
1937         #self.client.SetEnableGrid(True)     #turn on Grid 
1938         self
.client
.SetXSpec('none')        #turns off x-axis scale 
1939         self
.client
.SetYSpec('auto') 
1940         self
.client
.Draw(_draw6Objects(), xAxis
= (0,7)) 
1942     def OnPlotRedraw(self
,event
): 
1943         self
.client
.Redraw() 
1945     def OnPlotClear(self
,event
): 
1948     def OnPlotScale(self
, event
): 
1949         if self
.client
.last_draw 
!= None: 
1950             graphics
, xAxis
, yAxis
= self
.client
.last_draw
 
1951             self
.client
.Draw(graphics
,(1,3.05),(0,1)) 
1953     def OnEnableZoom(self
, event
): 
1954         self
.client
.SetEnableZoom(event
.IsChecked()) 
1955         self
.mainmenu
.Check(217, not event
.IsChecked()) 
1957     def OnEnableGrid(self
, event
): 
1958         self
.client
.SetEnableGrid(event
.IsChecked()) 
1960     def OnEnableDrag(self
, event
): 
1961         self
.client
.SetEnableDrag(event
.IsChecked()) 
1962         self
.mainmenu
.Check(214, not event
.IsChecked()) 
1964     def OnEnableLegend(self
, event
): 
1965         self
.client
.SetEnableLegend(event
.IsChecked()) 
1967     def OnEnablePointLabel(self
, event
): 
1968         self
.client
.SetEnablePointLabel(event
.IsChecked()) 
1970     def OnScrUp(self
, event
): 
1971         self
.client
.ScrollUp(1) 
1973     def OnScrRt(self
,event
): 
1974         self
.client
.ScrollRight(2) 
1976     def OnReset(self
,event
): 
1979     def OnHelpAbout(self
, event
): 
1980         from wx
.lib
.dialogs 
import ScrolledMessageDialog
 
1981         about 
= ScrolledMessageDialog(self
, __doc__
, "About...") 
1984     def resetDefaults(self
): 
1985         """Just to reset the fonts back to the PlotCanvas defaults""" 
1986         self
.client
.SetFont(wx
.Font(10,wx
.SWISS
,wx
.NORMAL
,wx
.NORMAL
)) 
1987         self
.client
.SetFontSizeAxis(10) 
1988         self
.client
.SetFontSizeLegend(7) 
1989         self
.client
.SetXSpec('auto') 
1990         self
.client
.SetYSpec('auto') 
1995     class MyApp(wx
.App
): 
1997             wx
.InitAllImageHandlers() 
1998             frame 
= TestFrame(None, -1, "PlotCanvas") 
2000             self
.SetTopWindow(frame
) 
2007 if __name__ 
== '__main__':