From 2a10be37cccdd13e68f886b4e7c79237ade26433 Mon Sep 17 00:00:00 2001 From: jaraco Date: Sat, 31 May 2008 23:52:40 +0000 Subject: [PATCH] Implemented methods that were stubbed with NotImplementedError(s). Copied flatten method from my internal development and add it to util.py. Removed reference to itools from Line.py. Cleaned up docstring tests that weren't actually executable. --- lib/SVG/Line.py | 2 +- lib/SVG/Pie.py | 8 +++---- lib/SVG/__init__.py | 52 +++++++++++++++++++++++++++++++++++++++------ lib/SVG/util.py | 52 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 13 deletions(-) diff --git a/lib/SVG/Line.py b/lib/SVG/Line.py index b4709ec..8e897ed 100644 --- a/lib/SVG/Line.py +++ b/lib/SVG/Line.py @@ -3,7 +3,7 @@ # $Id$ from operator import itemgetter, add -from itools import flatten +from util import flatten import SVG from Plot import float_range diff --git a/lib/SVG/Pie.py b/lib/SVG/Pie.py index 090ea1b..048bc61 100644 --- a/lib/SVG/Pie.py +++ b/lib/SVG/Pie.py @@ -103,19 +103,19 @@ class Pie(SVG.Graph): """ Add a data set to the graph - >>> graph.add_data({data:[1,2,3,4]}) + >>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP Note that a 'title' key is ignored. Multiple calls to add_data will sum the elements, and the pie will display the aggregated data. e.g. - >>> graph.add_data({data:[1,2,3,4]}) - >>> graph.add_data({data:[2,3,5,7]}) + >>> graph.add_data({data:[1,2,3,4]}) # doctest: +SKIP + >>> graph.add_data({data:[2,3,5,7]}) # doctest: +SKIP is the same as: - graph.add_data({data:[3,5,8,11]}) + graph.add_data({data:[3,5,8,11]}) # doctest: +SKIP """ self.data = map(robust_add, self.data, data_descriptor['data']) diff --git a/lib/SVG/__init__.py b/lib/SVG/__init__.py index e522c15..44d034c 100644 --- a/lib/SVG/__init__.py +++ b/lib/SVG/__init__.py @@ -126,11 +126,11 @@ Copyright © 2008 Jason R. Coombs """This method allows you do add data to the graph object. It can be called several times to add more data sets in. - >>> data_sales_02 = [12, 45, 21] - >>> graph.add_data({ + >>> data_sales_02 = [12, 45, 21] # doctest: +SKIP + >>> graph.add_data({ # doctest: +SKIP ... 'data': data_sales_02, ... 'title': 'Sales 2002' - ...}) + ... }) # doctest: +SKIP """ self.validate_data(conf) self.process_data(conf) @@ -151,7 +151,8 @@ Copyright © 2008 Jason R. Coombs """This method removes all data from the object so that you can reuse it to create a new graph but with the same config options. - >>> graph.clear_data()""" + >>> graph.clear_data() # doctest: +SKIP + """ self.data = [] def burn(self): @@ -476,10 +477,31 @@ Copyright © 2008 Jason R. Coombs self.root.appendChild(text) def draw_graph_subtitle(self): - raise NotImplementedError + y_subtitle_options = [subtitle_font_size, title_font_size+10] + y_subtitle = y_subtitle_options[self.show_graph_title] + text = self._create_element('text', { + 'x': str(self.width/2), + 'y': str(y_subtitle), + 'class': 'subTitle', + }) + text.appendChild(self._doc.createTextNode(self.graph_title)) + self.root.appendChild(text) def draw_x_title(self): - raise NotImplementedError + y = self.graph_height + self.border_top + self.x_title_font_size + if self.show_x_labels: + y_size = self.x_label_font_size+5 + if self.stagger_x_labels: y_size*=2 + y += y_size + x = self.width / 2 + + text = self._create_element('text', { + 'x': str(x), + 'y': str(y), + 'class': 'xAxisTitle', + }) + text.appendChild(self._doc.createTextNode(self.x_title)) + self.root.appendChild(text) def draw_y_title(self): x = self.y_title_font_size @@ -528,8 +550,24 @@ Copyright © 2008 Jason R. Coombs x_offset = self.graph_width + self.border_left + 10 y_offset = self.border_top + 20 if self.key_position == 'bottom': - raise NotImplementedError + x_offset, y_offset = self.calculate_offsets_bottom() group.setAttribute('transform', 'translate(%(x_offset)d %(y_offset)d)' % vars()) + + def calculate_offsets_bottom(self): + x_offset = self.border_left + 20 + y_offset = self.border_top + self.graph_height + 5 + if self.show_x_labels: + max_x_label_height_px = x_label_font_size + if self.rotate_x_labels: + longest_label_length = max(map(len, self.get_x_labels())) + # note: I think 0.6 is the ratio of width to height of characters + max_x_label_height_px *= longest_label_length * 0.6 + y_offset += max_x_label_height_px + if self.stagger_x_labels: + y_offset += max_x_label_height_px + 5 + if self.show_x_title: + y_offset += x_title_font_size + 5 + return x_offset, y_offset def style(self): "hard code the styles into the xml if style sheets are not used." diff --git a/lib/SVG/util.py b/lib/SVG/util.py index f03fdf9..cf05046 100644 --- a/lib/SVG/util.py +++ b/lib/SVG/util.py @@ -109,4 +109,54 @@ class TimeScale(object): def __mul__(self, delta): scale = divide_timedelta(delta, self.range) - return scale*self.width \ No newline at end of file + return scale*self.width + +# the following three functions were copied from jaraco.util.iter_ + +# todo, factor out caching capability +class iterable_test(dict): + "Test objects for iterability, caching the result by type" + def __init__(self, ignore_classes=(basestring,)): + """ignore_classes must include basestring, because if a string + is iterable, so is a single character, and the routine runs + into an infinite recursion""" + assert basestring in ignore_classes, 'basestring must be in ignore_classes' + self.ignore_classes = ignore_classes + + def __getitem__(self, candidate): + return dict.get(self, type(candidate)) or self._test(candidate) + + def _test(self, candidate): + try: + if isinstance(candidate, self.ignore_classes): + raise TypeError + iter(candidate) + result = True + except TypeError: + result = False + self[type(candidate)] = result + return result + +def iflatten(subject, test=None): + if test is None: + test = iterable_test() + if not test[subject]: + yield subject + else: + for elem in subject: + for subelem in iflatten(elem, test): + yield subelem + +def flatten(subject, test=None): + """flatten an iterable with possible nested iterables. + Adapted from + http://mail.python.org/pipermail/python-list/2003-November/233971.html + >>> flatten(['a','b',['c','d',['e','f'],'g'],'h']) == ['a','b','c','d','e','f','g','h'] + True + + Note this will normally ignore string types as iterables. + >>> flatten(['ab', 'c']) + ['ab', 'c'] + """ + return list(iflatten(subject, test)) +