diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index 494dd88..161da67 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # This file is part of pygal -from pygal import Bar, Gauge, Pyramid, Funnel, Dot, CHARTS_BY_NAME +from pygal import Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, CHARTS_BY_NAME from pygal.style import styles @@ -104,4 +104,11 @@ def get_test_routes(app): graph.add('2', [7, -4, 10, None, 8, 3, 1]) return graph.render_response() + @app.route('/test/stacked') + def test_stacked(): + stacked = StackedBar() + stacked.add('1', [1, 2, 3]) + stacked.add('2', [4, 5, 6]) + return stacked.render_response() + return filter(lambda x: x.startswith('test'), locals()) diff --git a/pygal/ghost.py b/pygal/ghost.py index 1b5f251..51db3e2 100644 --- a/pygal/ghost.py +++ b/pygal/ghost.py @@ -58,6 +58,8 @@ class Ghost(object): def add(self, title, values): """Add a serie to this graph""" + if not hasattr(values, '__iter__') and not isinstance(values, dict): + values = [values] self.raw_series.append((title, values)) def _check(self): diff --git a/pygal/graph/horizontal.py b/pygal/graph/horizontal.py index e9b6fa8..87d8ad4 100644 --- a/pygal/graph/horizontal.py +++ b/pygal/graph/horizontal.py @@ -30,12 +30,6 @@ class HorizontalGraph(Graph): super(HorizontalGraph, self).__init__(*args, **kwargs) def _compute(self): - if self.x_labels: - self.x_labels = list(reversed(self.x_labels)) - super(HorizontalGraph, self)._compute() self._x_labels, self._y_labels = self._y_labels, self._x_labels self._box.swap() - # Y axis is inverted - for serie in self.series: - serie.values = list(reversed(serie.values)) diff --git a/pygal/graph/line.py b/pygal/graph/line.py index ca494d6..d4a706b 100644 --- a/pygal/graph/line.py +++ b/pygal/graph/line.py @@ -90,11 +90,7 @@ class Line(Graph): serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not self.fill else '')) - def _compute(self): - x_pos = [ - x / (self._len - 1) for x in range(self._len) - ] if self._len != 1 else [.5] # Center if only one value - + def _points(self, x_pos): for serie in self.series: serie.points = [ (x_pos[i], v) @@ -102,6 +98,13 @@ class Line(Graph): if self.interpolate: serie.interpolated = self._interpolate(serie.values, x_pos) + def _compute(self): + x_pos = [ + x / (self._len - 1) for x in range(self._len) + ] if self._len != 1 else [.5] # Center if only one value + + self._points(x_pos) + if self.include_x_axis: self._box.ymin = min(self._min, 0) self._box.ymax = max(self._max, 0) diff --git a/pygal/graph/stackedline.py b/pygal/graph/stackedline.py index 5cd52fe..9b8bddc 100644 --- a/pygal/graph/stackedline.py +++ b/pygal/graph/stackedline.py @@ -20,7 +20,7 @@ Stacked Line chart """ - +from __future__ import division from pygal.graph.line import Line from pygal.adapters import none_to_zero @@ -42,10 +42,7 @@ class StackedLine(Line): self._previous_line = values return new_values - def _compute(self): - x_pos = [ - x / float(self._len - 1) for x in range(self._len) - ] if self._len != 1 else [.5] # Center if only one value + def _points(self, x_pos): accumulation = [0] * self._len for serie in self.series: accumulation = map(sum, zip(accumulation, serie.values)) @@ -54,5 +51,3 @@ class StackedLine(Line): for i, v in enumerate(accumulation)] if self.interpolate: serie.interpolated = self._interpolate(accumulation, x_pos) - - return super(StackedLine, self)._compute() diff --git a/pygal/test/test_graph.py b/pygal/test/test_graph.py index 509bd20..38071dd 100644 --- a/pygal/test/test_graph.py +++ b/pygal/test/test_graph.py @@ -103,6 +103,35 @@ def test_empty_lists(Chart): assert len(q(".legend")) == 2 +def test_non_iterable_value(Chart): + chart = Chart() + chart.add('A', 1) + chart.add('B', 2) + chart.x_labels = ('red', 'green', 'blue') + chart1 = chart.render() + chart = Chart() + chart.add('A', [1]) + chart.add('B', [2]) + chart.x_labels = ('red', 'green', 'blue') + chart2 = chart.render() + assert chart1 == chart2 + + +def test_iterable_types(Chart): + chart = Chart() + chart.add('A', [1, 2]) + chart.add('B', []) + chart.x_labels = ('red', 'green', 'blue') + chart1 = chart.render() + + chart = Chart() + chart.add('A', (1, 2)) + chart.add('B', tuple()) + chart.x_labels = ('red', 'green', 'blue') + chart2 = chart.render() + assert chart1 == chart2 + + def test_values_by_dict(Chart): chart = Chart() chart.add('A', {'red': 10, 'green': 12, 'blue': 14}) diff --git a/pygal/test/test_stacked.py b/pygal/test/test_stacked.py new file mode 100644 index 0000000..1739a10 --- /dev/null +++ b/pygal/test/test_stacked.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# This file is part of pygal +# +# A python svg graph plotting library +# Copyright © 2012 Kozea +# +# This library is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with pygal. If not, see . +from pygal import StackedLine + + +def test_stacked_line(): + stacked = StackedLine() + stacked.add('one_two', [1, 2]) + stacked.add('ten_twelve', [10, 12]) + q = stacked.render_pyquery() + assert set(q("desc.value").text().split(' ')) == set( + ('1', '2', '11', '14')) + + +def test_stacked_line_log(): + stacked = StackedLine(logarithmic=True) + stacked.add('one_two', [1, 2]) + stacked.add('ten_twelve', [10, 12]) + q = stacked.render_pyquery() + assert set(q("desc.value").text().split(' ')) == set( + ('1', '2', '11', '14')) + + +def test_stacked_line_interpolate(): + stacked = StackedLine(interpolate='cubic') + stacked.add('one_two', [1, 2]) + stacked.add('ten_twelve', [10, 12]) + q = stacked.render_pyquery() + assert set(q("desc.value").text().split(' ')) == set( + ('1', '2', '11', '14')) diff --git a/pygal/util.py b/pygal/util.py index 46929de..e5de874 100644 --- a/pygal/util.py +++ b/pygal/util.py @@ -312,6 +312,9 @@ def prepare_values(raw, config, cls): if k in config.x_labels: value_list[config.x_labels.index(k)] = v raw_values = value_list + else: + raw_values = list(raw_values) + for index, raw_value in enumerate( raw_values + ( (width - len(raw_values)) * [None] # aligning values