From d1eba64bdfb582ad3ed50a9b9929a0abf628d303 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Mon, 4 Jan 2016 15:46:53 +0100 Subject: [PATCH] Bar print value positioning with `print_values_position`. Can be `top`, `center` or `bottom` (thanks @chartique) Fix #291 --- demo/moulinrouge/tests.py | 11 +++++++---- docs/changelog.rst | 2 +- pygal/graph/bar.py | 24 ++++++++++++++++++++++-- pygal/graph/graph.py | 24 ++++++++++++++++++++---- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index fef07e2..e12cb39 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -3,7 +3,7 @@ from pygal import ( Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, StackedLine, XY, CHARTS_BY_NAME, Config, Line, Histogram, Box, - Pie, Treemap, TimeLine, DateLine, Radar, + Pie, Treemap, TimeLine, DateLine, Radar, HorizontalBar, DateTimeLine) try: @@ -353,9 +353,12 @@ def get_test_routes(app): @app.route('/test/bar/position') def test_bar_print_values_position(): - bar = Bar(print_values=True, print_values_position='top') - bar.add('1', [1, 2, 3]) - bar.add('2', [4, 5, 6]) + bar = HorizontalBar(print_values=True, print_values_position='top', + style=styles['default']( + value_font_family='googlefont:Raleway', + value_font_size=46)) + bar.add('1', [-1, 2, 3]) + bar.add('2', [4, -5, 6]) bar.x_labels = [2, 4, 6] bar.x_labels_major = [4] return bar.render_response() diff --git a/docs/changelog.rst b/docs/changelog.rst index 14ee8ed..1a5bd02 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,7 +5,7 @@ Changelog 2.0.13 UNRELEASED ====== -* Bar label positioning +* Bar print value positioning with `print_values_position`. Can be `top`, `center` or `bottom` (thanks @chartique #291) 2.0.12 ====== diff --git a/pygal/graph/bar.py b/pygal/graph/bar.py index 77c2d5a..d8c8d1f 100644 --- a/pygal/graph/bar.py +++ b/pygal/graph/bar.py @@ -65,11 +65,32 @@ class Bar(Graph): x, y, width, height): transpose = swap if self.horizontal else ident x_center, y_center = transpose((x + width / 2, y + height / 2)) + x_top, y_top = transpose((x + width, y + height)) + x_bottom, y_bottom = transpose((x, y)) + sign = -1 if height < self.zero else 1 self._tooltip_data( parent, val, x_center, y_center, "centered", self._get_x_label(i)) + + if self.print_values_position == 'top': + if self.horizontal: + x = x_bottom - sign * self.style.value_font_size / 2 + y = y_center + else: + x = x_center + y = y_bottom - sign * self.style.value_font_size / 2 + elif self.print_values_position == 'bottom': + if self.horizontal: + x = x_top - sign * self.style.value_font_size / 2 + y = y_center + else: + x = x_center + y = y_top - sign * self.style.value_font_size / 2 + else: + x = x_center + y = y_center self._static_value( - serie_node, val, x_center, y_center, metadata) + serie_node, val, x, y, metadata, "centered") def bar(self, serie, rescale=False): """Draw a bar graph for a serie""" @@ -103,7 +124,6 @@ class Bar(Graph): self._box.ymin = min(self._min, self.zero) if self._max: self._box.ymax = max(self._max, self.zero) - self._x_pos = [ x / self._len for x in range(self._len + 1) ] if self._len > 1 else [0, 1] # Center if only one value diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 7b7e18a..e40b5f9 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -491,25 +491,31 @@ class Graph(PublicApi): self.svg.node(node, 'desc', class_="x_label").text = to_str(xlabel) - def _static_value(self, serie_node, value, x, y, metadata): + def _static_value(self, serie_node, value, x, y, metadata, classes=None): """Write the print value""" label = metadata and metadata.get('label') + classes = classes and [classes] or [] + if self.print_labels and label: + label_cls = classes + ['label'] if self.print_values: y -= self.style.value_font_size / 2 self.svg.node( serie_node['text_overlay'], 'text', - class_='centered label', + class_=' '.join(label_cls), x=x, y=y + self.style.value_font_size / 3 ).text = label y += self.style.value_font_size if self.print_values or self.dynamic_print_values: + val_cls = classes + ['value'] + if self.dynamic_print_values: + val_cls.append('showable') + self.svg.node( serie_node['text_overlay'], 'text', - class_='centered value%s' % ( - ' showable' if self.dynamic_print_values else ''), + class_=' '.join(val_cls), x=x, y=y + self.style.value_font_size / 3 ).text = value if self.print_zeroes or value != '0' else '' @@ -676,6 +682,16 @@ class Graph(PublicApi): self.margin_box.left += height self._y_title_height = height + self.spacing + # Inner margin + if self.print_values_position == 'top': + gw = self.width - self.margin_box.x + gh = self.height - self.margin_box.y + alpha = 1.1 * (self.style.value_font_size / gh) * self._box.height + if self._max > 0: + self._box.ymax += alpha + if self._min < 0: + self._box.ymin -= alpha + @cached_property def _legends(self): """Getter for series title"""