1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434 |
- #Copyright ReportLab Europe Ltd. 2000-2017
- #see license.txt for license details
- #history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/charts/barcharts.py
- __version__='3.3.0'
- __doc__="""This module defines a variety of Bar Chart components.
- The basic flavors are stacked and side-by-side, available in horizontal and
- vertical versions.
- """
- import copy, functools
- from ast import literal_eval
- from reportlab.lib import colors
- from reportlab.lib.validators import isNumber, isNumberOrNone, isColor, isColorOrNone, isString,\
- isListOfStrings, SequenceOf, isBoolean, isNoneOrShape, isStringOrNone,\
- NoneOr, isListOfNumbersOrNone, EitherOr, OneOf, isInt
- from reportlab.lib.utils import flatten, isStr, yieldNoneSplits
- from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol
- 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, tpcGetItem
- from reportlab.graphics.shapes import Line, Rect, Group, Drawing, NotImplementedError, PolyLine
- from reportlab.graphics.charts.axes import XCategoryAxis, YValueAxis, YCategoryAxis, XValueAxis
- from reportlab.graphics.charts.textlabels import BarChartLabel, NA_Label, NoneOrInstanceOfNA_Label
- from reportlab.graphics.charts.areas import PlotArea
- from reportlab.graphics.charts.legends import _objStr
- from reportlab import cmp
- 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.'),
- strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
- symbol = AttrMapValue(None, desc='A widget to be used instead of a normal bar.',advancedUsage=1),
- name = AttrMapValue(isString, desc='Text to be associated with a bar (eg seriesname)'),
- swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ...",advancedUsage=1),
- minDimen = AttrMapValue(isNumberOrNone, desc='minimum width/height that will be drawn.'),
- isLine = AttrMapValue(NoneOr(isBoolean), desc='if this bar should be drawn as a line'),
- )
- def __init__(self):
- self.strokeColor = None
- self.fillColor = colors.blue
- self.strokeWidth = 0.5
- self.symbol = None
- self.strokeDashArray = None
- # Bar chart classes.
- class BarChart(PlotArea):
- "Abstract base class, unusable by itself."
- _attrMap = AttrMap(BASE=PlotArea,
- useAbsolute = AttrMapValue(EitherOr((isBoolean,EitherOr((isString,isNumber)))), desc='Flag to use absolute spacing values; use string of gsb for finer control\n(g=groupSpacing,s=barSpacing,b=barWidth).',advancedUsage=1),
- 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. Can be a list or list of lists of such.'),
- barLabelCallOut = AttrMapValue(None, desc='Callout function(label)\nlabel._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)',advancedUsage=1),
- 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.',advancedUsage=1),
- naLabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for N/A values.',advancedUsage=1),
- annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
- categoryLabelBarSize = AttrMapValue(isNumber, desc='width to leave for a category label to go between categories.'),
- categoryLabelBarOrder = AttrMapValue(OneOf('first','last','auto'), desc='where any label bar should appear first/last'),
- barRecord = AttrMapValue(None, desc='callable(bar,label=labelText,value=value,**kwds) to record bar information', advancedUsage=1),
- zIndexOverrides = AttrMapValue(isStringOrNone, desc='''None (the default ie use old z ordering scheme) or a ',' separated list of key=value (int/float) for new zIndex ordering. If used defaults are
- background=0,
- categoryAxis=1,
- valueAxis=2,
- bars=3,
- barLabels=4,
- categoryAxisGrid=5,
- valueAxisGrid=6,
- annotations=7'''),
- categoryNALabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for a group of N/A values.',advancedUsage=1),
- seriesOrder = AttrMapValue(SequenceOf(SequenceOf(isInt,emptyOK=0,NoneOK=0,lo=1),emptyOK=0,NoneOK=1,lo=1),"dynamic 'mixed' category style case"),
- )
- def makeSwatchSample(self, rowNo, x, y, width, height):
- baseStyle = self.bars
- styleIdx = rowNo % len(baseStyle)
- style = baseStyle[styleIdx]
- strokeColor = getattr(style, 'strokeColor', getattr(baseStyle,'strokeColor',None))
- fillColor = getattr(style, 'fillColor', getattr(baseStyle,'fillColor',None))
- strokeDashArray = getattr(style, 'strokeDashArray', getattr(baseStyle,'strokeDashArray',None))
- strokeWidth = getattr(style, 'strokeWidth', getattr(style, 'strokeWidth',None))
- swatchMarker = getattr(style, 'swatchMarker', getattr(baseStyle, 'swatchMarker',None))
- if swatchMarker:
- return uSymbol2Symbol(swatchMarker,x+width/2.,y+height/2.,fillColor)
- elif getattr(style,'isLine',False):
- yh2 = y+height/2.
- if hasattr(style, 'symbol'):
- S = style.symbol
- elif hasattr(baseStyle, 'symbol'):
- S = baseStyle.symbol
- else:
- S = None
- L = Line(x,yh2, x+width, yh2,
- strokeColor=style.strokeColor or style.fillColor,
- strokeWidth=style.strokeWidth,
- strokeDashArray = style.strokeDashArray)
- if S: S = uSymbol2Symbol(S,x+width/2.,yh2,style.strokeColor or style.fillColor)
- if S and L:
- g = Group()
- g.add(L)
- g.add(S)
- return g
- return S or L
- else:
- return Rect(x,y,width,height,strokeWidth=strokeWidth,strokeColor=strokeColor,
- strokeDashArray=strokeDashArray,fillColor=fillColor)
- def getSeriesName(self,i,default=None):
- '''return series name i or default'''
- return _objStr(getattr(self.bars[i],'name',default))
- 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()
- self.categoryAxis._attrMap['style'].validate = OneOf('stacked','parallel','parallel_3d','mixed')
- 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.strokeWidth = 1
- self.bars.strokeColor = colors.black
- self.bars.strokeDashArray = None
- self.bars[0].fillColor = colors.red
- self.bars[1].fillColor = colors.green
- self.bars[2].fillColor = colors.blue
- self.naLabel = self.categoryNALabel = None
- self.zIndexOverrides = None
- 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 getSeriesOrder(self):
- bs = getattr(self,'seriesOrder',None)
- n = len(self.data)
- if not bs:
- R = [(ss,) for ss in range(n)]
- else:
- bars = self.bars
- unseen = set(range(n))
- lines = set()
- R = []
- for s in bs:
- g = {ss for ss in s if 0<=ss<=n}
- gl = {ss for ss in g if bars.checkAttr(ss,'isLine',False)}
- if gl:
- g -= gl
- lines |= gl
- unseen -= gl
- if g:
- R.append(tuple(g))
- unseen -= g
- if unseen:
- R.extend((ss,) for ss in sorted(unseen))
- if lines:
- R.extend((ss,) for ss in sorted(lines))
- self._seriesOrder = R
- def _getConfigureData(self):
- cAStyle = self.categoryAxis.style
- data = self.data
- cc = max(list(map(len,data))) #category count
- _data = data
- if cAStyle not in ('parallel','parallel_3d'):
- #stacked or mixed
- data = []
- def _accumulate(*D):
- pdata = max((len(d) for d in D))*[0]
- ndata = pdata[:]
- for d in D:
- for i,v in enumerate(d):
- v = v or 0
- if v<=-1e-6:
- ndata[i] += v
- else:
- pdata[i] += v
- data.append(ndata)
- data.append(pdata)
- if cAStyle=='stacked':
- _accumulate(*_data)
- else:
- self.getSeriesOrder()
- for b in self._seriesOrder:
- _accumulate(*(_data[j] for j in b))
- 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 org
- crossesAt = vA.scale(0)
- return crossesAt if vA.forceZero or (crossesAt>=org and crossesAt<=org+length) else org
- def _drawFinish(self):
- '''finalize the drawing of a barchart'''
- cA = self.categoryAxis
- vA = self.valueAxis
- cA.configure(self._configureData)
- self.calcBarPositions()
- g = Group()
- zIndex = getattr(self,'zIndexOverrides',None)
- if not zIndex:
- g.add(self.makeBackground())
- cAdgl = getattr(cA,'drawGridLast',False)
- vAdgl = getattr(vA,'drawGridLast',False)
- if not cAdgl: cA.makeGrid(g,parent=self, dim=vA.getGridDims)
- if not vAdgl: vA.makeGrid(g,parent=self, dim=cA.getGridDims)
- g.add(self.makeBars())
- g.add(cA)
- g.add(vA)
- if cAdgl: cA.makeGrid(g,parent=self, dim=vA.getGridDims)
- if vAdgl: vA.makeGrid(g,parent=self, dim=cA.getGridDims)
- for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
- else:
- Z=dict(
- background=0,
- categoryAxis=1,
- valueAxis=2,
- bars=3,
- barLabels=4,
- categoryAxisGrid=5,
- valueAxisGrid=6,
- annotations=7,
- )
- for z in zIndex.strip().split(','):
- z = z.strip()
- if not z: continue
- try:
- k,v=z.split('=')
- except:
- raise ValueError('Badly formatted zIndex clause %r in %r\nallowed variables are\n%s' % (z,zIndex,'\n'.join(['%s=%r'% (k,Z[k]) for k in sorted(Z.keys())])))
- if k not in Z:
- raise ValueError('Unknown zIndex variable %r in %r\nallowed variables are\n%s' % (k,Z,'\n'.join(['%s=%r'% (k,Z[k]) for k in sorted(Z.keys())])))
- try:
- v = literal_eval(v) #only constants allowed
- assert isinstance(v,(float,int))
- except:
- raise ValueError('Bad zIndex value %r in clause %r of zIndex\nallowed variables are\n%s' % (v,z,zIndex,'\n'.join(['%s=%r'% (k,Z[k]) for k in sorted(Z.keys())])))
- Z[k] = v
- Z = [(v,k) for k,v in Z.items()]
- Z.sort()
- b = self.makeBars()
- bl = b.contents.pop(-1)
- for v,k in Z:
- if k=='background':
- g.add(self.makeBackground())
- elif k=='categoryAxis':
- g.add(cA)
- elif k=='categoryAxisGrid':
- cA.makeGrid(g,parent=self, dim=vA.getGridDims)
- elif k=='valueAxis':
- g.add(vA)
- elif k=='valueAxisGrid':
- vA.makeGrid(g,parent=self, dim=cA.getGridDims)
- elif k=='bars':
- g.add(b)
- elif k=='barLabels':
- g.add(bl)
- elif k=='annotations':
- 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(list(map(len,data)))
- wG = self.groupSpacing
- barSpacing = self.barSpacing
- barWidth = self.barWidth
- clbs = getattr(self,'categoryLabelBarSize',0)
- clbo = getattr(self,'categoryLabelBarOrder','auto')
- if clbo=='auto': clbo = flipXY and 'last' or 'first'
- clbo = clbo=='first'
- style = cA.style
- bars = self.bars
- lineCount = sum((int(bars.checkAttr(_,'isLine',False)) for _ in range(seriesCount)))
- seriesMLineCount = seriesCount - lineCount
- if style=='mixed':
- ss = self._seriesOrder
- barsPerGroup = len(ss) - lineCount
- wB = barsPerGroup*barWidth
- wS = (barsPerGroup-1)*barSpacing
- if barsPerGroup>1:
- bGapB = barWidth
- bGapS = barSpacing
- else:
- bGapB = bGapS = 0
- accumNeg = barsPerGroup*rowLength*[0]
- accumPos = accumNeg[:]
- elif style in ('parallel','parallel_3d'):
- barsPerGroup = 1
- wB = seriesMLineCount*barWidth
- wS = (seriesMLineCount-1)*barSpacing
- bGapB = barWidth
- bGapS = barSpacing
- else:
- barsPerGroup = seriesMLineCount
- accumNeg = rowLength*[0]
- accumPos = accumNeg[:]
- wB = barWidth
- wS = bGapB = bGapS = 0
-
- self._groupWidth = groupWidth = wG+wB+wS
- useAbsolute = self.useAbsolute
- if useAbsolute:
- if not isinstance(useAbsolute,str):
- useAbsolute = 7 #all three are fixed
- else:
- useAbsolute = 0 + 1*('b' in useAbsolute)+2*('g' in useAbsolute)+4*('s' in useAbsolute)
- else:
- useAbsolute = 0
- aW0 = float(cScale(0)[1])
- aW = aW0 - clbs
- if useAbsolute==0: #case 0 all are free
- self._normFactor = fB = fG = fS = aW/groupWidth
- elif useAbsolute==7: #all fixed
- fB = fG = fS = 1.0
- _cscale = cA._scale
- elif useAbsolute==1: #case 1 barWidth is fixed
- fB = 1.0
- fG = fS = (aW-wB)/(wG+wS)
- elif useAbsolute==2: #groupspacing is fixed
- fG=1.0
- fB = fS = (aW-wG)/(wB+wS)
- elif useAbsolute==3: #groupspacing & barwidth are fixed
- fB = fG = 1.0
- fS = (aW-wG-wB)/wS if wS else 0
- elif useAbsolute==4: #barspacing is fixed
- fS=1.0
- fG = fB = (aW-wS)/(wG+wB)
- elif useAbsolute==5: #barspacing & barWidth are fixed
- fS = fB = 1.0
- fG = (aW-wB-wS)/wG
- elif useAbsolute==6: #barspacing & groupspacing are fixed
- fS = fG = 1
- fB = (aW-wS-wG)/wB
- self._normFactorB = fB
- self._normFactorG = fG
- self._normFactorS = fS
- # '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
- width = barWidth*fB
- offs = 0.5*wG*fG
- bGap = bGapB*fB+bGapS*fS
- if clbs:
- if clbo: #the lable bar comes first
- lbpf = (offs+clbs/6.0)/aW0
- offs += clbs
- else:
- lbpf = (offs+wB*fB+wS*fS+clbs/6.0)/aW0
- cA.labels.labelPosFrac = lbpf
- self._barPositions = []
- aBP = self._barPositions.append
- reversePlotOrder = self.reversePlotOrder
- def _addBar(colNo, accx):
- # Ufff...
- if useAbsolute==7:
- x = groupWidth*_cscale(colNo) + xVal + org
- else:
- (g, _) = cScale(colNo)
- x = g + xVal
- datum = row[colNo]
- if datum is None:
- height = None
- y = baseLine
- else:
- if style not in ('parallel','parallel_3d') and not isLine:
- if datum<=-1e-6:
- y = vScale(accumNeg[accx])
- if y>baseLine: y = baseLine
- accumNeg[accx] += datum
- datum = accumNeg[accx]
- else:
- y = vScale(accumPos[accx])
- if y<baseLine: y = baseLine
- accumPos[accx] += datum
- datum = accumPos[accx]
- 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))
- if style!='mixed':
- lineSeen = 0
- for rowNo, row in enumerate(data): #iterate over the separate series
- barRow = []
- xVal = barsPerGroup - 1 - rowNo if reversePlotOrder else rowNo
- xVal = offs + xVal*bGap
- isLine = bars.checkAttr(rowNo, 'isLine', False)
- if isLine:
- lineSeen += 1
- xVal = offs+(seriesMLineCount-1)*bGap*0.5
- else:
- xVal -= lineSeen*bGap
- for colNo in range(rowLength): #iterate over categories
- _addBar(colNo,colNo)
- aBP(barRow)
- else:
- lineSeen = 0
- for sb,sg in enumerate(self._seriesOrder): #the sub bar nos and series groups
- style = 'parallel' if len(sg)<=1 else 'stacked'
- for rowNo in sg: #the individual series
- xVal = barsPerGroup - 1 - sb if reversePlotOrder else sb
- xVal = offs + xVal*bGap
- barRow = []
- row = data[rowNo]
- isLine = bars.checkAttr(rowNo, 'isLine', False)
- if isLine:
- lineSeen += 1
- xVal = offs+(barsPerGroup-1)*bGap*0.5
- else:
- xVal -= lineSeen*bGap
- for colNo in range(rowLength): #iterate over categories
- _addBar(colNo,colNo*barsPerGroup + sb)
- aBP(barRow)
- def _getLabelText(self, rowNo, colNo):
- '''return formatted label text'''
- labelFmt = self.barLabelFormat
- if isinstance(labelFmt,(list,tuple)):
- labelFmt = labelFmt[rowNo]
- if isinstance(labelFmt,(list,tuple)):
- labelFmt = labelFmt[colNo]
- if labelFmt is None:
- labelText = None
- elif labelFmt == 'values':
- labelText = self.barLabelArray[rowNo][colNo]
- elif isStr(labelFmt):
- labelText = labelFmt % self.data[rowNo][colNo]
- elif hasattr(labelFmt,'__call__'):
- 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'
- nudge = label.nudge
- bt = getattr(label,'boxTarget','normal')
- anti = bt=='anti'
- if anti: nudge = -nudge
- pm = value = height
- if anti: value = 0
- a = x + 0.5*width
- nudge = (height>=0 and 1 or -1)*nudge
- if bt=='mid':
- b = y+height*0.5
- elif bt=='hi':
- if value>=0:
- b = y + value + nudge
- else:
- b = y - nudge
- pm = -pm
- elif bt=='lo':
- if value<=0:
- b = y + value + nudge
- else:
- b = y - nudge
- pm = -pm
- else:
- b = y + value + nudge
- label._pmv = pm #the plus minus val
- return a,b,pm
- 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, calcOnly=False, na=None):
- if na is None: 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
- return self._addLabel(na.text, na, g, rowNo, colNo, x, y, width, height, calcOnly=calcOnly)
- def _addLabel(self, text, label, g, rowNo, colNo, x, y, width, height, calcOnly=False):
- if label.visible:
- labelWidth = stringWidth(text, label.fontName, label.fontSize)
- flipXY = self._flipXY
- if flipXY:
- y0, x0, pm = self._labelXY(label,y,x,height,width)
- else:
- x0, y0, pm = self._labelXY(label,x,y,width,height)
- 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
- if calcOnly: return x0+dx, y0+dy
- 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
- if style.strokeDashArray:
- r.strokeDashArray = style.strokeDashArray
- g.add(r)
- def _makeBars(self,g,lg):
- bars = self.bars
- br = getattr(self,'barRecord',None)
- BP = self._barPositions
- flipXY = self._flipXY
- catNAL = self.categoryNALabel
- catNNA = {}
- if catNAL:
- CBL = []
- rowNoL = len(self.data) - 1
- #find all the categories that have at least one value
- for rowNo, row in enumerate(BP):
- for colNo, (x, y, width, height) in enumerate(row):
- if None not in (width,height):
- catNNA[colNo] = 1
- lines = [].append
- lineSyms = [].append
- for rowNo, row in enumerate(BP):
- styleCount = len(bars)
- styleIdx = rowNo % styleCount
- rowStyle = bars[styleIdx]
- isLine = bars.checkAttr(rowNo, 'isLine', False)
- linePts = [].append
- for colNo, (x,y,width,height) in enumerate(row):
- style = (styleIdx,colNo) in bars and bars[(styleIdx,colNo)] or rowStyle
- if None in (width,height):
- if not catNAL or colNo in catNNA:
- self._addNABarLabel(lg,rowNo,colNo,x,y,width,height)
- elif catNAL and colNo not in CBL:
- r0 = self._addNABarLabel(lg,rowNo,colNo,x,y,width,height,True,catNAL)
- if r0:
- x, y, width, height = BP[rowNoL][colNo]
- r1 = self._addNABarLabel(lg,rowNoL,colNo,x,y,width,height,True,catNAL)
- x = (r0[0]+r1[0])/2.0
- y = (r0[1]+r1[1])/2.0
- self._addNABarLabel(lg,rowNoL,colNo,x,y,0.0001,0.0001,na=catNAL)
- CBL.append(colNo)
- if isLine: linePts(None)
- 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
- minDimen=getattr(style,'minDimen',None)
- if minDimen:
- if flipXY:
- if width<0:
- width = min(-style.minDimen,width)
- else:
- width = max(style.minDimen,width)
- else:
- if height<0:
- height = min(-style.minDimen,height)
- else:
- height = max(style.minDimen,height)
- if isLine:
- if not flipXY:
- yL = y + height
- xL = x + width*0.5
- else:
- xL = x + width
- yL = y + height*0.5
- linePts(xL)
- linePts(yL)
- if symbol:
- sym = uSymbol2Symbol(tpcGetItem(symbol,colNo),xL,yL,style.strokeColor or style.fillColor)
- if sym: lineSyms(sym)
- elif 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)
- if br: br(g.contents[-1],label=self._getLabelText(rowNo,colNo),value=self.data[rowNo][colNo],rowNo=rowNo,colNo=colNo)
- self._addBarLabel(lg,rowNo,colNo,x,y,width,height)
- for linePts in yieldNoneSplits(linePts.__self__):
- if linePts:
- lines(PolyLine(linePts,
- strokeColor=rowStyle.strokeColor or rowStyle.fillColor,
- strokeWidth=rowStyle.strokeWidth,
- strokeDashArray = rowStyle.strokeDashArray))
- for pl in lines.__self__:
- g.add(pl)
- for sym in lineSyms.__self__:
- g.add(sym)
- def _computeLabelPosition(self, text, label, rowNo, colNo, x, y, width, height):
- if label.visible:
- labelWidth = stringWidth(text, label.fontName, label.fontSize)
- flipXY = self._flipXY
- if flipXY:
- y0, x0, pm = self._labelXY(label,y,x,height,width)
- else:
- x0, y0, pm = self._labelXY(label,x,y,width,height)
- 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)
- return pm,label.getBounds()
- def _computeBarPositions(self):
- """Information function, can be called by charts which want to with space around bars"""
- cA, vA = self.categoryAxis, self.valueAxis
- if vA: vA.joinAxis = cA
- if cA: 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)
- cA.configure(self._configureData)
- self.calcBarPositions()
- def _computeMaxSpace(self,size,required):
- '''helper for madmen who want to put stuff inside their barcharts
- basically after _computebarPositions we slide a line of length size
- down the bar profile on either side of the bars to find the
- maximum space. If the space at any point is >= required then we're
- done. Otherwise we return the largest space location and amount.
- '''
- flipXY = self._flipXY
- self._computeBarPositions()
- lenData = len(self.data)
- BP = self._barPositions
- C = []
- aC = C.append
- if flipXY:
- lo = self.x
- hi = lo + self.width
- end = self.y+self.height
- for bp in BP:
- for x, y, w, h in bp:
- v = x+w
- z = y+h
- aC((min(y,z),max(y,z), min(x,v) - lo, hi - max(x,v)))
- else:
- lo = self.y
- hi = lo + self.height
- end = self.x+self.width
- for bp in BP:
- for x, y, w, h in bp:
- v = y+h
- z = x+w
- aC((min(x,z), max(x,z), min(y,v) - lo, hi - max(y,v)))
- C.sort()
- R = [C[0]]
- for c in C:
- r = R[-1]
- if r[0]<c[1] and c[0]<r[1]: #merge overlapping space
- R[-1] = (min(r[0],c[0]),max(r[1],c[1]),min(r[2],c[2]),min(r[3],c[3]))
- else:
- R.append(c)
- C = R
- maxS = -0x7fffffff
- maxP = None
- nC = len(C)
- for i,ci in enumerate(C):
- v0 = ci[0]
- v1 = v0+size
- if v1>end: break
- j = i
- alo = ahi = 0x7fffffff
- while j<nC and C[j][1]<=v1:
- alo = min(C[j][2],alo)
- ahi = min(C[j][3],ahi)
- j += 1
- if alo>ahi:
- if alo>maxS:
- maxS = alo
- maxP = flipXY and (lo,v0,lo+alo,v0+size,0) or (v0,lo,v0+size,lo+alo,0)
- if maxS >= required: break
- elif ahi>maxS:
- maxS = ahi
- maxP = flipXY and (hi-ahi,v0,hi,v0+size,1) or (v0,hi-ahi,v0+size,hi,1)
- if maxS >= required: break
- return maxS, maxP
- def _computeSimpleBarLabelPositions(self):
- """Information function, can be called by charts which want to mess with labels"""
- cA, vA = self.categoryAxis, self.valueAxis
- if vA: vA.joinAxis = cA
- if cA: 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)
- cA.configure(self._configureData)
- self.calcBarPositions()
- bars = self.bars
- R = [].append
- BP = self._barPositions
- for rowNo, row in enumerate(BP):
- C = [].append
- for colNo, (x, y, width, height) in enumerate(row):
- if None in (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
- C(self._computeLabelPosition(na.text, na, rowNo, colNo, x, y, width, height))
- else:
- C(None)
- else:
- text = self._getLabelText(rowNo,colNo)
- if text:
- C(self._computeLabelPosition(text, self.barLabels[(rowNo, colNo)], rowNo, colNo, x, y, width, height))
- else:
- C(None)
- R(C.__self__)
- return R.__self__
- 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(list(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: vA.joinAxis = cA
- if cA: 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._key = functools.cmp_to_key(cmp)
- def add(self,what):
- self._data.append(what)
- def value(self):
- return self._data
- def sort(self):
- self._data.sort(key=self._key)
- 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 reportlab.graphics.charts.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
- fg_value = fg.value()
- cAStyle = self.categoryAxis.style
- if cAStyle=='stacked':
- fg_value=fg_value.reverse()
- elif cAStyle=='mixed':
- fg_value = [_[1] for _ in sorted((((t[1],t[2],t[3],t[4]),t) for t in fg_value))]
- for t in fg_value:
- if 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)
- for t in fg_value:
- if t==1:
- z0,z1,x,y,width,height,rowNo,colNo = t[1:]
- BarChart._addBarLabel(self,g,rowNo,colNo,x,y,width,height)
- 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 = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
- catNames = [n+'-99' for n in 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 = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
- catNames = [n+'-99' for n in 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):
- 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')
- bc._computeSimpleBarLabelPositions()
|