From 48023e151f006927dc8be260a63360793678b9cb Mon Sep 17 00:00:00 2001
From: Robin Dunn
Date: Tue, 9 Nov 2004 18:03:21 +0000
Subject: [PATCH] Backed out Oliver's PyPlot changes at Gordon's request.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@30390 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
wxPython/docs/CHANGES.html | 12 -
wxPython/docs/CHANGES.txt | 8 -
wxPython/wx/lib/plot.py | 596 ++++++++-----------------------------
3 files changed, 116 insertions(+), 500 deletions(-)
diff --git a/wxPython/docs/CHANGES.html b/wxPython/docs/CHANGES.html
index d9f81d366c..992e4b380c 100644
--- a/wxPython/docs/CHANGES.html
+++ b/wxPython/docs/CHANGES.html
@@ -163,18 +163,6 @@ OSX build. Other platforms will raise an exception if you try to use
it.
wxPython on OSX can now be built in Unicode mode, can support multiple
version installs, and comes with an uninstaller script.
-
diff --git a/wxPython/docs/CHANGES.txt b/wxPython/docs/CHANGES.txt
index c36d8cbd91..41c070fa35 100644
--- a/wxPython/docs/CHANGES.txt
+++ b/wxPython/docs/CHANGES.txt
@@ -192,14 +192,6 @@ it.
wxPython on OSX can now be built in Unicode mode, can support multiple
version installs, and comes with an uninstaller script.
-PyPlot updates from Oliver Schoenborn:
- - Factored out "draw command" so extensions easier to implement: clean
- separation b/w user's "draw" (Draw) and internal "draw" (_draw)
- - Added ability to define your own ticks at PlotCanvas.Draw
- - Added better bar charts: PolyBar class and getBarTicksGen()
- - Put legend writing for a Poly* into Poly* class where it belongs
- - If no legend specified or exists, no gap created
-
diff --git a/wxPython/wx/lib/plot.py b/wxPython/wx/lib/plot.py
index 1462407433..5fabf66063 100644
--- a/wxPython/wx/lib/plot.py
+++ b/wxPython/wx/lib/plot.py
@@ -34,14 +34,6 @@
# - Added functions GetClosestPoints (all curves) and GetClosestPoint (only closest curve)
# can be in either user coords or screen coords.
#
-# Nov 2004 Oliver Schoenborn (oliver.schoenborn@utoronto.ca) with
-# valuable input from and testing by Gary)
-# - Factored out "draw command" so extensions easier to implement: clean
-# separation b/w user's "draw" (Draw) and internal "draw" (_draw)
-# - Added ability to define your own ticks at PlotCanvas.Draw
-# - Added better bar charts: PolyBar class and getBarTicksGen()
-# - Put legend writing for a Poly* into Poly* class where it belongs
-# - If no legend specified or exists, no gap created
#
"""
@@ -159,20 +151,6 @@ class PolyPoints:
self.currentShift= shift
# else unchanged use the current scaling
- def drawLegend(self, dc, printerScale, symWidth, hgap, textHeight, x0, y0):
- legend = self.getLegend()
- if legend != '':
- self._drawLegendSym(dc, printerScale, symWidth, x0, y0)
- dc.DrawText(legend, x0+symWidth+hgap, y0-textHeight/2)
- return True
-
- return False
-
- def _drawLegendSym(self, dc, printerScale, symWidth, x0, y0):
- # default:
- pnt= (x0+symWidth/2., y0)
- self.draw(dc, printerScale, coord= _Numeric.array([pnt]))
-
def getLegend(self):
return self.attributes['legend']
@@ -223,7 +201,7 @@ class PolyLine(PolyPoints):
colour = self.attributes['colour']
width = self.attributes['width'] * printerScale
style= self.attributes['style']
- pen = wx.Pen(colour, width, style)
+ pen = wx.Pen(wx.NamedColour(colour), width, style)
pen.SetCap(wx.CAP_BUTT)
dc.SetPen(pen)
if coord == None:
@@ -231,11 +209,6 @@ class PolyLine(PolyPoints):
else:
dc.DrawLines(coord) # draw legend line
- def _drawLegendSym(self, dc, printerScale, symWidth, x0, y0):
- pnt1= (x0, y0)
- pnt2= (x0+symWidth/1.5, y0)
- self.draw(dc, printerScale, coord= _Numeric.array([pnt1,pnt2]))
-
def getSymExtent(self, printerScale):
"""Width and Height of Marker"""
h= self.attributes['width'] * printerScale
@@ -264,7 +237,7 @@ class PolyMarker(PolyPoints):
'colour'= 'black', - wx.Pen Colour any wx.NamedColour
'width'= 1, - Pen width
'size'= 2, - Marker size
- 'fillcolour'= same as colour, - wx.Brush Colour
+ '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
@@ -289,11 +262,11 @@ class PolyMarker(PolyPoints):
fillstyle = self.attributes['fillstyle']
marker = self.attributes['marker']
- dc.SetPen(wx.Pen(colour, width))
+ dc.SetPen(wx.Pen(wx.NamedColour(colour), width))
if fillcolour:
- dc.SetBrush(wx.Brush(fillcolour,fillstyle))
+ dc.SetBrush(wx.Brush(wx.NamedColour(fillcolour),fillstyle))
else:
- dc.SetBrush(wx.Brush(colour, fillstyle))
+ dc.SetBrush(wx.Brush(wx.NamedColour(colour), fillstyle))
if coord == None:
self._drawmarkers(dc, self.scaled, marker, size)
else:
@@ -351,148 +324,6 @@ class PolyMarker(PolyPoints):
lines= _Numeric.concatenate((coords,coords),axis=1)+f
dc.DrawLineList(lines.astype(_Numeric.Int32))
-class PolyBar(PolyPoints):
- """Class to define one or more bars for bar chart. All bars in a
- PolyBar share same style etc.
- - All methods except __init__ are private.
- """
-
- _attributes = {'colour': 'black',
- 'width': 1,
- 'size': 2,
- 'legend': '',
- 'fillcolour': None,
- 'fillstyle': wx.SOLID}
-
- def __init__(self, points, **attr):
- """Creates several bars.
- points - sequence (array, tuple or list) of (x,y) points
- indicating *top left* corner of bar. Can also be
- just one (x,y) tuple if labels attribute is just
- a string (not a list)
- **attr - key word attributes
- Defaults:
- 'colour'= wx.BLACK, - wx.Pen Colour
- 'width'= 1, - Pen width
- 'size'= 2, - Bar size
- 'fillcolour'= same as colour, - wx.Brush Colour
- 'fillstyle'= wx.SOLID, - wx.Brush fill style (use wx.TRANSPARENT for no fill)
- 'legend'= '' - string used for PolyBar legend
- 'labels'= '' - string if only one point, or list of labels, one per point
- Note that if no legend is given the label is used.
- """
- barPoints = [(0,0)] # needed so height of bar can be determined
- self.labels = []
- # add labels and points to above data members:
- try:
- labels = attr['labels']
- del attr['labels']
- except:
- labels = None
- if labels is None: # figure out if we have 1 point or many
- try:
- points[0][0] # ok: we have a seq of points
- barPoints.extend(points)
- except: # failed, so have just one point:
- barPoints.append(points)
- elif isinstance(labels, list) or isinstance(labels, tuple):
- # labels is a sequence so points must be too, with same length:
- self.labels.extend(labels)
- barPoints.extend(points)
- if len(labels) != len(points):
- msg = "%d bar labels missing" % (len(points)-len(labels))
- raise ValueError, msg
- else: # label given, but only one, so must be only one point:
- barPoints.append(points)
- self.labels.append(labels)
-
- PolyPoints.__init__(self, barPoints, 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']
-
- dc.SetPen(wx.Pen(colour,int(width)))
- if fillcolour:
- dc.SetBrush(wx.Brush(fillcolour,fillstyle))
- else:
- dc.SetBrush(wx.Brush(colour, fillstyle))
- if coord == None:
- self._drawbar(dc, self.scaled, size)
- else:
- self._drawLegendMarker(dc, coord, size) #draw legend marker
-
- def _drawLegendMarker(self, dc, coord, size):
- fact= 10
- wh= 2*fact
- rect= _Numeric.zeros((len(coord),4),_Numeric.Float)+[0.0,0.0,wh,wh]
- rect[:,0:2]= coord-[fact,fact]
- dc.DrawRectangleList(rect.astype(_Numeric.Int32))
-
- def offset(self, heights, howMany = 1, shift=0.25):
- """Return a list of points, where x's are those of this bar,
- shifted by shift*howMany (in caller's units, not pixel units), and
- heights are given in heights."""
- points = [(point[0][0]+shift*howMany,point[1]) for point
- in zip(self.points[1:], heights)]
- return points
-
- def getSymExtent(self, printerScale):
- """Width and Height of bar symbol"""
- s= 5*self.attributes['size'] * printerScale
- return (s,s)
-
- def getLegend(self):
- """This returns a comma-separated list of bar labels (one string)"""
- try: legendItem = self.attributes['legend']
- except:
- legendItem = ", ".join(self.labels)
- return legendItem
-
- def _drawbar(self, dc, coords, size=1):
- width = 5.0*size
- y0 = coords[0][1]
- bars = []
- for coord in coords[1:]:
- x = coord[0] # - width/2 not as good
- y = coord[1]
- height = int(y0) - int(y)
- bars.append((int(x),int(y),int(width),int(height)))
- #print x,y,width, height
- dc.DrawRectangleList(bars)
-
- def getTicks(self):
- """Get the list of (tick pos, label) pairs for this PolyBar.
- It is unlikely you need this, but it is used by
- getBarTicksGen."""
- ticks = []
- # remember to skip first point:
- for point, label in zip(self.points[1:], self.labels):
- ticks.append((point[0], label))
- return ticks
-
-def getBarTicksGen(*polyBars):
- """Get a function that can be given as xTicks argument when
- calling PolyCanvas.Draw() when plotting one or more PolyBar.
- The returned function allows the bar chart to have the bars
- labelled at the x axis. """
- ticks = []
- for polyBar in polyBars:
- ticks.extend(polyBar.getTicks())
-
- def tickGenerator(lower,upper,gg,ff):
- tickPairs = []
- for tick in ticks:
- if lower <= tick[0] < upper:
- tickPairs.append(tick)
- return tickPairs
-
- return tickGenerator
-
-
class PlotGraphics:
"""Container to hold PolyXXX objects and graph labels
- All methods except __init__ are private.
@@ -628,7 +459,7 @@ class PlotCanvas(wx.Window):
self._hasDragged= False
# Drawing Variables
- self._drawCmd = None
+ self.last_draw = None
self._pointScale= 1
self._pointShift= 0
self._xSpec= 'auto'
@@ -844,16 +675,16 @@ class PlotCanvas(wx.Window):
def Reset(self):
"""Unzoom the plot."""
self.last_PointLabel = None #reset pointLabel
- if self.BeenDrawn():
- self._drawCmd.resetAxes(self._xSpec, self._ySpec)
- self._draw()
+ 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.BeenDrawn():
- self._drawCmd.scrollAxisX(units, self._xSpec)
- self._draw()
+ 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."""
@@ -907,43 +738,33 @@ class PlotCanvas(wx.Window):
def GetXMaxRange(self):
"""Returns (minX, maxX) x-axis range for displayed graph"""
- return self._drawCmd.getXMaxRange()
+ 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):
"""Returns (minY, maxY) y-axis range for displayed graph"""
- return self._drawCmd.getYMaxRange()
+ 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):
"""Returns (minX, maxX) x-axis for currently displayed portion of graph"""
- return self._drawCmd.xAxis
+ return self.last_draw[1]
def GetYCurrentRange(self):
"""Returns (minY, maxY) y-axis for currently displayed portion of graph"""
- return self._drawCmd.yAxis
+ return self.last_draw[2]
- def BeenDrawn(self):
- """Return true if Draw() has been called once, false otherwise."""
- return self._drawCmd is not None
-
- def Draw(self, graphics, xAxis = None, yAxis = None, dc = None,
- xTicks = None, yTicks = None):
+ def Draw(self, graphics, xAxis = None, yAxis = None, dc = None):
"""Draw objects in graphics with specified x and y axis.
graphics- instance of PlotGraphics with list of PolyXXX objects
xAxis - tuple with (min, max) axis range to view
yAxis - same as xAxis
dc - drawing context - doesn't have to be specified.
- If it's not, the offscreen buffer is used.
- xTicks and yTicks - can be either
- - a list of floats where the ticks should be, only visible
- ticks will be used;
- - a function that generates a list of (tick, label) pairs;
- The function must be of form func(lower, upper, grid,
- format), and the pairs must contain only ticks between lower and
- upper. Function can use grid and format that are computed by
- PlotCanvas, if desired, where grid is the spacing between ticks,
- and format is the format string for how tick labels
- will appear. See tickGeneratorDefault and
- tickGeneratorFromList for examples of such functions.
+ If it's not, the offscreen buffer is used
"""
# check Axis is either tuple or none
if type(xAxis) not in [type(None),tuple]:
@@ -951,18 +772,12 @@ class PlotCanvas(wx.Window):
if type(yAxis) not in [type(None),tuple]:
raise TypeError, "yAxis should be None or (minY,maxY)"
- self._drawCmd = DrawCmd(graphics, xAxis, yAxis,
- self._xSpec, self._ySpec,
- xTicks, yTicks)
- self._draw(dc)
-
- def _draw(self, dc=None):
- """Implement the draw command, with dc if given, using any new
- settings (legend toggled, axis specs, zoom, etc). """
- assert self._drawCmd is not None
-
# check case for axis = (a,b) where a==b caused by improper zooms
- if self._drawCmd.isEmpty():
+ if xAxis != None:
+ if xAxis[0] == xAxis[1]:
+ return
+ if yAxis != None:
+ if yAxis[0] == yAxis[1]:
return
if dc == None:
@@ -976,15 +791,33 @@ class PlotCanvas(wx.Window):
# 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, xAxis, yAxis) # saves most recient values
+
# Get ticks and textExtents for axis if required
if self._xSpec is not 'none':
- xticks = self._drawCmd.getTicksX()
+ xticks = self._ticks(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._drawCmd.getTicksY()
+ yticks = self._ticks(yAxis[0], yAxis[1])
yTextExtentBottom= dc.GetTextExtent(yticks[0][1])
yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
yTextExtent= (max(yTextExtentBottom[0],yTextExtentTop[0]),
@@ -994,12 +827,10 @@ class PlotCanvas(wx.Window):
yTextExtent= (0,0) # No text for ticks
# TextExtents for Title and Axis Labels
- graphics = self._drawCmd.graphics
titleWH, xLabelWH, yLabelWH= self._titleLablesWH(dc, graphics)
# TextExtents for Legend
- legendBoxWH, legendSymExt, legendHGap, legendTextExt \
- = self._legendWH(dc, graphics)
+ 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
@@ -1025,11 +856,7 @@ class PlotCanvas(wx.Window):
# drawing legend makers and text
if self._legendEnabled:
- self._drawLegend(dc,graphics,rhsW,topH,legendBoxWH,
- legendSymExt, legendHGap, legendTextExt)
-
- #sizes axis to axis type, create lower left and upper right corners of plot
- p1, p2 = self._drawCmd.getBoundingBox()
+ 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))
@@ -1054,15 +881,16 @@ class PlotCanvas(wx.Window):
def Redraw(self, dc= None):
"""Redraw the existing plot."""
- if self.BeenDrawn():
- self._draw(dc)
+ 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), self._Buffer)
dc.Clear()
- self._drawCmd = None
+ self.last_draw = None
def Zoom(self, Center, Ratio):
""" Zoom on the plot
@@ -1070,19 +898,15 @@ class PlotCanvas(wx.Window):
Zooms by the Ratio = (Xratio, Yratio) given
"""
self.last_PointLabel = None #reset maker
- if self.BeenDrawn():
- self._drawCmd.zoom(Center, Ratio)
- self._draw()
+ 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 ChangeAxes(self, xAxis, yAxis):
- """Change axes specified at last Draw() command. If
- Draw() never called yet, RuntimeError raised. """
- if not self.BeenDrawn():
- raise RuntimeError, "Must call Draw() at least once"
- self._drawCmd.changeAxisX(xAxis, self._xSpec)
- self._drawCmd.changeAxisY(yAxis, self._ySpec)
- self._draw()
-
def GetClosestPoints(self, pntXY, pointScaled= True):
"""Returns list with
[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
@@ -1093,11 +917,19 @@ class PlotCanvas(wx.Window):
if pointScaled == True based on screen coords
if pointScaled == False based on user coords
"""
- if self.BeenDrawn():
- return self._drawCmd.getClosestPoints(pntXY, pointScaled)
- else:
+ 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 GetClosetPoint(self, pntXY, pointScaled= True):
"""Returns list with
@@ -1146,7 +978,6 @@ class PlotCanvas(wx.Window):
# event handlers **********************************
def OnMotion(self, event):
- if not self.BeenDrawn(): return
if self._zoomEnabled and event.LeftIsDown():
if self._hasDragged:
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
@@ -1156,11 +987,9 @@ class PlotCanvas(wx.Window):
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # add new
def OnMouseLeftDown(self,event):
- if self.BeenDrawn():
- self._zoomCorner1[0], self._zoomCorner1[1]= self.GetXY(event)
+ self._zoomCorner1[0], self._zoomCorner1[1]= self.GetXY(event)
def OnMouseLeftUp(self, event):
- if not self.BeenDrawn(): return
if self._zoomEnabled:
if self._hasDragged == True:
self._drawRubberBand(self._zoomCorner1, self._zoomCorner2) # remove old
@@ -1169,9 +998,8 @@ class PlotCanvas(wx.Window):
minX, minY= _Numeric.minimum( self._zoomCorner1, self._zoomCorner2)
maxX, maxY= _Numeric.maximum( self._zoomCorner1, self._zoomCorner2)
self.last_PointLabel = None #reset pointLabel
- self._drawCmd.changeAxisX((minX,maxX))
- self._drawCmd.changeAxisY((minY,maxY))
- self._draw()
+ 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)
@@ -1206,10 +1034,11 @@ class PlotCanvas(wx.Window):
self.last_PointLabel = None #reset pointLabel
- if self.BeenDrawn():
- self._draw()
- else:
+ 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"""
@@ -1237,8 +1066,9 @@ class PlotCanvas(wx.Window):
def _printDraw(self, printDC):
"""Used for printing."""
- if self.BeenDrawn():
- self._draw(printDC)
+ 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"""
@@ -1257,21 +1087,30 @@ class PlotCanvas(wx.Window):
dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV) #(NOT src) XOR dst
- def _drawLegend(self,dc,graphics,rhsW,topH,legendBoxWH, legendSymExt, hgap, legendTextExt):
+ 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))
- legendIdx = 0
- for gr in graphics:
- s = legendIdx * lineHeight
- drew = gr.drawLegend(dc, self.printerScale,
- legendSymExt[0], hgap, legendTextExt[1],
- trhc[0]+legendLHS, trhc[1]+s+lineHeight/2.)
- if drew:
- legendIdx += 1
+ 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):
@@ -1290,25 +1129,23 @@ class PlotCanvas(wx.Window):
"""Returns the size in screen units for legend box"""
if self._legendEnabled != True:
legendBoxWH= symExt= txtExt= (0,0)
- hgap= 0
else:
- #find max symbol size and gap b/w symbol and text
+ # find max symbol size
symExt= graphics.getSymExtent(self.printerScale)
- hgap = symExt[0]*0.1
# 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]+hgap+txtExt[0]
+ 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, hgap, txtExt)
+ return (legendBoxWH, symExt, txtExt)
def _drawRubberBand(self, corner1, corner2):
"""Draws/erases rect box from corner1 to corner2"""
@@ -1386,7 +1223,7 @@ class PlotCanvas(wx.Window):
def _drawAxes(self, dc, p1, p2, scale, shift, xticks, yticks):
penWidth= self.printerScale # increases thickness for printing only
- dc.SetPen(wx.Pen(wx.BLACK, penWidth))
+ dc.SetPen(wx.Pen(wx.NamedColour('BLACK'), penWidth))
# set length of tick marks--long ones make grid
if self._gridEnabled:
@@ -1427,147 +1264,7 @@ class PlotCanvas(wx.Window):
pt[1]-0.5*h)
text = 0 # axis values not drawn on right side
-
-class DrawCmd:
- """Represent a "draw" command, i.e. the one given in call to
- PlotCanvas.Draw(). The axis specs are not saved to preserve
- backward compatibility: they could be specified before the
- first Draw() command."""
- def __init__(self, graphics, xAxis, yAxis, xSpec, ySpec, xTicks, yTicks):
- """Same args as PlotCanvas.Draw(), plus axis specs."""
- self.graphics = graphics
- self.xTickGen = xTicks
- self.yTickGen = yTicks
- self.xAxisInit, self.yAxisInit = xAxis, yAxis
-
- self.xAxis = None
- self.yAxis = None
- self.changeAxisX(xAxis, xSpec)
- self.changeAxisY(yAxis, ySpec)
- assert self.xAxis is not None
- assert self.yAxis is not None
-
- def isEmpty(self):
- """Return true if either axis has 0 size."""
- if self.xAxis[0] == self.xAxis[1]:
- return True
- if self.yAxis[0] == self.yAxis[1]:
- return True
-
- return False
-
- def resetAxes(self, xSpec, ySpec):
- """Reset the axes to what they were initially, using axes specs given."""
- self.changeAxisX(self.xAxisInit, xSpec)
- self.changeAxisY(self.yAxisInit, ySpec)
-
- def getBoundingBox(self):
- """Returns p1, p2 (two pairs)"""
- p1 = _Numeric.array((self.xAxis[0], self.yAxis[0]))
- p2 = _Numeric.array((self.xAxis[1], self.yAxis[1]))
- return p1, p2
-
- def getClosestPoints(self, pntXY, pointScaled=True):
- ll = []
- for curveNum,obj in enumerate(self.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)
- ll.append(cn)
- return ll
-
- def scrollAxisX(self, units, xSpec):
- """Scroll y axis by units, using ySpec for axis spec."""
- self.changeAxisX((self.xAxis[0]+units, self.xAxis[1]+units), xSpec)
-
- def scrollAxisY(self, units, ySpec):
- """Scroll y axis by units, using ySpec for axis spec."""
- self.changeAxisY((self.yAxis[0]+units, self.yAxis[1]+units), ySpec)
-
- def changeAxisX(self, xAxis=None, xSpec=None):
- """Change the x axis to new given, or if None use ySpec to get it. """
- assert type(xAxis) in [type(None),tuple]
- if xAxis is None:
- p1, p2 = self.graphics.boundingBox() #min, max points of graphics
- self.xAxis = self._axisInterval(xSpec, p1[0], p2[0]) #in user units
- else:
- self.xAxis = xAxis
-
- def changeAxisY(self, yAxis=None, ySpec=None):
- """Change the y axis to new given, or if None use ySpec to get it. """
- assert type(yAxis) in [type(None),tuple]
- if yAxis is None:
- p1, p2 = self.graphics.boundingBox() #min, max points of graphics
- self.yAxis = self._axisInterval(ySpec, p1[1], p2[1])
- else:
- self.yAxis = yAxis
-
- def zoom(self, center, ratio):
- """Zoom to center and ratio."""
- x,y = Center
- w = (self.xAxis[1] - self.xAxis[0]) * Ratio[0]
- h = (self.yAxis[1] - self.yAxis[0]) * Ratio[1]
- self.xAxis = ( x - w/2, x + w/2 )
- self.yAxis = ( y - h/2, y + h/2 )
-
- def getXMaxRange(self):
- p1, p2 = self.graphics.boundingBox() #min, max points of graphics
- return self._axisInterval(self._xSpec, p1[0], p2[0]) #in user units
-
- def getYMaxRange(self):
- p1, p2 = self.graphics.boundingBox() #min, max points of graphics
- return self._axisInterval(self._ySpec, p1[1], p2[1]) #in user units
-
- def getTicksX(self):
- """Get the ticks along y axis. Actually pairs of (t, str)."""
- xticks = self._ticks(self.xAxis[0], self.xAxis[1], self.xTickGen)
- if xticks == []: # try without the generator
- xticks = self._ticks(self.xAxis[0], self.xAxis[1])
- return xticks
-
- def getTicksY(self):
- """Get the ticks along y axis. Actually pairs of (t, str)."""
- yticks = self._ticks(self.yAxis[0], self.yAxis[1], self.yTickGen)
- if yticks == []: # try without the generator
- yticks = self._ticks(self.yAxis[0], self.yAxis[1])
- return yticks
-
- 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 _ticks(self, lower, upper, generator=None):
- """Get ticks between lower and upper, using generator if given. """
+ def _ticks(self, lower, upper):
ideal = (upper-lower)/7.
log = _Numeric.log10(ideal)
power = _Numeric.floor(log)
@@ -1588,35 +1285,16 @@ class DrawCmd:
else:
digits = -int(power)
format = '%'+`digits+2`+'.'+`digits`+'f'
-
- if generator is None:
- return tickGeneratorDefault(lower, upper, grid, format)
- elif isinstance(generator, list):
- return tickGeneratorFromList(generator, lower, upper, format)
- else:
- return generator(lower, upper, grid, format)
+ 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 tickGeneratorDefault(lower, upper, grid, format):
- """Default tick generator used by PlotCanvas.Draw() if not specified."""
- ticks = []
- t = -grid*_Numeric.floor(-lower/grid)
- while t <= upper:
- ticks.append( (t, format % (t,)) )
- t = t + grid
- return ticks
-
-def tickGeneratorFromList(ticks, lower, upper, format):
- """Tick generator used by PlotCanvas.Draw() if
- a list is given for xTicks or yTicks. """
- tickPairs = []
- for tick in ticks:
- if lower <= tick <= upper:
- tickPairs.append((tick, format % tick))
- return tickPairs
-
#-------------------------------------------------------------------------------
# Used to layout the printer page
@@ -1791,28 +1469,6 @@ def _draw6Objects():
return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
"Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
-def _draw7Objects():
- # four bars of various styles colors and sizes
- bar1 = PolyBar((0,1), colour='green', size=4, labels='Green bar')
- bar2 = PolyBar((2,1), colour='red', size=7, labels='Red bar',
- fillstyle=wx.CROSSDIAG_HATCH)
- bar3 = PolyBar([(1,3),(3,4)], colour='blue',
- labels=['Blue bar 1', 'Blue bar 2'],
- fillstyle=wx.TRANSPARENT)
- bars = [bar1, bar2, bar3]
- return PlotGraphics(bars,
- "Graph Title", "Bar names", "Bar height"), getBarTicksGen(*bars)
-
-def _draw8Objects():
- # four bars in two groups, overlayed to a line plot
- x1,x2 = 0.0,0.5
- bar1 = PolyBar([(x1,1.),(x1+2,2.)], colour='green', size=4,
- legend="1998", fillstyle=wx.CROSSDIAG_HATCH)
- bar2 = PolyBar(bar1.offset([1.2,2.5]), colour='red', size=7,
- legend="2000", labels=['cars','trucks'])
- line1 = PolyLine([(x1,1.5), (x2,1.2), (x1+2,1), (x2+2,2)], colour='blue')
- return PlotGraphics([bar1, bar2, line1],
- "Graph Title", "Bar names", "Bar height"), getBarTicksGen(bar1, bar2)
class TestFrame(wx.Frame):
def __init__(self, parent, id, title):
@@ -1852,10 +1508,6 @@ class TestFrame(wx.Frame):
self.Bind(wx.EVT_MENU,self.OnPlotDraw5, id=210)
menu.Append(260, 'Draw6', 'Draw plots6')
self.Bind(wx.EVT_MENU,self.OnPlotDraw6, id=260)
- menu.Append(261, 'Draw7', 'Draw plots7')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw7, id=261)
- menu.Append(262, 'Draw8', 'Draw plots8')
- self.Bind(wx.EVT_MENU,self.OnPlotDraw8, id=262)
menu.Append(211, '&Redraw', 'Redraw plots')
@@ -2006,22 +1658,6 @@ class TestFrame(wx.Frame):
self.client.SetYSpec('auto')
self.client.Draw(_draw6Objects(), xAxis= (0,7))
- def OnPlotDraw7(self, event):
- #Bar Graph and custom ticks Example, using PolyBar
- self.resetDefaults()
- bars, tickGenerator = _draw7Objects()
- #note that yTicks is not necessary here, used only
- #to show off custom ticks
- self.client.Draw(bars, xAxis=(0,4),yAxis = (0,5),
- xTicks=tickGenerator, yTicks=[0,1,3])
-
- def OnPlotDraw8(self, event):
- #Bar Graph and custom ticks Example, using PolyBar
- self.resetDefaults()
- plot, tickGenerator = _draw8Objects()
- self.client.Draw(plot, xAxis=(0,4),yAxis = (0,5), xTicks=tickGenerator)
-
-
def OnPlotRedraw(self,event):
self.client.Redraw()
@@ -2029,9 +1665,9 @@ class TestFrame(wx.Frame):
self.client.Clear()
def OnPlotScale(self, event):
- if self.client.BeenDrawn():
- self.client.ChangeAxes((1,3.05),(0,1))
-
+ 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())
--
2.45.2