给自定义的plone内容类型增加图表功能
给自定义的plone内容类型增加图表功能
http://www.315ok.org/blogfolder/274
http://www.315ok.org/logo.png
给自定义的plone内容类型增加图表功能
给自定义的plone内容类型增加图表功能
本教程将介绍怎样为定制的Archetype内容类型增加Python-generated 的图表功能。 I describe a general method that allows you to use any Python graphing libraries that output images (png, gif, jpg, etc.). I use PyChart in this specific example.
IntroductionOk, so you want to add graphs to your custom content type, but you're not sure exactly how to do it. You may have searched for 3rd party Zope products to do the job, but you've found they're out of date, or don't do exactly what you want to do. On the other hand, you already know how to generate graphs with a favourite Python graphing package (or at least you know they exist). You just want to know how to use these packages with Plone, and generate graphs for your content views. This How-To will discuss the background concepts, then show you an example in a custom Archetype.
Storing Images On The ZODBZODB BackgroundIf you haven't noticed by now, Zope is built upon the ZODB (which I pronounce "ZopeDB"). This is a persistent database, that magically keeps all of your objects around. Any time the server is up, your objects are around, as you last left them. This really simplifies your programming model, since your objects are persistent. You don't have to explicitly pull data out of files, or relational databases. You're objects are just there.
As you may know, this ZODB works great for pickleable objects, but things like real files (i.e., things on the real file-system) require special steps to access, like external methods. The point is, there's ususally no real reason to use real files. In fact, it's preferred to keep things in the ZODB instead.
StringIO FilesOk, if we're not going to use real files, how do we store the image files that the graphics programs want to generate? For example, how do we handle PyChart's canvas.init(outfile) call? The answer is simple, we use StringIO objects. These behave like files, but are pickleable, and work with the ZODB.
Originally, I tried using the Image object from Products.CMFDefault.Image, but I got errors because the object was not file-like. I found that the StringIO class works fine, except that I have to manually set the MIME type (e.g., to 'image/png') Otherwise, the AT mutuator for the ImageField sets it to 'application/octet-stream'.
Python Graphing PackagesThere are several full-featured graphing libraries for Python. I personally use PyChart, for no special reason, and I'm satisfied with the flexibility and power. It makes great pie charts with minimal work. Other people may favor Matplotlib or GNUPlot for Python. Here are some links: PyChart Matplotlib GNUPlot.py
InstallationTo install the graphics packages, you'll mostly follow the package-specific instructions. But remember, you're installing the package into your Zope's python (in the Zope installation directory tree) and not your system's python (e.g., /usr/bin/python, /sw/bin/python, etc.). Typically, a python package is set up so you can simply run
$ZOPEBIN/python setup.py install
as user from the package folder, where the ZOPEBIN environment variable is set to the path of python in your Zope installation tree.
Your graphics package may require other graphics libraries like PIL (Python Image Library). If they're not already installed in your Zope instance, then install them following the directions as above.
Example CodeAll anyone really needs are commented code examples. This content type contains an ImageField, and the MakePlot() function generates a bar graph.
IntroductionOk, so you want to add graphs to your custom content type, but you're not sure exactly how to do it. You may have searched for 3rd party Zope products to do the job, but you've found they're out of date, or don't do exactly what you want to do. On the other hand, you already know how to generate graphs with a favourite Python graphing package (or at least you know they exist). You just want to know how to use these packages with Plone, and generate graphs for your content views. This How-To will discuss the background concepts, then show you an example in a custom Archetype.
Storing Images On The ZODBZODB BackgroundIf you haven't noticed by now, Zope is built upon the ZODB (which I pronounce "ZopeDB"). This is a persistent database, that magically keeps all of your objects around. Any time the server is up, your objects are around, as you last left them. This really simplifies your programming model, since your objects are persistent. You don't have to explicitly pull data out of files, or relational databases. You're objects are just there.
As you may know, this ZODB works great for pickleable objects, but things like real files (i.e., things on the real file-system) require special steps to access, like external methods. The point is, there's ususally no real reason to use real files. In fact, it's preferred to keep things in the ZODB instead.
StringIO FilesOk, if we're not going to use real files, how do we store the image files that the graphics programs want to generate? For example, how do we handle PyChart's canvas.init(outfile) call? The answer is simple, we use StringIO objects. These behave like files, but are pickleable, and work with the ZODB.
Originally, I tried using the Image object from Products.CMFDefault.Image, but I got errors because the object was not file-like. I found that the StringIO class works fine, except that I have to manually set the MIME type (e.g., to 'image/png') Otherwise, the AT mutuator for the ImageField sets it to 'application/octet-stream'.
Python Graphing PackagesThere are several full-featured graphing libraries for Python. I personally use PyChart, for no special reason, and I'm satisfied with the flexibility and power. It makes great pie charts with minimal work. Other people may favor Matplotlib or GNUPlot for Python. Here are some links: PyChart Matplotlib GNUPlot.py
InstallationTo install the graphics packages, you'll mostly follow the package-specific instructions. But remember, you're installing the package into your Zope's python (in the Zope installation directory tree) and not your system's python (e.g., /usr/bin/python, /sw/bin/python, etc.). Typically, a python package is set up so you can simply run
$ZOPEBIN/python setup.py install
as user from the package folder, where the ZOPEBIN environment variable is set to the path of python in your Zope installation tree.
Your graphics package may require other graphics libraries like PIL (Python Image Library). If they're not already installed in your Zope instance, then install them following the directions as above.
Example CodeAll anyone really needs are commented code examples. This content type contains an ImageField, and the MakePlot() function generates a bar graph.
from Products.Archetypes.public import BaseContent, BaseSchema, Schema, registerType
from Products.Archetypes.public import ImageField
from cStringIO import StringIO
# import from the graphics package
from pychart import theme, canvas, area, axis, bar_plot, error_bar
class MyContentType (BaseContent):
""" An example custom Archetype that generates a PNG graph that's viewed
correctly by all browsers. Call PlotImage(), then look at base_view,
or aim the browser directly at the URL of the myImage.
"""
# Add an ImageField to your schema
# Read up on the deails of ImageField, it has nice features like scaling
schema = BaseSchema.copy() + Schema((
ImageField('myImage',
default_content_type='image/png',
widget=ImageWidget(label="PyChart Generated Image",),
)
))
def MakePlot (self,):
""" Call this function (e.g., by URL from the browser) to draw the plot.
After you call this function, you can see the plot on the base_view page,
or directly by URL. """
# use a StringIO to save the image, because it must be pickleable for the ZODB
imageFile = StringIO()
# show that the mimetype starts out as image/png
print "default content type", self.schema['myImage'].getContentType()
# This is a PyChart example that makes a Bar Plot
# replace this section with your graphing code
theme.use_color = True
theme.output_format="png"
theme.reinitialize()
chart = canvas.init(imageFile) # output the image to our imageFile
data = [(10, 20, 5, 5), (20, 65, 5, 5),
(30, 55, 4, 4), (40, 45, 2, 2), (50, 25, 3, 3)]
ar = area.T(x_axis = axis.X(label = "X label"),
y_axis = axis.Y(label = "Y label"))
ar.add_plot(bar_plot.T(label="foo", data = data,
fill_style = None,
error_bar = error_bar.bar3,
error_minus_col = 2,
error_plus_col = 3))
ar.draw(chart) # draw the area onto our canvas
chart.close() # close the output file
# use the built-in mutator
self.setMyImage(imageFile)
# show that the mutator changed the content type
print "content type after mutator", self.schema['myImage'].getContentType()
# finally, set the correct mimetype (note the first argument)
self.schema['myImage'].setContentType(instance=self, value='image/png')
RegisterType(MyContentType,'YourProductName')