views:

346

answers:

4

I am using matplotlib in a django app and would like to directly return the rendered image. So far I can go plt.savefig(...), then return the location of the image

What I want to do is:

return HttpResponse(plt.renderfig(...), mimetype="image/png")

Any ideas?

A: 

Employ ducktyping and pass a object of your own, in disguise of file object

class MyFile(object):
    def __init__(self):
        self._data = ""
    def write(self, data):
        self._data += data

myfile = MyFile()
fig.savefig(myfile)
print myfile._data

you can use myfile = StringIO.StringIO() instead in real code and return data in reponse e.g.

output = StringIO.StringIO()
fig.savefig(output)
contents = output.getvalue()
return HttpResponse(contents , mimetype="image/png")
Anurag Uniyal
This would work quite well, but as wierob mentioned, HttpResponse already supports file access (which I didn't realize). So you can skip creating your own class.Anyone doing something similar outside of django, this definitely looks like a good idea.
DarwinSurvivor
actually I am not creating my own class that was just a possiblity, as I said user StringIO, and yes using respose object itself is best.
Anurag Uniyal
+2  A: 

what about cStringIO?

import pylab
import cStringIO
pylab.plot([3,7,2,1])
output = cStringIO.StringIO()
pylab.savefig('test.png', dpi=75)
pylab.savefig(output, dpi=75)
print output.getvalue() == open('test.png', 'rb').read() # True
sunqiang
Hmm, this would work for other systems, but I don't think django would handle direct printing very well since it uses HttpResponse objects for all of its outbound communication.
DarwinSurvivor
@darwinsuvivor, agree with u, I just wanna to show that savefig to a cStringIO.StringIO() object is the same as savefig to PNG file. after that, there is no need to use print here. It looks @wierob's answer is completly suited to our present situation.
sunqiang
+9  A: 

Django's HttpResponse object supports file-like API and you can pass a file-object to savefig.

response = HttpResponse(mimetype="image/png")
# create your image as usual, e.g. pylab.plot(...)
pylab.savefig(response, format="png")
return response

Hence, you can return the image directly in the HttpResponse.

wierob
There are a few other suggestions that look like they would work, but this DEFINITELY looks like the easiest for me to use. I only have to add about 2 or 3 lines of code!Thank-You
DarwinSurvivor
+1  A: 

There is a recipe in the Matplotlib Cookbook that does exactly this. At its core, it looks like:

def simple(request):
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    from matplotlib.figure import Figure

    fig=Figure()
    ax=fig.add_subplot(111)
    ax.plot(range(10), range(10), '-')
    canvas=FigureCanvas(fig)
    response=django.http.HttpResponse(content_type='image/png')
    canvas.print_png(response)
    return response

Put that in your views file, point your URL to it, and you're off and running.

Edit: As noted, this is a simplified version of a recipe in the cookbook. However, it looks like there is a difference between calling print_png and savefig, at least in the initial test that I did. Calling fig.savefig(response, format='png') gave an image with that was larger and had a white background, while the original canvas.print_png(response) gave a slightly smaller image with a grey background. So, I would replace the last few lines above with:

    canvas=FigureCanvas(fig)
    response=django.http.HttpResponse(content_type='image/png')
    fig.savefig(response, format='png')
    return response

You still need to have the canvas instantiated, though.

Tim Whitcomb
Is there a difference (speed, efficiency, etc) between using canvase.print_png and simply doing a save_fig() on the HttpResponse?
DarwinSurvivor
You'd have to measure the difference in speed, but there is a difference in presentation as noted in the edit. This also does not depend on the pylab/pyplot wrapper functions, which I like for this sort of application.
Tim Whitcomb