11

The following code will of course create a PNG named test and save it on the server:

from matplotlib.figure import Figure                         
from matplotlib.backends.backend_agg import FigureCanvasAgg  

fig = Figure(figsize=[4,4])                                  
ax = fig.add_axes([.1,.1,.8,.8])                             
ax.scatter([1,2], [3,4])                                     
canvas = FigureCanvasAgg(fig)                                
canvas.print_figure("test.png")

Then to view the image in the browser, we have to go to example.com/test.png. This means we have to call the page with the Python code first to create the test.png file, then go to the PNG file. Is there a way to draw the PNG and output from the Python page that creates the image? Thanks!

Jason Strimpel
  • 14,670
  • 21
  • 76
  • 106
  • You might know this already but if you would like to have an interactive chart instead of a static image you could use mpld3 (https://mpld3.github.io/). – Sandro Jan 09 '20 at 19:38

2 Answers2

24

First you need a page to load a url from the webserver controller which generates the image:

<img src="/matplot/makegraph?arg1=foo" />

Then, embed the matplotlib code into the makegraph controller. You just need to capture the canvas rendered PNG in a memory buffer, then create an HTTP response and write the bytes back to the browser:

import cStringIO
from matplotlib.figure import Figure                      
from matplotlib.backends.backend_agg import FigureCanvasAgg

fig = Figure(figsize=[4,4])                               
ax = fig.add_axes([.1,.1,.8,.8])                          
ax.scatter([1,2], [3,4])                                  
canvas = FigureCanvasAgg(fig)

# write image data to a string buffer and get the PNG image bytes
buf = cStringIO.StringIO()
canvas.print_png(buf)
data = buf.getvalue()

# pseudo-code for generating the http response from your
# webserver, and writing the bytes back to the browser.
# replace this with corresponding code for your web framework
headers = {
    'Content-Type': 'image/png',
    'Content-Length': len(data)
    }
response.write(200, 'OK', headers, data)

Note: you may want to add caching for these if they're frequently generated with the same arguments, e.g. construct a key from the args and write the image data to memcache, then check memcache before regenerating the graph.

samplebias
  • 37,113
  • 6
  • 107
  • 103
  • 3
    It appears you can also do plt.savefig(buf,format="png",facecolor="white") or fig.savefig(). So you dont have to deal with the canvas object. – aaa90210 Dec 14 '12 at 01:38
5

Just to update for python3

The StringIO and cStringIO modules are gone. Instead, import the io module and use io.StringIO https://docs.python.org/3.5/whatsnew/3.0.html?highlight=cstringio

So now would be something like:

import io
from matplotlib.figure import Figure     
from matplotlib import pyplot as plt                 

fig = Figure(figsize=[4,4])                               
ax = fig.add_axes([.1,.1,.8,.8])                          
ax.scatter([1,2], [3,4])                                  

buf = io.BytesIO()
fig.savefig(buf, format='png')
plt.close(fig)
data=buf.getvalue()

# In my case I would have used Django for the webpage
response = HttpResponse(data, content_type='image/png')
return response
Dom
  • 51
  • 1
  • 2