/+junk/pygooglechart-py3k

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/%2Bjunk/pygooglechart-py3k

« back to all changes in this revision

Viewing changes to pygooglechart.py

  • Committer: gak
  • Date: 2007-12-09 07:26:06 UTC
  • Revision ID: git-v1:c3e5f266abfdded59a5a460395e1880bf892ff9a
trunk updates

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
import os
24
24
import urllib
25
 
import urllib2
26
25
import math
27
26
import random
28
27
import re
32
31
 
33
32
reo_colour = re.compile('^([A-Fa-f0-9]{2,2}){3,4}$')
34
33
 
35
 
 
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
# -----------------------------------------------------------------------------
44
42
 
45
 
 
46
43
class PyGoogleChartException(Exception):
47
44
    pass
48
45
 
49
 
 
50
46
class DataOutOfRangeException(PyGoogleChartException):
51
47
    pass
52
48
 
53
 
 
54
49
class UnknownDataTypeException(PyGoogleChartException):
55
50
    pass
56
51
 
57
 
 
58
52
class NoDataGivenException(PyGoogleChartException):
59
53
    pass
60
54
 
61
 
 
62
55
class InvalidParametersException(PyGoogleChartException):
63
56
    pass
64
57
 
65
 
 
66
 
class BadContentTypeException(PyGoogleChartException):
67
 
    pass
68
 
 
69
 
 
70
58
# Data Classes
71
59
# -----------------------------------------------------------------------------
72
60
 
73
 
 
74
61
class Data(object):
75
 
 
76
62
    def __init__(self, data):
77
63
        assert(type(self) != Data)  # This is an abstract class
78
64
        self.data = data
79
65
 
80
 
 
81
66
class SimpleData(Data):
82
67
    enc_map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
83
 
 
84
68
    def __repr__(self):
85
69
        encoded_data = []
86
70
        for data in self.data:
94
78
                    raise DataOutOfRangeException()
95
79
            encoded_data.append(''.join(sub_data))
96
80
        return 'chd=s:' + ','.join(encoded_data)
97
 
 
98
81
    @staticmethod
99
82
    def max_value():
100
83
        return 61
101
84
 
102
 
 
103
85
class TextData(Data):
104
 
 
105
86
    def __repr__(self):
106
87
        encoded_data = []
107
88
        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)
118
 
 
119
99
    @staticmethod
120
100
    def max_value():
121
101
        return 100
122
102
 
123
 
 
124
103
class ExtendedData(Data):
125
104
    enc_map = \
126
105
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'
127
 
 
128
106
    def __repr__(self):
129
107
        encoded_data = []
130
108
        enc_size = len(ExtendedData.enc_map)
144
122
                        value))
145
123
            encoded_data.append(''.join(sub_data))
146
124
        return 'chd=e:' + ','.join(encoded_data)
147
 
 
148
125
    @staticmethod
149
126
    def max_value():
150
127
        return 4095
152
129
# Axis Classes
153
130
# -----------------------------------------------------------------------------
154
131
 
155
 
 
156
132
class Axis(object):
157
133
    BOTTOM = 'x'
158
134
    TOP = 't'
159
135
    LEFT = 'y'
160
136
    RIGHT = 'r'
161
137
    TYPES = (BOTTOM, TOP, LEFT, RIGHT)
162
 
 
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
168
 
 
169
143
    def set_index(self, index):
170
144
        self.index = index
171
 
 
172
145
    def set_positions(self, positions):
173
146
        self.positions = positions
174
 
 
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
181
 
 
182
153
    def style_to_url(self):
183
154
        bits = []
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)
191
 
 
192
162
    def positions_to_url(self):
193
163
        bits = []
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)
197
167
 
198
 
 
199
168
class LabelAxis(Axis):
200
 
 
201
169
    def __init__(self, axis, values, **kwargs):
202
170
        Axis.__init__(self, axis, **kwargs)
203
 
        self.values = [str(a) for a in values]
204
 
 
 
171
        self.values = [ str(a) for a in values ]
205
172
    def __repr__(self):
206
173
        return '%i:|%s' % (self.index, '|'.join(self.values))
207
174
 
208
 
 
209
175
class RangeAxis(Axis):
210
 
 
211
176
    def __init__(self, axis, low, high, **kwargs):
212
177
        Axis.__init__(self, axis, **kwargs)
213
178
        self.low = low
214
179
        self.high = high
215
 
 
216
180
    def __repr__(self):
217
181
        return '%i,%s,%s' % (self.index, self.low, self.high)
218
182
 
219
183
# Chart Classes
220
184
# -----------------------------------------------------------------------------
221
185
 
222
 
 
223
186
class Chart(object):
224
 
    """Abstract class for all chart types.
225
 
 
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.
228
 
    """
229
187
 
230
188
    BASE_URL = 'http://chart.apis.google.com/chart?'
231
189
    BACKGROUND = 'bg'
292
250
 
293
251
    # Downloading
294
252
    # -------------------------------------------------------------------------
295
 
 
296
 
    def download(self, file_name):
297
 
        opener = urllib2.urlopen(self.get_url())
298
 
 
299
 
        if opener.headers['content-type'] != 'image/png':
300
 
            raise BadContentTypeException('Server responded with a ' \
301
 
                'content-type of %s' % opener.headers['content-type'])
302
 
 
 
253
    def download_graph(self, file_name):
303
254
        open(file_name, 'wb').write(urllib.urlopen(self.get_url()).read())
304
255
 
305
256
    # Simple settings
316
267
        assert(isinstance(legend, list) or isinstance(legend, tuple) or
317
268
            legend is None)
318
269
        if legend:
319
 
            self.legend = [urllib.quote(a) for a in legend]
 
270
            self.legend = [ urllib.quote(a) for a in legend ]
320
271
        else:
321
272
            self.legend = None
322
273
 
397
348
 
398
349
    def add_data(self, data):
399
350
        self.data.append(data)
400
 
        return len(self.data) - 1  # return the "index" of the data set
401
351
 
402
352
    def data_to_url(self, data_class=None):
403
353
        if not data_class:
468
418
    # -------------------------------------------------------------------------
469
419
 
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 ])
472
422
 
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'))
489
439
 
490
 
 
491
440
class ScatterChart(Chart):
492
 
 
493
441
    def __init__(self, *args, **kwargs):
494
442
        Chart.__init__(self, *args, **kwargs)
495
 
 
496
443
    def type_to_url(self):
497
444
        return 'cht=s'
498
445
 
499
446
 
500
447
class LineChart(Chart):
501
 
 
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 = {}
506
451
        self.grid = None
507
 
 
508
452
    def set_line_style(self, index, thickness=1, line_segment=None, \
509
453
            blank_segment=None):
510
454
        value = []
513
457
            value.append(str(line_segment))
514
458
            value.append(str(blank_segment))
515
459
        self.line_styles[index] = value
516
 
 
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, \
520
463
            blank_segment)
521
 
 
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)
536
478
        return url_bits
537
479
 
538
 
 
539
480
class SimpleLineChart(LineChart):
540
 
 
541
481
    def type_to_url(self):
542
482
        return 'cht=lc'
543
483
 
544
 
 
545
484
class XYLineChart(LineChart):
546
 
 
547
485
    def type_to_url(self):
548
486
        return 'cht=lxy'
549
487
 
550
 
 
551
488
class BarChart(Chart):
552
 
 
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
557
 
 
558
493
    def set_bar_width(self, bar_width):
559
494
        self.bar_width = bar_width
560
 
 
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)
564
498
        return url_bits
565
499
 
566
 
 
567
500
class StackedHorizontalBarChart(BarChart):
568
 
 
569
501
    def type_to_url(self):
570
502
        return 'cht=bhs'
571
503
 
572
 
 
573
504
class StackedVerticalBarChart(BarChart):
574
 
 
575
505
    def type_to_url(self):
576
506
        return 'cht=bvs'
577
507
 
578
 
 
579
508
class GroupedBarChart(BarChart):
580
 
 
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
585
 
 
586
513
    def set_bar_spacing(self, spacing):
587
514
        self.bar_spacing = spacing
588
 
 
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)
600
526
        return url_bits
601
527
 
602
 
 
603
528
class GroupedHorizontalBarChart(GroupedBarChart):
604
 
 
605
529
    def type_to_url(self):
606
530
        return 'cht=bhg'
607
531
 
608
 
 
609
532
class GroupedVerticalBarChart(GroupedBarChart):
610
 
 
611
533
    def type_to_url(self):
612
534
        return 'cht=bvg'
613
535
 
614
 
 
615
536
class PieChart(Chart):
616
 
 
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 = []
621
 
 
622
541
    def set_pie_labels(self, labels):
623
542
        self.pie_labels = labels
624
 
 
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))
629
547
        return url_bits
630
548
 
631
 
 
632
549
class PieChart2D(PieChart):
633
 
 
634
550
    def type_to_url(self):
635
551
        return 'cht=p'
636
552
 
637
 
 
638
553
class PieChart3D(PieChart):
639
 
 
640
554
    def type_to_url(self):
641
555
        return 'cht=p3'
642
556
 
643
 
 
644
557
class VennChart(Chart):
645
 
 
646
558
    def type_to_url(self):
647
559
        return 'cht=v'
648
560
 
649
 
 
650
561
def test():
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)
688
599
 
689
600
    chart.add_fill_simple('303030A0')
690
601
 
691
 
    chart.download('test.png')
 
602
    chart.download_graph('test.png')
692
603
 
693
604
    url = chart.get_url()
694
605
    print url
697
608
        open('meh.png', 'wb').write(data)
698
609
        os.system('start meh.png')
699
610
 
700
 
 
701
611
if __name__ == '__main__':
702
612
    test()
 
613