1
#Copyright ReportLab Europe Ltd. 2000-2004
2
#see license.txt for license details
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/piecharts.py
4
# experimental pie chart script. Two types of pie - one is a monolithic
5
#widget with all top-level properties, the other delegates most stuff to
6
#a wedges collection whic lets you customize the group or every individual
9
"""Basic Pie Chart class.
11
This permits you to customize and pop out individual wedges;
12
supports elliptical and circular pies.
14
__version__=''' $Id$ '''
17
from math import sin, cos, pi
19
from reportlab.lib import colors
20
from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\
21
isListOfNumbers, isColorOrNone, isString,\
22
isListOfStringsOrNone, OneOf, SequenceOf,\
23
isBoolean, isListOfColors, isNumberOrNone,\
24
isNoneOrListOfNoneOrStrings, isTextAnchor,\
25
isNoneOrListOfNoneOrNumbers, isBoxAnchor,\
27
from reportlab.lib.attrmap import *
28
from reportlab.pdfgen.canvas import Canvas
29
from reportlab.graphics.shapes import Group, Drawing, Ellipse, Wedge, String, STATE_DEFAULTS, ArcPath, Polygon
30
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
31
from textlabels import Label
33
_ANGLE2BOXANCHOR={0:'w', 45:'sw', 90:'s', 135:'se', 180:'e', 225:'ne', 270:'n', 315: 'nw', -45: 'nw'}
34
class WedgeLabel(Label):
35
def _checkDXY(self,ba):
37
def _getBoxAnchor(self):
38
na = (int((self._pmv%360)/45.)*45)%360
39
if not (na % 90): # we have a right angle case
40
da = (self._pmv - na) % 360
42
na = na + (da>0 and 45 or -45)
43
ba = _ANGLE2BOXANCHOR[na]
47
class WedgeProperties(PropHolder):
48
"""This holds descriptive information about the wedges in a pie chart.
50
It is not to be confused with the 'wedge itself'; this just holds
51
a recipe for how to format one, and does not allow you to hack the
52
angles. It can format a genuine Wedge object for you with its
57
strokeWidth = AttrMapValue(isNumber),
58
fillColor = AttrMapValue(isColorOrNone),
59
strokeColor = AttrMapValue(isColorOrNone),
60
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
61
popout = AttrMapValue(isNumber),
62
fontName = AttrMapValue(isString),
63
fontSize = AttrMapValue(isNumber),
64
fontColor = AttrMapValue(isColorOrNone),
65
labelRadius = AttrMapValue(isNumber),
66
label_dx = AttrMapValue(isNumber),
67
label_dy = AttrMapValue(isNumber),
68
label_angle = AttrMapValue(isNumber),
69
label_boxAnchor = AttrMapValue(isBoxAnchor),
70
label_boxStrokeColor = AttrMapValue(isColorOrNone),
71
label_boxStrokeWidth = AttrMapValue(isNumber),
72
label_boxFillColor = AttrMapValue(isColorOrNone),
73
label_strokeColor = AttrMapValue(isColorOrNone),
74
label_strokeWidth = AttrMapValue(isNumber),
75
label_text = AttrMapValue(isStringOrNone),
76
label_leading = AttrMapValue(isNumberOrNone),
77
label_width = AttrMapValue(isNumberOrNone),
78
label_maxWidth = AttrMapValue(isNumberOrNone),
79
label_height = AttrMapValue(isNumberOrNone),
80
label_textAnchor = AttrMapValue(isTextAnchor),
81
label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
82
label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
83
label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
84
label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
85
label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
91
self.strokeColor = STATE_DEFAULTS["strokeColor"]
92
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
94
self.fontName = STATE_DEFAULTS["fontName"]
95
self.fontSize = STATE_DEFAULTS["fontSize"]
96
self.fontColor = STATE_DEFAULTS["fillColor"]
97
self.labelRadius = 1.2
98
self.label_dx = self.label_dy = self.label_angle = 0
99
self.label_text = None
100
self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
101
self.label_boxAnchor = 'c'
102
self.label_boxStrokeColor = None #boxStroke
103
self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
104
self.label_boxFillColor = None
105
self.label_strokeColor = None
106
self.label_strokeWidth = 0.1
107
self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None
108
self.label_textAnchor = 'start'
109
self.label_visible = 1
111
def _addWedgeLabel(self,text,add,angle,labelX,labelY,wedgeStyle,labelClass=WedgeLabel):
113
if self.simpleLabels:
114
theLabel = String(labelX, labelY, text)
115
theLabel.textAnchor = "middle"
117
theLabel = labelClass()
118
theLabel._pmv = angle
121
theLabel.dx = wedgeStyle.label_dx
122
theLabel.dy = wedgeStyle.label_dy
123
theLabel.angle = wedgeStyle.label_angle
124
theLabel.boxAnchor = wedgeStyle.label_boxAnchor
125
theLabel.boxStrokeColor = wedgeStyle.label_boxStrokeColor
126
theLabel.boxStrokeWidth = wedgeStyle.label_boxStrokeWidth
127
theLabel.boxFillColor = wedgeStyle.label_boxFillColor
128
theLabel.strokeColor = wedgeStyle.label_strokeColor
129
theLabel.strokeWidth = wedgeStyle.label_strokeWidth
130
_text = wedgeStyle.label_text
131
if _text is None: _text = text
132
theLabel._text = _text
133
theLabel.leading = wedgeStyle.label_leading
134
theLabel.width = wedgeStyle.label_width
135
theLabel.maxWidth = wedgeStyle.label_maxWidth
136
theLabel.height = wedgeStyle.label_height
137
theLabel.textAnchor = wedgeStyle.label_textAnchor
138
theLabel.visible = wedgeStyle.label_visible
139
theLabel.topPadding = wedgeStyle.label_topPadding
140
theLabel.leftPadding = wedgeStyle.label_leftPadding
141
theLabel.rightPadding = wedgeStyle.label_rightPadding
142
theLabel.bottomPadding = wedgeStyle.label_bottomPadding
143
theLabel.fontSize = wedgeStyle.fontSize
144
theLabel.fontName = wedgeStyle.fontName
145
theLabel.fillColor = wedgeStyle.fontColor
148
def _fixLabels(labels,n):
153
if i>0: labels = labels + ['']*i
158
x = AttrMapValue(isNumber, desc='X position of the chart within its container.'),
159
y = AttrMapValue(isNumber, desc='Y position of the chart within its container.'),
160
width = AttrMapValue(isNumber, desc='width of pie bounding box. Need not be same as width.'),
161
height = AttrMapValue(isNumber, desc='height of pie bounding box. Need not be same as height.'),
162
data = AttrMapValue(isListOfNumbers, desc='list of numbers defining wedge sizes; need not sum to 1'),
163
labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"),
164
startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"),
165
direction = AttrMapValue( OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"),
166
slices = AttrMapValue(None, desc="collection of wedge descriptor objects"),
167
simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel"),
168
other_threshold = AttrMapValue(isNumber, desc='A value for doing thresh holding, not used yet.'),
178
self.labels = None # or list of strings
180
self.direction = "clockwise"
181
self.simpleLabels = 1
183
self.slices = TypedPropertyCollection(WedgeProperties)
184
self.slices[0].fillColor = colors.darkcyan
185
self.slices[1].fillColor = colors.blueviolet
186
self.slices[2].fillColor = colors.blue
187
self.slices[3].fillColor = colors.cyan
190
d = Drawing(200, 100)
197
pc.data = [10,20,30,40,50,60]
198
pc.labels = ['a','b','c','d','e','f']
200
pc.slices.strokeWidth=0.5
201
pc.slices[3].popout = 10
202
pc.slices[3].strokeWidth = 2
203
pc.slices[3].strokeDashArray = [2,2]
204
pc.slices[3].labelRadius = 1.75
205
pc.slices[3].fontColor = colors.red
206
pc.slices[0].fillColor = colors.darkcyan
207
pc.slices[1].fillColor = colors.blueviolet
208
pc.slices[2].fillColor = colors.blue
209
pc.slices[3].fillColor = colors.cyan
210
pc.slices[4].fillColor = colors.aquamarine
211
pc.slices[5].fillColor = colors.cadetblue
212
pc.slices[6].fillColor = colors.lightcoral
217
def normalizeData(self):
218
from operator import add
220
self._sum = sum = float(reduce(add,data,0))
221
return abs(sum)>=1e-8 and map(lambda x,f=360./sum: f*x, data) or len(data)*[0]
223
def makeWedges(self):
224
# normalize slice data
225
normData = self.normalizeData()
227
labels = _fixLabels(self.labels,n)
229
xradius = self.width/2.0
230
yradius = self.height/2.0
231
centerx = self.x + xradius
232
centery = self.y + yradius
234
if self.direction == "anticlockwise":
241
styleCount = len(self.slices)
243
startAngle = self.startAngle #% 360
244
for angle in normData:
245
endAngle = (startAngle + (angle * whichWay)) #% 360
246
if abs(startAngle-endAngle)>=1e-5:
247
if startAngle < endAngle:
254
#if we didn't use %stylecount here we'd end up with the later wedges
255
#all having the default style
256
wedgeStyle = self.slices[i%styleCount]
259
cx, cy = centerx, centery
260
if wedgeStyle.popout <> 0:
262
averageAngle = (a1+a2)/2.0
263
aveAngleRadians = averageAngle * pi/180.0
264
popdistance = wedgeStyle.popout
265
cx = centerx + popdistance * cos(aveAngleRadians)
266
cy = centery + popdistance * sin(aveAngleRadians)
269
theWedge = Wedge(cx, cy, xradius, a1, a2, yradius=yradius)
271
theWedge = Ellipse(cx, cy, xradius, yradius)
273
theWedge.fillColor = wedgeStyle.fillColor
274
theWedge.strokeColor = wedgeStyle.strokeColor
275
theWedge.strokeWidth = wedgeStyle.strokeWidth
276
theWedge.strokeDashArray = wedgeStyle.strokeDashArray
281
averageAngle = (a1+a2)/2.0
282
aveAngleRadians = averageAngle*pi/180.0
283
labelRadius = wedgeStyle.labelRadius
284
labelX = cx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius)
285
labelY = cy + (0.5 * self.height * sin(aveAngleRadians) * labelRadius)
286
_addWedgeLabel(self,text,g.add,averageAngle,labelX,labelY,wedgeStyle)
288
startAngle = endAngle
295
g.add(self.makeWedges())
298
class LegendedPie(Pie):
299
"""Pie with a two part legend (one editable with swatches, one hidden without swatches)."""
301
_attrMap = AttrMap(BASE=Pie,
302
drawLegend = AttrMapValue(isBoolean, desc="If true then create and draw legend"),
303
legend1 = AttrMapValue(None, desc="Handle to legend for pie"),
304
legendNumberFormat = AttrMapValue(None, desc="Formatting routine for number on right hand side of legend."),
305
legendNumberOffset = AttrMapValue(isNumber, desc="Horizontal space between legend and numbers on r/hand side"),
306
pieAndLegend_colors = AttrMapValue(isListOfColors, desc="Colours used for both swatches and pie"),
307
legend_names = AttrMapValue(isNoneOrListOfNoneOrStrings, desc="Names used in legend (or None)"),
308
legend_data = AttrMapValue(isNoneOrListOfNoneOrNumbers, desc="Numbers used on r/hand side of legend (or None)"),
309
leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'),
310
rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'),
311
topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'),
312
bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
321
self.data = [38.4, 20.7, 18.9, 15.4, 6.6]
323
self.direction = 'clockwise'
324
PCMYKColor, black = colors.PCMYKColor, colors.black
325
self.pieAndLegend_colors = [PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV'),
326
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV'),
327
PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=75),
328
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=75),
329
PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=50),
330
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=50)]
332
#Allows us up to six 'wedges' to be coloured
333
self.slices[0].fillColor=self.pieAndLegend_colors[0]
334
self.slices[1].fillColor=self.pieAndLegend_colors[1]
335
self.slices[2].fillColor=self.pieAndLegend_colors[2]
336
self.slices[3].fillColor=self.pieAndLegend_colors[3]
337
self.slices[4].fillColor=self.pieAndLegend_colors[4]
338
self.slices[5].fillColor=self.pieAndLegend_colors[5]
340
self.slices.strokeWidth = 0.75
341
self.slices.strokeColor = black
344
self.legendNumberOffset = 51
345
self.legendNumberFormat = '%.1f%%'
346
self.legend_data = self.data
349
from reportlab.graphics.charts.legends import Legend
350
self.legend1 = Legend()
351
self.legend1.x = self.width+legendOffset
352
self.legend1.y = self.height
353
self.legend1.deltax = 5.67
354
self.legend1.deltay = 14.17
355
self.legend1.dxTextSpace = 11.39
356
self.legend1.dx = 5.67
357
self.legend1.dy = 5.67
358
self.legend1.columnMaximum = 7
359
self.legend1.alignment = 'right'
360
self.legend_names = ['AAA:','AA:','A:','BBB:','NR:']
361
for f in range(0,len(self.data)):
362
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f]))
363
self.legend1.fontName = "Helvetica-Bold"
364
self.legend1.fontSize = 6
365
self.legend1.strokeColor = black
366
self.legend1.strokeWidth = 0.5
368
self._legend2 = Legend()
369
self._legend2.dxTextSpace = 0
371
self._legend2.alignment = 'right'
372
self._legend2.fontName = "Helvetica-Oblique"
373
self._legend2.fontSize = 6
374
self._legend2.strokeColor = self.legend1.strokeColor
377
self.rightPadding = 5
379
self.bottomPadding = 5
384
self.legend1.colorNamePairs = []
385
self._legend2.colorNamePairs = []
386
for f in range(0,len(self.data)):
387
if self.legend_names == None:
388
self.slices[f].fillColor = self.pieAndLegend_colors[f]
389
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], None))
392
self.slices[f].fillColor = self.pieAndLegend_colors[f]
393
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f]))
395
self.slices[f].fillColor = self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)]
396
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)], self.legend_names[f]))
397
if self.legend_data != None:
398
ldf = self.legend_data[f]
399
lNF = self.legendNumberFormat
400
from types import StringType
401
if ldf is None or lNF is None:
403
elif type(lNF) is StringType:
408
p = self.legend_names[f]
409
if self.legend_data != None:
410
ldf = self.legend_data[f]
411
lNF = self.legendNumberFormat
412
if ldf is None or lNF is None:
414
elif type(lNF) is StringType:
419
msg = "Unknown formatter type %s, expected string or function" % self.legendNumberFormat
421
self._legend2.colorNamePairs.append((None,ldf))
425
#hide from user - keeps both sides lined up!
426
self._legend2.x = self.legend1.x+self.legendNumberOffset
427
self._legend2.y = self.legend1.y
428
self._legend2.deltax = self.legend1.deltax
429
self._legend2.deltay = self.legend1.deltay
430
self._legend2.dy = self.legend1.dy
431
self._legend2.columnMaximum = self.legend1.columnMaximum
433
p.shift(self.leftPadding, self.bottomPadding)
436
def _getDrawingDimensions(self):
437
tx = self.rightPadding
439
tx = tx+self.legend1.x+self.legendNumberOffset #self._legend2.x
440
tx = tx + self._legend2._calculateMaxWidth(self._legend2.colorNamePairs)
441
ty = self.bottomPadding+self.height+self.topPadding
444
def demo(self, drawing=None):
446
tx,ty = self._getDrawingDimensions()
447
drawing = Drawing(tx, ty)
448
drawing.add(self.draw())
451
from utils3d import _getShaded, _2rad, _360, _pi_2, _2pi
452
class Wedge3dProperties(PropHolder):
453
"""This holds descriptive information about the wedges in a pie chart.
455
It is not to be confused with the 'wedge itself'; this just holds
456
a recipe for how to format one, and does not allow you to hack the
457
angles. It can format a genuine Wedge object for you with its
461
fillColor = AttrMapValue(isColorOrNone),
462
fillColorShaded = AttrMapValue(isColorOrNone),
463
fontColor = AttrMapValue(isColorOrNone),
464
fontName = AttrMapValue(isString),
465
fontSize = AttrMapValue(isNumber),
466
label_angle = AttrMapValue(isNumber),
467
label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
468
label_boxAnchor = AttrMapValue(isBoxAnchor),
469
label_boxFillColor = AttrMapValue(isColorOrNone),
470
label_boxStrokeColor = AttrMapValue(isColorOrNone),
471
label_boxStrokeWidth = AttrMapValue(isNumber),
472
label_dx = AttrMapValue(isNumber),
473
label_dy = AttrMapValue(isNumber),
474
label_height = AttrMapValue(isNumberOrNone),
475
label_leading = AttrMapValue(isNumberOrNone),
476
label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
477
label_maxWidth = AttrMapValue(isNumberOrNone),
478
label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
479
label_strokeColor = AttrMapValue(isColorOrNone),
480
label_strokeWidth = AttrMapValue(isNumber),
481
label_text = AttrMapValue(isStringOrNone),
482
label_textAnchor = AttrMapValue(isTextAnchor),
483
label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
484
label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
485
label_width = AttrMapValue(isNumberOrNone),
486
labelRadius = AttrMapValue(isNumber),
487
popout = AttrMapValue(isNumber),
488
shading = AttrMapValue(isNumber),
489
strokeColor = AttrMapValue(isColorOrNone),
490
strokeColorShaded = AttrMapValue(isColorOrNone),
491
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
492
strokeWidth = AttrMapValue(isNumber),
493
visible = AttrMapValue(isBoolean,'set to false to skip displaying'),
500
self.strokeColorShaded = self.fillColorShaded = self.fillColor = None
501
self.strokeColor = STATE_DEFAULTS["strokeColor"]
502
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
504
self.fontName = STATE_DEFAULTS["fontName"]
505
self.fontSize = STATE_DEFAULTS["fontSize"]
506
self.fontColor = STATE_DEFAULTS["fillColor"]
507
self.labelRadius = 1.2
508
self.label_dx = self.label_dy = self.label_angle = 0
509
self.label_text = None
510
self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
511
self.label_boxAnchor = 'c'
512
self.label_boxStrokeColor = None #boxStroke
513
self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
514
self.label_boxFillColor = None
515
self.label_strokeColor = None
516
self.label_strokeWidth = 0.1
517
self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None
518
self.label_textAnchor = 'start'
519
self.label_visible = 1
522
def __init__(self,lo,hi):
528
self.mid = (lo+hi)*0.5
531
return '_SL3D(%.2f,%.2f)' % (self.lo,self.hi)
535
_attrMap = AttrMap(BASE=Pie,
536
perspective = AttrMapValue(isNumber, desc='A flattening parameter.'),
537
depth_3d = AttrMapValue(isNumber, desc='depth of the pie.'),
538
angle_3d = AttrMapValue(isNumber, desc='The view angle.'),
545
return self.slices[i].popout or 0
548
return self._cx+(d and self._xdepth_3d or 0)+self._popout(i)*cos(_2rad(self._sl3d[i].mid))
550
return self._cy+(d and self._ydepth_3d or 0)+self._popout(i)*sin(_2rad(self._sl3d[i].mid))
552
return self.CX(i,d)+self._radiusx*cos(_2rad(o))
554
return self.CY(i,d)+self._radiusy*sin(_2rad(o))
556
def rad_dist(self,a):
558
return min(abs(a-_3dva),abs(a-_3dva+360))
565
self.data = [12.50,20.10,2.00,22.00,5.00,18.00,13.00]
566
self.labels = None # or list of strings
568
self.direction = "clockwise"
569
self.simpleLabels = 1
570
self.slices = TypedPropertyCollection(Wedge3dProperties)
571
self.slices[0].fillColor = colors.darkcyan
572
self.slices[1].fillColor = colors.blueviolet
573
self.slices[2].fillColor = colors.blue
574
self.slices[3].fillColor = colors.cyan
575
self.slices[4].fillColor = colors.azure
576
self.slices[5].fillColor = colors.crimson
577
self.slices[6].fillColor = colors.darkviolet
579
def _fillSide(self,L,i,angle,strokeColor,strokeWidth,fillColor):
580
rd = self.rad_dist(angle)
581
if rd<self.rad_dist(self._sl3d[i].mid):
582
p = [self.CX(i,0),self.CY(i,0),
583
self.CX(i,1),self.CY(i,1),
584
self.OX(i,angle,1),self.OY(i,angle,1),
585
self.OX(i,angle,0),self.OY(i,angle,0)]
586
L.append((rd,Polygon(p, strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)))
590
_3d_angle = self.angle_3d
591
_3dva = self._3dva = _360(_3d_angle+90)
593
self._xdepth_3d = cos(a0)*self.depth_3d
594
self._ydepth_3d = sin(a0)*self.depth_3d
595
self._cx = self.x+self.width/2.0
596
self._cy = self.y+(self.height - self._ydepth_3d)/2.0
597
radius = self._radius = self._cx-self.x
598
self._radiusx = radiusx = radius
599
self._radiusy = radiusy = (1.0 - self.perspective/100.0)*radius
600
data = self.normalizeData()
607
rad_dist = self.rad_dist
608
_fillSide = self._fillSide
610
_sl3d = self._sl3d = []
612
last = _360(self.startAngle)
613
a0 = self.direction=='clockwise' and -1 or 1
616
angle1, angle0 = last, v+last
618
if a0>0: angle0, angle1 = angle1, angle0
619
_sl3d.append(_SL3D(angle0,angle1))
620
#print '%d: %.2f %.2f --> %s' %(len(_sl3d)-1,angle0,angle1,_sl3d[-1])
622
labels = _fixLabels(self.labels,n)
629
class WedgeLabel3d(WedgeLabel):
630
def _checkDXY(self,ba):
632
if not hasattr(self,'_ody'):
634
self.dy = -self._ody + self._ydepth_3d
635
WedgeLabel3d._ydepth_3d = self._ydepth_3d
639
if not style.visible: continue
643
if abs(hi-lo)<=1e-7: continue
644
fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading)
645
strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor
646
strokeWidth = style.strokeWidth
651
#background shaded pie bottom
652
g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy,
653
strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,
656
if lo < a0 < hi: angle0 = a0
657
if lo < a1 < hi: angle1 = a1
659
p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)
660
p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1)
661
p.lineTo(OX(i,angle1,0),OY(i,angle1,0))
662
p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1)
664
if angle0<=_3dva and angle1>=_3dva:
667
rd = min(rad_dist(angle0),rad_dist(angle1))
669
_fillSide(S,i,lo,strokeColor,strokeWidth,fillColor)
670
_fillSide(S,i,hi,strokeColor,strokeWidth,fillColor)
673
fillColor = style.fillColor
674
strokeColor = style.strokeColor or fillColor
675
T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy,
676
strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1))
680
rat = style.labelRadius
684
_addWedgeLabel(self,text,L.append,mid,OX(i,mid,0),OY(i,mid,0),style,labelClass=WedgeLabel3d)
685
self._radiusx = radiusx
686
self._radiusy = radiusy
688
S.sort(lambda a,b: -cmp(a[0],b[0]))
689
map(g.add,map(lambda x:x[1],S)+T+L)
693
d = Drawing(200, 100)
700
pc.data = [10,20,30,40,50,60]
701
pc.labels = ['a','b','c','d','e','f']
703
pc.slices.strokeWidth=0.5
704
pc.slices[3].popout = 10
705
pc.slices[3].strokeWidth = 2
706
pc.slices[3].strokeDashArray = [2,2]
707
pc.slices[3].labelRadius = 1.75
708
pc.slices[3].fontColor = colors.red
709
pc.slices[0].fillColor = colors.darkcyan
710
pc.slices[1].fillColor = colors.blueviolet
711
pc.slices[2].fillColor = colors.blue
712
pc.slices[3].fillColor = colors.cyan
713
pc.slices[4].fillColor = colors.aquamarine
714
pc.slices[5].fillColor = colors.cadetblue
715
pc.slices[6].fillColor = colors.lightcoral
716
self.slices[1].visible = 0
717
self.slices[3].visible = 1
718
self.slices[4].visible = 1
719
self.slices[5].visible = 1
720
self.slices[6].visible = 0
727
"Make a degenerated pie chart with only one slice."
729
d = Drawing(400, 200)
736
pc.slices.strokeWidth=1#0.5
744
"Make a degenerated pie chart with only one slice."
746
d = Drawing(400, 200)
755
pc.slices.strokeWidth=1#0.5
763
"Make a typical pie chart with with one slice treated in a special way."
765
d = Drawing(400, 200)
770
pc.data = [10, 20, 30, 40, 50, 60]
771
pc.labels = ['a', 'b', 'c', 'd', 'e', 'f']
773
pc.slices.strokeWidth=1#0.5
774
pc.slices[3].popout = 20
775
pc.slices[3].strokeWidth = 2
776
pc.slices[3].strokeDashArray = [2,2]
777
pc.slices[3].labelRadius = 1.75
778
pc.slices[3].fontColor = colors.red
786
"Make a pie chart with nine slices."
788
d = Drawing(400, 200)
793
pc.data = [0.31, 0.148, 0.108,
796
pc.labels = ['1', '2', '3', '4', '5', '6', '7', '8', 'X']
800
pc.slices.strokeWidth=1#0.5
802
pc.slices[0].fillColor = colors.steelblue
803
pc.slices[1].fillColor = colors.thistle
804
pc.slices[2].fillColor = colors.cornflower
805
pc.slices[3].fillColor = colors.lightsteelblue
806
pc.slices[4].fillColor = colors.aquamarine
807
pc.slices[5].fillColor = colors.cadetblue
808
pc.slices[6].fillColor = colors.lightcoral
809
pc.slices[7].fillColor = colors.tan
810
pc.slices[8].fillColor = colors.darkseagreen
818
"Make a pie chart with a very slim slice."
820
d = Drawing(400, 200)
826
pc.data = [74, 1, 25]
830
pc.slices.strokeWidth=1#0.5
831
pc.slices[0].fillColor = colors.steelblue
832
pc.slices[1].fillColor = colors.thistle
833
pc.slices[2].fillColor = colors.cornflower
841
"Make a pie chart with several very slim slices."
843
d = Drawing(400, 200)
849
pc.data = [74, 1, 1, 1, 1, 22]
853
pc.slices.strokeWidth=1#0.5
854
pc.slices[0].fillColor = colors.steelblue
855
pc.slices[1].fillColor = colors.thistle
856
pc.slices[2].fillColor = colors.cornflower
857
pc.slices[3].fillColor = colors.lightsteelblue
858
pc.slices[4].fillColor = colors.aquamarine
859
pc.slices[5].fillColor = colors.cadetblue