Repotrlab 2.0

bzr revid: pinky-b8333ccaab5dad8ff965b6c380fadadf30d5212a
This commit is contained in:
pinky
2007-01-07 21:41:39 +00:00
parent 62524af2f4
commit 05bb5d5829
287 changed files with 92075 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch1_intro.py
from reportlab.tools.docco.rl_doc_utils import *
import reportlab
title("Graphics Guide")
centred('ReportLab Version ' + reportlab.Version)
nextTemplate("Normal")
########################################################################
#
# Chapter 1
#
########################################################################
heading1("Introduction")
heading2("About this document")
disc("""
This document is intended to be a helpful and reasonably full
introduction to the use of the ReportLab Graphics sub-package.
Starting with simple drawings and shapes, we will take you through the
slightly more complex reusable widgets all the way through to our
powerful and flexible chart library. You will see examples of using
reportlab/graphics to make bar charts, line charts, line plots, pie
charts... and a smiley face.
""")
disc("""
We presume that you have already installed both the Python programming
language and the core ReportLab library. If you have not done either
of these, look in the ReportLab User Guide where chapter one
talks you through all the required steps.
""")
disc("""
We recommend that you read some or all of the User Guide and have at
least a basic understanding of how the ReportLab library works before
you start getting to grips with ReportLab Graphics.
""")
disc("")
todo("""
Be warned! This document is in a <em>very</em> preliminary form. We need
your help to make sure it is complete and helpful. Please send any
feedback to our user mailing list, reportlab-users@reportlab.com.
""")
heading2("What is ReportLab?")
disc("""ReportLab is a software library that lets you directly
create documents in Adobe's Portable Document Format (PDF) using
the Python programming language. """)
disc("""The ReportLab library directly creates PDF based on
your graphics commands. There are no intervening steps. Your applications
can generate reports extremely fast - sometimes orders
of magnitude faster than traditional report-writing
tools.""")
heading2("What is ReportLab Graphics?")
disc("""
ReportLab Graphics is one of the sub-packages to the ReportLab
library. It started off as a stand-alone set of programs, but is now a
fully integrated part of the ReportLab toolkit that allows you to use
its powerful charting and graphics features to improve your PDF forms
and reports.
""")
heading2("Getting Involved")
disc("""ReportLab is an Open Source project. Although we are
a commercial company we provide the core PDF generation
sources freely, even for commercial purposes, and we make no income directly
from these modules. We also welcome help from the community
as much as any other Open Source project. There are many
ways in which you can help:""")
bullet("""General feedback on the core API. Does it work for you?
Are there any rough edges? Does anything feel clunky and awkward?""")
bullet("""New objects to put in reports, or useful utilities for the library.
We have an open standard for report objects, so if you have written a nice
chart or table class, why not contribute it?""")
bullet("""Demonstrations and Case Studies: If you have produced some nice
output, send it to us (with or without scripts). If ReportLab solved a
problem for you at work, write a little 'case study' and send it in.
And if your web site uses our tools to make reports, let us link to it.
We will be happy to display your work (and credit it with your name
and company) on our site!""")
bullet("""Working on the core code: we have a long list of things
to refine or to implement. If you are missing some features or
just want to help out, let us know!""")
disc("""The first step for anyone wanting to learn more or
get involved is to join the mailing list. To Subscribe visit
$http://two.pairlist.net/mailman/listinfo/reportlab-users$.
From there you can also browse through the group's archives
and contributions. The mailing list is
the place to report bugs and get support. """)
@@ -0,0 +1,376 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch2_concepts.py
from reportlab.tools.docco.rl_doc_utils import *
heading1("General Concepts")
disc("""
In this chapter we will present some of the more fundamental principles of
the graphics library, which will show-up later in various places.
""")
heading2("Drawings and Renderers")
disc("""
A <i>Drawing</i> is a platform-independent description of a collection of
shapes.
It is not directly associated with PDF, Postscript or any other output
format.
Fortunately, most vector graphics systems have followed the Postscript
model and it is possible to describe shapes unambiguously.
""")
disc("""
A drawing contains a number of primitive <i>Shapes</i>.
Normal shapes are those widely known as rectangles, circles, lines,
etc.
One special (logic) shape is a <i>Group</i>, which can hold other
shapes and apply a transformation to them.
Groups represent composites of shapes and allow to treat the
composite as if it were a single shape.
Just about anything can be built up from a small number of basic
shapes.
""")
disc("""
The package provides several <i>Renderers</i> which know how to draw a
drawing into different formats.
These include PDF (of course), Postscript, and bitmap output.
The bitmap renderer uses Raph Levien's <i>libart</i> rasterizer
and Fredrik Lundh's <i>Python Imaging Library</i> (PIL).
Very recently, an experimental SVG renderer was also added.
It makes use of Python's standard library XML modules, so you don't
need to install the XML-SIG's additional package named PyXML.
If you have the right extensions installed, you can generate drawings
in bitmap form for the web as well as vector form for PDF documents,
and get "identical output".
""")
disc("""
The PDF renderer has special "privileges" - a Drawing object is also
a <i>Flowable</i> and, hence, can be placed directly in the story
of any Platypus document, or drawn directly on a <i>Canvas</i> with
one line of code.
In addition, the PDF renderer has a utility function to make
a one-page PDF document quickly.
""")
disc("""
The SVG renderer is special as it is still pretty experimental.
The SVG code it generates is not really optimised in any way and
maps only the features available in ReportLab Graphics (RLG) to
SVG. This means there is no support for SVG animation, interactivity,
scripting or more sophisticated clipping, masking or graduation
shapes.
So, be careful, and please report any bugs you find!
""")
disc("""
We expect to add both input and output filters for many vector
graphics formats in future.
SVG was the most prominent first one to start with for which there
is now an output filter in the graphics package.
An SVG input filter will probably become available in Summer 2002
as an additional module.
GUIs will be able to obtain screen images from the bitmap output
filter working with PIL, so a chart could appear in a Tkinter
GUI window.
""")
heading2("Coordinate System")
disc("""
The Y-direction in our X-Y coordinate system points from the
bottom <i>up</i>.
This is consistent with PDF, Postscript and mathematical notation.
It also appears to be more natural for people, especially when
working with charts.
Note that in other graphics models (such as SVG) the Y-coordinate
points <i>down</i>.
For the SVG renderer this is actually no problem as it will take
your drawings and flip things as needed, so your SVG output looks
just as expected.
""")
disc("""
The X-coordinate points, as usual, from left to right.
So far there doesn't seem to be any model advocating the opposite
direction - at least not yet (with interesting exceptions, as it
seems, for Arabs looking at time series charts...).
""")
heading2("Getting Started")
disc("""
Let's create a simple drawing containing the string "Hello World",
displayed on top of a coloured rectangle.
After creating it we will save the drawing to a standalone PDF file.
""")
eg("""
from reportlab.lib import colors
from reportlab.graphics.shapes import *
d = Drawing(400, 200)
d.add(Rect(50, 50, 300, 100, fillColor=colors.yellow))
d.add(String(150,100, 'Hello World',
fontSize=18, fillColor=colors.red))
from reportlab.graphics import renderPDF
renderPDF.drawToFile(d, 'example1.pdf', 'My First Drawing')
""")
disc("This will produce a PDF file containing the following graphic:")
from reportlab.graphics.shapes import *
from reportlab.graphics import testshapes
t = testshapes.getDrawing01()
draw(t, "'Hello World'")
disc("""
Each renderer is allowed to do whatever is appropriate for its format,
and may have whatever API is needed.
If it refers to a file format, it usually has a $drawToFile$ function,
and that's all you need to know about the renderer.
Let's save the same drawing in Encapsulated Postscript format:
""")
##eg("""
## from reportlab.graphics import renderPS
## renderPS.drawToFile(D, 'example1.eps', 'My First Drawing')
##""")
eg("""
from reportlab.graphics import renderPS
renderPS.drawToFile(d, 'example1.eps')
""")
disc("""
This will produce an EPS file with the identical drawing, which
may be imported into publishing tools such as Quark Express.
If we wanted to generate the same drawing as a bitmap file for
a website, say, all we need to do is write code like this:
""")
eg("""
from reportlab.graphics import renderPM
renderPM.saveToFile(d, 'example1.png', 'PNG')
""")
disc("""
Many other bitmap formats, like GIF, JPG, TIFF, BMP and PPN are
genuinely available, making it unlikely you'll need to add external
postprocessing steps to convert to the final format you need.
""")
disc("""
To produce an SVG file containing the identical drawing, which
may be imported into graphical editing tools such as Illustrator
all we need to do is write code like this:
""")
eg("""
from reportlab.graphics import renderSVG
renderSVG.drawToFile(d, 'example1.svg')
""")
heading2("Attribute Verification")
disc("""
Python is very dynamic and lets us execute statements at run time that
can easily be the source for unexpected behaviour.
One subtle 'error' is when assigning to an attribute that the framework
doesn't know about because the used attribute's name contains a typo.
Python lets you get away with it (adding a new attribute to an object,
say), but the graphics framework will not detect this 'typo' without
taking special counter-measures.
""")
disc("""
There are two verification techniques to avoid this situation.
The default is for every object to check every assignment at run
time, such that you can only assign to 'legal' attributes.
This is what happens by default.
As this imposes a small performance penalty, this behaviour can
be turned off when you need it to be.
""")
eg("""
>>> r = Rect(10,10,200,100, fillColor=colors.red)
>>>
>>> r.fullColor = colors.green # note the typo
>>> r.x = 'not a number' # illegal argument type
>>> del r.width # that should confuse it
""")
disc("""
These statements would be caught by the compiler in a statically
typed language, but Python lets you get away with it.
The first error could leave you staring at the picture trying to
figure out why the colors were wrong.
The second error would probably become clear only later, when
some back-end tries to draw the rectangle.
The third, though less likely, results in an invalid object that
would not know how to draw itself.
""")
eg("""
>>> r = shapes.Rect(10,10,200,80)
>>> r.fullColor = colors.green
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "C:\code\users\andy\graphics\shapes.py", line 254, in __setattr__
validateSetattr(self,attr,value) #from reportlab.lib.attrmap
File "C:\code\users\andy\lib\attrmap.py", line 74, in validateSetattr
raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
AttributeError: Illegal attribute 'fullColor' in class Rect
>>>
""")
disc("""
This imposes a performance penalty, so this behaviour can be turned
off when you need it to be.
To do this, you should use the following lines of code before you
first import reportlab.graphics.shapes:
""")
eg("""
>>> import reportlab.rl_config
>>> reportlab.rl_config.shapeChecking = 0
>>> from reportlab.graphics import shapes
>>>
""")
disc("""
Once you turn off $shapeChecking$, the classes are actually built
without the verification hook; code should get faster, then.
Currently the penalty seems to be about 25% on batches of charts,
so it is hardly worth disabling.
However, if we move the renderers to C in future (which is eminently
possible), the remaining 75% would shrink to almost nothing and
the saving from verification would be significant.
""")
disc("""
Each object, including the drawing itself, has a $verify()$ method.
This either succeeds, or raises an exception.
If you turn off automatic verification, then you should explicitly
call $verify()$ in testing when developing the code, or perhaps
once in a batch process.
""")
heading2("Property Editing")
disc("""
A cornerstone of the reportlab/graphics which we will cover below is
that you can automatically document widgets.
This means getting hold of all of their editable properties,
including those of their subcomponents.
""")
disc("""
Another goal is to be able to create GUIs and config files for
drawings.
A generic GUI can be built to show all editable properties
of a drawing, and let you modify them and see the results.
The Visual Basic or Delphi development environment are good
examples of this kind of thing.
In a batch charting application, a file could list all the
properties of all the components in a chart, and be merged
with a database query to make a batch of charts.
""")
disc("""
To support these applications we have two interfaces, $getProperties$
and $setProperties$, as well as a convenience method $dumpProperties$.
The first returns a dictionary of the editable properties of an
object; the second sets them en masse.
If an object has publicly exposed 'children' then one can recursively
set and get their properties too.
This will make much more sense when we look at <i>Widgets</i> later on,
but we need to put the support into the base of the framework.
""")
eg("""
>>> r = shapes.Rect(0,0,200,100)
>>> import pprint
>>> pprint.pprint(r.getProperties())
{'fillColor': Color(0.00,0.00,0.00),
'height': 100,
'rx': 0,
'ry': 0,
'strokeColor': Color(0.00,0.00,0.00),
'strokeDashArray': None,
'strokeLineCap': 0,
'strokeLineJoin': 0,
'strokeMiterLimit': 0,
'strokeWidth': 1,
'width': 200,
'x': 0,
'y': 0}
>>> r.setProperties({'x':20, 'y':30, 'strokeColor': colors.red})
>>> r.dumpProperties()
fillColor = Color(0.00,0.00,0.00)
height = 100
rx = 0
ry = 0
strokeColor = Color(1.00,0.00,0.00)
strokeDashArray = None
strokeLineCap = 0
strokeLineJoin = 0
strokeMiterLimit = 0
strokeWidth = 1
width = 200
x = 20
y = 30
>>> """)
disc("""
<i>Note: $pprint$ is the standard Python library module that allows
you to 'pretty print' output over multiple lines rather than having
one very long line.</i>
""")
disc("""
These three methods don't seem to do much here, but as we will see
they make our widgets framework much more powerful when dealing with
non-primitive objects.
""")
heading2("Naming Children")
disc("""
You can add objects to the $Drawing$ and $Group$ objects.
These normally go into a list of contents.
However, you may also give objects a name when adding them.
This allows you to refer to and possibly change any element
of a drawing after constructing it.
""")
eg("""
>>> d = shapes.Drawing(400, 200)
>>> s = shapes.String(10, 10, 'Hello World')
>>> d.add(s, 'caption')
>>> s.caption.text
'Hello World'
>>>
""")
disc("""
Note that you can use the same shape instance in several contexts
in a drawing; if you choose to use the same $Circle$ object in many
locations (e.g. a scatter plot) and use different names to access
it, it will still be a shared object and the changes will be
global.
""")
disc("""
This provides one paradigm for creating and modifying interactive
drawings.
""")
+416
View File
@@ -0,0 +1,416 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch3_shapes.py
from reportlab.tools.docco.rl_doc_utils import *
from reportlab.graphics.shapes import *
heading1("Shapes")
disc("""
This chapter describes the concept of shapes and their importance
as building blocks for all output generated by the graphics library.
Some properties of existing shapes and their relationship to
diagrams are presented and the notion of having different renderers
for different output formats is briefly introduced.
""")
heading2("Available Shapes")
disc("""
Drawings are made up of Shapes.
Absolutely anything can be built up by combining the same set of
primitive shapes.
The module $shapes.py$ supplies a number of primitive shapes and
constructs which can be added to a drawing.
They are:
""")
bullet("Rect")
bullet("Circle")
bullet("Ellipse")
bullet("Wedge (a pie slice)")
bullet("Polygon")
bullet("Line")
bullet("PolyLine")
bullet("String")
bullet("Group")
bullet("Path (<i>not implemented yet, but will be added in the future</i>)")
disc("""
The following drawing, taken from our test suite, shows most of the
basic shapes (except for groups).
Those with a filled green surface are also called <i>solid shapes</i>
(these are $Rect$, $Circle$, $Ellipse$, $Wedge$ and $Polygon$).
""")
from reportlab.graphics import testshapes
t = testshapes.getDrawing06()
draw(t, "Basic shapes")
heading2("Shape Properties")
disc("""
Shapes have two kinds of properties - some to define their geometry
and some to define their style.
Let's create a red rectangle with 3-point thick green borders:
""")
eg("""
>>> from reportlab.graphics.shapes import Rect
>>> from reportlab.lib.colors import red, green
>>> r = Rect(5, 5, 200, 100)
>>> r.fillColor = red
>>> r.strokeColor = green
>>> r.strokeWidth = 3
>>>
""")
from reportlab.graphics.shapes import Rect
from reportlab.lib.colors import red, green
d = Drawing(220, 120)
r = Rect(5, 5, 200, 100)
r.fillColor = red
r.strokeColor = green
r.strokeWidth = 3
d.add(r)
draw(d, "red rectangle with green border")
disc("""
<i>Note: In future examples we will omit the import statements.</i>
""")
disc("""
All shapes have a number of properties which can be set.
At an interactive prompt, we can use their <i>dumpProperties()</i>
method to list these.
Here's what you can use to configure a Rect:
""")
eg("""
>>> r.dumpProperties()
fillColor = Color(1.00,0.00,0.00)
height = 100
rx = 0
ry = 0
strokeColor = Color(0.00,0.50,0.00)
strokeDashArray = None
strokeLineCap = 0
strokeLineJoin = 0
strokeMiterLimit = 0
strokeWidth = 3
width = 200
x = 5
y = 5
>>>
""")
disc("""
Shapes generally have <i>style properties</i> and <i>geometry
properties</i>.
$x$, $y$, $width$ and $height$ are part of the geometry and must
be provided when creating the rectangle, since it does not make
much sense without those properties.
The others are optional and come with sensible defaults.
""")
disc("""
You may set other properties on subsequent lines, or by passing them
as optional arguments to the constructor.
We could also have created our rectangle this way:
""")
eg("""
>>> r = Rect(5, 5, 200, 100,
fillColor=red,
strokeColor=green,
strokeWidth=3)
""")
disc("""
Let's run through the style properties. $fillColor$ is obvious.
$stroke$ is publishing terminology for the edge of a shape;
the stroke has a color, width, possibly a dash pattern, and
some (rarely used) features for what happens when a line turns
a corner.
$rx$ and $ry$ are optional geometric properties and are used to
define the corner radius for a rounded rectangle.
""")
disc("All the other solid shapes share the same style properties.")
heading2("Lines")
disc("""
We provide single straight lines, PolyLines and curves.
Lines have all the $stroke*$ properties, but no $fillColor$.
Here are a few Line and PolyLine examples and the corresponding
graphics output:
""")
eg("""
Line(50,50, 300,100,
strokeColor=colors.blue, strokeWidth=5)
Line(50,100, 300,50,
strokeColor=colors.red,
strokeWidth=10,
strokeDashArray=[10, 20])
PolyLine([120,110, 130,150, 140,110, 150,150, 160,110,
170,150, 180,110, 190,150, 200,110],
strokeWidth=2,
strokeColor=colors.purple)
""")
d = Drawing(400, 200)
d.add(Line(50,50, 300,100,strokeColor=colors.blue, strokeWidth=5))
d.add(Line(50,100, 300,50,
strokeColor=colors.red,
strokeWidth=10,
strokeDashArray=[10, 20]))
d.add(PolyLine([120,110, 130,150, 140,110, 150,150, 160,110,
170,150, 180,110, 190,150, 200,110],
strokeWidth=2,
strokeColor=colors.purple))
draw(d, "Line and PolyLine examples")
heading2("Strings")
disc("""
The ReportLab Graphics package is not designed for fancy text
layout, but it can place strings at desired locations and with
left/right/center alignment.
Let's specify a $String$ object and look at its properties:
""")
eg("""
>>> s = String(10, 50, 'Hello World')
>>> s.dumpProperties()
fillColor = Color(0.00,0.00,0.00)
fontName = Times-Roman
fontSize = 10
text = Hello World
textAnchor = start
x = 10
y = 50
>>>
""")
disc("""
Strings have a textAnchor property, which may have one of the
values 'start', 'middle', 'end'.
If this is set to 'start', x and y relate to the start of the
string, and so on.
This provides an easy way to align text.
""")
disc("""
Strings use a common font standard: the Type 1 Postscript fonts
present in Acrobat Reader.
We can thus use the basic 14 fonts in ReportLab and get accurate
metrics for them.
We have recently also added support for extra Type 1 fonts
and the renderers all know how to render Type 1 fonts.
""")
##Until now we have worked with bitmap renderers which have to use
##TrueType fonts and which make some substitutions; this could lead
##to differences in text wrapping or even the number of labels on
##a chart between renderers.
disc("""
Here is a more fancy example using the code snippet below.
Please consult the ReportLab User Guide to see how non-standard
like 'LettErrorRobot-Chrome' fonts are being registered!
""")
eg("""
d = Drawing(400, 200)
for size in range(12, 36, 4):
d.add(String(10+size*2, 10+size*2, 'Hello World',
fontName='Times-Roman',
fontSize=size))
d.add(String(130, 120, 'Hello World',
fontName='Courier',
fontSize=36))
d.add(String(150, 160, 'Hello World',
fontName='LettErrorRobot-Chrome',
fontSize=36))
""")
from reportlab.pdfbase import pdfmetrics
from reportlab import rl_config
rl_config.warnOnMissingFontGlyphs = 0
afmFile, pfbFile = getJustFontPaths()
T1face = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile)
T1faceName = 'LettErrorRobot-Chrome'
pdfmetrics.registerTypeFace(T1face)
T1font = pdfmetrics.Font(T1faceName, T1faceName, 'WinAnsiEncoding')
pdfmetrics.registerFont(T1font)
d = Drawing(400, 200)
for size in range(12, 36, 4):
d.add(String(10+size*2, 10+size*2, 'Hello World',
fontName='Times-Roman',
fontSize=size))
d.add(String(130, 120, 'Hello World',
fontName='Courier',
fontSize=36))
d.add(String(150, 160, 'Hello World',
fontName='LettErrorRobot-Chrome',
fontSize=36))
draw(d, 'fancy font example')
heading2("""Paths""")
disc("""
Postscript paths are a widely understood concept in graphics.
They are not implemented in $reportlab/graphics$ as yet, but they
will be, soon.
""")
# NB This commented out section is for 'future compatibility' - paths haven't
# been implemented yet, but when they are we can uncomment this back in.
##disc("""Postscript paths are a widely understood concept in graphics. A Path
## is a way of defining a region in space. You put an imaginary pen down,
## draw straight and curved segments, and even pick the pen up and move
## it. At the end of this you have described a region, which may consist
## of several distinct close shapes or unclosed lines. At the end, this
## 'path' is 'stroked and filled' according to its properties. A Path has
## the same style properties as a solid shape. It can be used to create
## any irregular shape.""")
##
##disc("""In Postscript-based imaging models such as PDF, Postscript and SVG,
## everything is done with paths. All the specific shapes covered above
## are instances of paths; even text strings (which are shapes in which
## each character is an outline to be filled). Here we begin creating a
## path with a straight line and a bezier curve:""")
##
##eg("""
##>>> P = Path(0,0, strokeWidth=3, strokeColor=red)
##>>> P.lineTo(0, 50)
##>>> P.curveTo(10,50,80,80,100,30)
##>>>
##""")
##disc("""As well as being the only way to draw complex shapes, paths offer some
## performance advantages in renderers which support them. If you want to
## create a scatter plot with 5000 blue circles of different sizes, you
## can create 5000 circles, or one path object. With the latter, you only
## need to set the color and line width once. PINGO just remembers the
## drawing sequence, and writes it out into the file. In renderers which
## do not support paths, the renderer will still have to decompose it
## into 5000 circles so you won't save anything.""")
##
##disc("""<b>Note that our current path implementation is an approximation; it
## should be finished off accurately for PDF and PS.</b>""")
heading2("Groups")
disc("""
Finally, we have Group objects.
A group has a list of contents, which are other nodes.
It can also apply a transformation - its contents can be rotated,
scaled or shifted.
If you know the math, you can set the transform directly.
Otherwise it provides methods to rotate, scale and so on.
Here we make a group which is rotated and translated:
""")
eg("""
>>> g =Group(shape1, shape2, shape3)
>>> g.rotate(30)
>>> g.translate(50, 200)
""")
disc("""
Groups provide a tool for reuse.
You can make a bunch of shapes to represent some component - say,
a coordinate system - and put them in one group called "Axis".
You can then put that group into other groups, each with a different
translation and rotation, and you get a bunch of axis.
It is still the same group, being drawn in different places.
""")
disc("""Let's do this with some only slightly more code:""")
eg("""
d = Drawing(400, 200)
Axis = Group(
Line(0,0,100,0), # x axis
Line(0,0,0,50), # y axis
Line(0,10,10,10), # ticks on y axis
Line(0,20,10,20),
Line(0,30,10,30),
Line(0,40,10,40),
Line(10,0,10,10), # ticks on x axis
Line(20,0,20,10),
Line(30,0,30,10),
Line(40,0,40,10),
Line(50,0,50,10),
Line(60,0,60,10),
Line(70,0,70,10),
Line(80,0,80,10),
Line(90,0,90,10),
String(20, 35, 'Axes', fill=colors.black)
)
firstAxisGroup = Group(Axis)
firstAxisGroup.translate(10,10)
d.add(firstAxisGroup)
secondAxisGroup = Group(Axis)
secondAxisGroup.translate(150,10)
secondAxisGroup.rotate(15)
d.add(secondAxisGroup)
thirdAxisGroup = Group(Axis,
transform=mmult(translate(300,10),
rotate(30)))
d.add(thirdAxisGroup)
""")
d = Drawing(400, 200)
Axis = Group(
Line(0,0,100,0), # x axis
Line(0,0,0,50), # y axis
Line(0,10,10,10), # ticks on y axis
Line(0,20,10,20),
Line(0,30,10,30),
Line(0,40,10,40),
Line(10,0,10,10), # ticks on x axis
Line(20,0,20,10),
Line(30,0,30,10),
Line(40,0,40,10),
Line(50,0,50,10),
Line(60,0,60,10),
Line(70,0,70,10),
Line(80,0,80,10),
Line(90,0,90,10),
String(20, 35, 'Axes', fill=colors.black)
)
firstAxisGroup = Group(Axis)
firstAxisGroup.translate(10,10)
d.add(firstAxisGroup)
secondAxisGroup = Group(Axis)
secondAxisGroup.translate(150,10)
secondAxisGroup.rotate(15)
d.add(secondAxisGroup)
thirdAxisGroup = Group(Axis,
transform=mmult(translate(300,10),
rotate(30)))
d.add(thirdAxisGroup)
draw(d, "Groups examples")
@@ -0,0 +1,422 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch4_widgets.py
from reportlab.tools.docco.rl_doc_utils import *
from reportlab.graphics.shapes import *
from reportlab.graphics.widgets import signsandsymbols
heading1("Widgets")
disc("""
We now describe widgets and how they relate to shapes.
Using many examples it is shown how widgets make reusable
graphics components.
""")
heading2("Shapes vs. Widgets")
disc("""Up until now, Drawings have been 'pure data'. There is no code in them
to actually do anything, except assist the programmer in checking and
inspecting the drawing. In fact, that's the cornerstone of the whole
concept and is what lets us achieve portability - a renderer only
needs to implement the primitive shapes.""")
disc("""We want to build reusable graphic objects, including a powerful chart
library. To do this we need to reuse more tangible things than
rectangles and circles. We should be able to write objects for other
to reuse - arrows, gears, text boxes, UML diagram nodes, even fully
fledged charts.""")
disc("""
The Widget standard is a standard built on top of the shapes module.
Anyone can write new widgets, and we can build up libraries of them.
Widgets support the $getProperties()$ and $setProperties()$ methods,
so you can inspect and modify as well as document them in a uniform
way.
""")
bullet("A widget is a reusable shape ")
bullet("""it can be initialized with no arguments
when its $draw()$ method is called it creates a primitive Shape or a
Group to represent itself""")
bullet("""It can have any parameters you want, and they can drive the way it is
drawn""")
bullet("""it has a $demo()$ method which should return an attractively drawn
example of itself in a 200x100 rectangle. This is the cornerstone of
the automatic documentation tools. The $demo()$ method should also have
a well written docstring, since that is printed too!""")
disc("""Widgets run contrary to the idea that a drawing is just a bundle of
shapes; surely they have their own code? The way they work is that a
widget can convert itself to a group of primitive shapes. If some of
its components are themselves widgets, they will get converted too.
This happens automatically during rendering; the renderer will not see
your chart widget, but just a collection of rectangles, lines and
strings. You can also explicitly 'flatten out' a drawing, causing all
widgets to be converted to primitives.""")
heading2("Using a Widget")
disc("""
Let's imagine a simple new widget.
We will use a widget to draw a face, then show how it was implemented.""")
eg("""
>>> from reportlab.lib import colors
>>> from reportlab.graphics import shapes
>>> from reportlab.graphics import widgetbase
>>> from reportlab.graphics import renderPDF
>>> d = shapes.Drawing(200, 100)
>>> f = widgetbase.Face()
>>> f.skinColor = colors.yellow
>>> f.mood = "sad"
>>> d.add(f)
>>> renderPDF.drawToFile(d, 'face.pdf', 'A Face')
""")
from reportlab.graphics import widgetbase
d = Drawing(200, 120)
f = widgetbase.Face()
f.x = 50
f.y = 10
f.skinColor = colors.yellow
f.mood = "sad"
d.add(f)
draw(d, 'A sample widget')
disc("""
Let's see what properties it has available, using the $setProperties()$
method we have seen earlier:
""")
eg("""
>>> f.dumpProperties()
eyeColor = Color(0.00,0.00,1.00)
mood = sad
size = 80
skinColor = Color(1.00,1.00,0.00)
x = 10
y = 10
>>>
""")
disc("""
One thing which seems strange about the above code is that we did not
set the size or position when we made the face.
This is a necessary trade-off to allow a uniform interface for
constructing widgets and documenting them - they cannot require
arguments in their $__init__()$ method.
Instead, they are generally designed to fit in a 200 x 100
window, and you move or resize them by setting properties such as
x, y, width and so on after creation.
""")
disc("""
In addition, a widget always provides a $demo()$ method.
Simple ones like this always do something sensible before setting
properties, but more complex ones like a chart would not have any
data to plot.
The documentation tool calls $demo()$ so that your fancy new chart
class can create a drawing showing what it can do.
""")
disc("""
Here are a handful of simple widgets available in the module
<i>signsandsymbols.py</i>:
""")
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.widgets import signsandsymbols
d = Drawing(230, 230)
ne = signsandsymbols.NoEntry()
ds = signsandsymbols.DangerSign()
fd = signsandsymbols.FloppyDisk()
ns = signsandsymbols.NoSmoking()
ne.x, ne.y = 10, 10
ds.x, ds.y = 120, 10
fd.x, fd.y = 10, 120
ns.x, ns.y = 120, 120
d.add(ne)
d.add(ds)
d.add(fd)
d.add(ns)
draw(d, 'A few samples from signsandsymbols.py')
disc("""
And this is the code needed to generate them as seen in the drawing above:
""")
eg("""
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.widgets import signsandsymbols
d = Drawing(230, 230)
ne = signsandsymbols.NoEntry()
ds = signsandsymbols.DangerSign()
fd = signsandsymbols.FloppyDisk()
ns = signsandsymbols.NoSmoking()
ne.x, ne.y = 10, 10
ds.x, ds.y = 120, 10
fd.x, fd.y = 10, 120
ns.x, ns.y = 120, 120
d.add(ne)
d.add(ds)
d.add(fd)
d.add(ns)
""")
heading2("Compound Widgets")
disc("""Let's imagine a compound widget which draws two faces side by side.
This is easy to build when you have the Face widget.""")
eg("""
>>> tf = widgetbase.TwoFaces()
>>> tf.faceOne.mood
'happy'
>>> tf.faceTwo.mood
'sad'
>>> tf.dumpProperties()
faceOne.eyeColor = Color(0.00,0.00,1.00)
faceOne.mood = happy
faceOne.size = 80
faceOne.skinColor = None
faceOne.x = 10
faceOne.y = 10
faceTwo.eyeColor = Color(0.00,0.00,1.00)
faceTwo.mood = sad
faceTwo.size = 80
faceTwo.skinColor = None
faceTwo.x = 100
faceTwo.y = 10
>>>
""")
disc("""The attributes 'faceOne' and 'faceTwo' are deliberately exposed so you
can get at them directly. There could also be top-level attributes,
but there aren't in this case.""")
heading2("Verifying Widgets")
disc("""The widget designer decides the policy on verification, but by default
they work like shapes - checking every assignment - if the designer
has provided the checking information.""")
heading2("Implementing Widgets")
disc("""We tried to make it as easy to implement widgets as possible. Here's
the code for a Face widget which does not do any type checking:""")
eg("""
class Face(Widget):
\"\"\"This draws a face with two eyes, mouth and nose.\"\"\"
def __init__(self):
self.x = 10
self.y = 10
self.size = 80
self.skinColor = None
self.eyeColor = colors.blue
self.mood = 'happy'
def draw(self):
s = self.size # abbreviate as we will use this a lot
g = shapes.Group()
g.transform = [1,0,0,1,self.x, self.y]
# background
g.add(shapes.Circle(s * 0.5, s * 0.5, s * 0.5,
fillColor=self.skinColor))
# CODE OMITTED TO MAKE MORE SHAPES
return g
""")
disc("""We left out all the code to draw the shapes in this document, but you
can find it in the distribution in $widgetbase.py$.""")
disc("""By default, any attribute without a leading underscore is returned by
setProperties. This is a deliberate policy to encourage consistent
coding conventions.""")
disc("""Once your widget works, you probably want to add support for
verification. This involves adding a dictionary to the class called
$_verifyMap$, which map from attribute names to 'checking functions'.
The $widgetbase.py$ module defines a bunch of checking functions with names
like $isNumber$, $isListOfShapes$ and so on. You can also simply use $None$,
which means that the attribute must be present but can have any type.
And you can and should write your own checking functions. We want to
restrict the "mood" custom attribute to the values "happy", "sad" or
"ok". So we do this:""")
eg("""
class Face(Widget):
\"\"\"This draws a face with two eyes. It exposes a
couple of properties to configure itself and hides
all other details\"\"\"
def checkMood(moodName):
return (moodName in ('happy','sad','ok'))
_verifyMap = {
'x': shapes.isNumber,
'y': shapes.isNumber,
'size': shapes.isNumber,
'skinColor':shapes.isColorOrNone,
'eyeColor': shapes.isColorOrNone,
'mood': checkMood
}
""")
disc("""This checking will be performed on every attribute assignment; or, if
$config.shapeChecking$ is off, whenever you call $myFace.verify()$.""")
heading2("Documenting Widgets")
disc("""
We are working on a generic tool to document any Python package or
module; this is already checked into ReportLab and will be used to
generate a reference for the ReportLab package.
When it encounters widgets, it adds extra sections to the
manual including:""")
bullet("the doc string for your widget class ")
bullet("the code snippet from your <i>demo()</i> method, so people can see how to use it")
bullet("the drawing produced by the <i>demo()</i> method ")
bullet("the property dump for the widget in the drawing. ")
disc("""
This tool will mean that we can have guaranteed up-to-date
documentation on our widgets and charts, both on the web site
and in print; and that you can do the same for your own widgets,
too!
""")
heading2("Widget Design Strategies")
disc("""We could not come up with a consistent architecture for designing
widgets, so we are leaving that problem to the authors! If you do not
like the default verification strategy, or the way
$setProperties/getProperties$ works, you can override them yourself.""")
disc("""For simple widgets it is recommended that you do what we did above:
select non-overlapping properties, initialize every property on
$__init__$ and construct everything when $draw()$ is called. You can
instead have $__setattr__$ hooks and have things updated when certain
attributes are set. Consider a pie chart. If you want to expose the
individual wedges, you might write code like this:""")
eg("""
from reportlab.graphics.charts import piecharts
pc = piecharts.Pie()
pc.defaultColors = [navy, blue, skyblue] #used in rotation
pc.data = [10,30,50,25]
pc.slices[7].strokeWidth = 5
""")
#removed 'pc.backColor = yellow' from above code example
disc("""The last line is problematic as we have only created four wedges - in
fact we might not have created them yet. Does $pc.wedges[7]$ raise an
error? Is it a prescription for what should happen if a seventh wedge
is defined, used to override the default settings? We dump this
problem squarely on the widget author for now, and recommend that you
get a simple one working before exposing 'child objects' whose
existence depends on other properties' values :-)""")
disc("""We also discussed rules by which parent widgets could pass properties
to their children. There seems to be a general desire for a global way
to say that 'all wedges get their lineWidth from the lineWidth of
their parent' without a lot of repetitive coding. We do not have a
universal solution, so again leave that to widget authors. We hope
people will experiment with push-down, pull-down and pattern-matching
approaches and come up with something nice. In the meantime, we
certainly can write monolithic chart widgets which work like the ones
in, say, Visual Basic and Delphi.""")
disc("""For now have a look at the following sample code using an early
version of a pie chart widget and the output it generates:""")
eg("""
from reportlab.lib.colors import *
from reportlab.graphics import shapes,renderPDF
from reportlab.graphics.charts.piecharts import Pie
d = Drawing(400,200)
d.add(String(100,175,"Without labels", textAnchor="middle"))
d.add(String(300,175,"With labels", textAnchor="middle"))
pc = Pie()
pc.x = 25
pc.y = 50
pc.data = [10,20,30,40,50,60]
pc.slices[0].popout = 5
d.add(pc, 'pie1')
pc2 = Pie()
pc2.x = 150
pc2.y = 50
pc2.data = [10,20,30,40,50,60]
pc2.labels = ['a','b','c','d','e','f']
d.add(pc2, 'pie2')
pc3 = Pie()
pc3.x = 275
pc3.y = 50
pc3.data = [10,20,30,40,50,60]
pc3.labels = ['a','b','c','d','e','f']
pc3.wedges.labelRadius = 0.65
pc3.wedges.fontName = "Helvetica-Bold"
pc3.wedges.fontSize = 16
pc3.wedges.fontColor = colors.yellow
d.add(pc3, 'pie3')
""")
# Hack to force a new paragraph before the todo() :-(
disc("")
from reportlab.lib.colors import *
from reportlab.graphics import shapes,renderPDF
from reportlab.graphics.charts.piecharts import Pie
d = Drawing(400,200)
d.add(String(100,175,"Without labels", textAnchor="middle"))
d.add(String(300,175,"With labels", textAnchor="middle"))
pc = Pie()
pc.x = 25
pc.y = 50
pc.data = [10,20,30,40,50,60]
pc.slices[0].popout = 5
d.add(pc, 'pie1')
pc2 = Pie()
pc2.x = 150
pc2.y = 50
pc2.data = [10,20,30,40,50,60]
pc2.labels = ['a','b','c','d','e','f']
d.add(pc2, 'pie2')
pc3 = Pie()
pc3.x = 275
pc3.y = 50
pc3.data = [10,20,30,40,50,60]
pc3.labels = ['a','b','c','d','e','f']
pc3.slices.labelRadius = 0.65
pc3.slices.fontName = "Helvetica-Bold"
pc3.slices.fontSize = 16
pc3.slices.fontColor = colors.yellow
d.add(pc3, 'pie3')
draw(d, 'Some sample Pies')
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,59 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/gengraphguide.py
__version__=''' $Id: gengraphguide.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
__doc__ = """
This module contains the script for building the graphics guide.
"""
def run(pagesize=None, verbose=1, outDir=None):
import os
from reportlab.tools.docco.rl_doc_utils import setStory, getStory, RLDocTemplate, defaultPageSize
from reportlab.tools.docco import rl_doc_utils
from reportlab.lib.utils import open_and_read, _RL_DIR
if not outDir: outDir = os.path.join(_RL_DIR,'docs')
destfn = os.path.join(outDir,'graphguide.pdf')
doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize)
#this builds the story
setStory()
G = {}
exec 'from reportlab.tools.docco.rl_doc_utils import *' in G, G
doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize)
for f in (
'ch1_intro',
'ch2_concepts',
'ch3_shapes',
'ch4_widgets',
'ch5_charts',
):
exec open_and_read(f+'.py',mode='t') in G, G
del G
story = getStory()
if verbose: print 'Built story contains %d flowables...' % len(story)
doc.build(story)
if verbose: print 'Saved "%s"' % destfn
def makeSuite():
"standard test harness support - run self as separate process"
from reportlab.test.utils import ScriptThatMakesFileTest
return ScriptThatMakesFileTest('../docs/graphguide', 'gengraphguide.py', 'graphguide.pdf')
def main():
import sys
verbose = '-s' not in sys.argv
if not verbose: sys.argv.remove('-s')
if len(sys.argv) > 1:
try:
pagesize = eval(sys.argv[1])
except:
print 'Expected page size in argument 1', sys.argv[1]
raise
print 'set page size to',sys.argv[1]
else:
pagesize = None
run(pagesize,verbose)
if __name__=="__main__":
main()