33
32
reo_colour = re.compile('^([A-Fa-f0-9]{2,2}){3,4}$')
36
34
def _check_colour(colour):
37
35
if not reo_colour.match(colour):
38
36
raise InvalidParametersException('Colours need to be in ' \
42
40
# Exception Classes
43
41
# -----------------------------------------------------------------------------
46
43
class PyGoogleChartException(Exception):
50
46
class DataOutOfRangeException(PyGoogleChartException):
54
49
class UnknownDataTypeException(PyGoogleChartException):
58
52
class NoDataGivenException(PyGoogleChartException):
62
55
class InvalidParametersException(PyGoogleChartException):
66
class BadContentTypeException(PyGoogleChartException):
71
59
# -----------------------------------------------------------------------------
74
61
class Data(object):
76
62
def __init__(self, data):
77
63
assert(type(self) != Data) # This is an abstract class
81
66
class SimpleData(Data):
82
67
enc_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
84
68
def __repr__(self):
86
70
for data in self.data:
115
96
raise DataOutOfRangeException()
116
97
encoded_data.append(','.join(sub_data))
117
98
return 'chd=t:' + '|'.join(encoded_data)
124
103
class ExtendedData(Data):
126
105
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'
128
106
def __repr__(self):
129
107
encoded_data = []
130
108
enc_size = len(ExtendedData.enc_map)
153
130
# -----------------------------------------------------------------------------
156
132
class Axis(object):
161
137
TYPES = (BOTTOM, TOP, LEFT, RIGHT)
163
138
def __init__(self, axis, **kw):
164
139
assert(axis in Axis.TYPES)
165
140
self.has_style = False
166
141
self.index = None
167
142
self.positions = None
169
143
def set_index(self, index):
170
144
self.index = index
172
145
def set_positions(self, positions):
173
146
self.positions = positions
175
147
def set_style(self, colour, font_size=None, alignment=None):
176
148
_check_colour(colour)
177
149
self.colour = colour
178
150
self.font_size = font_size
179
151
self.alignment = alignment
180
152
self.has_style = True
182
153
def style_to_url(self):
184
155
bits.append(str(self.index))
188
159
if self.alignment is not None:
189
160
bits.append(str(self.alignment))
190
161
return ','.join(bits)
192
162
def positions_to_url(self):
194
164
bits.append(str(self.index))
195
bits += [str(a) for a in self.positions]
165
bits += [ str(a) for a in self.positions ]
196
166
return ','.join(bits)
199
168
class LabelAxis(Axis):
201
169
def __init__(self, axis, values, **kwargs):
202
170
Axis.__init__(self, axis, **kwargs)
203
self.values = [str(a) for a in values]
171
self.values = [ str(a) for a in values ]
205
172
def __repr__(self):
206
173
return '%i:|%s' % (self.index, '|'.join(self.values))
209
175
class RangeAxis(Axis):
211
176
def __init__(self, axis, low, high, **kwargs):
212
177
Axis.__init__(self, axis, **kwargs)
216
180
def __repr__(self):
217
181
return '%i,%s,%s' % (self.index, self.low, self.high)
220
184
# -----------------------------------------------------------------------------
223
186
class Chart(object):
224
"""Abstract class for all chart types.
226
width are height specify the dimensions of the image. title sets the title
227
of the chart. legend requires a list that corresponds to datasets.
230
188
BASE_URL = 'http://chart.apis.google.com/chart?'
231
189
BACKGROUND = 'bg'
294
252
# -------------------------------------------------------------------------
296
def download(self, file_name):
297
opener = urllib2.urlopen(self.get_url())
299
if opener.headers['content-type'] != 'image/png':
300
raise BadContentTypeException('Server responded with a ' \
301
'content-type of %s' % opener.headers['content-type'])
253
def download_graph(self, file_name):
303
254
open(file_name, 'wb').write(urllib.urlopen(self.get_url()).read())
305
256
# Simple settings
468
418
# -------------------------------------------------------------------------
470
420
def markers_to_url(self):
471
return 'chm=%s' % '|'.join([','.join(a) for a in self.markers])
421
return 'chm=%s' % '|'.join([ ','.join(a) for a in self.markers ])
473
423
def add_marker(self, index, point, marker_type, colour, size):
474
424
self.markers.append((marker_type, colour, str(index), str(point), \
487
437
def add_fill_simple(self, colour):
488
438
self.markers.append(('B', colour, '1', '1', '1'))
491
440
class ScatterChart(Chart):
493
441
def __init__(self, *args, **kwargs):
494
442
Chart.__init__(self, *args, **kwargs)
496
443
def type_to_url(self):
500
447
class LineChart(Chart):
502
448
def __init__(self, *args, **kwargs):
503
assert(type(self) != LineChart) # This is an abstract class
504
449
Chart.__init__(self, *args, **kwargs)
505
450
self.line_styles = {}
508
452
def set_line_style(self, index, thickness=1, line_segment=None, \
509
453
blank_segment=None):
513
457
value.append(str(line_segment))
514
458
value.append(str(blank_segment))
515
459
self.line_styles[index] = value
517
460
def set_grid(self, x_step, y_step, line_segment=1, \
518
461
blank_segment=0):
519
462
self.grid = '%s,%s,%s,%s' % (x_step, y_step, line_segment, \
522
464
def get_url_bits(self):
523
465
url_bits = Chart.get_url_bits(self)
524
466
if self.line_styles:
535
477
url_bits.append('chg=%s' % self.grid)
539
480
class SimpleLineChart(LineChart):
541
481
def type_to_url(self):
545
484
class XYLineChart(LineChart):
547
485
def type_to_url(self):
551
488
class BarChart(Chart):
553
489
def __init__(self, *args, **kwargs):
554
490
assert(type(self) != BarChart) # This is an abstract class
555
491
Chart.__init__(self, *args, **kwargs)
556
492
self.bar_width = None
558
493
def set_bar_width(self, bar_width):
559
494
self.bar_width = bar_width
561
495
def get_url_bits(self):
562
496
url_bits = Chart.get_url_bits(self)
563
497
url_bits.append('chbh=%i' % self.bar_width)
567
500
class StackedHorizontalBarChart(BarChart):
569
501
def type_to_url(self):
573
504
class StackedVerticalBarChart(BarChart):
575
505
def type_to_url(self):
579
508
class GroupedBarChart(BarChart):
581
509
def __init__(self, *args, **kwargs):
582
510
assert(type(self) != GroupedBarChart) # This is an abstract class
583
511
BarChart.__init__(self, *args, **kwargs)
584
512
self.bar_spacing = None
586
513
def set_bar_spacing(self, spacing):
587
514
self.bar_spacing = spacing
589
515
def get_url_bits(self):
590
516
# Skip 'BarChart.get_url_bits' and call Chart directly so the parent
591
517
# doesn't add "chbh" before we do.
599
525
url_bits.append('chbh=%i' % self.bar_width)
603
528
class GroupedHorizontalBarChart(GroupedBarChart):
605
529
def type_to_url(self):
609
532
class GroupedVerticalBarChart(GroupedBarChart):
611
533
def type_to_url(self):
615
536
class PieChart(Chart):
617
537
def __init__(self, *args, **kwargs):
618
538
assert(type(self) != PieChart) # This is an abstract class
619
539
Chart.__init__(self, *args, **kwargs)
620
540
self.pie_labels = []
622
541
def set_pie_labels(self, labels):
623
542
self.pie_labels = labels
625
543
def get_url_bits(self):
626
544
url_bits = Chart.get_url_bits(self)
627
545
if self.pie_labels:
628
546
url_bits.append('chl=%s' % '|'.join(self.pie_labels))
632
549
class PieChart2D(PieChart):
634
550
def type_to_url(self):
638
553
class PieChart3D(PieChart):
640
554
def type_to_url(self):
644
557
class VennChart(Chart):
646
558
def type_to_url(self):
651
562
chart = GroupedVerticalBarChart(320, 200)
652
563
chart = PieChart2D(320, 200)
653
564
chart = ScatterChart(320, 200)
654
565
chart = SimpleLineChart(320, 200)
655
sine_data = [math.sin(float(a) / 10) * 2000 + 2000 for a in xrange(100)]
656
random_data = [a * random.random() * 30 for a in xrange(40)]
657
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) ]
658
569
# chart.set_bar_width(50)
659
570
# chart.set_bar_spacing(0)
660
571
chart.add_data(sine_data)