40
44
# Exception Classes
41
45
# -----------------------------------------------------------------------------
43
48
class PyGoogleChartException(Exception):
46
52
class DataOutOfRangeException(PyGoogleChartException):
49
56
class UnknownDataTypeException(PyGoogleChartException):
52
60
class NoDataGivenException(PyGoogleChartException):
55
64
class InvalidParametersException(PyGoogleChartException):
68
class BadContentTypeException(PyGoogleChartException):
59
73
# -----------------------------------------------------------------------------
61
76
class Data(object):
62
78
def __init__(self, data):
63
79
assert(type(self) != Data) # This is an abstract class
66
83
class SimpleData(Data):
67
84
enc_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
68
86
def __repr__(self):
70
88
for data in self.data:
130
155
# -----------------------------------------------------------------------------
132
158
class Axis(object):
137
163
TYPES = (BOTTOM, TOP, LEFT, RIGHT)
138
def __init__(self, axis, **kw):
139
assert(axis in Axis.TYPES)
165
def __init__(self, axis_index, axis_type, **kw):
166
assert(axis_type in Axis.TYPES)
140
167
self.has_style = False
168
self.axis_index = axis_index
169
self.axis_type = axis_type
142
170
self.positions = None
143
def set_index(self, index):
172
def set_index(self, axis_index):
173
self.axis_index = axis_index
145
175
def set_positions(self, positions):
146
176
self.positions = positions
147
178
def set_style(self, colour, font_size=None, alignment=None):
148
179
_check_colour(colour)
149
180
self.colour = colour
150
181
self.font_size = font_size
151
182
self.alignment = alignment
152
183
self.has_style = True
153
185
def style_to_url(self):
155
bits.append(str(self.index))
187
bits.append(str(self.axis_index))
156
188
bits.append(self.colour)
157
189
if self.font_size is not None:
158
190
bits.append(str(self.font_size))
159
191
if self.alignment is not None:
160
192
bits.append(str(self.alignment))
161
193
return ','.join(bits)
162
195
def positions_to_url(self):
164
bits.append(str(self.index))
165
bits += [ str(a) for a in self.positions ]
197
bits.append(str(self.axis_index))
198
bits += [str(a) for a in self.positions]
166
199
return ','.join(bits)
168
202
class LabelAxis(Axis):
169
def __init__(self, axis, values, **kwargs):
170
Axis.__init__(self, axis, **kwargs)
171
self.values = [ str(a) for a in values ]
204
def __init__(self, axis_index, axis_type, values, **kwargs):
205
Axis.__init__(self, axis_index, axis_type, **kwargs)
206
self.values = [str(a) for a in values]
172
208
def __repr__(self):
173
return '%i:|%s' % (self.index, '|'.join(self.values))
209
return '%i:|%s' % (self.axis_index, '|'.join(self.values))
175
212
class RangeAxis(Axis):
176
def __init__(self, axis, low, high, **kwargs):
177
Axis.__init__(self, axis, **kwargs)
214
def __init__(self, axis_index, axis_type, low, high, **kwargs):
215
Axis.__init__(self, axis_index, axis_type, **kwargs)
180
219
def __repr__(self):
181
return '%i,%s,%s' % (self.index, self.low, self.high)
220
return '%i,%s,%s' % (self.axis_index, self.low, self.high)
184
223
# -----------------------------------------------------------------------------
186
226
class Chart(object):
227
"""Abstract class for all chart types.
229
width are height specify the dimensions of the image. title sets the title
230
of the chart. legend requires a list that corresponds to datasets.
188
233
BASE_URL = 'http://chart.apis.google.com/chart?'
189
234
BACKGROUND = 'bg'
360
414
# -------------------------------------------------------------------------
362
def set_axis_labels(self, axis, values):
363
assert(axis in Axis.TYPES)
364
self.axis[axis] = LabelAxis(axis, values)
366
def set_axis_range(self, axis, low, high):
367
assert(axis in Axis.TYPES)
368
self.axis[axis] = RangeAxis(axis, low, high)
370
def set_axis_positions(self, axis, positions):
371
assert(axis in Axis.TYPES)
372
if not self.axis[axis]:
373
raise InvalidParametersException('Please create an axis first')
374
self.axis[axis].set_positions(positions)
376
def set_axis_style(self, axis, colour, font_size=None, alignment=None):
377
assert(axis in Axis.TYPES)
378
if not self.axis[axis]:
379
raise InvalidParametersException('Please create an axis first')
380
self.axis[axis].set_style(colour, font_size, alignment)
416
def set_axis_labels(self, axis_type, values):
417
assert(axis_type in Axis.TYPES)
418
values = [ urllib.quote(a) for a in values ]
419
axis_index = len(self.axis)
420
axis = LabelAxis(axis_index, axis_type, values)
421
self.axis.append(axis)
424
def set_axis_range(self, axis_type, low, high):
425
assert(axis_type in Axis.TYPES)
426
axis_index = len(self.axis)
427
axis = RangeAxis(axis_index, axis_type, low, high)
428
self.axis.append(axis)
431
def set_axis_positions(self, axis_index, positions):
433
self.axis[axis_index].set_positions(positions)
435
raise InvalidParametersException('Axis index %i has not been ' \
438
def set_axis_style(self, axis_index, colour, font_size=None, \
441
self.axis[axis_index].set_style(colour, font_size, alignment)
443
raise InvalidParametersException('Axis index %i has not been ' \
382
446
def axis_to_url(self):
383
447
available_axis = []
418
478
# -------------------------------------------------------------------------
420
480
def markers_to_url(self):
421
return 'chm=%s' % '|'.join([ ','.join(a) for a in self.markers ])
481
return 'chm=%s' % '|'.join([','.join(a) for a in self.markers])
423
483
def add_marker(self, index, point, marker_type, colour, size):
424
484
self.markers.append((marker_type, colour, str(index), str(point), \
437
497
def add_fill_simple(self, colour):
438
498
self.markers.append(('B', colour, '1', '1', '1'))
440
501
class ScatterChart(Chart):
441
503
def __init__(self, *args, **kwargs):
442
504
Chart.__init__(self, *args, **kwargs)
443
506
def type_to_url(self):
447
510
class LineChart(Chart):
448
512
def __init__(self, *args, **kwargs):
513
assert(type(self) != LineChart) # This is an abstract class
449
514
Chart.__init__(self, *args, **kwargs)
450
515
self.line_styles = {}
452
518
def set_line_style(self, index, thickness=1, line_segment=None, \
453
519
blank_segment=None):
457
523
value.append(str(line_segment))
458
524
value.append(str(blank_segment))
459
525
self.line_styles[index] = value
460
527
def set_grid(self, x_step, y_step, line_segment=1, \
461
528
blank_segment=0):
462
529
self.grid = '%s,%s,%s,%s' % (x_step, y_step, line_segment, \
464
532
def get_url_bits(self):
465
533
url_bits = Chart.get_url_bits(self)
466
534
if self.line_styles:
477
545
url_bits.append('chg=%s' % self.grid)
480
549
class SimpleLineChart(LineChart):
481
551
def type_to_url(self):
484
555
class XYLineChart(LineChart):
485
557
def type_to_url(self):
488
561
class BarChart(Chart):
489
563
def __init__(self, *args, **kwargs):
490
564
assert(type(self) != BarChart) # This is an abstract class
491
565
Chart.__init__(self, *args, **kwargs)
492
566
self.bar_width = None
493
568
def set_bar_width(self, bar_width):
494
569
self.bar_width = bar_width
495
571
def get_url_bits(self):
496
572
url_bits = Chart.get_url_bits(self)
497
573
url_bits.append('chbh=%i' % self.bar_width)
500
577
class StackedHorizontalBarChart(BarChart):
501
579
def type_to_url(self):
504
583
class StackedVerticalBarChart(BarChart):
505
585
def type_to_url(self):
508
589
class GroupedBarChart(BarChart):
509
591
def __init__(self, *args, **kwargs):
510
592
assert(type(self) != GroupedBarChart) # This is an abstract class
511
593
BarChart.__init__(self, *args, **kwargs)
512
594
self.bar_spacing = None
513
596
def set_bar_spacing(self, spacing):
514
597
self.bar_spacing = spacing
515
599
def get_url_bits(self):
516
600
# Skip 'BarChart.get_url_bits' and call Chart directly so the parent
517
601
# doesn't add "chbh" before we do.
525
609
url_bits.append('chbh=%i' % self.bar_width)
528
613
class GroupedHorizontalBarChart(GroupedBarChart):
529
615
def type_to_url(self):
532
619
class GroupedVerticalBarChart(GroupedBarChart):
533
621
def type_to_url(self):
536
625
class PieChart(Chart):
537
627
def __init__(self, *args, **kwargs):
538
628
assert(type(self) != PieChart) # This is an abstract class
539
629
Chart.__init__(self, *args, **kwargs)
540
630
self.pie_labels = []
541
632
def set_pie_labels(self, labels):
542
self.pie_labels = labels
633
self.pie_labels = [urllib.quote(a) for a in labels]
543
635
def get_url_bits(self):
544
636
url_bits = Chart.get_url_bits(self)
545
637
if self.pie_labels:
546
638
url_bits.append('chl=%s' % '|'.join(self.pie_labels))
549
642
class PieChart2D(PieChart):
550
644
def type_to_url(self):
553
648
class PieChart3D(PieChart):
554
650
def type_to_url(self):
557
654
class VennChart(Chart):
558
656
def type_to_url(self):
562
661
chart = GroupedVerticalBarChart(320, 200)
563
662
chart = PieChart2D(320, 200)
564
663
chart = ScatterChart(320, 200)
565
664
chart = SimpleLineChart(320, 200)
566
sine_data = [ math.sin(float(a) / 10) * 2000 + 2000 for a in xrange(100) ]
567
random_data = [ a * random.random() * 30 for a in xrange(40) ]
568
random_data2 = [ random.random() * 4000 for a in xrange(10) ]
665
sine_data = [math.sin(float(a) / 10) * 2000 + 2000 for a in xrange(100)]
666
random_data = [a * random.random() * 30 for a in xrange(40)]
667
random_data2 = [random.random() * 4000 for a in xrange(10)]
569
668
# chart.set_bar_width(50)
570
669
# chart.set_bar_spacing(0)
571
670
chart.add_data(sine_data)
581
680
# 'aabbcc00', 0.5)
582
681
# chart.fill_linear_stripes(Chart.CHART, 20, '204070', .2, '300040', .2,
583
682
# 'aabbcc00', 0.2)
584
chart.set_axis_range(Axis.LEFT, 0, 10)
585
chart.set_axis_range(Axis.RIGHT, 5, 30)
586
chart.set_axis_labels(Axis.BOTTOM, [1, 25, 95])
587
chart.set_axis_positions(Axis.BOTTOM, [1, 25, 95])
588
chart.set_axis_style(Axis.BOTTOM, 'FFFFFF', 15)
683
axis_left_index = chart.set_axis_range(Axis.LEFT, 0, 10)
684
axis_left_index = chart.set_axis_range(Axis.LEFT, 0, 10)
685
axis_left_index = chart.set_axis_range(Axis.LEFT, 0, 10)
686
axis_right_index = chart.set_axis_range(Axis.RIGHT, 5, 30)
687
axis_bottom_index = chart.set_axis_labels(Axis.BOTTOM, [1, 25, 95])
688
chart.set_axis_positions(axis_bottom_index, [1, 25, 95])
689
chart.set_axis_style(axis_bottom_index, '003050', 15)
590
691
# chart.set_pie_labels(('apples', 'oranges', 'bananas'))