diff --git a/pygal/config.py b/pygal/config.py
index e1e7ce14..fa575fc3 100644
--- a/pygal/config.py
+++ b/pygal/config.py
@@ -256,7 +256,6 @@ class Config(CommonConfig):
list, "Misc", "Extraneous defs to be inserted in svg",
"Useful for adding gradients / patterns…",
str)
-
# Look #
title = Key(
None, str, "Look",
@@ -498,6 +497,11 @@ class Config(CommonConfig):
"Label string length truncation threshold",
"None = auto, Negative for none")
+ percent_values = Key(
+ False, bool, "Look",
+ "Bar Values",
+ "Set to True to display a bar's percentage contribution per series atop the bar.")
+
# Misc #
js = Key(
('//kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js',),
diff --git a/pygal/graph/bar.py b/pygal/graph/bar.py
index 488b759a..3a8cabba 100644
--- a/pygal/graph/bar.py
+++ b/pygal/graph/bar.py
@@ -62,7 +62,8 @@ def _bar(self, serie, parent, x, y, i, zero, secondary=False):
def _tooltip_and_print_values(
self, serie_node, serie, parent, i, val, metadata,
- x, y, width, height):
+ x, y, width, height, total):
+ percent = ((float(val)/total)*100)
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))
@@ -75,7 +76,6 @@ def _tooltip_and_print_values(
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
@@ -93,7 +93,9 @@ def _tooltip_and_print_values(
else:
x = x_center
y = y_center
- self._static_value(serie_node, val, x, y, metadata, "middle")
+
+ self.style.value_font_size = width / 1.41421
+ self._static_value(serie_node, val, percent, x, y, metadata, "middle")
def bar(self, serie, rescale=False):
"""Draw a bar graph for a serie"""
@@ -103,7 +105,7 @@ def bar(self, serie, rescale=False):
points = self._rescale(serie.points)
else:
points = serie.points
-
+ total = sum(list(filter(None, serie.values)))
for i, (x, y) in enumerate(points):
if None in (x, y) or (self.logarithmic and y <= 0):
continue
@@ -124,7 +126,7 @@ def bar(self, serie, rescale=False):
self._tooltip_and_print_values(
serie_node, serie, bar, i, val, metadata,
- x_, y_, width, height)
+ x_, y_, width, height, total)
def _compute(self):
"""Compute y min and max and y scale and set labels"""
diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py
index 23e0669f..12a66b37 100644
--- a/pygal/graph/graph.py
+++ b/pygal/graph/graph.py
@@ -491,12 +491,12 @@ def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None):
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, percent, x, y, metadata,
align_text='left', classes=None):
"""Write the print value"""
label = metadata and metadata.get('label')
classes = classes and [classes] or []
-
+ per_str = ('{0:.2f}%'.format(percent))
if self.print_labels and label:
label_cls = classes + ['label']
if self.print_values:
@@ -509,18 +509,26 @@ def _static_value(self, serie_node, value, x, y, metadata,
).text = label
y += self.style.value_font_size
- if self.print_values or self.dynamic_print_values:
+ if self.print_values or self.dynamic_print_values or self.percent_values:
val_cls = classes + ['value']
if self.dynamic_print_values:
val_cls.append('showable')
-
- self.svg.node(
- serie_node['text_overlay'], 'text',
- class_=' '.join(val_cls),
- x=x,
- y=y + self.style.value_font_size / 3,
- attrib={'text-anchor': align_text}
- ).text = value if self.print_zeroes or value != '0' else ''
+ if self.print_values:
+ self.svg.node(
+ serie_node['text_overlay'], 'text',
+ class_=' '.join(val_cls),
+ x=x,
+ y=y + self.style.value_font_size / 3,
+ attrib={'text-anchor': align_text}
+ ).text = value if self.print_zeroes or value != '0' else ''
+ if self.percent_values:
+ self.svg.node(
+ serie_node['text_overlay'], 'text',
+ class_=' '.join(val_cls),
+ x=x,
+ y=y + 4/3*self.style.value_font_size,
+ attrib={'text-anchor': align_text}
+ ).text = per_str if self.print_zeroes or per_str != '0.00%' else ''
def _points(self, x_pos):
"""
@@ -836,7 +844,7 @@ def _compute_x_labels_major(self):
elif self.x_labels_major_count:
label_count = len(self._x_labels)
major_count = self.x_labels_major_count
- if (major_count >= label_count):
+ if major_count >= label_count:
self._x_labels_major = [label[0] for label in self._x_labels]
else:
diff --git a/pygal/test/conftest.py b/pygal/test/conftest.py
index e6f08f48..8ff8aac1 100644
--- a/pygal/test/conftest.py
+++ b/pygal/test/conftest.py
@@ -23,7 +23,7 @@
import pygal
from pygal.etree import etree
import sys
-from . import get_data
+#from . import get_data
@pytest.fixture
diff --git a/pygal/test/test_bar.py b/pygal/test/test_bar.py
index 631990b0..18f293fa 100644
--- a/pygal/test/test_bar.py
+++ b/pygal/test/test_bar.py
@@ -18,7 +18,6 @@
# along with pygal. If not, see .
"""Bar chart related tests"""
-
from pygal import Bar
@@ -35,3 +34,48 @@ def test_simple_bar():
assert len(q(".axis.y")) == 1
assert len(q(".legend")) == 2
assert len(q(".plot .series rect")) == 2 * 3
+
+
+def test_difference():
+ """Tests the difference between labeled graphs and unlabeled graphs"""
+ bar = Bar(bar_values=False)
+ rng = [-3, -32, -39]
+ bar.add('test1', rng)
+ bar.add('test2', map(abs, rng))
+ bar.x_labels = map(str, rng)
+ bar_labelled = Bar(bar_values=True)
+ rng = [-3, -32, -39]
+ bar_labelled.add('test1', rng)
+ bar_labelled.add('test2', map(abs, rng))
+ bar.labelled = map(str, rng)
+
+ assert bar != bar_labelled
+
+
+def test_bar_percent_difference():
+ """Tests the difference between percent labeled graphs and unlabeled graphs"""
+ bar = Bar()
+ rng = [-3, -32, -39]
+ bar.add('test1', rng)
+ bar.add('test2', map(abs, rng))
+ bar.x_labels = map(str, rng)
+
+ barpercent = Bar(percent_values=True)
+ rng = [-3, -32, -39]
+ barpercent.add('test1', rng)
+ barpercent.add('test2', map(abs, rng))
+ barpercent.x_labels = map(str, rng)
+
+ assert (bar != barpercent)
+
+
+def test_chart_renders():
+ """Tests that print values and percent values renders"""
+ line_chart = Bar(print_values=True, percent_values=True, print_values_position='top')
+ line_chart.title = 'Browser usage evolution (in %)'
+ line_chart.x_labels = map(str, range(2002, 2013))
+ line_chart.add('Firefox', [None, None, 0, 16.6, 25, 31, 36.4, 45.5, 46.3, 42.8, 37.1])
+ line_chart.add('Chrome', [None, None, None, None, None, None, 0, 3.9, 10.8, 23.8, 35.3])
+ line_chart.add('IE', [85.8, 84.6, 84.7, 74.5, 66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1])
+ line_chart.add('Others', [14.2, 15.4, 15.3, 8.9, 9, 10.4, 8.9, 5.8, 6.7, 6.8, 7.5])
+ assert line_chart.render()
diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py
index 11001c92..df28354e 100644
--- a/pygal/test/test_config.py
+++ b/pygal/test/test_config.py
@@ -102,6 +102,7 @@ class LineConfig(Config):
def test_config_alterations_class():
"""Assert a config can be changed on config class"""
+
class LineConfig(Config):
no_prefix = True
show_legend = False
@@ -125,6 +126,7 @@ class LineConfig(Config):
def test_config_alterations_instance():
"""Assert a config can be changed on instance"""
+
class LineConfig(Config):
no_prefix = True
show_legend = False
@@ -149,6 +151,7 @@ class LineConfig(Config):
def test_config_alterations_kwargs():
"""Assert a config can be changed with keyword args"""
+
class LineConfig(Config):
no_prefix = True
show_legend = False
@@ -541,15 +544,15 @@ def test_formatters(Chart):
if Chart._dual or Chart == Box:
return
chart = Chart(formatter=lambda x, chart, serie: '%s%s$' % (
- x, serie.title))
+ x, serie.title))
chart.add('_a', [1, 2, {'value': 3, 'formatter': lambda x: u('%s¥') % x}])
chart.add('_b', [4, 5, 6], formatter=lambda x: u('%s€') % x)
chart.x_labels = [2, 4, 6]
chart.x_labels_major = [4]
q = chart.render_pyquery()
assert set([v.text for v in q(".value")]) == set((
- u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + (
- ('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ()))
+ u('4€'), u('5€'), u('6€'), '1_a$', '2_a$', u('3¥')) + (
+ ('6_a$', u('15€')) if Chart in (Pie, SolidGauge) else ()))
def test_classes(Chart):
@@ -574,3 +577,8 @@ def test_classes(Chart):
chart = Chart(classes=('graph', _ellipsis))
assert chart.render_pyquery().attr('class') == 'graph pygal-chart'
+
+
+def test_bar_value(Chart):
+ chart = Chart()
+ assert chart.bar_values == False
diff --git a/pygal/test/test_gauge.py b/pygal/test/test_gauge.py
new file mode 100644
index 00000000..80a853be
--- /dev/null
+++ b/pygal/test/test_gauge.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# This file is part of pygal
+#
+# A python svg graph plotting library
+# Copyright © 2012-2016 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 .
+
+"""Gauge Chart related tests."""
+
+
+from pygal import Gauge
+
+
+def test_render():
+ """Tests that a gauge plots"""
+ chart = Gauge()
+ chart.range = [0,40]
+ chart.add('Connor', 21)
+ chart.add('Mike', 30)
+ chart.add('Jules', 10)
+ assert chart.render()
diff --git a/pygal/test/test_graph.py b/pygal/test/test_graph.py
index 21226474..52d21a9b 100644
--- a/pygal/test/test_graph.py
+++ b/pygal/test/test_graph.py
@@ -20,6 +20,10 @@
"""Generate tests for different chart types with different data"""
import os
+from unittest import TestCase
+
+from pygal.graph.bar import Bar
+
import pygal
import uuid
import sys
@@ -431,10 +435,11 @@ def test_long_title(Chart, datas):
"""Test chart rendering with a long title"""
chart = Chart(
title="A chart is a graphical representation of data, in which "
- "'the data is represented by symbols, such as bars in a bar chart, "
- "lines in a line chart, or slices in a pie chart'. A chart can "
- "represent tabular numeric data, functions or some kinds of "
- "qualitative structure and provides different info.")
+ "'the data is represented by symbols, such as bars in a bar chart, "
+ "lines in a line chart, or slices in a pie chart'. A chart can "
+ "represent tabular numeric data, functions or some kinds of "
+ "qualitative structure and provides different info.")
chart = make_data(chart, datas)
q = chart.render_pyquery()
assert len(q('.titles text')) == 5
+
diff --git a/pygal/view.py b/pygal/view.py
index 9360a584..a106b7ee 100644
--- a/pygal/view.py
+++ b/pygal/view.py
@@ -26,7 +26,7 @@
class Margin(object):
- """Class reprensenting a margin (top, right, left, bottom)"""
+ """Class representing a margin (top, right, left, bottom)"""
def __init__(self, top, right, bottom, left):
"""Create the margin object from the top, right, left, bottom margin"""