1944 lines
50 KiB
Python
1944 lines
50 KiB
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/graphics/charts/barcharts.py
|
|
"""This module defines a variety of Bar Chart components.
|
|
|
|
The basic flavors are Side-by-side, available in horizontal and
|
|
vertical versions.
|
|
|
|
Stacked and percentile bar charts to follow...
|
|
"""
|
|
__version__=''' $Id$ '''
|
|
|
|
import string, copy
|
|
from types import FunctionType, StringType
|
|
|
|
from reportlab.lib import colors
|
|
from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isListOfStrings, SequenceOf, isBoolean, isNoneOrShape
|
|
from reportlab.lib.formatters import Formatter
|
|
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
|
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
|
|
from reportlab.graphics.shapes import Line, Rect, Group, Drawing, NotImplementedError
|
|
from reportlab.graphics.charts.axes import XCategoryAxis, YValueAxis
|
|
from reportlab.graphics.charts.axes import YCategoryAxis, XValueAxis
|
|
from reportlab.graphics.charts.textlabels import BarChartLabel, NA_Label, NoneOrInstanceOfNA_Label
|
|
from reportlab.graphics.charts.areas import PlotArea
|
|
|
|
class BarChartProperties(PropHolder):
|
|
_attrMap = AttrMap(
|
|
strokeColor = AttrMapValue(isColorOrNone, desc='Color of the bar border.'),
|
|
fillColor = AttrMapValue(isColorOrNone, desc='Color of the bar interior area.'),
|
|
strokeWidth = AttrMapValue(isNumber, desc='Width of the bar border.'),
|
|
symbol = AttrMapValue(None, desc='A widget to be used instead of a normal bar.'),
|
|
)
|
|
|
|
def __init__(self):
|
|
self.strokeColor = None
|
|
self.fillColor = colors.blue
|
|
self.strokeWidth = 0.5
|
|
self.symbol = None
|
|
|
|
# Bar chart classes.
|
|
class BarChart(PlotArea):
|
|
"Abstract base class, unusable by itself."
|
|
|
|
_attrMap = AttrMap(BASE=PlotArea,
|
|
useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'),
|
|
barWidth = AttrMapValue(isNumber, desc='The width of an individual bar.'),
|
|
groupSpacing = AttrMapValue(isNumber, desc='Width between groups of bars.'),
|
|
barSpacing = AttrMapValue(isNumber, desc='Width between individual bars.'),
|
|
bars = AttrMapValue(None, desc='Handle of the individual bars.'),
|
|
valueAxis = AttrMapValue(None, desc='Handle of the value axis.'),
|
|
categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'),
|
|
data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
|
|
barLabels = AttrMapValue(None, desc='Handle to the list of bar labels.'),
|
|
barLabelFormat = AttrMapValue(None, desc='Formatting string or function used for bar labels.'),
|
|
barLabelCallOut = AttrMapValue(None, desc='Callout function(label)\nlabel._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)'),
|
|
barLabelArray = AttrMapValue(None, desc='explicit array of bar label values, must match size of data if present.'),
|
|
reversePlotOrder = AttrMapValue(isBoolean, desc='If true, reverse common category plot order.'),
|
|
naLabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for N/A values.'),
|
|
annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
|
|
)
|
|
|
|
def __init__(self):
|
|
assert self.__class__.__name__ not in ('BarChart','BarChart3D'), 'Abstract Class %s Instantiated' % self.__class__.__name__
|
|
|
|
if self._flipXY:
|
|
self.categoryAxis = YCategoryAxis()
|
|
self.valueAxis = XValueAxis()
|
|
else:
|
|
self.categoryAxis = XCategoryAxis()
|
|
self.valueAxis = YValueAxis()
|
|
|
|
PlotArea.__init__(self)
|
|
self.barSpacing = 0
|
|
self.reversePlotOrder = 0
|
|
|
|
|
|
# this defines two series of 3 points. Just an example.
|
|
self.data = [(100,110,120,130),
|
|
(70, 80, 85, 90)]
|
|
|
|
# control bar spacing. is useAbsolute = 1 then
|
|
# the next parameters are in points; otherwise
|
|
# they are 'proportions' and are normalized to
|
|
# fit the available space. Half a barSpacing
|
|
# is allocated at the beginning and end of the
|
|
# chart.
|
|
self.useAbsolute = 0 #- not done yet
|
|
self.barWidth = 10
|
|
self.groupSpacing = 5
|
|
self.barSpacing = 0
|
|
|
|
self.barLabels = TypedPropertyCollection(BarChartLabel)
|
|
self.barLabels.boxAnchor = 'c'
|
|
self.barLabels.textAnchor = 'middle'
|
|
self.barLabelFormat = None
|
|
self.barLabelArray = None
|
|
# this says whether the origin is inside or outside
|
|
# the bar - +10 means put the origin ten points
|
|
# above the tip of the bar if value > 0, or ten
|
|
# points inside if bar value < 0. This is different
|
|
# to label dx/dy which are not dependent on the
|
|
# sign of the data.
|
|
self.barLabels.nudge = 0
|
|
|
|
# if you have multiple series, by default they butt
|
|
# together.
|
|
|
|
# we really need some well-designed default lists of
|
|
# colors e.g. from Tufte. These will be used in a
|
|
# cycle to set the fill color of each series.
|
|
self.bars = TypedPropertyCollection(BarChartProperties)
|
|
## self.bars.symbol = None
|
|
self.bars.strokeWidth = 1
|
|
self.bars.strokeColor = colors.black
|
|
|
|
self.bars[0].fillColor = colors.red
|
|
self.bars[1].fillColor = colors.green
|
|
self.bars[2].fillColor = colors.blue
|
|
self.naLabel = None#NA_Label()
|
|
|
|
|
|
def demo(self):
|
|
"""Shows basic use of a bar chart"""
|
|
if self.__class__.__name__=='BarChart':
|
|
raise NotImplementedError, 'Abstract Class BarChart has no demo'
|
|
drawing = Drawing(200, 100)
|
|
bc = self.__class__()
|
|
drawing.add(bc)
|
|
return drawing
|
|
|
|
def _getConfigureData(self):
|
|
cA = self.categoryAxis
|
|
data = self.data
|
|
if cA.style not in ('parallel','parallel_3d'):
|
|
_data = data
|
|
data = max(map(len,_data))*[0]
|
|
for d in _data:
|
|
for i in xrange(len(d)):
|
|
data[i] = data[i] + (d[i] or 0)
|
|
data = list(_data) + [data]
|
|
self._configureData = data
|
|
|
|
def _getMinMax(self):
|
|
'''Attempt to return the data range'''
|
|
self._getConfigureData()
|
|
self.valueAxis._setRange(self._configureData)
|
|
return self.valueAxis._valueMin, self.valueAxis._valueMax
|
|
|
|
def _drawBegin(self,org,length):
|
|
'''Position and configure value axis, return crossing value'''
|
|
vA = self.valueAxis
|
|
vA.setPosition(self.x, self.y, length)
|
|
self._getConfigureData()
|
|
vA.configure(self._configureData)
|
|
|
|
# if zero is in chart, put the other axis there, otherwise use low
|
|
crossesAt = vA.scale(0)
|
|
if crossesAt > org+length or crossesAt<org:
|
|
crossesAt = org
|
|
return crossesAt
|
|
|
|
def _drawFinish(self):
|
|
'''finalize the drawing of a barchart'''
|
|
cA = self.categoryAxis
|
|
vA = self.valueAxis
|
|
cA.configure(self._configureData)
|
|
self.calcBarPositions()
|
|
g = Group()
|
|
g.add(self.makeBackground())
|
|
|
|
# ensure any axes have correct spacing set
|
|
# for grids. It sucks that we have to do
|
|
# this here.
|
|
if self._flipXY == 0:
|
|
vA.gridStart = cA._x
|
|
vA.gridEnd = cA._x+cA._length
|
|
cA.gridStart = vA._y
|
|
cA.gridEnd = vA._y+vA._length
|
|
else:
|
|
cA.gridStart = vA._x
|
|
cA.gridEnd = vA._x+vA._length
|
|
vA.gridStart = cA._y
|
|
vA.gridEnd = cA._y+cA._length
|
|
|
|
cA.makeGrid(g,parent=self)
|
|
vA.makeGrid(g,parent=self)
|
|
g.add(self.makeBars())
|
|
g.add(cA)
|
|
g.add(vA)
|
|
for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
|
|
del self._configureData
|
|
return g
|
|
|
|
|
|
def calcBarPositions(self):
|
|
"""Works out where they go. default vertical.
|
|
|
|
Sets an attribute _barPositions which is a list of
|
|
lists of (x, y, width, height) matching the data.
|
|
"""
|
|
|
|
flipXY = self._flipXY
|
|
if flipXY:
|
|
org = self.y
|
|
else:
|
|
org = self.x
|
|
cA = self.categoryAxis
|
|
cScale = cA.scale
|
|
|
|
data = self.data
|
|
seriesCount = self._seriesCount = len(data)
|
|
self._rowLength = rowLength = max(map(len,data))
|
|
groupSpacing, barSpacing, barWidth = self.groupSpacing, self.barSpacing, self.barWidth
|
|
style = self.categoryAxis.style
|
|
if style=='parallel':
|
|
groupWidth = groupSpacing+(seriesCount*barWidth)+(seriesCount-1)*barSpacing
|
|
bGap = barWidth+barSpacing
|
|
else:
|
|
accum = rowLength*[0]
|
|
groupWidth = groupSpacing+barWidth
|
|
bGap = 0
|
|
self._groupWidth = groupWidth
|
|
useAbsolute = self.useAbsolute
|
|
|
|
if useAbsolute:
|
|
# bar dimensions are absolute
|
|
normFactor = 1.0
|
|
else:
|
|
# bar dimensions are normalized to fit. How wide
|
|
# notionally is one group of bars?
|
|
availWidth = cScale(0)[1]
|
|
normFactor = availWidth/float(groupWidth)
|
|
if self.debug:
|
|
print '%d series, %d points per series' % (seriesCount, self._rowLength)
|
|
print 'width = %d group + (%d bars * %d barWidth) + (%d gaps * %d interBar) = %d total' % (
|
|
groupSpacing, seriesCount, barWidth,
|
|
seriesCount-1, barSpacing, groupWidth)
|
|
|
|
# 'Baseline' correction...
|
|
vA = self.valueAxis
|
|
vScale = vA.scale
|
|
vm, vM = vA._valueMin, vA._valueMax
|
|
if vm <= 0 <= vM:
|
|
baseLine = vScale(0)
|
|
elif 0 < vm:
|
|
baseLine = vScale(vm)
|
|
elif vM < 0:
|
|
baseLine = vScale(vM)
|
|
self._baseLine = baseLine
|
|
|
|
COLUMNS = range(max(map(len,data)))
|
|
if useAbsolute:
|
|
_cScale = cA._scale
|
|
|
|
self._normFactor = normFactor
|
|
width = self.barWidth*normFactor
|
|
self._barPositions = []
|
|
reversePlotOrder = self.reversePlotOrder
|
|
for rowNo in range(seriesCount):
|
|
barRow = []
|
|
if reversePlotOrder:
|
|
xVal = seriesCount-1 - rowNo
|
|
else:
|
|
xVal = rowNo
|
|
xVal = 0.5*groupSpacing+xVal*bGap
|
|
for colNo in COLUMNS:
|
|
datum = data[rowNo][colNo]
|
|
|
|
# Ufff...
|
|
if useAbsolute:
|
|
x = groupWidth*_cScale(colNo) + xVal + org
|
|
else:
|
|
(g, gW) = cScale(colNo)
|
|
x = g + normFactor*xVal
|
|
|
|
if datum is None:
|
|
height = None
|
|
y = baseLine
|
|
else:
|
|
if style not in ('parallel','parallel_3d'):
|
|
y = vScale(accum[colNo])
|
|
if y<baseLine: y = baseLine
|
|
accum[colNo] = accum[colNo] + datum
|
|
datum = accum[colNo]
|
|
else:
|
|
y = baseLine
|
|
height = vScale(datum) - y
|
|
if -1e-8<height<=1e-8:
|
|
height = 1e-8
|
|
if datum<-1e-8: height = -1e-8
|
|
barRow.append(flipXY and (y,x,height,width) or (x, y, width, height))
|
|
|
|
self._barPositions.append(barRow)
|
|
|
|
|
|
def _getLabelText(self, rowNo, colNo):
|
|
'''return formatted label text'''
|
|
labelFmt = self.barLabelFormat
|
|
if labelFmt is None:
|
|
labelText = None
|
|
elif labelFmt == 'values':
|
|
labelText = self.barLabelArray[rowNo][colNo]
|
|
elif type(labelFmt) is StringType:
|
|
labelText = labelFmt % self.data[rowNo][colNo]
|
|
elif callable(labelFmt):
|
|
labelText = labelFmt(self.data[rowNo][colNo])
|
|
else:
|
|
msg = "Unknown formatter type %s, expected string or function" % labelFmt
|
|
raise Exception, msg
|
|
return labelText
|
|
|
|
def _labelXY(self,label,x,y,width,height):
|
|
'Compute x, y for a label'
|
|
if self._flipXY:
|
|
return x + width + (width>=0 and 1 or -1) * label.nudge, y + 0.5*height
|
|
else:
|
|
return x + 0.5*width, y + height + (height>=0 and 1 or -1) * label.nudge
|
|
|
|
def _addBarLabel(self, g, rowNo, colNo, x, y, width, height):
|
|
text = self._getLabelText(rowNo,colNo)
|
|
if text:
|
|
self._addLabel(text, self.barLabels[(rowNo, colNo)], g, rowNo, colNo, x, y, width, height)
|
|
|
|
def _addNABarLabel(self, g, rowNo, colNo, x, y, width, height):
|
|
na = self.naLabel
|
|
if na and na.text:
|
|
na = copy.copy(na)
|
|
v = self.valueAxis._valueMax<=0 and -1e-8 or 1e-8
|
|
if width is None: width = v
|
|
if height is None: height = v
|
|
self._addLabel(na.text, na, g, rowNo, colNo, x, y, width, height)
|
|
|
|
def _addLabel(self, text, label, g, rowNo, colNo, x, y, width, height):
|
|
if label.visible:
|
|
labelWidth = stringWidth(text, label.fontName, label.fontSize)
|
|
x0, y0 = self._labelXY(label,x,y,width,height)
|
|
flipXY = self._flipXY
|
|
if flipXY:
|
|
pm = width
|
|
else:
|
|
pm = height
|
|
label._pmv = pm #the plus minus val
|
|
fixedEnd = getattr(label,'fixedEnd', None)
|
|
if fixedEnd is not None:
|
|
v = fixedEnd._getValue(self,pm)
|
|
x00, y00 = x0, y0
|
|
if flipXY:
|
|
x0 = v
|
|
else:
|
|
y0 = v
|
|
else:
|
|
if flipXY:
|
|
x00 = x0
|
|
y00 = y+height/2.0
|
|
else:
|
|
x00 = x+width/2.0
|
|
y00 = y0
|
|
fixedStart = getattr(label,'fixedStart', None)
|
|
if fixedStart is not None:
|
|
v = fixedStart._getValue(self,pm)
|
|
if flipXY:
|
|
x00 = v
|
|
else:
|
|
y00 = v
|
|
|
|
if pm<0:
|
|
if flipXY:
|
|
dx = -2*label.dx
|
|
dy = 0
|
|
else:
|
|
dy = -2*label.dy
|
|
dx = 0
|
|
else:
|
|
dy = dx = 0
|
|
label.setOrigin(x0+dx, y0+dy)
|
|
label.setText(text)
|
|
sC, sW = label.lineStrokeColor, label.lineStrokeWidth
|
|
if sC and sW: g.insert(0,Line(x00,y00,x0,y0, strokeColor=sC, strokeWidth=sW))
|
|
g.add(label)
|
|
alx = getattr(self,'barLabelCallOut',None)
|
|
if alx:
|
|
label._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)
|
|
alx(label)
|
|
del label._callOutInfo
|
|
|
|
def _makeBar(self,g,x,y,width,height,rowNo,style):
|
|
r = Rect(x, y, width, height)
|
|
r.strokeWidth = style.strokeWidth
|
|
r.fillColor = style.fillColor
|
|
r.strokeColor = style.strokeColor
|
|
g.add(r)
|
|
|
|
def _makeBars(self,g,lg):
|
|
lenData = len(self.data)
|
|
bars = self.bars
|
|
for rowNo in range(lenData):
|
|
row = self._barPositions[rowNo]
|
|
styleCount = len(bars)
|
|
styleIdx = rowNo % styleCount
|
|
rowStyle = bars[styleIdx]
|
|
for colNo in range(len(row)):
|
|
barPos = row[colNo]
|
|
style = bars.has_key((styleIdx,colNo)) and bars[(styleIdx,colNo)] or rowStyle
|
|
(x, y, width, height) = barPos
|
|
if None in (width,height):
|
|
self._addNABarLabel(lg,rowNo,colNo,x,y,width,height)
|
|
continue
|
|
|
|
# Draw a rectangular symbol for each data item,
|
|
# or a normal colored rectangle.
|
|
symbol = None
|
|
if hasattr(style, 'symbol'):
|
|
symbol = copy.deepcopy(style.symbol)
|
|
elif hasattr(self.bars, 'symbol'):
|
|
symbol = self.bars.symbol
|
|
|
|
if symbol:
|
|
symbol.x = x
|
|
symbol.y = y
|
|
symbol.width = width
|
|
symbol.height = height
|
|
g.add(symbol)
|
|
elif abs(width)>1e-7 and abs(height)>=1e-7 and (style.fillColor is not None or style.strokeColor is not None):
|
|
self._makeBar(g,x,y,width,height,rowNo,style)
|
|
|
|
self._addBarLabel(lg,rowNo,colNo,x,y,width,height)
|
|
|
|
def makeBars(self):
|
|
g = Group()
|
|
lg = Group()
|
|
self._makeBars(g,lg)
|
|
g.add(lg)
|
|
return g
|
|
|
|
def _desiredCategoryAxisLength(self):
|
|
'''for dynamically computing the desired category axis length'''
|
|
style = self.categoryAxis.style
|
|
data = self.data
|
|
n = len(data)
|
|
m = max(map(len,data))
|
|
if style=='parallel':
|
|
groupWidth = (n-1)*self.barSpacing+n*self.barWidth
|
|
else:
|
|
groupWidth = self.barWidth
|
|
return m*(self.groupSpacing+groupWidth)
|
|
|
|
def draw(self):
|
|
cA, vA = self.categoryAxis, self.valueAxis
|
|
if vA: ovAjA, vA.joinAxis = vA.joinAxis, cA
|
|
if cA: ocAjA, cA.joinAxis = cA.joinAxis, vA
|
|
if self._flipXY:
|
|
cA.setPosition(self._drawBegin(self.x,self.width), self.y, self.height)
|
|
else:
|
|
cA.setPosition(self.x, self._drawBegin(self.y,self.height), self.width)
|
|
return self._drawFinish()
|
|
|
|
class VerticalBarChart(BarChart):
|
|
"Vertical bar chart with multiple side-by-side bars."
|
|
_flipXY = 0
|
|
|
|
class HorizontalBarChart(BarChart):
|
|
"Horizontal bar chart with multiple side-by-side bars."
|
|
_flipXY = 1
|
|
|
|
class _FakeGroup:
|
|
def __init__(self, cmp=None):
|
|
self._data = []
|
|
self._cmp = cmp
|
|
|
|
def add(self,what):
|
|
self._data.append(what)
|
|
|
|
def value(self):
|
|
return self._data
|
|
|
|
def sort(self):
|
|
self._data.sort(self._cmp)
|
|
|
|
class BarChart3D(BarChart):
|
|
_attrMap = AttrMap(BASE=BarChart,
|
|
theta_x = AttrMapValue(isNumber, desc='dx/dz'),
|
|
theta_y = AttrMapValue(isNumber, desc='dy/dz'),
|
|
zDepth = AttrMapValue(isNumber, desc='depth of an individual series'),
|
|
zSpace = AttrMapValue(isNumber, desc='z gap around series'),
|
|
)
|
|
theta_x = .5
|
|
theta_y = .5
|
|
zDepth = None
|
|
zSpace = None
|
|
|
|
def calcBarPositions(self):
|
|
BarChart.calcBarPositions(self)
|
|
seriesCount = self._seriesCount
|
|
zDepth = self.zDepth
|
|
if zDepth is None: zDepth = self.barWidth
|
|
zSpace = self.zSpace
|
|
if zSpace is None: zSpace = self.barSpacing
|
|
if self.categoryAxis.style=='parallel_3d':
|
|
_3d_depth = seriesCount*zDepth+(seriesCount+1)*zSpace
|
|
else:
|
|
_3d_depth = zDepth + 2*zSpace
|
|
_3d_depth *= self._normFactor
|
|
self._3d_dx = self.theta_x*_3d_depth
|
|
self._3d_dy = self.theta_y*_3d_depth
|
|
|
|
def _calc_z0(self,rowNo):
|
|
zDepth = self.zDepth
|
|
if zDepth is None: zDepth = self.barWidth
|
|
zSpace = self.zSpace
|
|
if zSpace is None: zSpace = self.barSpacing
|
|
if self.categoryAxis.style=='parallel_3d':
|
|
z0 = self._normFactor*(rowNo*(zDepth+zSpace)+zSpace)
|
|
else:
|
|
z0 = self._normFactor*zSpace
|
|
return z0
|
|
|
|
def _makeBar(self,g,x,y,width,height,rowNo,style):
|
|
zDepth = self.zDepth
|
|
if zDepth is None: zDepth = self.barWidth
|
|
zSpace = self.zSpace
|
|
if zSpace is None: zSpace = self.barSpacing
|
|
z0 = self._calc_z0(rowNo)
|
|
z1 = z0 + zDepth*self._normFactor
|
|
if width<0:
|
|
x += width
|
|
width = -width
|
|
x += z0*self.theta_x
|
|
y += z0*self.theta_y
|
|
if self._flipXY:
|
|
y += zSpace
|
|
else:
|
|
x += zSpace
|
|
g.add((0,z0,z1,x,y,width,height,rowNo,style))
|
|
|
|
def _addBarLabel(self, g, rowNo, colNo, x, y, width, height):
|
|
z0 = self._calc_z0(rowNo)
|
|
zSpace = self.zSpace
|
|
if zSpace is None: zSpace = self.barSpacing
|
|
z1 = z0
|
|
x += z0*self.theta_x
|
|
y += z0*self.theta_y
|
|
if self._flipXY:
|
|
y += zSpace
|
|
else:
|
|
x += zSpace
|
|
g.add((1,z0,z1,x,y,width,height,rowNo,colNo))
|
|
|
|
def makeBars(self):
|
|
from utils3d import _draw_3d_bar
|
|
fg = _FakeGroup(cmp=self._cmpZ)
|
|
self._makeBars(fg,fg)
|
|
fg.sort()
|
|
g = Group()
|
|
theta_x = self.theta_x
|
|
theta_y = self.theta_y
|
|
for t in fg.value():
|
|
if t[0]==1:
|
|
z0,z1,x,y,width,height,rowNo,colNo = t[1:]
|
|
BarChart._addBarLabel(self,g,rowNo,colNo,x,y,width,height)
|
|
elif t[0]==0:
|
|
z0,z1,x,y,width,height,rowNo,style = t[1:]
|
|
dz = z1 - z0
|
|
_draw_3d_bar(g, x, x+width, y, y+height, dz*theta_x, dz*theta_y,
|
|
fillColor=style.fillColor, fillColorShaded=None,
|
|
strokeColor=style.strokeColor, strokeWidth=style.strokeWidth,
|
|
shading=0.45)
|
|
return g
|
|
|
|
class VerticalBarChart3D(BarChart3D,VerticalBarChart):
|
|
_cmpZ=lambda self,a,b:cmp((-a[1],a[3],a[0],-a[4]),(-b[1],b[3],b[0],-b[4]))
|
|
|
|
class HorizontalBarChart3D(BarChart3D,HorizontalBarChart):
|
|
_cmpZ = lambda self,a,b: cmp((-a[1],a[4],a[0],-a[3]),(-b[1],b[4],b[0],-b[3])) #t, z0, z1, x, y = a[:5]
|
|
|
|
# Vertical samples.
|
|
def sampleV0a():
|
|
"A slightly pathologic bar chart with only TWO data items."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'ne'
|
|
bc.categoryAxis.labels.dx = 8
|
|
bc.categoryAxis.labels.dy = -2
|
|
bc.categoryAxis.labels.angle = 30
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV0b():
|
|
"A pathologic bar chart with only ONE data item."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(42,)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 50
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'ne'
|
|
bc.categoryAxis.labels.dx = 8
|
|
bc.categoryAxis.labels.dy = -2
|
|
bc.categoryAxis.labels.angle = 30
|
|
bc.categoryAxis.categoryNames = ['Jan-99']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV0c():
|
|
"A really pathologic bar chart with NO data items at all!"
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [()]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'ne'
|
|
bc.categoryAxis.labels.dx = 8
|
|
bc.categoryAxis.labels.dy = -2
|
|
bc.categoryAxis.categoryNames = []
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV1():
|
|
"Sample of multi-series bar chart."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [
|
|
(13, 5, 20, 22, 37, 45, 19, 4),
|
|
(14, 6, 21, 23, 38, 46, 20, 5)
|
|
]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'ne'
|
|
bc.categoryAxis.labels.dx = 8
|
|
bc.categoryAxis.labels.dy = -2
|
|
bc.categoryAxis.labels.angle = 30
|
|
|
|
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
|
|
catNames = map(lambda n:n+'-99', catNames)
|
|
bc.categoryAxis.categoryNames = catNames
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV2a():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.2),
|
|
(0.6, -4.9, -3, 4, 6.8)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 0
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.categoryAxis.labels.dy = -60
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV2b():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.2),
|
|
(0.6, -4.9, -3, 4, 6.8)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 5
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.categoryAxis.labels.dy = -60
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV2c():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.99),
|
|
(0.6, -4.9, -3, 4, 9.99)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 2
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n'
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
bc.categoryAxis.labels.dy = -60
|
|
|
|
bc.barLabels.nudge = 10
|
|
|
|
bc.barLabelFormat = '%0.2f'
|
|
bc.barLabels.dx = 0
|
|
bc.barLabels.dy = 0
|
|
bc.barLabels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.barLabels.fontName = 'Helvetica'
|
|
bc.barLabels.fontSize = 6
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV3():
|
|
"Faked horizontal bar chart using a vertical real one (deprecated)."
|
|
|
|
names = ("UK Equities", "US Equities", "European Equities", "Japanese Equities",
|
|
"Pacific (ex Japan) Equities", "Emerging Markets Equities",
|
|
"UK Bonds", "Overseas Bonds", "UK Index-Linked", "Cash")
|
|
|
|
series1 = (-1.5, 0.3, 0.5, 1.0, 0.8, 0.7, 0.4, 0.1, 1.0, 0.3)
|
|
series2 = (0.0, 0.33, 0.55, 1.1, 0.88, 0.77, 0.44, 0.11, 1.10, 0.33)
|
|
|
|
assert len(names) == len(series1), "bad data"
|
|
assert len(names) == len(series2), "bad data"
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 0
|
|
bc.y = 0
|
|
bc.height = 100
|
|
bc.width = 150
|
|
bc.data = (series1,)
|
|
bc.bars.fillColor = colors.green
|
|
|
|
bc.barLabelFormat = '%0.2f'
|
|
bc.barLabels.dx = 0
|
|
bc.barLabels.dy = 0
|
|
bc.barLabels.boxAnchor = 'w' # irrelevant (becomes 'c')
|
|
bc.barLabels.angle = 90
|
|
bc.barLabels.fontName = 'Helvetica'
|
|
bc.barLabels.fontSize = 6
|
|
bc.barLabels.nudge = 10
|
|
|
|
bc.valueAxis.visible = 0
|
|
bc.valueAxis.valueMin = -2
|
|
bc.valueAxis.valueMax = +2
|
|
bc.valueAxis.valueStep = 1
|
|
|
|
bc.categoryAxis.tickUp = 0
|
|
bc.categoryAxis.tickDown = 0
|
|
bc.categoryAxis.categoryNames = names
|
|
bc.categoryAxis.labels.angle = 90
|
|
bc.categoryAxis.labels.boxAnchor = 'w'
|
|
bc.categoryAxis.labels.dx = 0
|
|
bc.categoryAxis.labels.dy = -125
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 6
|
|
|
|
g = Group(bc)
|
|
g.translate(100, 175)
|
|
g.rotate(-90)
|
|
|
|
drawing.add(g)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV4a():
|
|
"A bar chart showing value axis region starting at *exactly* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV4b():
|
|
"A bar chart showing value axis region starting *below* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = -10
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV4c():
|
|
"A bar chart showing value axis region staring *above* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 10
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV4d():
|
|
"A bar chart showing value axis region entirely *below* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(-13, -20)]
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = -30
|
|
bc.valueAxis.valueMax = -10
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
###
|
|
|
|
##dataSample5 = [(10, 20), (20, 30), (30, 40), (40, 50), (50, 60)]
|
|
##dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30), (50, 20)]
|
|
dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30)]
|
|
|
|
def sampleV5a():
|
|
"A simple bar chart with no expressed spacing attributes."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV5b():
|
|
"A simple bar chart with proportional spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 0
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV5c1():
|
|
"Make sampe simple bar chart but with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 0
|
|
bc.barSpacing = 0
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV5c2():
|
|
"Make sampe simple bar chart but with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 0
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV5c3():
|
|
"Make sampe simple bar chart but with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 0
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleV5c4():
|
|
"Make sampe simple bar chart but with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'n'
|
|
bc.categoryAxis.labels.dy = -5
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
# Horizontal samples
|
|
|
|
def sampleH0a():
|
|
"Make a slightly pathologic bar chart with only TWO data items."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'se'
|
|
bc.categoryAxis.labels.angle = 30
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH0b():
|
|
"Make a pathologic bar chart with only ONE data item."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(42,)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 50
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'se'
|
|
bc.categoryAxis.labels.angle = 30
|
|
bc.categoryAxis.categoryNames = ['Jan-99']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH0c():
|
|
"Make a really pathologic bar chart with NO data items at all!"
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [()]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'se'
|
|
bc.categoryAxis.labels.angle = 30
|
|
bc.categoryAxis.categoryNames = []
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH1():
|
|
"Sample of multi-series bar chart."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [
|
|
(13, 5, 20, 22, 37, 45, 19, 4),
|
|
(14, 6, 21, 23, 38, 46, 20, 5)
|
|
]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
|
|
catNames = map(lambda n:n+'-99', catNames)
|
|
bc.categoryAxis.categoryNames = catNames
|
|
drawing.add(bc, 'barchart')
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH2a():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.2),
|
|
(0.6, -4.9, -3, 4, 6.8)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 80
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 0
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
bc.valueAxis.configure(bc.data)
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.categoryAxis.labels.dx = -150
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH2b():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.2),
|
|
(0.6, -4.9, -3, 4, 6.8)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 80
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 5
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.categoryAxis.labels.dx = -150
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH2c():
|
|
"Sample of multi-series bar chart."
|
|
|
|
data = [(2.4, -5.7, 2, 5, 9.99),
|
|
(0.6, -4.9, -3, 4, 9.99)
|
|
]
|
|
|
|
labels = ("Q3 2000", "Year to Date", "12 months",
|
|
"Annualised\n3 years", "Since 07.10.99")
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 80
|
|
bc.y = 50
|
|
bc.height = 120
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.barSpacing = 2
|
|
bc.groupSpacing = 10
|
|
bc.barWidth = 10
|
|
|
|
bc.valueAxis.valueMin = -15
|
|
bc.valueAxis.valueMax = +15
|
|
bc.valueAxis.valueStep = 5
|
|
bc.valueAxis.labels.fontName = 'Helvetica'
|
|
bc.valueAxis.labels.fontSize = 8
|
|
bc.valueAxis.labels.boxAnchor = 'n'
|
|
bc.valueAxis.labels.textAnchor = 'middle'
|
|
|
|
bc.categoryAxis.categoryNames = labels
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 8
|
|
bc.categoryAxis.labels.dx = -150
|
|
|
|
bc.barLabels.nudge = 10
|
|
|
|
bc.barLabelFormat = '%0.2f'
|
|
bc.barLabels.dx = 0
|
|
bc.barLabels.dy = 0
|
|
bc.barLabels.boxAnchor = 'n' # irrelevant (becomes 'c')
|
|
bc.barLabels.fontName = 'Helvetica'
|
|
bc.barLabels.fontSize = 6
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH3():
|
|
"A really horizontal bar chart (compared to the equivalent faked one)."
|
|
|
|
names = ("UK Equities", "US Equities", "European Equities", "Japanese Equities",
|
|
"Pacific (ex Japan) Equities", "Emerging Markets Equities",
|
|
"UK Bonds", "Overseas Bonds", "UK Index-Linked", "Cash")
|
|
|
|
series1 = (-1.5, 0.3, 0.5, 1.0, 0.8, 0.7, 0.4, 0.1, 1.0, 0.3)
|
|
series2 = (0.0, 0.33, 0.55, 1.1, 0.88, 0.77, 0.44, 0.11, 1.10, 0.33)
|
|
|
|
assert len(names) == len(series1), "bad data"
|
|
assert len(names) == len(series2), "bad data"
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 100
|
|
bc.y = 20
|
|
bc.height = 150
|
|
bc.width = 250
|
|
bc.data = (series1,)
|
|
bc.bars.fillColor = colors.green
|
|
|
|
bc.barLabelFormat = '%0.2f'
|
|
bc.barLabels.dx = 0
|
|
bc.barLabels.dy = 0
|
|
bc.barLabels.boxAnchor = 'w' # irrelevant (becomes 'c')
|
|
bc.barLabels.fontName = 'Helvetica'
|
|
bc.barLabels.fontSize = 6
|
|
bc.barLabels.nudge = 10
|
|
|
|
bc.valueAxis.visible = 0
|
|
bc.valueAxis.valueMin = -2
|
|
bc.valueAxis.valueMax = +2
|
|
bc.valueAxis.valueStep = 1
|
|
|
|
bc.categoryAxis.tickLeft = 0
|
|
bc.categoryAxis.tickRight = 0
|
|
bc.categoryAxis.categoryNames = names
|
|
bc.categoryAxis.labels.boxAnchor = 'w'
|
|
bc.categoryAxis.labels.dx = -170
|
|
bc.categoryAxis.labels.fontName = 'Helvetica'
|
|
bc.categoryAxis.labels.fontSize = 6
|
|
|
|
g = Group(bc)
|
|
drawing.add(g)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH4a():
|
|
"A bar chart showing value axis region starting at *exactly* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH4b():
|
|
"A bar chart showing value axis region starting *below* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = -10
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH4c():
|
|
"A bar chart showing value axis region starting *above* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(13, 20)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 10
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH4d():
|
|
"A bar chart showing value axis region entirely *below* zero."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = [(-13, -20)]
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = -30
|
|
bc.valueAxis.valueMax = -10
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30)]
|
|
|
|
def sampleH5a():
|
|
"A simple bar chart with no expressed spacing attributes."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH5b():
|
|
"A simple bar chart with proportional spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 0
|
|
bc.barWidth = 40
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH5c1():
|
|
"A simple bar chart with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 0
|
|
bc.barSpacing = 0
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH5c2():
|
|
"Simple bar chart with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 0
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH5c3():
|
|
"Simple bar chart with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 20
|
|
bc.height = 155
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 0
|
|
bc.barSpacing = 2
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
|
|
def sampleH5c4():
|
|
"Simple bar chart with absolute spacing."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
def sampleSymbol1():
|
|
"Simple bar chart using symbol attribute."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 15
|
|
bc.barSpacing = 3
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
from reportlab.graphics.widgets.grids import ShadedRect
|
|
sym1 = ShadedRect()
|
|
sym1.fillColorStart = colors.black
|
|
sym1.fillColorEnd = colors.blue
|
|
sym1.orientation = 'horizontal'
|
|
sym1.strokeWidth = 0
|
|
|
|
sym2 = ShadedRect()
|
|
sym2.fillColorStart = colors.black
|
|
sym2.fillColorEnd = colors.pink
|
|
sym2.orientation = 'horizontal'
|
|
sym2.strokeWidth = 0
|
|
|
|
sym3 = ShadedRect()
|
|
sym3.fillColorStart = colors.blue
|
|
sym3.fillColorEnd = colors.white
|
|
sym3.orientation = 'vertical'
|
|
sym3.cylinderMode = 1
|
|
sym3.strokeWidth = 0
|
|
|
|
bc.bars.symbol = sym1
|
|
bc.bars[2].symbol = sym2
|
|
bc.bars[3].symbol = sym3
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
def sampleStacked1():
|
|
"Simple bar chart using symbol attribute."
|
|
|
|
drawing = Drawing(400, 200)
|
|
|
|
data = dataSample5
|
|
|
|
bc = VerticalBarChart()
|
|
bc.categoryAxis.style = 'stacked'
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = data
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 15
|
|
bc.valueAxis.valueMin = 0
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
from reportlab.graphics.widgets.grids import ShadedRect
|
|
bc.bars.symbol = ShadedRect()
|
|
bc.bars.symbol.fillColorStart = colors.red
|
|
bc.bars.symbol.fillColorEnd = colors.white
|
|
bc.bars.symbol.orientation = 'vertical'
|
|
bc.bars.symbol.cylinderMode = 1
|
|
bc.bars.symbol.strokeWidth = 0
|
|
|
|
bc.bars[1].symbol = ShadedRect()
|
|
bc.bars[1].symbol.fillColorStart = colors.magenta
|
|
bc.bars[1].symbol.fillColorEnd = colors.white
|
|
bc.bars[1].symbol.orientation = 'vertical'
|
|
bc.bars[1].symbol.cylinderMode = 1
|
|
bc.bars[1].symbol.strokeWidth = 0
|
|
|
|
bc.bars[2].symbol = ShadedRect()
|
|
bc.bars[2].symbol.fillColorStart = colors.green
|
|
bc.bars[2].symbol.fillColorEnd = colors.white
|
|
bc.bars[2].symbol.orientation = 'vertical'
|
|
bc.bars[2].symbol.cylinderMode = 1
|
|
bc.bars[2].symbol.strokeWidth = 0
|
|
|
|
bc.bars[3].symbol = ShadedRect()
|
|
bc.bars[3].symbol.fillColorStart = colors.blue
|
|
bc.bars[3].symbol.fillColorEnd = colors.white
|
|
bc.bars[3].symbol.orientation = 'vertical'
|
|
bc.bars[3].symbol.cylinderMode = 1
|
|
bc.bars[3].symbol.strokeWidth = 0
|
|
|
|
drawing.add(bc)
|
|
|
|
return drawing
|
|
|
|
#class version of function sampleH5c4 above
|
|
class SampleH5c4(Drawing):
|
|
"Simple bar chart with absolute spacing."
|
|
|
|
def __init__(self,width=400,height=200,*args,**kw):
|
|
apply(Drawing.__init__,(self,width,height)+args,kw)
|
|
bc = HorizontalBarChart()
|
|
bc.x = 50
|
|
bc.y = 50
|
|
bc.height = 125
|
|
bc.width = 300
|
|
bc.data = dataSample5
|
|
bc.strokeColor = colors.black
|
|
|
|
bc.useAbsolute = 1
|
|
bc.barWidth = 10
|
|
bc.groupSpacing = 20
|
|
bc.barSpacing = 10
|
|
|
|
bc.valueAxis.valueMin = 0
|
|
bc.valueAxis.valueMax = 60
|
|
bc.valueAxis.valueStep = 15
|
|
|
|
bc.categoryAxis.labels.boxAnchor = 'e'
|
|
bc.categoryAxis.categoryNames = ['Ying', 'Yang']
|
|
|
|
self.add(bc,name='HBC')
|