Generate Figures in TurboGears
| Status: | Contributed |
|---|
In TurboGears you can generate figures with Python modules on the server side, or generate figures with Javascript or Flash on the client side.
Server Side
Generating Graphs with matplotlib
Homepage: http://matplotlib.sourceforge.net/
The following decorator exposes individual figures as separate pages. Each figure is rendered as a PNG image:
import StringIO
from matplotlib.backends.backend_agg import FigureCanvasAgg
from turbogears import expose
def expose_matplot_figure(f=None, **render_opts):
"""Decorator which exposes and renders a matplotlib Figure.
Usage:
def sample_fig(self):
fig = matplotlib.figure.Figure(figsize=(9, 6))
fig.Name = "Sinewave"
ax = fig.add_subplot(111)
ax.set_xlabel("angle")
ax.set_ylabel("amplitude")
t = arange(0.0, 2.0, 0.01)
s1 = sin(2*pi*t)
ax.plot(t, s1, color="k")
return fig
@expose_matplot_figure
def sinegraph_default_dpi():
return self.sample_fig()
...same thing using a dict...
@expose_matplot_figure
def sinegraph_default_dpi_using_dict():
return dict(fig=self.sample_fig())
@expose_matplot_figure(dpi=20)
def sinegraph_20dpi():
return self.sample_fig()
@expose_matplot_figure
def sinegraph_32dpi():
return dict(fig=self.sample_fig(), dpi=32)
@expose_pylab_figure(dpi=72)
def sinegraph_results_dict_overrides_decorator(self):
return dict(fig=self.sample_fig(), dpi=32)
"""
def get_fig(result):
if isinstance(result, dict):
fig = result['fig']
del result['fig']
return fig
else:
return result
def get_render_opts(result):
if isinstance(result, dict):
render_opts.update(result)
return render_opts
def wrap_f_with_render_figure(f):
def render_figure(*pargs, **vargs):
# Make the PNG
result = f(*pargs, **vargs)
canvas = FigureCanvasAgg(get_fig(result))
rendered_fig = StringIO.StringIO()
canvas.print_figure(rendered_fig, **get_render_opts(result))
return rendered_fig.getvalue()
return expose(content_type="image/png")(render_figure)
if f is None:
# This corresponds to the usage @expose_matplotlib_figure(dpi=20)
return wrap_f_with_render_figure
else:
# This corresponds to the usage @expose_matplotlib_figure
return wrap_f_with_render_figure(f)Generating Graphs with pylab
The following decorator displays pylab graphs:
import StringIO
from threading import RLock()
import pylab
from turbogears import expose
pylab_lock = RLock()
def expose_pylab_figure(f=None, **render_opts):
""""Decorator which exposes and renders a pylab graph.
Warning:
Unfortunately pylab is not (as far as I know) thread safe
as it works with an implicit figure. Therefore the image
rendering code must be gated with a lock.
This will have no real effect if you're using a process oriented
server, but it may do bad things to perforce if you're using
a threading server.
Usage:
@expose_pylab_figure
def simple_plot():
plot([1, 2, 3])
@expose_pylab_figure(dpi=20)
def simple_plot_20dpi():
plot([1, 2, 3])
@expose_pylab_figure
def simple_plot_32dpi():
plot([1, 2, 3])
return dict(dpi=32)
@expose_pylab_figure(dpi=72)
def simple_plot_results_dict_overrides_decorator(self):
plot([1, 2, 3])
return dict(dpi=32)
"""
def get_render_opts(result):
if result is not None:
render_opts.update(result)
return render_opts
def wrap_f_with_render_figure(f):
def render_figure(*pargs, **vargs):
pylab_lock.acquire()
try:
result = f(*pargs, **vargs)
rendered_figure = StringIO.StringIO()
pylab.savefig(rendered_figure, **get_render_opts(result))
pylab.close('all')
return rendered_figure.getvalue()
finally:
pylab_lock.release()
return expose(content_type="image/png")(render_figure)
if f is None:
# This corresponds to the usage @expose_pylab_figure(dpi=20)
return wrap_f_with_render_figure
else:
# This corresponds to the usage @expose_pylab_figure
return wrap_f_with_render_figure(f)Client Side
With Javascript library: PlotKit
Install with:
$ [sudo] easy_install PlotKit
Here's a simple usage example.
In controllers.py:
from plotkit import EasyPlot
class Root(controllers.RootController):
@expose(template="wgtest.templates.welcome")
def index(self):
setA = [[0,0], [1,2], [2,3], [3,7], [4,8], [5,6]]
setB = [[0,0], [1,1], [2,4], [3,8], [4,7], [5,20]]
setC = [[0,1], [1,3], [2,5], [3,5], [4,3], [5,2]]
return dict(ep= EasyPlot(id="diag",
style="line",
width="300",
height="300",
data=[setA, setB, setC]))In welcome.kid, simply add
${ep.display()}into the HTML BODY element.
You can download the example code here.
With Flash & XML : XML/SWF Charts
XML/SWF Charts is an SWF (Flash) client side plotting API. Xou can feed xml data to XML/SWF Charts and it will plot for you.
Download it, extract charts.swf and the /charts_library folder into your project's static folder.
There are some tricks to use XML/SWF Charts with kid templates. Here's what you'd put into the HTML BODY element to insert the Flash object. Put this into a template called plot.kid:
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"
WIDTH="400"
HEIGHT="250"
id="charts"
ALIGN="">
<PARAM NAME="movie"
VALUE="static/charts.swf?library_path=static/charts_library&xml_source=${data}" />
<PARAM NAME="quality" VALUE="high" />
<PARAM NAME="bgcolor" VALUE="#666666" />
<EMBED src="static/charts.swf?library_path=static/charts_library&xml_source=${data}"
quality="high"
bgcolor="#666666"
WIDTH="400"
HEIGHT="250"
NAME="charts"
ALIGN=""
swLiveConnect="true"
TYPE="application/x-shockwave-flash"
PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
</EMBED>
</OBJECT>Here's the controller method, which displays the page with the XML/SQF Chart Flash object. Add this in controller.py:
@expose(template=".templates.plot")
def plot(self):
# return url: /data
return dict(data="data")And here's an example controller method, which delivers the chart data:
@expose()
def data(self):
template = """\
<chart>
<chart_type>column</chart_type>
<chart_data>
<row>
<null/>
<string>2001</string>
<string>2002</string>
<string>2003</string>
<string>2004</string>
</row>
<row>
<string>Region A</string>
<number>5</number>
<number>10</number>
<number>30</number>
<number>63</number>
</row>
<row>
<string>Region B</string>
<number>100</number>
<number>20</number>
<number>65</number>
<number>55</number>
</row>
<row>
<string>Region C</string>
<number>56</number>
<number>21</number>
<number>5</number>
<number>90</number>
</row>
</chart_data>
</chart>
"""
return template