+++ /dev/null
-#-----------------------------------------------------------------------------
-# Name: wx.lib.plot.py
-# Purpose: Line, Bar and Scatter Graphs
-#
-# Author: Gordon Williams
-#
-# Created: 2003/11/03
-# RCS-ID: $Id$
-# Copyright: (c) 2002
-# Licence: Use as you wish.
-#-----------------------------------------------------------------------------
-# 12/15/2003 - Jeff Grimmett (grimmtooth@softhome.net)
-#
-# o 2.5 compatability update.
-# o Renamed to plot.py in the wx.lib directory.
-# o Reworked test frame to work with wx demo framework. This saves a bit
-# of tedious cut and paste, and the test app is excellent.
-#
-# 12/18/2003 - Jeff Grimmett (grimmtooth@softhome.net)
-#
-# o wxScrolledMessageDialog -> ScrolledMessageDialog
-#
-# Oct 6, 2004 Gordon Williams (g_will@cyberus.ca)
-# - Added bar graph demo
-# - Modified line end shape from round to square.
-# - Removed FloatDCWrapper for conversion to ints and ints in arguments
-#
-# Oct 15, 2004 Gordon Williams (g_will@cyberus.ca)
-# - Imported modules given leading underscore to name.
-# - Added Cursor Line Tracking and User Point Labels.
-# - Demo for Cursor Line Tracking and Point Labels.
-# - Size of plot preview frame adjusted to show page better.
-# - Added helper functions PositionUserToScreen and PositionScreenToUser in PlotCanvas.
-# - Added functions GetClosestPoints (all curves) and GetClosestPoint (only closest curve)
-# can be in either user coords or screen coords.
-#
-#
-
-"""
-This is a simple light weight plotting module that can be used with
-Boa or easily integrated into your own wxPython application. The
-emphasis is on small size and fast plotting for large data sets. It
-has a reasonable number of features to do line and scatter graphs
-easily as well as simple bar graphs. It is not as sophisticated or
-as powerful as SciPy Plt or Chaco. Both of these are great packages
-but consume huge amounts of computer resources for simple plots.
-They can be found at http://scipy.com
-
-This file contains two parts; first the re-usable library stuff, then,
-after a "if __name__=='__main__'" test, a simple frame and a few default
-plots for examples and testing.
-
-Based on wxPlotCanvas
-Written by K.Hinsen, R. Srinivasan;
-Ported to wxPython Harm van der Heijden, feb 1999
-
-Major Additions Gordon Williams Feb. 2003 (g_will@cyberus.ca)
- -More style options
- -Zooming using mouse "rubber band"
- -Scroll left, right
- -Grid(graticule)
- -Printing, preview, and page set up (margins)
- -Axis and title labels
- -Cursor xy axis values
- -Doc strings and lots of comments
- -Optimizations for large number of points
- -Legends
-
-Did a lot of work here to speed markers up. Only a factor of 4
-improvement though. Lines are much faster than markers, especially
-filled markers. Stay away from circles and triangles unless you
-only have a few thousand points.
-
-Times for 25,000 points
-Line - 0.078 sec
-Markers
-Square - 0.22 sec
-dot - 0.10
-circle - 0.87
-cross,plus - 0.28
-triangle, triangle_down - 0.90
-
-Thanks to Chris Barker for getting this version working on Linux.
-
-Zooming controls with mouse (when enabled):
- Left mouse drag - Zoom box.
- Left mouse double click - reset zoom.
- Right mouse click - zoom out centred on click location.
-"""
-
-import string as _string
-import time as _time
-import wx
-
-# Needs Numeric or numarray or NumPy
-try:
- import numpy.oldnumeric as _Numeric
-except:
- try:
- import numarray as _Numeric #if numarray is used it is renamed Numeric
- except:
- try:
- import Numeric as _Numeric
- except:
- msg= """
- This module requires the Numeric/numarray or NumPy module,
- which could not be imported. It probably is not installed
- (it's not part of the standard Python distribution). See the
- Numeric Python site (http://numpy.scipy.org) for information on
- downloading source or binaries."""
- raise ImportError, "Numeric,numarray or NumPy not found. \n" + msg
-
-
-
-#
-# Plotting classes...
-#
-class PolyPoints:
- """Base Class for lines and markers
- - All methods are private.
- """
-
- def __init__(self, points, attr):
- self._points = _Numeric.array(points).astype(_Numeric.Float64)
- self._logscale = (False, False)
- self.currentScale= (1,1)
- self.currentShift= (0,0)
- self.scaled = self.points
- self.attributes = {}
- self.attributes.update(self._attributes)
- for name, value in attr.items():
- if name not in self._attributes.keys():
- raise KeyError, "Style attribute incorrect. Should be one of %s" % self._attributes.keys()
- self.attributes[name] = value
-
- def setLogScale(self, logscale):
- self._logscale = logscale
-
- def __getattr__(self, name):
- if name == 'points':
- if len(self._points)>0:
- data = _Numeric.array(self._points,copy=True)
- if self._logscale[0]:
- data = self.log10(data, 0)
- if self._logscale[1]:
- data = self.log10(data, 1)
- return data
- else:
- return self._points
- else:
- raise AttributeError, name
-
- def log10(self, data, ind):
- data = _Numeric.compress(data[:,ind]>0,data,0)
- data[:,ind] = _Numeric.log10(data[:,ind])
- return data
-
- def boundingBox(self):
- if len(self.points) == 0:
- # no curves to draw
- # defaults to (-1,-1) and (1,1) but axis can be set in Draw
- minXY= _Numeric.array([-1.0,-1.0])
- maxXY= _Numeric.array([ 1.0, 1.0])
- else:
- minXY= _Numeric.minimum.reduce(self.points)
- maxXY= _Numeric.maximum.reduce(self.points)
- return minXY, maxXY
-
- def scaleAndShift(self, scale=(1,1), shift=(0,0)):
- if len(self.points) == 0:
- # no curves to draw
- return
- if (scale is not self.currentScale) or (shift is not self.currentShift):
- # update point scaling
- self.scaled = scale*self.points+shift
- self.currentScale= scale
- self.currentShift= shift
- # else unchanged use the current scaling
-
- def getLegend(self):
- return self.attributes['legend']
-
- def getClosestPoint(self, pntXY, pointScaled= True):
- """Returns the index of closest point on the curve, pointXY, scaledXY, distance
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- if pointScaled == True:
- #Using screen coords
- p = self.scaled
- pxy = self.currentScale * _Numeric.array(pntXY)+ self.currentShift
- else:
- #Using user coords
- p = self.points
- pxy = _Numeric.array(pntXY)
- #determine distance for each point
- d= _Numeric.sqrt(_Numeric.add.reduce((p-pxy)**2,1)) #sqrt(dx^2+dy^2)
- pntIndex = _Numeric.argmin(d)
- dist = d[pntIndex]
- return [pntIndex, self.points[pntIndex], self.scaled[pntIndex], dist]
-
-
-class PolyLine(PolyPoints):
- """Class to define line type and style
- - All methods except __init__ are private.
- """
-
- _attributes = {'colour': 'black',
- 'width': 1,
- 'style': wx.SOLID,
- 'legend': ''}
-
- def __init__(self, points, **attr):
- """Creates PolyLine object
- points - sequence (array, tuple or list) of (x,y) points making up line
- **attr - key word attributes
- Defaults:
- 'colour'= 'black', - wx.Pen Colour any wx.NamedColour
- 'width'= 1, - Pen width
- 'style'= wx.SOLID, - wx.Pen style
- 'legend'= '' - Line Legend to display
- """
- PolyPoints.__init__(self, points, attr)
-
- def draw(self, dc, printerScale, coord= None):
- colour = self.attributes['colour']
- width = self.attributes['width'] * printerScale
- style= self.attributes['style']
- if not isinstance(colour, wx.Colour):
- colour = wx.NamedColour(colour)
- pen = wx.Pen(colour, width, style)
- pen.SetCap(wx.CAP_BUTT)
- dc.SetPen(pen)
- if coord == None:
- dc.DrawLines(self.scaled)
- else:
- dc.DrawLines(coord) # draw legend line
-
- def getSymExtent(self, printerScale):
- """Width and Height of Marker"""
- h= self.attributes['width'] * printerScale
- w= 5 * h
- return (w,h)
-
-
-class PolyMarker(PolyPoints):
- """Class to define marker type and style
- - All methods except __init__ are private.
- """
-
- _attributes = {'colour': 'black',
- 'width': 1,
- 'size': 2,
- 'fillcolour': None,
- 'fillstyle': wx.SOLID,
- 'marker': 'circle',
- 'legend': ''}
-
- def __init__(self, points, **attr):
- """Creates PolyMarker object
- points - sequence (array, tuple or list) of (x,y) points
- **attr - key word attributes
- Defaults:
- 'colour'= 'black', - wx.Pen Colour any wx.NamedColour
- 'width'= 1, - Pen width
- 'size'= 2, - Marker size
- 'fillcolour'= same as colour, - wx.Brush Colour any wx.NamedColour
- 'fillstyle'= wx.SOLID, - wx.Brush fill style (use wx.TRANSPARENT for no fill)
- 'marker'= 'circle' - Marker shape
- 'legend'= '' - Marker Legend to display
-
- Marker Shapes:
- - 'circle'
- - 'dot'
- - 'square'
- - 'triangle'
- - 'triangle_down'
- - 'cross'
- - 'plus'
- """
-
- PolyPoints.__init__(self, points, attr)
-
- def draw(self, dc, printerScale, coord= None):
- colour = self.attributes['colour']
- width = self.attributes['width'] * printerScale
- size = self.attributes['size'] * printerScale
- fillcolour = self.attributes['fillcolour']
- fillstyle = self.attributes['fillstyle']
- marker = self.attributes['marker']
-
- if colour and not isinstance(colour, wx.Colour):
- colour = wx.NamedColour(colour)
- if fillcolour and not isinstance(fillcolour, wx.Colour):
- fillcolour = wx.NamedColour(fillcolour)
-
- dc.SetPen(wx.Pen(colour, width))
- if fillcolour:
- dc.SetBrush(wx.Brush(fillcolour,fillstyle))
- else:
- dc.SetBrush(wx.Brush(colour, fillstyle))
- if coord == None:
- self._drawmarkers(dc, self.scaled, marker, size)
- else:
- self._drawmarkers(dc, coord, marker, size) # draw legend marker
-
- def getSymExtent(self, printerScale):
- """Width and Height of Marker"""
- s= 5*self.attributes['size'] * printerScale
- return (s,s)
-
- def _drawmarkers(self, dc, coords, marker,size=1):
- f = eval('self._' +marker)
- f(dc, coords, size)
-
- def _circle(self, dc, coords, size=1):
- fact= 2.5*size
- wh= 5.0*size
- rect= _Numeric.zeros((len(coords),4),_Numeric.Float)+[0.0,0.0,wh,wh]
- rect[:,0:2]= coords-[fact,fact]
- dc.DrawEllipseList(rect.astype(_Numeric.Int32))
-
- def _dot(self, dc, coords, size=1):
- dc.DrawPointList(coords)
-
- def _square(self, dc, coords, size=1):
- fact= 2.5*size
- wh= 5.0*size
- rect= _Numeric.zeros((len(coords),4),_Numeric.Float)+[0.0,0.0,wh,wh]
- rect[:,0:2]= coords-[fact,fact]
- dc.DrawRectangleList(rect.astype(_Numeric.Int32))
-
- def _triangle(self, dc, coords, size=1):
- shape= [(-2.5*size,1.44*size), (2.5*size,1.44*size), (0.0,-2.88*size)]
- poly= _Numeric.repeat(coords,3)
- poly.shape= (len(coords),3,2)
- poly += shape
- dc.DrawPolygonList(poly.astype(_Numeric.Int32))
-
- def _triangle_down(self, dc, coords, size=1):
- shape= [(-2.5*size,-1.44*size), (2.5*size,-1.44*size), (0.0,2.88*size)]
- poly= _Numeric.repeat(coords,3)
- poly.shape= (len(coords),3,2)
- poly += shape
- dc.DrawPolygonList(poly.astype(_Numeric.Int32))
-
- def _cross(self, dc, coords, size=1):
- fact= 2.5*size
- for f in [[-fact,-fact,fact,fact],[-fact,fact,fact,-fact]]:
- lines= _Numeric.concatenate((coords,coords),axis=1)+f
- dc.DrawLineList(lines.astype(_Numeric.Int32))
-
- def _plus(self, dc, coords, size=1):
- fact= 2.5*size
- for f in [[-fact,0,fact,0],[0,-fact,0,fact]]:
- lines= _Numeric.concatenate((coords,coords),axis=1)+f
- dc.DrawLineList(lines.astype(_Numeric.Int32))
-
-class PlotGraphics:
- """Container to hold PolyXXX objects and graph labels
- - All methods except __init__ are private.
- """
-
- def __init__(self, objects, title='', xLabel='', yLabel= ''):
- """Creates PlotGraphics object
- objects - list of PolyXXX objects to make graph
- title - title shown at top of graph
- xLabel - label shown on x-axis
- yLabel - label shown on y-axis
- """
- if type(objects) not in [list,tuple]:
- raise TypeError, "objects argument should be list or tuple"
- self.objects = objects
- self.title= title
- self.xLabel= xLabel
- self.yLabel= yLabel
-
- def setLogScale(self, logscale):
- if type(logscale) != tuple:
- raise TypeError, 'logscale must be a tuple of bools, e.g. (False, False)'
- if len(self.objects) == 0:
- return
- for o in self.objects:
- o.setLogScale(logscale)
-
- def boundingBox(self):
- p1, p2 = self.objects[0].boundingBox()
- for o in self.objects[1:]:
- p1o, p2o = o.boundingBox()
- p1 = _Numeric.minimum(p1, p1o)
- p2 = _Numeric.maximum(p2, p2o)
- return p1, p2
-
- def scaleAndShift(self, scale=(1,1), shift=(0,0)):
- for o in self.objects:
- o.scaleAndShift(scale, shift)
-
- def setPrinterScale(self, scale):
- """Thickens up lines and markers only for printing"""
- self.printerScale= scale
-
- def setXLabel(self, xLabel= ''):
- """Set the X axis label on the graph"""
- self.xLabel= xLabel
-
- def setYLabel(self, yLabel= ''):
- """Set the Y axis label on the graph"""
- self.yLabel= yLabel
-
- def setTitle(self, title= ''):
- """Set the title at the top of graph"""
- self.title= title
-
- def getXLabel(self):
- """Get x axis label string"""
- return self.xLabel
-
- def getYLabel(self):
- """Get y axis label string"""
- return self.yLabel
-
- def getTitle(self, title= ''):
- """Get the title at the top of graph"""
- return self.title
-
- def draw(self, dc):
- for o in self.objects:
- #t=_time.clock() # profile info
- o.draw(dc, self.printerScale)
- #dt= _time.clock()-t
- #print o, "time=", dt
-
- def getSymExtent(self, printerScale):
- """Get max width and height of lines and markers symbols for legend"""
- symExt = self.objects[0].getSymExtent(printerScale)
- for o in self.objects[1:]:
- oSymExt = o.getSymExtent(printerScale)
- symExt = _Numeric.maximum(symExt, oSymExt)
- return symExt
-
- def getLegendNames(self):
- """Returns list of legend names"""
- lst = [None]*len(self)
- for i in range(len(self)):
- lst[i]= self.objects[i].getLegend()
- return lst
-
- def __len__(self):
- return len(self.objects)
-
- def __getitem__(self, item):
- return self.objects[item]
-
-
-#-------------------------------------------------------------------------------
-# Main window that you will want to import into your application.
-
-class PlotCanvas(wx.Panel):
- """
- Subclass of a wx.Panel which holds two scrollbars and the actual
- plotting canvas (self.canvas). It allows for simple general plotting
- of data with zoom, labels, and automatic axis scaling."""
-
- def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
- size=wx.DefaultSize, style=0, name="plotCanvas"):
- """Constructs a panel, which can be a child of a frame or
- any other non-control window"""
-
- wx.Panel.__init__(self, parent, id, pos, size, style, name)
-
- sizer = wx.FlexGridSizer(2,2,0,0)
- self.canvas = wx.Window(self, -1)
- self.sb_vert = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL)
- self.sb_vert.SetScrollbar(0,1000,1000,1000)
- self.sb_hor = wx.ScrollBar(self, -1, style=wx.SB_HORIZONTAL)
- self.sb_hor.SetScrollbar(0,1000,1000,1000)
-
- sizer.Add(self.canvas, 1, wx.EXPAND)
- sizer.Add(self.sb_vert, 0, wx.EXPAND)
- sizer.Add(self.sb_hor, 0, wx.EXPAND)
- sizer.Add((0,0))
-
- sizer.AddGrowableRow(0, 1)
- sizer.AddGrowableCol(0, 1)
-
- self.sb_vert.Show(False)
- self.sb_hor.Show(False)
-
- self.SetSizer(sizer)
- self.Fit()
-
- self.border = (1,1)
-
- self.SetBackgroundColour("white")
-
- # Create some mouse events for zooming
- self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
- self.canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
- self.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
- self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseDoubleClick)
- self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
-
- # scrollbar events
- self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_PAGEUP, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_LINEUP, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnScroll)
-
- # set curser as cross-hairs
- self.canvas.SetCursor(wx.CROSS_CURSOR)
- self.HandCursor = wx.CursorFromImage(getHandImage())
- self.GrabHandCursor = wx.CursorFromImage(getGrabHandImage())
- self.MagCursor = wx.CursorFromImage(getMagPlusImage())
-
- # Things for printing
- self.print_data = wx.PrintData()
- self.print_data.SetPaperId(wx.PAPER_LETTER)
- self.print_data.SetOrientation(wx.LANDSCAPE)
- self.pageSetupData= wx.PageSetupDialogData()
- self.pageSetupData.SetMarginBottomRight((25,25))
- self.pageSetupData.SetMarginTopLeft((25,25))
- self.pageSetupData.SetPrintData(self.print_data)
- self.printerScale = 1
- self.parent= parent
-
- # scrollbar variables
- self._sb_ignore = False
- self._adjustingSB = False
- self._sb_xfullrange = 0
- self._sb_yfullrange = 0
- self._sb_xunit = 0
- self._sb_yunit = 0
-
- self._dragEnabled = False
- self._screenCoordinates = _Numeric.array([0.0, 0.0])
-
- self._logscale = (False, False)
-
- # Zooming variables
- self._zoomInFactor = 0.5
- self._zoomOutFactor = 2
- self._zoomCorner1= _Numeric.array([0.0, 0.0]) # left mouse down corner
- self._zoomCorner2= _Numeric.array([0.0, 0.0]) # left mouse up corner
- self._zoomEnabled= False
- self._hasDragged= False
-
- # Drawing Variables
- self.last_draw = None
- self._pointScale= 1
- self._pointShift= 0
- self._xSpec= 'auto'
- self._ySpec= 'auto'
- self._gridEnabled= False
- self._legendEnabled= False
- self._titleEnabled= True
-
- # Fonts
- self._fontCache = {}
- self._fontSizeAxis= 10
- self._fontSizeTitle= 15
- self._fontSizeLegend= 7
-
- # pointLabels
- self._pointLabelEnabled= False
- self.last_PointLabel= None
- self._pointLabelFunc= None
- self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
-
- self._useScientificNotation = False
-
- self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
- self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
- # OnSize called to make sure the buffer is initialized.
- # This might result in OnSize getting called twice on some
- # platforms at initialization, but little harm done.
- self.OnSize(None) # sets the initial size based on client size
-
- self._gridColour = wx.NamedColour('black')
-
- def SetCursor(self, cursor):
- self.canvas.SetCursor(cursor)
-
- def GetGridColour(self):
- return self._gridColour
-
- def SetGridColour(self, colour):
- if isinstance(colour, wx.Colour):
- self._gridColour = colour
- else:
- self._gridColour = wx.NamedColour(colour)
-
-
- # SaveFile
- def SaveFile(self, fileName= ''):
- """Saves the file to the type specified in the extension. If no file
- name is specified a dialog box is provided. Returns True if sucessful,
- otherwise False.
-
- .bmp Save a Windows bitmap file.
- .xbm Save an X bitmap file.
- .xpm Save an XPM bitmap file.
- .png Save a Portable Network Graphics file.
- .jpg Save a Joint Photographic Experts Group file.
- """
- if _string.lower(fileName[-3:]) not in ['bmp','xbm','xpm','png','jpg']:
- dlg1 = wx.FileDialog(
- self,
- "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "",
- "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg",
- wx.SAVE|wx.OVERWRITE_PROMPT
- )
- try:
- while 1:
- if dlg1.ShowModal() == wx.ID_OK:
- fileName = dlg1.GetPath()
- # Check for proper exension
- if _string.lower(fileName[-3:]) not in ['bmp','xbm','xpm','png','jpg']:
- dlg2 = wx.MessageDialog(self, 'File name extension\n'
- 'must be one of\n'
- 'bmp, xbm, xpm, png, or jpg',
- 'File Name Error', wx.OK | wx.ICON_ERROR)
- try:
- dlg2.ShowModal()
- finally:
- dlg2.Destroy()
- else:
- break # now save file
- else: # exit without saving
- return False
- finally:
- dlg1.Destroy()
-
- # File name has required extension
- fType = _string.lower(fileName[-3:])
- if fType == "bmp":
- tp= wx.BITMAP_TYPE_BMP # Save a Windows bitmap file.
- elif fType == "xbm":
- tp= wx.BITMAP_TYPE_XBM # Save an X bitmap file.
- elif fType == "xpm":
- tp= wx.BITMAP_TYPE_XPM # Save an XPM bitmap file.
- elif fType == "jpg":
- tp= wx.BITMAP_TYPE_JPEG # Save a JPG file.
- else:
- tp= wx.BITMAP_TYPE_PNG # Save a PNG file.
- # Save Bitmap
- res= self._Buffer.SaveFile(fileName, tp)
- return res
-
- def PageSetup(self):
- """Brings up the page setup dialog"""
- data = self.pageSetupData
- data.SetPrintData(self.print_data)
- dlg = wx.PageSetupDialog(self.parent, data)
- try:
- if dlg.ShowModal() == wx.ID_OK:
- data = dlg.GetPageSetupData() # returns wx.PageSetupDialogData
- # updates page parameters from dialog
- self.pageSetupData.SetMarginBottomRight(data.GetMarginBottomRight())
- self.pageSetupData.SetMarginTopLeft(data.GetMarginTopLeft())
- self.pageSetupData.SetPrintData(data.GetPrintData())
- self.print_data=wx.PrintData(data.GetPrintData()) # updates print_data
- finally:
- dlg.Destroy()
-
- def Printout(self, paper=None):
- """Print current plot."""
- if paper != None:
- self.print_data.SetPaperId(paper)
- pdd = wx.PrintDialogData(self.print_data)
- printer = wx.Printer(pdd)
- out = PlotPrintout(self)
- print_ok = printer.Print(self.parent, out)
- if print_ok:
- self.print_data = wx.PrintData(printer.GetPrintDialogData().GetPrintData())
- out.Destroy()
-
- def PrintPreview(self):
- """Print-preview current plot."""
- printout = PlotPrintout(self)
- printout2 = PlotPrintout(self)
- self.preview = wx.PrintPreview(printout, printout2, self.print_data)
- if not self.preview.Ok():
- wx.MessageDialog(self, "Print Preview failed.\n" \
- "Check that default printer is configured\n", \
- "Print error", wx.OK|wx.CENTRE).ShowModal()
- self.preview.SetZoom(40)
- # search up tree to find frame instance
- frameInst= self
- while not isinstance(frameInst, wx.Frame):
- frameInst= frameInst.GetParent()
- frame = wx.PreviewFrame(self.preview, frameInst, "Preview")
- frame.Initialize()
- frame.SetPosition(self.GetPosition())
- frame.SetSize((600,550))
- frame.Centre(wx.BOTH)
- frame.Show(True)
-
- def setLogScale(self, logscale):
- if type(logscale) != tuple:
- raise TypeError, 'logscale must be a tuple of bools, e.g. (False, False)'
- if self.last_draw is not None:
- graphics, xAxis, yAxis= self.last_draw
- graphics.setLogScale(logscale)
- self.last_draw = (graphics, None, None)
- self.SetXSpec('min')
- self.SetYSpec('min')
- self._logscale = logscale
-
- def getLogScale(self):
- return self._logscale
-
- def SetFontSizeAxis(self, point= 10):
- """Set the tick and axis label font size (default is 10 point)"""
- self._fontSizeAxis= point
-
- def GetFontSizeAxis(self):
- """Get current tick and axis label font size in points"""
- return self._fontSizeAxis
-
- def SetFontSizeTitle(self, point= 15):
- """Set Title font size (default is 15 point)"""
- self._fontSizeTitle= point
-
- def GetFontSizeTitle(self):
- """Get current Title font size in points"""
- return self._fontSizeTitle
-
- def SetFontSizeLegend(self, point= 7):
- """Set Legend font size (default is 7 point)"""
- self._fontSizeLegend= point
-
- def GetFontSizeLegend(self):
- """Get current Legend font size in points"""
- return self._fontSizeLegend
-
- def SetShowScrollbars(self, value):
- """Set True to show scrollbars"""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- if value == self.GetShowScrollbars():
- return
- self.sb_vert.Show(value)
- self.sb_hor.Show(value)
- wx.CallAfter(self.Layout)
-
- def GetShowScrollbars(self):
- """Set True to show scrollbars"""
- return self.sb_vert.IsShown()
-
- def SetUseScientificNotation(self, useScientificNotation):
- self._useScientificNotation = useScientificNotation
-
- def GetUseScientificNotation(self):
- return self._useScientificNotation
-
- def SetEnableDrag(self, value):
- """Set True to enable drag."""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- if value:
- if self.GetEnableZoom():
- self.SetEnableZoom(False)
- self.SetCursor(self.HandCursor)
- else:
- self.SetCursor(wx.CROSS_CURSOR)
- self._dragEnabled = value
-
- def GetEnableDrag(self):
- return self._dragEnabled
-
- def SetEnableZoom(self, value):
- """Set True to enable zooming."""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- if value:
- if self.GetEnableDrag():
- self.SetEnableDrag(False)
- self.SetCursor(self.MagCursor)
- else:
- self.SetCursor(wx.CROSS_CURSOR)
- self._zoomEnabled= value
-
- def GetEnableZoom(self):
- """True if zooming enabled."""
- return self._zoomEnabled
-
- def SetEnableGrid(self, value):
- """Set True to enable grid."""
- if value not in [True,False,'Horizontal','Vertical']:
- raise TypeError, "Value should be True, False, Horizontal or Vertical"
- self._gridEnabled= value
- self.Redraw()
-
- def GetEnableGrid(self):
- """True if grid enabled."""
- return self._gridEnabled
-
- def SetEnableLegend(self, value):
- """Set True to enable legend."""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- self._legendEnabled= value
- self.Redraw()
-
- def GetEnableLegend(self):
- """True if Legend enabled."""
- return self._legendEnabled
-
- def SetEnableTitle(self, value):
- """Set True to enable title."""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- self._titleEnabled= value
- self.Redraw()
-
- def GetEnableTitle(self):
- """True if title enabled."""
- return self._titleEnabled
-
- def SetEnablePointLabel(self, value):
- """Set True to enable pointLabel."""
- if value not in [True,False]:
- raise TypeError, "Value should be True or False"
- self._pointLabelEnabled= value
- self.Redraw() #will erase existing pointLabel if present
- self.last_PointLabel = None
-
- def GetEnablePointLabel(self):
- """True if pointLabel enabled."""
- return self._pointLabelEnabled
-
- def SetPointLabelFunc(self, func):
- """Sets the function with custom code for pointLabel drawing
- ******** more info needed ***************
- """
- self._pointLabelFunc= func
-
- def GetPointLabelFunc(self):
- """Returns pointLabel Drawing Function"""
- return self._pointLabelFunc
-
- def Reset(self):
- """Unzoom the plot."""
- self.last_PointLabel = None #reset pointLabel
- if self.last_draw is not None:
- self._Draw(self.last_draw[0])
-
- def ScrollRight(self, units):
- """Move view right number of axis units."""
- self.last_PointLabel = None #reset pointLabel
- if self.last_draw is not None:
- graphics, xAxis, yAxis= self.last_draw
- xAxis= (xAxis[0]+units, xAxis[1]+units)
- self._Draw(graphics,xAxis,yAxis)
-
- def ScrollUp(self, units):
- """Move view up number of axis units."""
- self.last_PointLabel = None #reset pointLabel
- if self.last_draw is not None:
- graphics, xAxis, yAxis= self.last_draw
- yAxis= (yAxis[0]+units, yAxis[1]+units)
- self._Draw(graphics,xAxis,yAxis)
-
- def GetXY(self, event):
- """Wrapper around _getXY, which handles log scales"""
- x,y = self._getXY(event)
- if self.getLogScale()[0]:
- x = _Numeric.power(10,x)
- if self.getLogScale()[1]:
- y = _Numeric.power(10,y)
- return x,y
-
- def _getXY(self,event):
- """Takes a mouse event and returns the XY user axis values."""
- x,y= self.PositionScreenToUser(event.GetPosition())
- return x,y
-
- def PositionUserToScreen(self, pntXY):
- """Converts User position to Screen Coordinates"""
- userPos= _Numeric.array(pntXY)
- x,y= userPos * self._pointScale + self._pointShift
- return x,y
-
- def PositionScreenToUser(self, pntXY):
- """Converts Screen position to User Coordinates"""
- screenPos= _Numeric.array(pntXY)
- x,y= (screenPos-self._pointShift)/self._pointScale
- return x,y
-
- def SetXSpec(self, type= 'auto'):
- """xSpec- defines x axis type. Can be 'none', 'min' or 'auto'
- where:
- 'none' - shows no axis or tick mark values
- 'min' - shows min bounding box values
- 'auto' - rounds axis range to sensible values
- """
- self._xSpec= type
-
- def SetYSpec(self, type= 'auto'):
- """ySpec- defines x axis type. Can be 'none', 'min' or 'auto'
- where:
- 'none' - shows no axis or tick mark values
- 'min' - shows min bounding box values
- 'auto' - rounds axis range to sensible values
- """
- self._ySpec= type
-
- def GetXSpec(self):
- """Returns current XSpec for axis"""
- return self._xSpec
-
- def GetYSpec(self):
- """Returns current YSpec for axis"""
- return self._ySpec
-
- def GetXMaxRange(self):
- xAxis = self._getXMaxRange()
- if self.getLogScale()[0]:
- xAxis = _Numeric.power(10,xAxis)
- return xAxis
-
- def _getXMaxRange(self):
- """Returns (minX, maxX) x-axis range for displayed graph"""
- graphics= self.last_draw[0]
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- xAxis = self._axisInterval(self._xSpec, p1[0], p2[0]) # in user units
- return xAxis
-
- def GetYMaxRange(self):
- yAxis = self._getYMaxRange()
- if self.getLogScale()[1]:
- yAxis = _Numeric.power(10,yAxis)
- return yAxis
-
- def _getYMaxRange(self):
- """Returns (minY, maxY) y-axis range for displayed graph"""
- graphics= self.last_draw[0]
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
- return yAxis
-
- def GetXCurrentRange(self):
- xAxis = self._getXCurrentRange()
- if self.getLogScale()[0]:
- xAxis = _Numeric.power(10,xAxis)
- return xAxis
-
- def _getXCurrentRange(self):
- """Returns (minX, maxX) x-axis for currently displayed portion of graph"""
- return self.last_draw[1]
-
- def GetYCurrentRange(self):
- yAxis = self._getYCurrentRange()
- if self.getLogScale()[1]:
- yAxis = _Numeric.power(10,yAxis)
- return yAxis
-
- def _getYCurrentRange(self):
- """Returns (minY, maxY) y-axis for currently displayed portion of graph"""
- return self.last_draw[2]
-
- def Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
- """Wrapper around _Draw, which handles log axes"""
-
- graphics.setLogScale(self.getLogScale())
-
- # check Axis is either tuple or none
- if type(xAxis) not in [type(None),tuple]:
- raise TypeError, "xAxis should be None or (minX,maxX)"+str(type(xAxis))
- if type(yAxis) not in [type(None),tuple]:
- raise TypeError, "yAxis should be None or (minY,maxY)"+str(type(xAxis))
-
- # check case for axis = (a,b) where a==b caused by improper zooms
- if xAxis != None:
- if xAxis[0] == xAxis[1]:
- return
- if self.getLogScale()[0]:
- xAxis = _Numeric.log10(xAxis)
- if yAxis != None:
- if yAxis[0] == yAxis[1]:
- return
- if self.getLogScale()[1]:
- yAxis = _Numeric.log10(yAxis)
- self._Draw(graphics, xAxis, yAxis, dc)
-
- def _Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
- """\
- Draw objects in graphics with specified x and y axis.
- graphics- instance of PlotGraphics with list of PolyXXX objects
- xAxis - tuple with (min, max) axis range to view
- yAxis - same as xAxis
- dc - drawing context - doesn't have to be specified.
- If it's not, the offscreen buffer is used
- """
-
- if dc == None:
- # sets new dc and clears it
- dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
- dc.Clear()
-
- dc.BeginDrawing()
- # dc.Clear()
-
- # set font size for every thing but title and legend
- dc.SetFont(self._getFont(self._fontSizeAxis))
-
- # sizes axis to axis type, create lower left and upper right corners of plot
- if xAxis == None or yAxis == None:
- # One or both axis not specified in Draw
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- if xAxis == None:
- xAxis = self._axisInterval(self._xSpec, p1[0], p2[0]) # in user units
- if yAxis == None:
- yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
- # Adjust bounding box for axis spec
- p1[0],p1[1] = xAxis[0], yAxis[0] # lower left corner user scale (xmin,ymin)
- p2[0],p2[1] = xAxis[1], yAxis[1] # upper right corner user scale (xmax,ymax)
- else:
- # Both axis specified in Draw
- p1= _Numeric.array([xAxis[0], yAxis[0]]) # lower left corner user scale (xmin,ymin)
- p2= _Numeric.array([xAxis[1], yAxis[1]]) # upper right corner user scale (xmax,ymax)
-
- self.last_draw = (graphics, _Numeric.array(xAxis), _Numeric.array(yAxis)) # saves most recient values
-
- # Get ticks and textExtents for axis if required
- if self._xSpec is not 'none':
- xticks = self._xticks(xAxis[0], xAxis[1])
- xTextExtent = dc.GetTextExtent(xticks[-1][1])# w h of x axis text last number on axis
- else:
- xticks = None
- xTextExtent= (0,0) # No text for ticks
- if self._ySpec is not 'none':
- yticks = self._yticks(yAxis[0], yAxis[1])
- if self.getLogScale()[1]:
- yTextExtent = dc.GetTextExtent('-2e-2')
- else:
- yTextExtentBottom = dc.GetTextExtent(yticks[0][1])
- yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
- yTextExtent= (max(yTextExtentBottom[0],yTextExtentTop[0]),
- max(yTextExtentBottom[1],yTextExtentTop[1]))
- else:
- yticks = None
- yTextExtent= (0,0) # No text for ticks
-
- # TextExtents for Title and Axis Labels
- titleWH, xLabelWH, yLabelWH= self._titleLablesWH(dc, graphics)
-
- # TextExtents for Legend
- legendBoxWH, legendSymExt, legendTextExt = self._legendWH(dc, graphics)
-
- # room around graph area
- rhsW= max(xTextExtent[0], legendBoxWH[0]) # use larger of number width or legend width
- lhsW= yTextExtent[0]+ yLabelWH[1]
- bottomH= max(xTextExtent[1], yTextExtent[1]/2.)+ xLabelWH[1]
- topH= yTextExtent[1]/2. + titleWH[1]
- textSize_scale= _Numeric.array([rhsW+lhsW,bottomH+topH]) # make plot area smaller by text size
- textSize_shift= _Numeric.array([lhsW, bottomH]) # shift plot area by this amount
-
- # draw title if requested
- if self._titleEnabled:
- dc.SetFont(self._getFont(self._fontSizeTitle))
- titlePos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- titleWH[0]/2.,
- self.plotbox_origin[1]- self.plotbox_size[1])
- dc.DrawText(graphics.getTitle(),titlePos[0],titlePos[1])
-
- # draw label text
- dc.SetFont(self._getFont(self._fontSizeAxis))
- xLabelPos= (self.plotbox_origin[0]+ lhsW + (self.plotbox_size[0]-lhsW-rhsW)/2.- xLabelWH[0]/2.,
- self.plotbox_origin[1]- xLabelWH[1])
- dc.DrawText(graphics.getXLabel(),xLabelPos[0],xLabelPos[1])
- yLabelPos= (self.plotbox_origin[0],
- self.plotbox_origin[1]- bottomH- (self.plotbox_size[1]-bottomH-topH)/2.+ yLabelWH[0]/2.)
- if graphics.getYLabel(): # bug fix for Linux
- dc.DrawRotatedText(graphics.getYLabel(),yLabelPos[0],yLabelPos[1],90)
-
- # drawing legend makers and text
- if self._legendEnabled:
- self._drawLegend(dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt)
-
- # allow for scaling and shifting plotted points
- scale = (self.plotbox_size-textSize_scale) / (p2-p1)* _Numeric.array((1,-1))
- shift = -p1*scale + self.plotbox_origin + textSize_shift * _Numeric.array((1,-1))
- self._pointScale= scale # make available for mouse events
- self._pointShift= shift
- self._drawAxes(dc, p1, p2, scale, shift, xticks, yticks)
-
- graphics.scaleAndShift(scale, shift)
- graphics.setPrinterScale(self.printerScale) # thicken up lines and markers if printing
-
- # set clipping area so drawing does not occur outside axis box
- ptx,pty,rectWidth,rectHeight= self._point2ClientCoord(p1, p2)
- dc.SetClippingRegion(ptx,pty,rectWidth,rectHeight)
- # Draw the lines and markers
- #start = _time.clock()
- graphics.draw(dc)
- # print "entire graphics drawing took: %f second"%(_time.clock() - start)
- # remove the clipping region
- dc.DestroyClippingRegion()
- dc.EndDrawing()
-
- self._adjustScrollbars()
-
- def Redraw(self, dc=None):
- """Redraw the existing plot."""
- if self.last_draw is not None:
- graphics, xAxis, yAxis= self.last_draw
- self._Draw(graphics,xAxis,yAxis,dc)
-
- def Clear(self):
- """Erase the window."""
- self.last_PointLabel = None #reset pointLabel
- dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
- dc.Clear()
- self.last_draw = None
-
- def Zoom(self, Center, Ratio):
- """ Zoom on the plot
- Centers on the X,Y coords given in Center
- Zooms by the Ratio = (Xratio, Yratio) given
- """
- self.last_PointLabel = None #reset maker
- x,y = Center
- if self.last_draw != None:
- (graphics, xAxis, yAxis) = self.last_draw
- w = (xAxis[1] - xAxis[0]) * Ratio[0]
- h = (yAxis[1] - yAxis[0]) * Ratio[1]
- xAxis = ( x - w/2, x + w/2 )
- yAxis = ( y - h/2, y + h/2 )
- self._Draw(graphics, xAxis, yAxis)
-
- def GetClosestPoints(self, pntXY, pointScaled= True):
- """Returns list with
- [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- list for each curve.
- Returns [] if no curves are being plotted.
-
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- if self.last_draw == None:
- #no graph available
- return []
- graphics, xAxis, yAxis= self.last_draw
- l = []
- for curveNum,obj in enumerate(graphics):
- #check there are points in the curve
- if len(obj.points) == 0:
- continue #go to next obj
- #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- cn = [curveNum]+ [obj.getLegend()]+ obj.getClosestPoint( pntXY, pointScaled)
- l.append(cn)
- return l
-
- def GetClosestPoint(self, pntXY, pointScaled= True):
- """Returns list with
- [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- list for only the closest curve.
- Returns [] if no curves are being plotted.
-
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- #closest points on screen based on screen scaling (pointScaled= True)
- #list [curveNumber, index, pointXY, scaledXY, distance] for each curve
- closestPts= self.GetClosestPoints(pntXY, pointScaled)
- if closestPts == []:
- return [] #no graph present
- #find one with least distance
- dists = [c[-1] for c in closestPts]
- mdist = min(dists) #Min dist
- i = dists.index(mdist) #index for min dist
- return closestPts[i] #this is the closest point on closest curve
-
- GetClosetPoint = GetClosestPoint
-
- def UpdatePointLabel(self, mDataDict):
- """Updates the pointLabel point on screen with data contained in
- mDataDict.
-
- mDataDict will be passed to your function set by
- SetPointLabelFunc. It can contain anything you
- want to display on the screen at the scaledXY point
- you specify.
-
- This function can be called from parent window with onClick,
- onMotion events etc.
- """
- if self.last_PointLabel != None:
- #compare pointXY
- if _Numeric.sometrue(mDataDict["pointXY"] != self.last_PointLabel["pointXY"]):
- #closest changed
- self._drawPointLabel(self.last_PointLabel) #erase old
- self._drawPointLabel(mDataDict) #plot new
- else:
- #just plot new with no erase
- self._drawPointLabel(mDataDict) #plot new
- #save for next erase
- self.last_PointLabel = mDataDict
-
- # event handlers **********************************
- def OnMotion(self, event):
- if self._zoomEnabled and event.LeftIsDown():
- if self._hasDragged:
- self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
- else:
- self._hasDragged= True
- self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
- self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # add new
- elif self._dragEnabled and event.LeftIsDown():
- coordinates = event.GetPosition()
- newpos, oldpos = map(_Numeric.array, map(self.PositionScreenToUser, [coordinates, self._screenCoordinates]))
- dist = newpos-oldpos
- self._screenCoordinates = coordinates
-
- if self.last_draw is not None:
- graphics, xAxis, yAxis= self.last_draw
- yAxis -= dist[1]
- xAxis -= dist[0]
- self._Draw(graphics,xAxis,yAxis)
-
- def OnMouseLeftDown(self,event):
- self._zoomCorner1[0], self._zoomCorner1[1]= self._getXY(event)
- self._screenCoordinates = _Numeric.array(event.GetPosition())
- if self._dragEnabled:
- self.SetCursor(self.GrabHandCursor)
- self.canvas.CaptureMouse()
-
- def OnMouseLeftUp(self, event):
- if self._zoomEnabled:
- if self._hasDragged == True:
- self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
- self._zoomCorner2[0], self._zoomCorner2[1]= self._getXY(event)
- self._hasDragged = False # reset flag
- minX, minY= _Numeric.minimum( self._zoomCorner1, self._zoomCorner2)
- maxX, maxY= _Numeric.maximum( self._zoomCorner1, self._zoomCorner2)
- self.last_PointLabel = None #reset pointLabel
- if self.last_draw != None:
- self._Draw(self.last_draw[0], xAxis = (minX,maxX), yAxis = (minY,maxY), dc = None)
- #else: # A box has not been drawn, zoom in on a point
- ## this interfered with the double click, so I've disables it.
- # X,Y = self._getXY(event)
- # self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
- if self._dragEnabled:
- self.SetCursor(self.HandCursor)
- if self.canvas.HasCapture():
- self.canvas.ReleaseMouse()
-
- def OnMouseDoubleClick(self,event):
- if self._zoomEnabled:
- # Give a little time for the click to be totally finished
- # before (possibly) removing the scrollbars and trigering
- # size events, etc.
- wx.FutureCall(200,self.Reset)
-
- def OnMouseRightDown(self,event):
- if self._zoomEnabled:
- X,Y = self._getXY(event)
- self.Zoom( (X,Y), (self._zoomOutFactor, self._zoomOutFactor) )
-
- def OnPaint(self, event):
- # All that is needed here is to draw the buffer to screen
- if self.last_PointLabel != None:
- self._drawPointLabel(self.last_PointLabel) #erase old
- self.last_PointLabel = None
- dc = wx.BufferedPaintDC(self.canvas, self._Buffer)
-
- def OnSize(self,event):
- # The Buffer init is done here, to make sure the buffer is always
- # the same size as the Window
- Size = self.canvas.GetClientSize()
- Size.width = max(1, Size.width)
- Size.height = max(1, Size.height)
-
- # Make new offscreen bitmap: this bitmap will always have the
- # current drawing in it, so it can be used to save the image to
- # a file, or whatever.
- self._Buffer = wx.EmptyBitmap(Size.width, Size.height)
- self._setSize()
-
- self.last_PointLabel = None #reset pointLabel
-
- if self.last_draw is None:
- self.Clear()
- else:
- graphics, xSpec, ySpec = self.last_draw
- self._Draw(graphics,xSpec,ySpec)
-
- def OnLeave(self, event):
- """Used to erase pointLabel when mouse outside window"""
- if self.last_PointLabel != None:
- self._drawPointLabel(self.last_PointLabel) #erase old
- self.last_PointLabel = None
-
- def OnScroll(self, evt):
- if not self._adjustingSB:
- self._sb_ignore = True
- sbpos = evt.GetPosition()
-
- if evt.GetOrientation() == wx.VERTICAL:
- fullrange,pagesize = self.sb_vert.GetRange(),self.sb_vert.GetPageSize()
- sbpos = fullrange-pagesize-sbpos
- dist = sbpos*self._sb_yunit-(self._getYCurrentRange()[0]-self._sb_yfullrange[0])
- self.ScrollUp(dist)
-
- if evt.GetOrientation() == wx.HORIZONTAL:
- dist = sbpos*self._sb_xunit-(self._getXCurrentRange()[0]-self._sb_xfullrange[0])
- self.ScrollRight(dist)
-
- # Private Methods **************************************************
- def _setSize(self, width=None, height=None):
- """DC width and height."""
- if width == None:
- (self.width,self.height) = self.canvas.GetClientSize()
- else:
- self.width, self.height= width,height
- self.plotbox_size = 0.97*_Numeric.array([self.width, self.height])
- xo = 0.5*(self.width-self.plotbox_size[0])
- yo = self.height-0.5*(self.height-self.plotbox_size[1])
- self.plotbox_origin = _Numeric.array([xo, yo])
-
- def _setPrinterScale(self, scale):
- """Used to thicken lines and increase marker size for print out."""
- # line thickness on printer is very thin at 600 dot/in. Markers small
- self.printerScale= scale
-
- def _printDraw(self, printDC):
- """Used for printing."""
- if self.last_draw != None:
- graphics, xSpec, ySpec= self.last_draw
- self._Draw(graphics,xSpec,ySpec,printDC)
-
- def _drawPointLabel(self, mDataDict):
- """Draws and erases pointLabels"""
- width = self._Buffer.GetWidth()
- height = self._Buffer.GetHeight()
- tmp_Buffer = wx.EmptyBitmap(width,height)
- dcs = wx.MemoryDC()
- dcs.SelectObject(tmp_Buffer)
- dcs.Clear()
- dcs.BeginDrawing()
- self._pointLabelFunc(dcs,mDataDict) #custom user pointLabel function
- dcs.EndDrawing()
-
- dc = wx.ClientDC( self.canvas )
- #this will erase if called twice
- dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV) #(NOT src) XOR dst
-
-
- def _drawLegend(self,dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, legendTextExt):
- """Draws legend symbols and text"""
- # top right hand corner of graph box is ref corner
- trhc= self.plotbox_origin+ (self.plotbox_size-[rhsW,topH])*[1,-1]
- legendLHS= .091* legendBoxWH[0] # border space between legend sym and graph box
- lineHeight= max(legendSymExt[1], legendTextExt[1]) * 1.1 #1.1 used as space between lines
- dc.SetFont(self._getFont(self._fontSizeLegend))
- for i in range(len(graphics)):
- o = graphics[i]
- s= i*lineHeight
- if isinstance(o,PolyMarker):
- # draw marker with legend
- pnt= (trhc[0]+legendLHS+legendSymExt[0]/2., trhc[1]+s+lineHeight/2.)
- o.draw(dc, self.printerScale, coord= _Numeric.array([pnt]))
- elif isinstance(o,PolyLine):
- # draw line with legend
- pnt1= (trhc[0]+legendLHS, trhc[1]+s+lineHeight/2.)
- pnt2= (trhc[0]+legendLHS+legendSymExt[0], trhc[1]+s+lineHeight/2.)
- o.draw(dc, self.printerScale, coord= _Numeric.array([pnt1,pnt2]))
- else:
- raise TypeError, "object is neither PolyMarker or PolyLine instance"
- # draw legend txt
- pnt= (trhc[0]+legendLHS+legendSymExt[0], trhc[1]+s+lineHeight/2.-legendTextExt[1]/2)
- dc.DrawText(o.getLegend(),pnt[0],pnt[1])
- dc.SetFont(self._getFont(self._fontSizeAxis)) # reset
-
- def _titleLablesWH(self, dc, graphics):
- """Draws Title and labels and returns width and height for each"""
- # TextExtents for Title and Axis Labels
- dc.SetFont(self._getFont(self._fontSizeTitle))
- if self._titleEnabled:
- title= graphics.getTitle()
- titleWH= dc.GetTextExtent(title)
- else:
- titleWH= (0,0)
- dc.SetFont(self._getFont(self._fontSizeAxis))
- xLabel, yLabel= graphics.getXLabel(),graphics.getYLabel()
- xLabelWH= dc.GetTextExtent(xLabel)
- yLabelWH= dc.GetTextExtent(yLabel)
- return titleWH, xLabelWH, yLabelWH
-
- def _legendWH(self, dc, graphics):
- """Returns the size in screen units for legend box"""
- if self._legendEnabled != True:
- legendBoxWH= symExt= txtExt= (0,0)
- else:
- # find max symbol size
- symExt= graphics.getSymExtent(self.printerScale)
- # find max legend text extent
- dc.SetFont(self._getFont(self._fontSizeLegend))
- txtList= graphics.getLegendNames()
- txtExt= dc.GetTextExtent(txtList[0])
- for txt in graphics.getLegendNames()[1:]:
- txtExt= _Numeric.maximum(txtExt,dc.GetTextExtent(txt))
- maxW= symExt[0]+txtExt[0]
- maxH= max(symExt[1],txtExt[1])
- # padding .1 for lhs of legend box and space between lines
- maxW= maxW* 1.1
- maxH= maxH* 1.1 * len(txtList)
- dc.SetFont(self._getFont(self._fontSizeAxis))
- legendBoxWH= (maxW,maxH)
- return (legendBoxWH, symExt, txtExt)
-
- def _drawRubberBand(self, corner1, corner2):
- """Draws/erases rect box from corner1 to corner2"""
- ptx,pty,rectWidth,rectHeight= self._point2ClientCoord(corner1, corner2)
- # draw rectangle
- dc = wx.ClientDC( self.canvas )
- dc.BeginDrawing()
- dc.SetPen(wx.Pen(wx.BLACK))
- dc.SetBrush(wx.Brush( wx.WHITE, wx.TRANSPARENT ) )
- dc.SetLogicalFunction(wx.INVERT)
- dc.DrawRectangle( ptx,pty, rectWidth,rectHeight)
- dc.SetLogicalFunction(wx.COPY)
- dc.EndDrawing()
-
- def _getFont(self,size):
- """Take font size, adjusts if printing and returns wx.Font"""
- s = size*self.printerScale
- of = self.GetFont()
- # Linux speed up to get font from cache rather than X font server
- key = (int(s), of.GetFamily (), of.GetStyle (), of.GetWeight ())
- font = self._fontCache.get (key, None)
- if font:
- return font # yeah! cache hit
- else:
- font = wx.Font(int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
- self._fontCache[key] = font
- return font
-
-
- def _point2ClientCoord(self, corner1, corner2):
- """Converts user point coords to client screen int coords x,y,width,height"""
- c1= _Numeric.array(corner1)
- c2= _Numeric.array(corner2)
- # convert to screen coords
- pt1= c1*self._pointScale+self._pointShift
- pt2= c2*self._pointScale+self._pointShift
- # make height and width positive
- pul= _Numeric.minimum(pt1,pt2) # Upper left corner
- plr= _Numeric.maximum(pt1,pt2) # Lower right corner
- rectWidth, rectHeight= plr-pul
- ptx,pty= pul
- return ptx, pty, rectWidth, rectHeight
-
- def _axisInterval(self, spec, lower, upper):
- """Returns sensible axis range for given spec"""
- if spec == 'none' or spec == 'min':
- if lower == upper:
- return lower-0.5, upper+0.5
- else:
- return lower, upper
- elif spec == 'auto':
- range = upper-lower
- if range == 0.:
- return lower-0.5, upper+0.5
- log = _Numeric.log10(range)
- power = _Numeric.floor(log)
- fraction = log-power
- if fraction <= 0.05:
- power = power-1
- grid = 10.**power
- lower = lower - lower % grid
- mod = upper % grid
- if mod != 0:
- upper = upper - mod + grid
- return lower, upper
- elif type(spec) == type(()):
- lower, upper = spec
- if lower <= upper:
- return lower, upper
- else:
- return upper, lower
- else:
- raise ValueError, str(spec) + ': illegal axis specification'
-
- def _drawAxes(self, dc, p1, p2, scale, shift, xticks, yticks):
-
- penWidth= self.printerScale # increases thickness for printing only
- dc.SetPen(wx.Pen(self._gridColour, penWidth))
-
- # set length of tick marks--long ones make grid
- if self._gridEnabled:
- x,y,width,height= self._point2ClientCoord(p1,p2)
- if self._gridEnabled == 'Horizontal':
- yTickLength= width/2.0 +1
- xTickLength= 3 * self.printerScale
- elif self._gridEnabled == 'Vertical':
- yTickLength= 3 * self.printerScale
- xTickLength= height/2.0 +1
- else:
- yTickLength= width/2.0 +1
- xTickLength= height/2.0 +1
- else:
- yTickLength= 3 * self.printerScale # lengthens lines for printing
- xTickLength= 3 * self.printerScale
-
- if self._xSpec is not 'none':
- lower, upper = p1[0],p2[0]
- text = 1
- for y, d in [(p1[1], -xTickLength), (p2[1], xTickLength)]: # miny, maxy and tick lengths
- a1 = scale*_Numeric.array([lower, y])+shift
- a2 = scale*_Numeric.array([upper, y])+shift
- dc.DrawLine(a1[0],a1[1],a2[0],a2[1]) # draws upper and lower axis line
- for x, label in xticks:
- pt = scale*_Numeric.array([x, y])+shift
- dc.DrawLine(pt[0],pt[1],pt[0],pt[1] + d) # draws tick mark d units
- if text:
- dc.DrawText(label,pt[0],pt[1])
- text = 0 # axis values not drawn on top side
-
- if self._ySpec is not 'none':
- lower, upper = p1[1],p2[1]
- text = 1
- h = dc.GetCharHeight()
- for x, d in [(p1[0], -yTickLength), (p2[0], yTickLength)]:
- a1 = scale*_Numeric.array([x, lower])+shift
- a2 = scale*_Numeric.array([x, upper])+shift
- dc.DrawLine(a1[0],a1[1],a2[0],a2[1])
- for y, label in yticks:
- pt = scale*_Numeric.array([x, y])+shift
- dc.DrawLine(pt[0],pt[1],pt[0]-d,pt[1])
- if text:
- dc.DrawText(label,pt[0]-dc.GetTextExtent(label)[0],
- pt[1]-0.5*h)
- text = 0 # axis values not drawn on right side
-
- def _xticks(self, *args):
- if self._logscale[0]:
- return self._logticks(*args)
- else:
- return self._ticks(*args)
-
- def _yticks(self, *args):
- if self._logscale[1]:
- return self._logticks(*args)
- else:
- return self._ticks(*args)
-
- def _logticks(self, lower, upper):
- #lower,upper = map(_Numeric.log10,[lower,upper])
- #print 'logticks',lower,upper
- ticks = []
- mag = _Numeric.power(10,_Numeric.floor(lower))
- if upper-lower > 6:
- t = _Numeric.power(10,_Numeric.ceil(lower))
- base = _Numeric.power(10,_Numeric.floor((upper-lower)/6))
- def inc(t):
- return t*base-t
- else:
- t = _Numeric.ceil(_Numeric.power(10,lower)/mag)*mag
- def inc(t):
- return 10**int(_Numeric.floor(_Numeric.log10(t)+1e-16))
- majortick = int(_Numeric.log10(mag))
- while t <= pow(10,upper):
- if majortick != int(_Numeric.floor(_Numeric.log10(t)+1e-16)):
- majortick = int(_Numeric.floor(_Numeric.log10(t)+1e-16))
- ticklabel = '1e%d'%majortick
- else:
- if upper-lower < 2:
- minortick = int(t/pow(10,majortick)+.5)
- ticklabel = '%de%d'%(minortick,majortick)
- else:
- ticklabel = ''
- ticks.append((_Numeric.log10(t), ticklabel))
- t += inc(t)
- if len(ticks) == 0:
- ticks = [(0,'')]
- return ticks
-
- def _ticks(self, lower, upper):
- ideal = (upper-lower)/7.
- log = _Numeric.log10(ideal)
- power = _Numeric.floor(log)
- fraction = log-power
- factor = 1.
- error = fraction
- for f, lf in self._multiples:
- e = _Numeric.fabs(fraction-lf)
- if e < error:
- error = e
- factor = f
- grid = factor * 10.**power
- if self._useScientificNotation and (power > 4 or power < -4):
- format = '%+7.1e'
- elif power >= 0:
- digits = max(1, int(power))
- format = '%' + `digits`+'.0f'
- else:
- digits = -int(power)
- format = '%'+`digits+2`+'.'+`digits`+'f'
- ticks = []
- t = -grid*_Numeric.floor(-lower/grid)
- while t <= upper:
- ticks.append( (t, format % (t,)) )
- t = t + grid
- return ticks
-
- _multiples = [(2., _Numeric.log10(2.)), (5., _Numeric.log10(5.))]
-
-
- def _adjustScrollbars(self):
- if self._sb_ignore:
- self._sb_ignore = False
- return
-
- self._adjustingSB = True
- needScrollbars = False
-
- # horizontal scrollbar
- r_current = self._getXCurrentRange()
- r_max = list(self._getXMaxRange())
- sbfullrange = float(self.sb_hor.GetRange())
-
- r_max[0] = min(r_max[0],r_current[0])
- r_max[1] = max(r_max[1],r_current[1])
-
- self._sb_xfullrange = r_max
-
- unit = (r_max[1]-r_max[0])/float(self.sb_hor.GetRange())
- pos = int((r_current[0]-r_max[0])/unit)
-
- if pos >= 0:
- pagesize = int((r_current[1]-r_current[0])/unit)
-
- self.sb_hor.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
- self._sb_xunit = unit
- needScrollbars = needScrollbars or (pagesize != sbfullrange)
- else:
- self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
-
- # vertical scrollbar
- r_current = self._getYCurrentRange()
- r_max = list(self._getYMaxRange())
- sbfullrange = float(self.sb_vert.GetRange())
-
- r_max[0] = min(r_max[0],r_current[0])
- r_max[1] = max(r_max[1],r_current[1])
-
- self._sb_yfullrange = r_max
-
- unit = (r_max[1]-r_max[0])/sbfullrange
- pos = int((r_current[0]-r_max[0])/unit)
-
- if pos >= 0:
- pagesize = int((r_current[1]-r_current[0])/unit)
- pos = (sbfullrange-1-pos-pagesize)
- self.sb_vert.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
- self._sb_yunit = unit
- needScrollbars = needScrollbars or (pagesize != sbfullrange)
- else:
- self.sb_vert.SetScrollbar(0, 1000, 1000, 1000)
-
- self.SetShowScrollbars(needScrollbars)
- self._adjustingSB = False
-
-#-------------------------------------------------------------------------------
-# Used to layout the printer page
-
-class PlotPrintout(wx.Printout):
- """Controls how the plot is made in printing and previewing"""
- # Do not change method names in this class,
- # we have to override wx.Printout methods here!
- def __init__(self, graph):
- """graph is instance of plotCanvas to be printed or previewed"""
- wx.Printout.__init__(self)
- self.graph = graph
-
- def HasPage(self, page):
- if page == 1:
- return True
- else:
- return False
-
- def GetPageInfo(self):
- return (1, 1, 1, 1) # disable page numbers
-
- def OnPrintPage(self, page):
- dc = self.GetDC() # allows using floats for certain functions
-## print "PPI Printer",self.GetPPIPrinter()
-## print "PPI Screen", self.GetPPIScreen()
-## print "DC GetSize", dc.GetSize()
-## print "GetPageSizePixels", self.GetPageSizePixels()
- # Note PPIScreen does not give the correct number
- # Calulate everything for printer and then scale for preview
- PPIPrinter= self.GetPPIPrinter() # printer dots/inch (w,h)
- #PPIScreen= self.GetPPIScreen() # screen dots/inch (w,h)
- dcSize= dc.GetSize() # DC size
- pageSize= self.GetPageSizePixels() # page size in terms of pixcels
- clientDcSize= self.graph.GetClientSize()
-
- # find what the margins are (mm)
- margLeftSize,margTopSize= self.graph.pageSetupData.GetMarginTopLeft()
- margRightSize, margBottomSize= self.graph.pageSetupData.GetMarginBottomRight()
-
- # calculate offset and scale for dc
- pixLeft= margLeftSize*PPIPrinter[0]/25.4 # mm*(dots/in)/(mm/in)
- pixRight= margRightSize*PPIPrinter[0]/25.4
- pixTop= margTopSize*PPIPrinter[1]/25.4
- pixBottom= margBottomSize*PPIPrinter[1]/25.4
-
- plotAreaW= pageSize[0]-(pixLeft+pixRight)
- plotAreaH= pageSize[1]-(pixTop+pixBottom)
-
- # ratio offset and scale to screen size if preview
- if self.IsPreview():
- ratioW= float(dcSize[0])/pageSize[0]
- ratioH= float(dcSize[1])/pageSize[1]
- pixLeft *= ratioW
- pixTop *= ratioH
- plotAreaW *= ratioW
- plotAreaH *= ratioH
-
- # rescale plot to page or preview plot area
- self.graph._setSize(plotAreaW,plotAreaH)
-
- # Set offset and scale
- dc.SetDeviceOrigin(pixLeft,pixTop)
-
- # Thicken up pens and increase marker size for printing
- ratioW= float(plotAreaW)/clientDcSize[0]
- ratioH= float(plotAreaH)/clientDcSize[1]
- aveScale= (ratioW+ratioH)/2
- self.graph._setPrinterScale(aveScale) # tickens up pens for printing
-
- self.graph._printDraw(dc)
- # rescale back to original
- self.graph._setSize()
- self.graph._setPrinterScale(1)
- self.graph.Redraw() #to get point label scale and shift correct
-
- return True
-
-
-#----------------------------------------------------------------------
-from wx import ImageFromStream, BitmapFromImage
-import cStringIO, zlib
-
-
-def getMagPlusData():
- return zlib.decompress(
-'x\xda\x01*\x01\xd5\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\
-\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\
-\x08\x08|\x08d\x88\x00\x00\x00\xe1IDATx\x9c\xb5U\xd1\x0e\xc4 \x08\xa3n\xff\
-\xff\xc5\xdb\xb8\xa7\xee<\x04\x86gFb\xb2\x88\xb6\x14\x90\x01m\x937m\x8f\x1c\
-\xd7yh\xe4k\xdb\x8e*\x01<\x05\x04\x07F\x1cU\x9d"\x19\x14\\\xe7\xa1\x1e\xf07"\
-\x90H+$?\x04\x16\x9c\xd1z\x04\x00J$m\x06\xdc\xee\x03Hku\x13\xd8C\x16\x84+"O\
-\x1b\xa2\x07\xca"\xb7\xc6sY\xbdD\x926\xf5.\xce\x06!\xd2)x\xcb^\'\x08S\xe4\
-\xe5x&5\xb4[A\xb5h\xb4j=\x9a\xc8\xf8\xecm\xd4\\\x9e\xdf\xbb?\x10\xf0P\x06\
-\x12\xed?=\xb6a\xd8=\xcd\xa2\xc8T\xd5U2t\x11\x95d\xa3"\x9aQ\x9e\x12\xb7M\x19\
-I\x9f\xff\x1e\xd8\xa63#q\xff\x07U\x8b\xd2\xd9\xa7k\xe9\xa1U\x94,\xbf\xe4\x88\
-\xe4\xf6\xaf\x12x$}\x8a\xc2Q\xf1\'\x89\xf2\x9b\xfbKE\xae\xd8\x07+\xd2\xa7c\
-\xdf\x0e\xc3D\x00\x00\x00\x00IEND\xaeB`\x82\xe2ovy' )
-
-def getMagPlusBitmap():
- return BitmapFromImage(getMagPlusImage())
-
-def getMagPlusImage():
- stream = cStringIO.StringIO(getMagPlusData())
- return ImageFromStream(stream)
-
-#----------------------------------------------------------------------
-def getGrabHandData():
- return zlib.decompress(
-'x\xda\x01Z\x01\xa5\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\
-\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\
-\x08\x08|\x08d\x88\x00\x00\x01\x11IDATx\x9c\xb5U\xd1\x12\x830\x08Kh\xff\xff\
-\x8b7\xb3\x97\xd1C\xa4Zw\x93;\x1fJ1\t\x98VJ\x92\xb5N<\x14\x04 I\x00\x80H\xb4\
-\xbd_\x8a9_{\\\x89\xf2z\x02\x18/J\x82\xb5\xce\xed\xfd\x12\xc9\x91\x03\x00_\
-\xc7\xda\x8al\x00{\xfdW\xfex\xf2zeO\x92h\xed\x80\x05@\xa45D\xc5\xb3\x98u\x12\
-\xf7\xab.\xa9\xd0k\x1eK\x95\xbb\x1a]&0\x92\xf0\'\xc6]gI\xda\tsr\xab\x8aI\x1e\
-\\\xe3\xa4\x0e\xb4*`7"\x07\x8f\xaa"x\x05\xe0\xdfo6B\xf3\x17\xe3\x98r\xf1\xaf\
-\x07\xd1Z\'%\x95\x0erW\xac\x8c\xe3\xe0\xfd\xd8AN\xae\xb8\xa3R\x9as>\x11\x8bl\
-yD\xab\x1f\xf3\xec\x1cY\x06\x89$\xbf\x80\xfb\x14\\dw\x90x\x12\xa3+\xeeD\x16%\
-I\xe3\x1c\xb8\xc7c\'\xd5Y8S\x9f\xc3Zg\xcf\x89\xe8\xaao\'\xbbk{U\xfd\xc0\xacX\
-\xab\xbb\xe8\xae\xfa)AEr\x15g\x86(\t\xfe\x19\xa4\xb5\xe9f\xfem\xde\xdd\xbf$\
-\xf8G<>\xa2\xc7\t>\tE\xfc\x8a\xf6\x8dqc\x00\x00\x00\x00IEND\xaeB`\x82\xdb\
-\xd0\x8f\n' )
-
-def getGrabHandBitmap():
- return BitmapFromImage(getGrabHandImage())
-
-def getGrabHandImage():
- stream = cStringIO.StringIO(getGrabHandData())
- return ImageFromStream(stream)
-
-#----------------------------------------------------------------------
-def getHandData():
- return zlib.decompress(
-'x\xda\x01Y\x01\xa6\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\
-\x00\x00\x00\x18\x08\x06\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\
-\x08\x08|\x08d\x88\x00\x00\x01\x10IDATx\x9c\xad\x96\xe1\x02\xc2 \x08\x849\
-\xf5\xfd\x9fx\xdb\xf5\'\x8c!\xa8\xab\xee\x975\xe5\x83\x0b\\@\xa9\xb2\xab\xeb\
-<\xa8\xebR\x1bv\xce\xb4\'\xc1\x81OL\x92\xdc\x81\x0c\x00\x1b\x88\xa4\x94\xda\
-\xe0\x83\x8b\x88\x00\x10\x92\xcb\x8a\xca,K\x1fT\xa1\x1e\x04\xe0f_\n\x88\x02\
-\xf1:\xc3\x83>\x81\x0c\x92\x02v\xe5+\xba\xce\x83\xb7f\xb8\xd1\x9c\x8fz8\xb2*\
-\x93\xb7l\xa8\xe0\x9b\xa06\xb8]_\xe7\xc1\x01\x10U\xe1m\x98\xc9\xefm"ck\xea\
-\x1a\x80\xa0Th\xb9\xfd\x877{V*Qk\xda,\xb4\x8b\xf4;[\xa1\xcf6\xaa4\x9cd\x85X\
-\xb0\r\\j\x83\x9dd\x92\xc3 \xf6\xbd\xab\x0c2\x05\xc0p\x9a\xa7]\xf4\x14\x18]3\
-7\x80}h?\xff\xa2\xa2\xe5e\x90\xact\xaf\xe8B\x14y[4\x83|\x13\xdc\x9e\xeb\x16e\
-\x90\xa7\xf2I\rw\x91\x87d\xd7p\x96\xbd\xd70\x07\xda\xe3v\x9a\xf5\xc5\xb2\xb2\
-+\xb24\xbc\xaew\xedZe\x9f\x02"\xc8J\xdb\x83\xf6oa\xf5\xb7\xa5\xbf8\x12\xffW\
-\xcf_\xbd;\xe4\x8c\x03\x10\xdb^\x00\x00\x00\x00IEND\xaeB`\x82\xd1>\x97B' )
-
-def getHandBitmap():
- return BitmapFromImage(getHandImage())
-
-def getHandImage():
- stream = cStringIO.StringIO(getHandData())
- return ImageFromStream(stream)
-
-
-
-#---------------------------------------------------------------------------
-# if running standalone...
-#
-# ...a sample implementation using the above
-#
-
-def _draw1Objects():
- # 100 points sin function, plotted as green circles
- data1 = 2.*_Numeric.pi*_Numeric.arange(200)/200.
- data1.shape = (100, 2)
- data1[:,1] = _Numeric.sin(data1[:,0])
- markers1 = PolyMarker(data1, legend='Green Markers', colour='green', marker='circle',size=1)
-
- # 50 points cos function, plotted as red line
- data1 = 2.*_Numeric.pi*_Numeric.arange(100)/100.
- data1.shape = (50,2)
- data1[:,1] = _Numeric.cos(data1[:,0])
- lines = PolyLine(data1, legend= 'Red Line', colour='red')
-
- # A few more points...
- pi = _Numeric.pi
- markers2 = PolyMarker([(0., 0.), (pi/4., 1.), (pi/2, 0.),
- (3.*pi/4., -1)], legend='Cross Legend', colour='blue',
- marker='cross')
-
- return PlotGraphics([markers1, lines, markers2],"Graph Title", "X Axis", "Y Axis")
-
-def _draw2Objects():
- # 100 points sin function, plotted as green dots
- data1 = 2.*_Numeric.pi*_Numeric.arange(200)/200.
- data1.shape = (100, 2)
- data1[:,1] = _Numeric.sin(data1[:,0])
- line1 = PolyLine(data1, legend='Green Line', colour='green', width=6, style=wx.DOT)
-
- # 50 points cos function, plotted as red dot-dash
- data1 = 2.*_Numeric.pi*_Numeric.arange(100)/100.
- data1.shape = (50,2)
- data1[:,1] = _Numeric.cos(data1[:,0])
- line2 = PolyLine(data1, legend='Red Line', colour='red', width=3, style= wx.DOT_DASH)
-
- # A few more points...
- pi = _Numeric.pi
- markers1 = PolyMarker([(0., 0.), (pi/4., 1.), (pi/2, 0.),
- (3.*pi/4., -1)], legend='Cross Hatch Square', colour='blue', width= 3, size= 6,
- fillcolour= 'red', fillstyle= wx.CROSSDIAG_HATCH,
- marker='square')
-
- return PlotGraphics([markers1, line1, line2], "Big Markers with Different Line Styles")
-
-def _draw3Objects():
- markerList= ['circle', 'dot', 'square', 'triangle', 'triangle_down',
- 'cross', 'plus', 'circle']
- m=[]
- for i in range(len(markerList)):
- m.append(PolyMarker([(2*i+.5,i+.5)], legend=markerList[i], colour='blue',
- marker=markerList[i]))
- return PlotGraphics(m, "Selection of Markers", "Minimal Axis", "No Axis")
-
-def _draw4Objects():
- # 25,000 point line
- data1 = _Numeric.arange(5e5,1e6,10)
- data1.shape = (25000, 2)
- line1 = PolyLine(data1, legend='Wide Line', colour='green', width=5)
-
- # A few more points...
- markers2 = PolyMarker(data1, legend='Square', colour='blue',
- marker='square')
- return PlotGraphics([line1, markers2], "25,000 Points", "Value X", "")
-
-def _draw5Objects():
- # Empty graph with axis defined but no points/lines
- points=[]
- line1 = PolyLine(points, legend='Wide Line', colour='green', width=5)
- return PlotGraphics([line1], "Empty Plot With Just Axes", "Value X", "Value Y")
-
-def _draw6Objects():
- # Bar graph
- points1=[(1,0), (1,10)]
- line1 = PolyLine(points1, colour='green', legend='Feb.', width=10)
- points1g=[(2,0), (2,4)]
- line1g = PolyLine(points1g, colour='red', legend='Mar.', width=10)
- points1b=[(3,0), (3,6)]
- line1b = PolyLine(points1b, colour='blue', legend='Apr.', width=10)
-
- points2=[(4,0), (4,12)]
- line2 = PolyLine(points2, colour='Yellow', legend='May', width=10)
- points2g=[(5,0), (5,8)]
- line2g = PolyLine(points2g, colour='orange', legend='June', width=10)
- points2b=[(6,0), (6,4)]
- line2b = PolyLine(points2b, colour='brown', legend='July', width=10)
-
- return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
- "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
-def _draw7Objects():
- # Empty graph with axis defined but no points/lines
- x = _Numeric.arange(1,1000,1)
- y1 = 4.5*x**2
- y2 = 2.2*x**3
- points1 = _Numeric.transpose([x,y1])
- points2 = _Numeric.transpose([x,y2])
- line1 = PolyLine(points1, legend='quadratic', colour='blue', width=1)
- line2 = PolyLine(points2, legend='cubic', colour='red', width=1)
- return PlotGraphics([line1,line2], "double log plot", "Value X", "Value Y")
-
-
-class TestFrame(wx.Frame):
- def __init__(self, parent, id, title):
- wx.Frame.__init__(self, parent, id, title,
- wx.DefaultPosition, (600, 400))
-
- # Now Create the menu bar and items
- self.mainmenu = wx.MenuBar()
-
- menu = wx.Menu()
- menu.Append(200, 'Page Setup...', 'Setup the printer page')
- self.Bind(wx.EVT_MENU, self.OnFilePageSetup, id=200)
-
- menu.Append(201, 'Print Preview...', 'Show the current plot on page')
- self.Bind(wx.EVT_MENU, self.OnFilePrintPreview, id=201)
-
- menu.Append(202, 'Print...', 'Print the current plot')
- self.Bind(wx.EVT_MENU, self.OnFilePrint, id=202)
-
- menu.Append(203, 'Save Plot...', 'Save current plot')
- self.Bind(wx.EVT_MENU, self.OnSaveFile, id=203)
-
- menu.Append(205, 'E&xit', 'Enough of this already!')
- self.Bind(wx.EVT_MENU, self.OnFileExit, id=205)
- self.mainmenu.Append(menu, '&File')
-
- menu = wx.Menu()
- menu.Append(206, 'Draw1', 'Draw plots1')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw1, id=206)
- menu.Append(207, 'Draw2', 'Draw plots2')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw2, id=207)
- menu.Append(208, 'Draw3', 'Draw plots3')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw3, id=208)
- menu.Append(209, 'Draw4', 'Draw plots4')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw4, id=209)
- menu.Append(210, 'Draw5', 'Draw plots5')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw5, id=210)
- menu.Append(260, 'Draw6', 'Draw plots6')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw6, id=260)
- menu.Append(261, 'Draw7', 'Draw plots7')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw7, id=261)
-
- menu.Append(211, '&Redraw', 'Redraw plots')
- self.Bind(wx.EVT_MENU,self.OnPlotRedraw, id=211)
- menu.Append(212, '&Clear', 'Clear canvas')
- self.Bind(wx.EVT_MENU,self.OnPlotClear, id=212)
- menu.Append(213, '&Scale', 'Scale canvas')
- self.Bind(wx.EVT_MENU,self.OnPlotScale, id=213)
- menu.Append(214, 'Enable &Zoom', 'Enable Mouse Zoom', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU,self.OnEnableZoom, id=214)
- menu.Append(215, 'Enable &Grid', 'Turn on Grid', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU,self.OnEnableGrid, id=215)
- menu.Append(217, 'Enable &Drag', 'Activates dragging mode', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU,self.OnEnableDrag, id=217)
- menu.Append(220, 'Enable &Legend', 'Turn on Legend', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU,self.OnEnableLegend, id=220)
- menu.Append(222, 'Enable &Point Label', 'Show Closest Point', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU,self.OnEnablePointLabel, id=222)
-
- menu.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit')
- self.Bind(wx.EVT_MENU,self.OnScrUp, id=225)
- menu.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units')
- self.Bind(wx.EVT_MENU,self.OnScrRt, id=230)
- menu.Append(235, '&Plot Reset', 'Reset to original plot')
- self.Bind(wx.EVT_MENU,self.OnReset, id=235)
-
- self.mainmenu.Append(menu, '&Plot')
-
- menu = wx.Menu()
- menu.Append(300, '&About', 'About this thing...')
- self.Bind(wx.EVT_MENU, self.OnHelpAbout, id=300)
- self.mainmenu.Append(menu, '&Help')
-
- self.SetMenuBar(self.mainmenu)
-
- # A status bar to tell people what's happening
- self.CreateStatusBar(1)
-
- self.client = PlotCanvas(self)
- #define the function for drawing pointLabels
- self.client.SetPointLabelFunc(self.DrawPointLabel)
- # Create mouse event for showing cursor coords in status bar
- self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
- # Show closest point when enabled
- self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
-
- self.Show(True)
-
- def DrawPointLabel(self, dc, mDataDict):
- """This is the fuction that defines how the pointLabels are plotted
- dc - DC that will be passed
- mDataDict - Dictionary of data that you want to use for the pointLabel
-
- As an example I have decided I want a box at the curve point
- with some text information about the curve plotted below.
- Any wxDC method can be used.
- """
- # ----------
- dc.SetPen(wx.Pen(wx.BLACK))
- dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
-
- sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
- dc.DrawRectangle( sx-5,sy-5, 10, 10) #10by10 square centered on point
- px,py = mDataDict["pointXY"]
- cNum = mDataDict["curveNum"]
- pntIn = mDataDict["pIndex"]
- legend = mDataDict["legend"]
- #make a string to display
- s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
- dc.DrawText(s, sx , sy+1)
- # -----------
-
- def OnMouseLeftDown(self,event):
- s= "Left Mouse Down at Point: (%.4f, %.4f)" % self.client._getXY(event)
- self.SetStatusText(s)
- event.Skip() #allows plotCanvas OnMouseLeftDown to be called
-
- def OnMotion(self, event):
- #show closest point (when enbled)
- if self.client.GetEnablePointLabel() == True:
- #make up dict with info for the pointLabel
- #I've decided to mark the closest point on the closest curve
- dlst= self.client.GetClosestPoint( self.client._getXY(event), pointScaled= True)
- if dlst != []: #returns [] if none
- curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
- #make up dictionary to pass to my user function (see DrawPointLabel)
- mDataDict= {"curveNum":curveNum, "legend":legend, "pIndex":pIndex,\
- "pointXY":pointXY, "scaledXY":scaledXY}
- #pass dict to update the pointLabel
- self.client.UpdatePointLabel(mDataDict)
- event.Skip() #go to next handler
-
- def OnFilePageSetup(self, event):
- self.client.PageSetup()
-
- def OnFilePrintPreview(self, event):
- self.client.PrintPreview()
-
- def OnFilePrint(self, event):
- self.client.Printout()
-
- def OnSaveFile(self, event):
- self.client.SaveFile()
-
- def OnFileExit(self, event):
- self.Close()
-
- def OnPlotDraw1(self, event):
- self.resetDefaults()
- self.client.Draw(_draw1Objects())
-
- def OnPlotDraw2(self, event):
- self.resetDefaults()
- self.client.Draw(_draw2Objects())
-
- def OnPlotDraw3(self, event):
- self.resetDefaults()
- self.client.SetFont(wx.Font(10,wx.SCRIPT,wx.NORMAL,wx.NORMAL))
- self.client.SetFontSizeAxis(20)
- self.client.SetFontSizeLegend(12)
- self.client.SetXSpec('min')
- self.client.SetYSpec('none')
- self.client.Draw(_draw3Objects())
-
- def OnPlotDraw4(self, event):
- self.resetDefaults()
- drawObj= _draw4Objects()
- self.client.Draw(drawObj)
-## # profile
-## start = _time.clock()
-## for x in range(10):
-## self.client.Draw(drawObj)
-## print "10 plots of Draw4 took: %f sec."%(_time.clock() - start)
-## # profile end
-
- def OnPlotDraw5(self, event):
- # Empty plot with just axes
- self.resetDefaults()
- drawObj= _draw5Objects()
- # make the axis X= (0,5), Y=(0,10)
- # (default with None is X= (-1,1), Y= (-1,1))
- self.client.Draw(drawObj, xAxis= (0,5), yAxis= (0,10))
-
- def OnPlotDraw6(self, event):
- #Bar Graph Example
- self.resetDefaults()
- #self.client.SetEnableLegend(True) #turn on Legend
- #self.client.SetEnableGrid(True) #turn on Grid
- self.client.SetXSpec('none') #turns off x-axis scale
- self.client.SetYSpec('auto')
- self.client.Draw(_draw6Objects(), xAxis= (0,7))
-
- def OnPlotDraw7(self, event):
- #log scale example
- self.resetDefaults()
- self.client.setLogScale((True,True))
- self.client.Draw(_draw7Objects())
-
- def OnPlotRedraw(self,event):
- self.client.Redraw()
-
- def OnPlotClear(self,event):
- self.client.Clear()
-
- def OnPlotScale(self, event):
- if self.client.last_draw != None:
- graphics, xAxis, yAxis= self.client.last_draw
- self.client.Draw(graphics,(1,3.05),(0,1))
-
- def OnEnableZoom(self, event):
- self.client.SetEnableZoom(event.IsChecked())
- self.mainmenu.Check(217, not event.IsChecked())
-
- def OnEnableGrid(self, event):
- self.client.SetEnableGrid(event.IsChecked())
-
- def OnEnableDrag(self, event):
- self.client.SetEnableDrag(event.IsChecked())
- self.mainmenu.Check(214, not event.IsChecked())
-
- def OnEnableLegend(self, event):
- self.client.SetEnableLegend(event.IsChecked())
-
- def OnEnablePointLabel(self, event):
- self.client.SetEnablePointLabel(event.IsChecked())
-
- def OnScrUp(self, event):
- self.client.ScrollUp(1)
-
- def OnScrRt(self,event):
- self.client.ScrollRight(2)
-
- def OnReset(self,event):
- self.client.Reset()
-
- def OnHelpAbout(self, event):
- from wx.lib.dialogs import ScrolledMessageDialog
- about = ScrolledMessageDialog(self, __doc__, "About...")
- about.ShowModal()
-
- def resetDefaults(self):
- """Just to reset the fonts back to the PlotCanvas defaults"""
- self.client.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
- self.client.SetFontSizeAxis(10)
- self.client.SetFontSizeLegend(7)
- self.client.setLogScale((False,False))
- self.client.SetXSpec('auto')
- self.client.SetYSpec('auto')
-
-
-
-def __test():
-
- class MyApp(wx.App):
- def OnInit(self):
- wx.InitAllImageHandlers()
- frame = TestFrame(None, -1, "PlotCanvas")
- #frame.Show(True)
- self.SetTopWindow(frame)
- return True
-
-
- app = MyApp(0)
- app.MainLoop()
-
-if __name__ == '__main__':
- __test()