Hello everyone,I'm running matplotlib 0.91.2, python 2.5.1, under Windows, and I have the following problem:
I have a function, svgf(), that takes some xml data as input, generates a figure from it, and then saves it to a file or returns the svg data as another bunch of xml.
It appears that when I generate a polar plot, or a figure with >1 subplots, calling close() at the end does not dispose of the figure. This means that the next time svgf() is called, the new graph is drawn underneath what was already there.
I'm attaching three files: - svg_factory.py, containing the svgf() function, - test_loop.py, which calls svgf(), and - out.xml, some example data passed by the test_loop script to svgf().I've tried all sorts of things to dispose of the figure at the end, including using the garbage collector to find any referents to the figure and del them, to no avail.
My Python is a bit rusty, and this is my first attempt to do something with matplotlib, so I assume the answer's probably that I'm doing something stupid; any pointers in the right direction would be a great help, as this function is going to end up inside something demo'd to The Bosses on Wednesday. :/
Kind regards, André Fischer
<?xml version="1.0"?> <graphic render="v-tile" width="400" height="800" label="Global Communication Index" label-position="top" standalone="no" include="embed" theme="bsc"> <plot legend="bottom" label=""> <chart type="line" legend="no"> <grid axis="x" origin="on" scale="preset" ticks="off" grid="on" style="axis" label=""> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> </grid> <grid axis="y" origin="on" scale="linear 0,4 0,8 0,1" ticks="off" grid="on" styles="axis" label=""> </grid> <data label="T arget" style="target"> <decimal marker="none">0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> </data> <data label="Actual" style="actual"> <decimal>0.575</decimal> <decimal>0.5825</decimal> <decimal>0.5875</decimal> <decimal>0.585</decimal> <decimal>0.6</decimal> <decimal>0.615</decimal> <decimal>0.5975</decimal> <decimal>0.565</decimal> <decimal>0.425</decimal> </data> <data label="Minimum" style="minimum"> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> </data> </chart> <chart type="line" legend="no"> <grid axis="x" origin="on" scale="preset" ticks="off" grid="on" style="axis" label=""> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> </grid> <grid axis="y" origin="on" scale="linear 0,4 0,8 0,1" ticks="off" grid="on" styles="axis" label=""> </grid> <data label="T arget" style="target"> <decimal marker="none">0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> </data> <data label="Actual" style="zeu"> <decimal>0.65</decimal> <decimal>0.63</decimal> <decimal>0.6</decimal> <decimal>0.64</decimal> <decimal>0.65</decimal> <decimal>0.66</decimal> <decimal>0.64</decimal> <decimal>0.61</decimal> <decimal>0.55</decimal> </data> <data label="Minimum" style="minimum"> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> </data> </chart> <chart type="line" legend="no"> <grid axis="x" origin="on" scale="preset" ticks="off" grid="on" style="axis" label=""> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> </grid> <grid axis="y" origin="on" scale="linear 0,4 0,8 0,1" ticks="off" grid="on" styles="axis" label=""> </grid> <data label="T arget" style="target"> <decimal marker="none">0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> </data> <data label="Actual" style="zna"> <decimal>0.5</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.65</decimal> <decimal>0.6</decimal> <decimal>0.5</decimal> <decimal>0</decimal> </data> <data label="Minimum" style="minimum"> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> </data> </chart> <chart type="line" legend="no"> <grid axis="x" origin="on" scale="preset" ticks="off" grid="on" style="axis" label=""> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> <string>""</string> </grid> <grid axis="y" origin="on" scale="linear 0,4 0,8 0,1" ticks="off" grid="on" styles="axis" label=""> </grid> <data label="T arget" style="target"> <decimal marker="none">0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> </data> <data label="Actual" style="zla"> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.6</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> </data> <data label="Minimum" style="minimum"> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> </data> </chart> <chart type="line"> <grid axis="x" origin="on" scale="preset" ticks="off" grid="on" style="axis" label=""> <string>"Jan"</string> <string>"Feb"</string> <string>"Mar"</string> <string>"Apr"</string> <string>"May"</string> <string>"Jun"</string> <string>"Jul"</string> <string>"Aug"</string> <string>"Sep"</string> <string>"Oct"</string> <string>"Nov"</string> <string>"Dec"</string> </grid> <grid axis="y" origin="on" scale="linear 0,4 0,8 0,1" ticks="off" grid="on" styles="axis" label=""> </grid> <data label="T arget" style="target"> <decimal marker="none">0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> <decimal>0.7</decimal> </data> <data label="Actual" style="zap"> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> <decimal>0.6</decimal> <decimal>0.55</decimal> </data> <data label="Minimum" style="minimum"> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> <decimal>0.5</decimal> </data> </chart> </plot> </graphic>
# SVG Factory # Version 1.0.2 # Author: A. Fischer # Contact: [EMAIL PROTECTED] # Phone: +41 76 380 3634 import sys, xml.sax from xml.dom.ext.reader import Sax2 #from pylab import * import pylab from matplotlib.font_manager import * import string import gc # Global declarations chartLegends = () chartList = () chartTitle = '' chartTitlePosition = 'top' chartXlabels = () currentAxis = '' currentSeries = '' fontdict = createFontDict(findSystemFonts()) fChartTitleFont = ['Impact','sans-serif'] fChartTitleFontSize = '24' fChartTitleFontStyle = 'normal' fLegendFont = ['Arial', 'sans-serif'] fLegendFontSize = 9 fLegendFontStyle = 'normal' fAxisFont = ['Times New Roman', 'serif'] fAxisFontSize = 10 fAxisFontStyle = 'italic' graphXsize = 0 graphYsize = 0 graphUnit = 'px' inAxis = False inSeries = False legendPosition = 'best' outputChartType = 'line' # currently 'line' or 'radar' outputFormat = 'svg' # can be svg or png, depending on whether the desired output is a standalone graphic or not plotData = () plotNames = () seriesData = () standalone = 'no' # no means SVG, yes means PNG styles = {} # holds the line styles for plotting plotStyles = () # holds the names of the styles for the individual data plots subplotTiling = 'single' # or 'v-tile' for tiling the subplots in the chart xlabels = () ylabels = () vGraphicNode = 0 # must evaluate to 1 vGraphicLabel = 0 # 0 or 1 vPlotNodes = 0 # >= 1 xmlData = None # ---------------------------------------------------------------------------------------- # svgf: takes an input string in valid XML format, and returns a string in SVG format. # ---------------------------------------------------------------------------------------- def svgf(inputData, fileName=None): global chartLegends global chartList global chartTitle global chartTitlePosition global chartXlabels global currentAxis global currentSeries global fChartTitleFont global fChartTitleFontSize global fChartTitleFontStyle global fLegendFont global fLegendFontSize global fLegendFontStyle global fAxisFont global fAxisFontSize global fAxisFontStyle global graphXsize global graphYsize global graphUnit global inAxis global legendPosition global outputChartType global outputFormat global plotData global plotNames global plotStyles global seriesData global standalone global subplotTiling global styles global xlabels global ylabels global vGraphicNode global vGraphicLabel global vPlotNodes global xmlData vGraphicNode = 0 # must evaluate to 1 vGraphicLabel = 0 # 0 or 1 vPlotNodes = 0 # >= 1 errorMessage = 'ERROR: ' DPI = 96 try: # check to see that inputData is not empty foo = 1 / len(inputData) except ZeroDivisionError: errorMessage += 'Empty String (zero-length) input data received.' else: # now see that Sax2 sees this as XML data configreader = Sax2.Reader() configData = configreader.fromStream('svg_factory.config.xml') ReadStyles(configData) configreader.releaseNode(configData) try: reader = Sax2.Reader() xmlData = reader.fromString(inputData) except: errorMessage += 'Invalid XML data received.' else: # the data appears to be XML try: # get the <graphic> root node out. Validate(xmlData) CheckValidCounts() Iterate(xmlData) except InputDataError, details: errorMessage += details.value else: # Do the plotting #original_objects = [id(x) for x in gc.get_objects()] pylab.ioff() outputFigure = pylab.figure(figsize=(graphXsize / DPI, graphYsize / DPI), dpi=DPI, frameon=True, edgecolor='w') outputFigure.add_axes([0.1,0.1,0.9,0.9],polar=(outputChartType == 'radar')) oFig = outputFigure.gca() if outputChartType == 'line': pylab.xticks(pylab.arange(0),('')) #yticks(arange(0),(''),fontproperties = FontProperties(family=['Times New Roman','serif'],size=9)) if subplotTiling == 'single': numPlots = 1 else: numPlots = len(chartList) for c in range(0, numPlots): if subplotTiling == 'h-tile': pylab.subplot(1,numPlots,c+1) else: pylab.subplot(numPlots,1,c+1) for i in range(0, len(chartList[c])): st = styles[plotStyles[i]] pylab.plot (pylab.arange(len(chartList[c][i])), chartList[c][i], label=chartLegends[c][i], color=st.linecolor, ls=st.linestyle, lw=st.linewidth, mew=st.edgewidth, mec=st.edgecolor, mfc=st.facecolor, marker=st.marker) pylab.legend(chartLegends[c],legendPosition,prop = FontProperties(family=fLegendFont,size=fLegendFontSize,style=fLegendFontStyle)) locs,labels = pylab.yticks() for l in labels: l.set_fontproperties(FontProperties(family=fAxisFont,size=fAxisFontSize,style=fAxisFontStyle)) pylab.xticks (pylab.arange(12),chartXlabels[c],fontproperties = FontProperties(family=fAxisFont, style=fAxisFontStyle, size=fAxisFontSize)) # Add 10% of max-min on the Y-axis top and bottom to ensure everything fits in # so no plots end up at the very top or bottom, all the while keeping the rest # of the bulit-in auto-scaling goodness. tmpymin, tmpymax = pylab.ylim() ymin = tmpymin - (tmpymax - tmpymin) * 0.1 ymax = tmpymax + (tmpymax - tmpymin) * 0.1 pylab.ylim(ymin,ymax) else: # it's a radar chart for c in range(0, len(chartList)): pylab.subplot (len(chartList),1,c+1, polar=True) for i in range(0, len(chartList[c])): st = styles[plotStyles[i]] plotticks = pylab.arange(0, 2 * pylab.pi * 1.0001, (2 * pylab.pi) / (len(chartXlabels[c]) - 1)) ticks = pylab.arange(0,360 * 1.0001,(360.0 / (len(chartXlabels[c]) - 1))) # locs,labels = rgrids() # for l in labels: # l.set_fontproperties(FontProperties(family=fAxisFont,size=fAxisFontSize,style=fAxisFontStyle)) pylab.thetagrids(ticks, chartXlabels[c], frac=1.0, va='top', rotation='horizontal', fontproperties = FontProperties(family=fAxisFont, style=fAxisFontStyle, size=fAxisFontSize)) pylab.plot (plotticks,chartList[c][i], label=chartXlabels[c], color=st.linecolor, ls=st.linestyle, lw=st.linewidth, mew=st.edgewidth, mec=st.edgecolor, mfc=st.facecolor, marker=st.marker) pylab.legend(plotNames,legendPosition,numpoints=1,prop = FontProperties(family=fLegendFont, style=fLegendFontStyle, size=fLegendFontSize)) pylab.figtext(0.5, 0.99, chartTitle, fontproperties = FontProperties(size=fChartTitleFontSize, family=fChartTitleFont, style=fChartTitleFontStyle), backgroundcolor='w', va='top', ha='center') # save the figure to a temp file and then either # return the content of the file as a string or # an error message, depending. reader.releaseNode(xmlData) # show() result = '' if fileName is not None: outputFilename = fileName else: outputFilename = 'tmpFile.' + outputFormat pylab.savefig(outputFilename, format = outputFormat) pylab.clf() #del outputFigure #outputFigure.destroy() del xmlData if outputFormat == 'svg': tmpFile = open(outputFilename,'r') result = tmpFile.read() tmpFile.close() os.remove(outputFilename) else: result = outputFilename if len(errorMessage) > 7: result = errorMessage pylab.close('all') return result # ---------------------------------------------------------------------------------------- # Iterate (recursively) over the nodes in the XML input data # ---------------------------------------------------------------------------------------- def Iterate(node, nodeName=None): global chartLegends global chartList global chartTitle global chartXlabels global currentAxis global currentSeries global graphXsize global graphYsize global graphUnit global inAxis global legendPosition global outputChartType global outputFormat global plotData global plotNames global plotStyles global seriesData global standalone global subplotTiling global xlabels global ylabels doChildren = True for n in node.childNodes: if (n.nodeType == 1): # these nodes contain useful stuff if n.nodeName == 'graphic': chartTitle = GetNodeAttr(n,'label') chartTitlePosition = GetNodeAttr(n, 'label-position') standalone = GetNodeAttr(n, 'standalone') graphXsize = float(GetNodeAttr(n, 'width')) graphYsize = float(GetNodeAttr(n, 'height')) graphUnit = GetNodeAttr(n, 'Unit') if standalone == 'no': outputFormat = 'svg' else: outputFormat = 'png' subplotTiling = GetNodeAttr(n,'render') elif n.nodeName == 'plot': legendPosition = GetNodeAttr(n, 'legend') if legendPosition is None: legendPosition = 'best' if (legendPosition == 'top'): legendPosition = 'upper center' if (legendPosition == 'bottom'): legendPosition = 'lower center' if (legendPosition == 'left'): legendPosition = 'center left' if (legendPosition == 'right'): legendPosition = 'center right' elif n.nodeName == 'chart': outputChartType = GetNodeAttr(n, 'type') doChildren = False Iterate(n) if outputChartType == 'radar': xlabels += ('',) chartXlabels += (xlabels,) chartList += (plotData,) if outputChartType == 'line': chartLegends += (plotNames,) plotNames = tuple() xlabels = tuple() plotData = tuple() elif n.nodeName == 'grid': axis = GetNodeAttr(n,'axis') if axis == 'x': currentAxis = 'X' inAxis = True elif axis == 'y': currentAxis = 'Y' elif n.nodeName == 'string': if inAxis == True: v = Iterate(n) if currentAxis == 'X': xlabels += (v.strip('"'),) elif currentAxis == 'Y': ylabels += (v.strip('"'),) elif n.nodeName == 'data': # Here we need special handling for the child nodes, as # there may be several data series to plot doChildren = False Iterate(n) if outputChartType == 'radar': seriesData += (seriesData[0],) plotData += (seriesData,) plotNames += (GetNodeAttr(n,'label'),) plotStyles += (GetNodeAttr(n, 'style'),) seriesData = tuple() # clear this for the next series data elif n.nodeName == 'decimal': v = Iterate(n) seriesData += (v,) if (n.nodeType == 3) and (n.nodeValue.strip() != ''): # it's a 'leaf' node, return the value return n.nodeValue.strip().encode('latin-1') if doChildren == True: Iterate(n) # ---------------------------------------------------------------------------------------- # GetNodeAttr: returns the value associated with the named attribute of a particular node # Returns None if the node doesn't have an attribute by that name. # ---------------------------------------------------------------------------------------- def GetNodeAttr(node, name): for a in node.attributes: if a.name == name: return a.value # ---------------------------------------------------------------------------------------- # Traverse the XML Data, looking for the required number of elements, attributes etc. # ---------------------------------------------------------------------------------------- def Validate(node): global vGraphicNode global vGraphicLabel global vPlotNodes for n in node.childNodes: if n.nodeType == 1: if n.nodeName == 'graphic': vGraphicNode += 1 print "VAL: graphic:",vGraphicNode label = GetNodeAttr(n,'label') if label is not None: vGraphicLabel += 1 if n.nodeName == 'plot': vPlotNodes += 1 Validate(n) # ---------------------------------------------------------------------------------------- # Check the values found by Validate() against requirements as defined in the spec. # ---------------------------------------------------------------------------------------- def CheckValidCounts(): global vGraphicNode global vGraphicLabel global vPlotNodes errorMessage = 'XML data validation failed - ' if vGraphicNode != 1: errorMessage += 'only 1 <graphic> element allowed, but '+str(vGraphicNode)+' found.' raise InputDataError, errorMessage if vGraphicLabel > 1: errorMessage += '<graphic> element has more than one "label" attribute.' raise InputDataError, errorMessage if vPlotNodes == 0: errorMessage += 'no <plot> nodes found.' raise InputDataError, errorMessage # ---------------------------------------------------------------------------------------- # Read the xml style definitions and set appropriate variables. # ---------------------------------------------------------------------------------------- def ReadStyles(node): global styles global fChartTitleFont global fChartTitleFontSize global fChartTitleFontStyle global fLegendFont global fLegendFontSize global fLegendFontStyle global fAxisFont global fAxisFontSize global fAxisFontStyle for n in node.childNodes: if n.nodeType == 1: if n.nodeName == 'chart_title': fChartTitleFont = GetNodeAttr(n, 'family').split(',') fs = GetNodeAttr(n, 'size') try: fChartTitleFontSize = int(fs) except ValueError: fChartTitleFontSize = fs fChartTitleFontStyle = GetNodeAttr(n, 'style') if n.nodeName == 'legend': fLegendFont = GetNodeAttr(n, 'family').split(',') fs = GetNodeAttr(n, 'size') try: fLegendFontSize = int(fs) except ValueError: fLegendFontSize = fs fLegendFontStyle = GetNodeAttr(n, 'style') if n.nodeName == 'axis': fAxisFont = GetNodeAttr(n, 'family').split(',') fs = GetNodeAttr(n, 'size') try: fAxisFontSize = int(fs) except ValueError: fAxisFontSize = fs fAxisFontStyle = GetNodeAttr(n, 'style') if n.nodeName == 'style': st = Style() st.name = GetNodeAttr(n, 'name') st.marker = GetNodeAttr(n, 'marker') st.edgecolor = GetNodeAttr(n, 'edgecolor') st.edgewidth = GetNodeAttr(n, 'edgewidth') st.facecolor = GetNodeAttr(n, 'facecolor') st.linestyle = GetNodeAttr(n, 'linestyle') st.linewidth = GetNodeAttr(n, 'linewidth') st.linecolor = GetNodeAttr(n, 'linecolor') styles[st.name] = st ReadStyles(n) # ---------------------------------------------------------------------------------------- # Simplified exception class, will return the passed string as an error message. # ---------------------------------------------------------------------------------------- class InputDataError(Exception): def __init__(self,value): self.value = value def __str__(self): return repr(self.value) # ---------------------------------------------------------------------------------------- # Empty class to hold style attributes. These are then built into the styles{} dictionary # ---------------------------------------------------------------------------------------- class Style: pass
from svg_factory import svgf inputFile = open('RadarChartData.xml','r') inputData = inputFile.read() inputFile.close() result = svgf(inputData) if 'ERROR' in result: print result print "Result length",len(result) outputFile = open('test.svg','w') outputFile.write(result) outputFile.close()
------------------------------------------------------------------------- This SF.net email is sponsored by the 2008 JavaOne(SM) Conference Don't miss this year's exciting event. There's still time to save $100. Use priority code J8TL2D2. http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________ Matplotlib-users mailing list Matplotlib-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-users