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