diff --git a/demo/moulinrouge/__init__.py b/demo/moulinrouge/__init__.py
index 3ed919b8..a50e99d5 100644
--- a/demo/moulinrouge/__init__.py
+++ b/demo/moulinrouge/__init__.py
@@ -289,22 +289,23 @@ def interpolation():
'config': b64encode(pickle.dumps(config))
})
- for params in [{'type': 'catmull_rom'}, {'type': 'finite_difference'},
- {'type': 'cardinal',
- 'c': .25}, {'type': 'cardinal',
- 'c': .5}, {'type': 'cardinal', 'c': .75},
- {'type': 'cardinal',
- 'c': 1.5}, {'type': 'cardinal',
- 'c': 2}, {'type': 'cardinal', 'c': 5},
- {'type': 'kochanek_bartels', 'b': 1, 'c': 1,
- 't': 1}, {'type': 'kochanek_bartels', 'b': -1, 'c': 1,
- 't': 1}, {'type': 'kochanek_bartels', 'b': 1,
- 'c': -1, 't': 1},
- {'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': -1}, {
- 'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': -1
- }, {'type': 'kochanek_bartels', 'b': -1, 'c': -1,
- 't': 1}, {'type': 'kochanek_bartels', 'b': -1,
- 'c': -1, 't': -1}]:
+ for params in [
+ {'type': 'catmull_rom'}, {'type': 'finite_difference'},
+ {'type': 'cardinal', 'c': .25},
+ {'type': 'cardinal', 'c': .5},
+ {'type': 'cardinal', 'c': .75},
+ {'type': 'cardinal', 'c': 1.5},
+ {'type': 'cardinal', 'c': 2},
+ {'type': 'cardinal', 'c': 5},
+ {'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': 1},
+ {'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1},
+ {'type': 'kochanek_bartels', 'b': 1, 'c': -1, 't': 1},
+ {'type': 'kochanek_bartels', 'b': 1, 'c': 1, 't': -1},
+ {'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': -1},
+ {'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': 1},
+ {'type': 'kochanek_bartels', 'b': -1, 'c': -1, 't': -1}
+ ]:
+
config.title = "Hermite interpolation with params %r" % params
config.interpolate = 'hermite'
config.interpolation_parameters = params
diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py
index 1937c43b..4063045c 100644
--- a/demo/moulinrouge/tests.py
+++ b/demo/moulinrouge/tests.py
@@ -1160,13 +1160,13 @@ def test_legend_link_for(chart):
'href': 'http://en.wikipedia.org/wiki/Red'
}
}],
- title={
- 'title': 'Red',
- 'tooltip': 'Cramoisi',
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Red'
- }
- })
+ title={
+ 'title': 'Red',
+ 'tooltip': 'Cramoisi',
+ 'xlink': {
+ 'href': 'http://en.wikipedia.org/wiki/Red'
+ }
+ })
chart.add({
'title': 'Yellow',
diff --git a/pygal/__init__.py b/pygal/__init__.py
index 3a1bc582..37839feb 100644
--- a/pygal/__init__.py
+++ b/pygal/__init__.py
@@ -55,10 +55,11 @@
from pygal.graph.treemap import Treemap
from pygal.graph.xy import XY
-CHARTS_BY_NAME = dict([
- (k, v) for k, v in locals().items()
- if isinstance(v, type) and issubclass(v, Graph) and v != Graph
-])
+
+CHARTS_BY_NAME = dict(
+ [(k, v) for k, v in locals().items()
+ if isinstance(v, type) and issubclass(v, Graph) and v != Graph])
+
from pygal.graph.map import BaseMap
@@ -66,10 +67,8 @@
try:
module = entry.load()
except Exception:
- warnings.warn(
- 'Unable to load %s pygal plugin \n\n%s' %
- (entry, traceback.format_exc()), Warning
- )
+ warnings.warn('Unable to load %s pygal plugin \n\n%s' % (
+ entry, traceback.format_exc()), Warning)
continue
setattr(maps, entry.name, module)
for k, v in module.__dict__.items():
@@ -81,6 +80,7 @@
class PluginImportFixer(object):
+
"""
Allow external map plugins to be imported from pygal.maps package.
diff --git a/pygal/colors.py b/pygal/colors.py
index 5027fa8c..385863e4 100644
--- a/pygal/colors.py
+++ b/pygal/colors.py
@@ -81,10 +81,8 @@ def h_to_rgb(h):
if 3 * h < 2:
return m1 + 6 * (2 / 3 - h) * (m2 - m1)
return m1
-
- r, g, b = map(
- lambda x: round(x * 255), map(h_to_rgb, (h + 1 / 3, h, h - 1 / 3))
- )
+ r, g, b = map(lambda x: round(x * 255),
+ map(h_to_rgb, (h + 1 / 3, h, h - 1 / 3)))
return r, g, b
@@ -109,8 +107,7 @@ def parse_color(color):
assert len(color) == 8
type = type or '#rrggbbaa'
r, g, b, a = [
- int(''.join(c), 16) for c in zip(color[::2], color[1::2])
- ]
+ int(''.join(c), 16) for c in zip(color[::2], color[1::2])]
a /= 255
elif color.startswith('rgb('):
type = 'rgb'
@@ -119,8 +116,8 @@ def parse_color(color):
elif color.startswith('rgba('):
type = 'rgba'
color = color[5:-1]
- r, g, b, a = [int(c) for c in color.split(',')[:-1]
- ] + [float(color.split(',')[-1])]
+ r, g, b, a = [int(c) for c in color.split(',')[:-1]] + [
+ float(color.split(',')[-1])]
return r, g, b, a, type
@@ -137,9 +134,8 @@ def unparse_color(r, g, b, a, type):
if type == '#rgba':
if r % 17 == 0 and g % 17 == 0 and b % 17 == 0:
- return '#%x%x%x%x' % (
- int(r / 17), int(g / 17), int(b / 17), int(a * 15)
- )
+ return '#%x%x%x%x' % (int(r / 17), int(g / 17), int(b / 17),
+ int(a * 15))
type = '#rrggbbaa'
if type == '#rrggbb':
diff --git a/pygal/config.py b/pygal/config.py
index 2c66324e..b593deba 100644
--- a/pygal/config.py
+++ b/pygal/config.py
@@ -29,6 +29,7 @@
class Key(object):
+
"""
Represents a config parameter.
@@ -43,8 +44,8 @@ class Key(object):
_categories = []
def __init__(
- self, default_value, type_, category, doc, subdoc="", subtype=None
- ):
+ self, default_value, type_, category, doc,
+ subdoc="", subtype=None):
"""Create a configuration key"""
self.value = default_value
self.type = type_
@@ -68,8 +69,10 @@ def __repr__(self):
Default: %r
%s%s
""" % (
- self.type.__name__, (' of %s' % self.subtype.__name__)
- if self.subtype else '', self.value, self.doc,
+ self.type.__name__,
+ (' of %s' % self.subtype.__name__) if self.subtype else '',
+ self.value,
+ self.doc,
(' %s' % self.subdoc) if self.subdoc else ''
)
@@ -104,8 +107,9 @@ def coerce(self, value):
return value
elif self.type == list:
return self.type(
- map(self.subtype, map(lambda x: x.strip(), value.split(',')))
- )
+ map(
+ self.subtype, map(
+ lambda x: x.strip(), value.split(','))))
elif self.type == dict:
rv = {}
for pair in value.split(','):
@@ -114,13 +118,14 @@ def coerce(self, value):
val = val.strip()
try:
rv[key] = self.subtype(val)
- except Exception:
+ except:
rv[key] = val
return rv
return self.type(value)
class MetaConfig(type):
+
"""Config metaclass. Used to get the key name and set it on the value."""
def __new__(mcs, classname, bases, classdict):
@@ -132,7 +137,8 @@ def __new__(mcs, classname, bases, classdict):
return type.__new__(mcs, classname, bases, classdict)
-class BaseConfig(MetaConfig('ConfigBase', (object, ), {})):
+class BaseConfig(MetaConfig('ConfigBase', (object,), {})):
+
"""
This class holds the common method for configs.
@@ -144,8 +150,9 @@ def __init__(self, **kwargs):
"""Can be instanciated with config kwargs"""
for k in dir(self):
v = getattr(self, k)
- if (k not in self.__dict__ and not k.startswith('_')
- and not hasattr(v, '__call__')):
+ if (k not in self.__dict__ and not
+ k.startswith('_') and not
+ hasattr(v, '__call__')):
if isinstance(v, Key):
if v.is_list and v.value is not None:
v = list(v.value)
@@ -163,10 +170,9 @@ def _update(self, kwargs):
from pygal.util import merge
dir_self_set = set(dir(self))
merge(
- self.__dict__,
- dict([(k, v) for (k, v) in kwargs.items()
- if not k.startswith('_') and k in dir_self_set])
- )
+ self.__dict__, dict([
+ (k, v) for (k, v) in kwargs.items()
+ if not k.startswith('_') and k in dir_self_set]))
def to_dict(self):
"""Export a JSON serializable dictionary of the config"""
@@ -186,355 +192,371 @@ def copy(self):
class CommonConfig(BaseConfig):
+
"""Class holding options used in both chart and serie configuration"""
stroke = Key(
- True, bool, "Look", "Line dots (set it to false to get a scatter plot)"
- )
+ True, bool, "Look",
+ "Line dots (set it to false to get a scatter plot)")
show_dots = Key(True, bool, "Look", "Set to false to remove dots")
show_only_major_dots = Key(
False, bool, "Look",
- "Set to true to show only major dots according to their majored label"
- )
+ "Set to true to show only major dots according to their majored label")
dots_size = Key(2.5, float, "Look", "Radius of the dots")
- fill = Key(False, bool, "Look", "Fill areas under lines")
+ fill = Key(
+ False, bool, "Look", "Fill areas under lines")
- stroke_style = Key(
- None, dict, "Look", "Stroke style of serie element.",
- "This is a dict which can contain a "
- "'width', 'linejoin', 'linecap', 'dasharray' "
- "and 'dashoffset'"
- )
+ stroke_style = Key(None, dict, "Look", "Stroke style of serie element.",
+ "This is a dict which can contain a "
+ "'width', 'linejoin', 'linecap', 'dasharray' "
+ "and 'dashoffset'")
rounded_bars = Key(
None, int, "Look",
- "Set this to the desired radius in px (for Bar-like charts)"
- )
+ "Set this to the desired radius in px (for Bar-like charts)")
inner_radius = Key(
- 0, float, "Look", "Piechart inner radius (donut), must be <.9"
- )
+ 0, float, "Look", "Piechart inner radius (donut), must be <.9")
allow_interruptions = Key(
- False, bool, "Look", "Break lines on None values"
- )
+ False, bool, "Look", "Break lines on None values")
formatter = Key(
None, callable, "Value",
"A function to convert raw value to strings for this chart or serie",
"Default to value_formatter in most charts, it depends on dual charts."
- "(Can be overriden by value with the formatter metadata.)"
- )
+ "(Can be overriden by value with the formatter metadata.)")
class Config(CommonConfig):
+
"""Class holding config values"""
style = Key(
- DefaultStyle, Style, "Style", "Style holding values injected in css"
- )
+ DefaultStyle, Style, "Style", "Style holding values injected in css")
css = Key(
('file://style.css', 'file://graph.css'), list, "Style",
"List of css file",
"It can be any uri from file:///tmp/style.css to //domain/style.css",
- str
- )
+ str)
- classes = Key(('pygal-chart', ), list, "Style",
- "Classes of the root svg node", str)
+ classes = Key(
+ ('pygal-chart',),
+ list, "Style", "Classes of the root svg node",
+ str)
- defs = Key([], list, "Misc", "Extraneous defs to be inserted in svg",
- "Useful for adding gradients / patterns…", str)
+ defs = Key(
+ [],
+ list, "Misc", "Extraneous defs to be inserted in svg",
+ "Useful for adding gradients / patterns…",
+ str)
# Look #
title = Key(
- None, str, "Look", "Graph title.", "Leave it to None to disable title."
- )
+ None, str, "Look",
+ "Graph title.", "Leave it to None to disable title.")
x_title = Key(
- None, str, "Look", "Graph X-Axis title.",
- "Leave it to None to disable X-Axis title."
- )
+ None, str, "Look",
+ "Graph X-Axis title.", "Leave it to None to disable X-Axis title.")
y_title = Key(
- None, str, "Look", "Graph Y-Axis title.",
- "Leave it to None to disable Y-Axis title."
- )
+ None, str, "Look",
+ "Graph Y-Axis title.", "Leave it to None to disable Y-Axis title.")
- width = Key(800, int, "Look", "Graph width")
+ y2_title = Key(
+ None, str, "Look",
+ "Graph Secondary-Axis title.", "Leave it to None to disable Secondary-Axis title.")
- height = Key(600, int, "Look", "Graph height")
+ width = Key(
+ 800, int, "Look", "Graph width")
- show_x_guides = Key(
- False, bool, "Look", "Set to true to always show x guide lines"
- )
+ height = Key(
+ 600, int, "Look", "Graph height")
- show_y_guides = Key(
- True, bool, "Look", "Set to false to hide y guide lines"
- )
+ show_x_guides = Key(False, bool, "Look",
+ "Set to true to always show x guide lines")
- show_legend = Key(True, bool, "Look", "Set to false to remove legend")
+ show_y_guides = Key(True, bool, "Look",
+ "Set to false to hide y guide lines")
+
+ show_legend = Key(
+ True, bool, "Look", "Set to false to remove legend")
legend_at_bottom = Key(
- False, bool, "Look", "Set to true to position legend at bottom"
- )
+ False, bool, "Look", "Set to true to position legend at bottom")
legend_at_bottom_columns = Key(
- None, int, "Look", "Set to true to position legend at bottom"
- )
+ None, int, "Look", "Set to true to position legend at bottom")
- legend_box_size = Key(12, int, "Look", "Size of legend boxes")
+ legend_box_size = Key(
+ 12, int, "Look", "Size of legend boxes")
rounded_bars = Key(
- None, int, "Look", "Set this to the desired radius in px"
- )
+ None, int, "Look", "Set this to the desired radius in px")
stack_from_top = Key(
False, bool, "Look", "Stack from top to zero, this makes the stacked "
- "data match the legend order"
- )
+ "data match the legend order")
- spacing = Key(10, int, "Look", "Space between titles/legend/axes")
+ spacing = Key(
+ 10, int, "Look",
+ "Space between titles/legend/axes")
- margin = Key(20, int, "Look", "Margin around chart")
+ margin = Key(
+ 20, int, "Look",
+ "Margin around chart")
- margin_top = Key(None, int, "Look", "Margin around top of chart")
+ margin_top = Key(
+ None, int, "Look",
+ "Margin around top of chart")
- margin_right = Key(None, int, "Look", "Margin around right of chart")
+ margin_right = Key(
+ None, int, "Look",
+ "Margin around right of chart")
- margin_bottom = Key(None, int, "Look", "Margin around bottom of chart")
+ margin_bottom = Key(
+ None, int, "Look",
+ "Margin around bottom of chart")
- margin_left = Key(None, int, "Look", "Margin around left of chart")
+ margin_left = Key(
+ None, int, "Look",
+ "Margin around left of chart")
tooltip_border_radius = Key(0, int, "Look", "Tooltip border radius")
tooltip_fancy_mode = Key(
True, bool, "Look", "Fancy tooltips",
- "Print legend, x label in tooltip and use serie color for value."
- )
+ "Print legend, x label in tooltip and use serie color for value.")
inner_radius = Key(
- 0, float, "Look", "Piechart inner radius (donut), must be <.9"
- )
+ 0, float, "Look", "Piechart inner radius (donut), must be <.9")
- half_pie = Key(False, bool, "Look", "Create a half-pie chart")
+ half_pie = Key(
+ False, bool, "Look", "Create a half-pie chart")
x_labels = Key(
- None, list, "Label", "X labels, must have same len than data.",
- "Leave it to None to disable x labels display.", str
- )
+ None, list, "Label",
+ "X labels, must have same len than data.",
+ "Leave it to None to disable x labels display.",
+ str)
x_labels_major = Key(
- None,
- list,
- "Label",
+ None, list, "Label",
"X labels that will be marked major.",
- subtype=str
- )
+ subtype=str)
x_labels_major_every = Key(
- None, int, "Label", "Mark every n-th x label as major."
- )
+ None, int, "Label",
+ "Mark every n-th x label as major.")
x_labels_major_count = Key(
- None, int, "Label", "Mark n evenly distributed labels as major."
- )
+ None, int, "Label",
+ "Mark n evenly distributed labels as major.")
- show_x_labels = Key(True, bool, "Label", "Set to false to hide x-labels")
+ show_x_labels = Key(
+ True, bool, "Label", "Set to false to hide x-labels")
show_minor_x_labels = Key(
- True, bool, "Label", "Set to false to hide x-labels not marked major"
- )
+ True, bool, "Label", "Set to false to hide x-labels not marked major")
y_labels = Key(
- None, list, "Label", "You can specify explicit y labels",
- "Must be a list of numbers", float
- )
+ None, list, "Label",
+ "You can specify explicit y labels",
+ "Must be a list of numbers", float)
y_labels_major = Key(
- None,
- list,
- "Label",
+ None, list, "Label",
"Y labels that will be marked major. Default: auto",
- subtype=str
- )
+ subtype=str)
y_labels_major_every = Key(
- None, int, "Label", "Mark every n-th y label as major."
- )
+ None, int, "Label",
+ "Mark every n-th y label as major.")
y_labels_major_count = Key(
- None, int, "Label", "Mark n evenly distributed y labels as major."
- )
+ None, int, "Label",
+ "Mark n evenly distributed y labels as major.")
show_minor_y_labels = Key(
- True, bool, "Label", "Set to false to hide y-labels not marked major"
- )
+ True, bool, "Label", "Set to false to hide y-labels not marked major")
- show_y_labels = Key(True, bool, "Label", "Set to false to hide y-labels")
+ show_y_labels = Key(
+ True, bool, "Label", "Set to false to hide y-labels")
x_label_rotation = Key(
- 0, int, "Label", "Specify x labels rotation angles", "in degrees"
- )
+ 0, int, "Label", "Specify x labels rotation angles", "in degrees")
y_label_rotation = Key(
- 0, int, "Label", "Specify y labels rotation angles", "in degrees"
- )
+ 0, int, "Label", "Specify y labels rotation angles", "in degrees")
missing_value_fill_truncation = Key(
"x", str, "Look",
"Filled series with missing x and/or y values at the end of a series "
"are closed at the first value with a missing "
- "'x' (default), 'y' or 'either'"
- )
+ "'x' (default), 'y' or 'either'")
# Value #
x_value_formatter = Key(
formatters.default, callable, "Value",
"A function to convert abscissa numeric value to strings "
- "(used in XY and Date charts)"
- )
+ "(used in XY and Date charts)")
value_formatter = Key(
formatters.default, callable, "Value",
- "A function to convert ordinate numeric value to strings"
- )
+ "A function to convert ordinate numeric value to strings")
logarithmic = Key(
False, bool, "Value", "Display values in logarithmic scale"
)
+ x_logarithmic = Key(
+ False, bool, "Value", "Display x values in logarithmic scale"
+ )
+
+ y_logarithmic = Key(
+ False, bool, "Value", "Display y values in logarithmic scale"
+ )
+
interpolate = Key(
None, str, "Value", "Interpolation",
- "May be %s" % ' or '.join(INTERPOLATIONS)
- )
+ "May be %s" % ' or '.join(INTERPOLATIONS))
interpolation_precision = Key(
- 250, int, "Value", "Number of interpolated points between two values"
- )
+ 250, int, "Value", "Number of interpolated points between two values")
interpolation_parameters = Key(
{}, dict, "Value", "Various parameters for parametric interpolations",
"ie: For hermite interpolation, you can set the cardinal tension with"
- "{'type': 'cardinal', 'c': .5}", int
- )
+ "{'type': 'cardinal', 'c': .5}", int)
box_mode = Key(
'extremes', str, "Value", "Sets the mode to be used. "
- "(Currently only supported on box plot)", "May be %s" %
- ' or '.join(["1.5IQR", "extremes", "tukey", "stdev", "pstdev"])
- )
+ "(Currently only supported on box plot)",
+ "May be %s" % ' or '.join([
+ "1.5IQR", "extremes", "tukey", "stdev", "pstdev"]))
order_min = Key(
- None, int, "Value", "Minimum order of scale, defaults to None"
- )
+ None, int, "Value",
+ "Minimum order of scale, defaults to None")
min_scale = Key(
- 4, int, "Value", "Minimum number of scale graduation for auto scaling"
- )
+ 4, int, "Value",
+ "Minimum number of scale graduation for auto scaling")
max_scale = Key(
- 16, int, "Value", "Maximum number of scale graduation for auto scaling"
- )
+ 16, int, "Value",
+ "Maximum number of scale graduation for auto scaling")
range = Key(
None, list, "Value", "Explicitly specify min and max of values",
- "(ie: (0, 100))", int
- )
+ "(ie: (0, 100))", int)
secondary_range = Key(
None, list, "Value",
- "Explicitly specify min and max of secondary values", "(ie: (0, 100))",
- int
- )
+ "Explicitly specify min and max of secondary values",
+ "(ie: (0, 100))", int)
+
+ secondary_axis_round = Key(
+ None, int, "Value",
+ "Round the secondary axis values to decimal order")
xrange = Key(
None, list, "Value", "Explicitly specify min and max of x values "
- "(used in XY and Date charts)", "(ie: (0, 100))", int
- )
+ "(used in XY and Date charts)",
+ "(ie: (0, 100))", int)
- include_x_axis = Key(False, bool, "Value", "Always include x axis")
+ include_x_axis = Key(
+ False, bool, "Value", "Always include x axis")
zero = Key(
- 0, int, "Value", "Set the ordinate zero value",
- "Useful for filling to another base than abscissa"
- )
+ 0, int, "Value",
+ "Set the ordinate zero value",
+ "Useful for filling to another base than abscissa")
# Text #
no_data_text = Key(
- "No data", str, "Text", "Text to display when no data is given"
- )
+ "No data", str, "Text", "Text to display when no data is given")
- print_values = Key(False, bool, "Text", "Display values as text over plot")
+ print_values = Key(
+ False, bool,
+ "Text", "Display values as text over plot")
dynamic_print_values = Key(
- False, bool, "Text", "Show values only on hover"
- )
+ False, bool,
+ "Text", "Show values only on hover")
print_values_position = Key(
- 'center', str, "Text", "Customize position of `print_values`. "
- "(For bars: `top`, `center` or `bottom`)"
- )
+ 'center', str,
+ "Text", "Customize position of `print_values`. "
+ "(For bars: `top`, `center` or `bottom`)")
- print_zeroes = Key(True, bool, "Text", "Display zero values as well")
+ print_zeroes = Key(
+ True, bool,
+ "Text", "Display zero values as well")
- print_labels = Key(False, bool, "Text", "Display value labels")
+ print_labels = Key(
+ False, bool,
+ "Text", "Display value labels")
truncate_legend = Key(
- None, int, "Text", "Legend string length truncation threshold",
- "None = auto, Negative for none"
- )
+ None, int, "Text",
+ "Legend string length truncation threshold",
+ "None = auto, Negative for none")
truncate_label = Key(
- None, int, "Text", "Label string length truncation threshold",
- "None = auto, Negative for none"
- )
+ None, int, "Text",
+ "Label string length truncation threshold",
+ "None = auto, Negative for none")
# Misc #
- js = Key(('//kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js', ),
- list, "Misc", "List of js file",
- "It can be any uri from file:///tmp/ext.js to //domain/ext.js",
- str)
+ js = Key(
+ ('//kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js',),
+ list, "Misc", "List of js file",
+ "It can be any uri from file:///tmp/ext.js to //domain/ext.js",
+ str)
disable_xml_declaration = Key(
False, bool, "Misc",
"Don't write xml declaration and return str instead of string",
- "useful for writing output directly in html"
- )
+ "useful for writing output directly in html")
force_uri_protocol = Key(
- 'https', str, "Misc", "Default uri protocol",
+ 'https', str, "Misc",
+ "Default uri protocol",
"Default protocol for external files. "
- "Can be set to None to use a // uri"
- )
+ "Can be set to None to use a // uri")
explicit_size = Key(
- False, bool, "Misc", "Write width and height attributes"
- )
+ False, bool, "Misc", "Write width and height attributes")
- pretty_print = Key(False, bool, "Misc", "Pretty print the svg")
+ pretty_print = Key(
+ False, bool, "Misc", "Pretty print the svg")
strict = Key(
- False, bool, "Misc", "If True don't try to adapt / filter wrong values"
- )
+ False, bool, "Misc",
+ "If True don't try to adapt / filter wrong values")
- no_prefix = Key(False, bool, "Misc", "Don't prefix css")
+ no_prefix = Key(
+ False, bool, "Misc",
+ "Don't prefix css")
inverse_y_axis = Key(False, bool, "Misc", "Inverse Y axis direction")
class SerieConfig(CommonConfig):
+
"""Class holding serie config values"""
title = Key(
- None, str, "Look", "Serie title.", "Leave it to None to disable title."
- )
+ None, str, "Look",
+ "Serie title.", "Leave it to None to disable title.")
secondary = Key(
- False, bool, "Misc", "Set it to put the serie in a second axis"
- )
+ False, bool, "Misc",
+ "Set it to put the serie in a second axis")
diff --git a/pygal/css/graph.css b/pygal/css/graph.css
index 98f853de..a638fb73 100644
--- a/pygal/css/graph.css
+++ b/pygal/css/graph.css
@@ -68,12 +68,10 @@
{{ id }}.axis .guide.line {
stroke-dasharray: {{ style.guide_stroke_dasharray }};
- stroke: {{ style.guide_stroke_color }};
}
{{ id }}.axis .major.guide.line {
stroke-dasharray: {{ style.major_guide_stroke_dasharray }};
- stroke: {{ style.major_guide_stroke_color }};
}
{{ id }}.horizontal .axis.y .guide.line,
@@ -107,8 +105,7 @@
{{ id }}.dot {
stroke-width: 1px;
- fill-opacity: {{ style.dot_opacity }};
- stroke-opacity: {{ style.dot_opacity }};
+ fill-opacity: 1;
}
{{ id }}.dot.active {
diff --git a/pygal/css/style.css b/pygal/css/style.css
index 74cb59e2..171c3be7 100644
--- a/pygal/css/style.css
+++ b/pygal/css/style.css
@@ -68,11 +68,11 @@
}
{{ id }}.axis .guide.line {
- stroke: {{ style.foreground_subtle }};
+ stroke: {{ style.guide_color }};
}
{{ id }}.axis .major.line {
- stroke: {{ style.foreground }};
+ stroke: {{ style.guide_color }};
}
{{ id }}.axis text.major {
@@ -94,7 +94,6 @@
{{ id }}.reactive {
fill-opacity: {{ style.opacity }};
stroke-opacity: {{ style.stroke_opacity }};
- stroke-width: {{ style.stroke_width }};
}
{{ id }}.ci {
@@ -105,7 +104,7 @@
{{ id }}.active .reactive {
fill-opacity: {{ style.opacity_hover }};
stroke-opacity: {{ style.stroke_opacity_hover }};
- stroke-width: {{ style.stroke_width_hover }};
+ stroke-width: 4;
}
{{ id }}.ci .reactive.active {
diff --git a/pygal/etree.py b/pygal/etree.py
index 5f561b42..6ed513fa 100644
--- a/pygal/etree.py
+++ b/pygal/etree.py
@@ -25,6 +25,7 @@
class Etree(object):
+
"""Etree wrapper using lxml.etree or standard xml.etree"""
def __init__(self):
@@ -45,8 +46,8 @@ def __init__(self):
def __getattribute__(self, attr):
"""Retrieve attr from current active etree implementation"""
- if (attr not in object.__getattribute__(self, '__dict__')
- and attr not in Etree.__dict__):
+ if (attr not in object.__getattribute__(self, '__dict__') and
+ attr not in Etree.__dict__):
return object.__getattribute__(self._etree, attr)
return object.__getattribute__(self, attr)
diff --git a/pygal/formatters.py b/pygal/formatters.py
index e16b1057..7747b5f7 100644
--- a/pygal/formatters.py
+++ b/pygal/formatters.py
@@ -45,16 +45,14 @@ def __call__(self, val):
order = val and int(floor(log(abs(val)) / log(1000)))
orders = self.ORDERS.split(" ")[int(order > 0)]
if order == 0 or order > len(orders):
- return float_format(val / (1000**int(order)))
+ return float_format(val / (1000 ** int(order)))
return (
- float_format(val / (1000**int(order))) +
- orders[int(order) - int(order > 0)]
- )
+ float_format(val / (1000 ** int(order))) +
+ orders[int(order) - int(order > 0)])
class Significant(Formatter):
"""Show precision significant digit of float"""
-
def __init__(self, precision=10):
self.format = '%%.%dg' % precision
diff --git a/pygal/graph/__init__.py b/pygal/graph/__init__.py
index dcd25ba1..98c527c0 100644
--- a/pygal/graph/__init__.py
+++ b/pygal/graph/__init__.py
@@ -16,4 +16,5 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Graph package containing all builtin charts"""
diff --git a/pygal/graph/bar.py b/pygal/graph/bar.py
index d1897b5f..96f9214f 100644
--- a/pygal/graph/bar.py
+++ b/pygal/graph/bar.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Bar chart that presents grouped data with rectangular bars with lengths
proportional to the values that they represent.
@@ -26,6 +27,7 @@
class Bar(Graph):
+
"""Bar graph class"""
_series_margin = .06
@@ -50,25 +52,15 @@ def _bar(self, serie, parent, x, y, i, zero, secondary=False):
width -= 2 * serie_margin
height = self.view.y(zero) - y
r = serie.rounded_bars * 1 if serie.rounded_bars else 0
- alter(
- self.svg.transposable_node(
- parent,
- 'rect',
- x=x,
- y=y,
- rx=r,
- ry=r,
- width=width,
- height=height,
- class_='rect reactive tooltip-trigger'
- ), serie.metadata.get(i)
- )
+ alter(self.svg.transposable_node(
+ parent, 'rect',
+ x=x, y=y, rx=r, ry=r, width=width, height=height,
+ class_='rect reactive tooltip-trigger'), serie.metadata.get(i))
return x, y, width, height
def _tooltip_and_print_values(
- self, serie_node, serie, parent, i, val, metadata, x, y, width,
- height
- ):
+ self, serie_node, serie, parent, i, val, metadata,
+ 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))
@@ -79,8 +71,8 @@ def _tooltip_and_print_values(
v = serie.values[i]
sign = -1 if v < self.zero else 1
self._tooltip_data(
- parent, val, x_center, y_center, "centered", self._get_x_label(i)
- )
+ parent, val, x_center, y_center, "centered",
+ self._get_x_label(i))
if self.print_values_position == 'top':
if self.horizontal:
@@ -117,21 +109,20 @@ def bar(self, serie, rescale=False):
val = self._format(serie, i)
bar = decorate(
- self.svg, self.svg.node(bars, class_='bar'), metadata
- )
+ self.svg,
+ self.svg.node(bars, class_='bar'),
+ metadata)
x_, y_, width, height = self._bar(
- serie, bar, x, y, i, self.zero, secondary=rescale
- )
+ serie, bar, x, y, i, self.zero, secondary=rescale)
self._confidence_interval(
serie_node['overlay'], x_ + width / 2, y_, serie.values[i],
- metadata
- )
+ metadata)
self._tooltip_and_print_values(
- serie_node, serie, bar, i, val, metadata, x_, y_, width, height
- )
+ serie_node, serie, bar, i, val, metadata,
+ x_, y_, width, height)
def _compute(self):
"""Compute y min and max and y scale and set labels"""
diff --git a/pygal/graph/base.py b/pygal/graph/base.py
index 88e0add3..3b108b40 100644
--- a/pygal/graph/base.py
+++ b/pygal/graph/base.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Base for pygal charts"""
import os
@@ -33,6 +34,7 @@
class BaseGraph(object):
+
"""Chart internal behaviour related functions"""
_adapters = []
@@ -64,8 +66,8 @@ def __setattr__(self, name, value):
def __getattribute__(self, name):
"""Get an attribute from the class or from the state if there is one"""
if name.startswith('__') or name == 'state' or getattr(
- self, 'state',
- None) is None or name not in self.state.__dict__:
+ self, 'state', None
+ ) is None or name not in self.state.__dict__:
return super(BaseGraph, self).__getattribute__(name)
return getattr(self.state, name)
@@ -89,7 +91,7 @@ def prepare_values(self, raw, offset=0):
if not raw:
return
- adapters = list(self._adapters) or [lambda x: x]
+ adapters = list(self._adapters) or [lambda x:x]
if self.logarithmic:
for fun in not_zero, positive:
if fun in adapters:
@@ -99,18 +101,19 @@ def prepare_values(self, raw, offset=0):
self._adapt = reduce(compose, adapters) if not self.strict else ident
self._x_adapt = reduce(
- compose, self._x_adapters
- ) if not self.strict and getattr(self, '_x_adapters', None) else ident
+ compose, self._x_adapters) if not self.strict and getattr(
+ self, '_x_adapters', None) else ident
series = []
raw = [(
- list(raw_values) if not isinstance(raw_values, dict) else
- raw_values, serie_config_kwargs
+ list(raw_values) if not isinstance(
+ raw_values, dict) else raw_values,
+ serie_config_kwargs
) for raw_values, serie_config_kwargs in raw]
- width = max([len(values)
- for values, _ in raw] + [len(self.x_labels or [])])
+ width = max([len(values) for values, _ in raw] +
+ [len(self.x_labels or [])])
for raw_values, serie_config_kwargs in raw:
metadata = {}
@@ -125,9 +128,10 @@ def prepare_values(self, raw, offset=0):
value_list[self.x_labels.index(k)] = v
raw_values = value_list
- for index, raw_value in enumerate(raw_values + (
- (width - len(raw_values)) * [None] # aligning values
- if len(raw_values) < width else [])):
+ for index, raw_value in enumerate(
+ raw_values + (
+ (width - len(raw_values)) * [None] # aligning values
+ if len(raw_values) < width else [])):
if isinstance(raw_value, dict):
raw_value = dict(raw_value)
value = raw_value.pop('value', None)
@@ -151,8 +155,8 @@ def prepare_values(self, raw, offset=0):
value = (value, self.zero)
if self._x_adapt:
value = (
- self._x_adapt(value[0]), self._adapt(value[1])
- )
+ self._x_adapt(value[0]),
+ self._adapt(value[1]))
if isinstance(self, BaseMap):
value = (self._adapt(value[0]), value[1])
else:
@@ -162,14 +166,11 @@ def prepare_values(self, raw, offset=0):
values.append(value)
serie_config = SerieConfig()
- serie_config(
- **dict((k, v) for k, v in self.state.__dict__.items()
- if k in dir(serie_config))
- )
+ serie_config(**dict((k, v) for k, v in self.state.__dict__.items()
+ if k in dir(serie_config)))
serie_config(**serie_config_kwargs)
series.append(
- Serie(offset + len(series), values, serie_config, metadata)
- )
+ Serie(offset + len(series), values, serie_config, metadata))
return series
def setup(self, **kwargs):
@@ -182,12 +183,11 @@ def setup(self, **kwargs):
self.state = State(self, **kwargs)
if isinstance(self.style, type):
self.style = self.style()
- self.series = self.prepare_values([
- rs for rs in self.raw_series if not rs[1].get('secondary')
- ]) or []
- self.secondary_series = self.prepare_values([
- rs for rs in self.raw_series if rs[1].get('secondary')
- ], len(self.series)) or []
+ self.series = self.prepare_values(
+ [rs for rs in self.raw_series if not rs[1].get('secondary')]) or []
+ self.secondary_series = self.prepare_values(
+ [rs for rs in self.raw_series if rs[1].get('secondary')],
+ len(self.series)) or []
self.horizontal = getattr(self, 'horizontal', False)
self.svg = Svg(self)
self._x_labels = None
@@ -196,23 +196,20 @@ def setup(self, **kwargs):
self._y_2nd_labels = None
self.nodes = {}
self.margin_box = Margin(
- self.margin_top or self.margin, self.margin_right or self.margin,
- self.margin_bottom or self.margin, self.margin_left or self.margin
- )
+ self.margin_top or self.margin,
+ self.margin_right or self.margin,
+ self.margin_bottom or self.margin,
+ self.margin_left or self.margin)
self._box = Box()
self.view = None
if self.logarithmic and self.zero == 0:
# Explicit min to avoid interpolation dependency
- positive_values = list(
- filter(
- lambda x: x > 0, [
- val[1] or 1 if self._dual else val
- for serie in self.series for val in serie.safe_values
- ]
- )
- )
-
- self.zero = min(positive_values or (1, )) or 1
+ positive_values = list(filter(
+ lambda x: x > 0,
+ [val[1] or 1 if self._dual else val
+ for serie in self.series for val in serie.safe_values]))
+
+ self.zero = min(positive_values or (1,)) or 1
if self._len < 3:
self.interpolate = None
self._draw()
diff --git a/pygal/graph/box.py b/pygal/graph/box.py
index 15c51637..56d9e54d 100644
--- a/pygal/graph/box.py
+++ b/pygal/graph/box.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Box plot: a convenient way to display series as box with whiskers and outliers
Different types are available throught the box_mode option
@@ -28,6 +29,7 @@
class Box(Graph):
+
"""
Box plot
For each series, shows the median value, the 25th and 75th percentiles,
@@ -45,20 +47,17 @@ def _value_format(self, value, serie):
"""
if self.box_mode == "extremes":
return (
- 'Min: %s\nQ1 : %s\nQ2 : %s\nQ3 : %s\nMax: %s' %
- tuple(map(self._y_format, serie.points[1:6]))
- )
+ 'Min: %s\nQ1 : %s\nQ2 : %s\nQ3 : %s\nMax: %s' % tuple(
+ map(self._y_format, serie.points[1:6])))
elif self.box_mode in ["tukey", "stdev", "pstdev"]:
return (
'Min: %s\nLower Whisker: %s\nQ1: %s\nQ2: %s\nQ3: %s\n'
- 'Upper Whisker: %s\nMax: %s' %
- tuple(map(self._y_format, serie.points))
- )
+ 'Upper Whisker: %s\nMax: %s' % tuple(map(
+ self._y_format, serie.points)))
elif self.box_mode == '1.5IQR':
# 1.5IQR mode
- return 'Q1: %s\nQ2: %s\nQ3: %s' % tuple(
- map(self._y_format, serie.points[2:5])
- )
+ return 'Q1: %s\nQ2: %s\nQ3: %s' % tuple(map(
+ self._y_format, serie.points[2:5]))
else:
return self._y_format(serie.points)
@@ -71,7 +70,8 @@ def _compute(self):
serie.points, serie.outliers = \
self._box_points(serie.values, self.box_mode)
- self._x_pos = [(i + .5) / self._order for i in range(self._order)]
+ self._x_pos = [
+ (i + .5) / self._order for i in range(self._order)]
if self._min:
self._box.ymin = min(self._min, self.zero)
@@ -98,17 +98,17 @@ def _boxf(self, serie):
metadata = serie.metadata.get(0)
- box = decorate(self.svg, self.svg.node(boxes, class_='box'), metadata)
+ box = decorate(
+ self.svg,
+ self.svg.node(boxes, class_='box'),
+ metadata)
val = self._format(serie, 0)
x_center, y_center = self._draw_box(
- box, serie.points[1:6], serie.outliers, serie.index, metadata
- )
- self._tooltip_data(
- box, val, x_center, y_center, "centered",
- self._get_x_label(serie.index)
- )
+ box, serie.points[1:6], serie.outliers, serie.index, metadata)
+ self._tooltip_data(box, val, x_center, y_center, "centered",
+ self._get_x_label(serie.index))
self._static_value(serie_node, val, x_center, y_center, metadata)
def _draw_box(self, parent_node, quartiles, outliers, box_index, metadata):
@@ -122,72 +122,55 @@ def _draw_box(self, parent_node, quartiles, outliers, box_index, metadata):
width -= 2 * series_margin
# draw lines for whiskers - bottom, median, and top
- for i, whisker in enumerate((quartiles[0], quartiles[2],
- quartiles[4])):
+ for i, whisker in enumerate(
+ (quartiles[0], quartiles[2], quartiles[4])):
whisker_width = width if i == 1 else width / 2
shift = (width - whisker_width) / 2
xs = left_edge + shift
xe = left_edge + width - shift
- alter(
- self.svg.line(
- parent_node,
- coords=[(xs, self.view.y(whisker)),
- (xe, self.view.y(whisker))],
- class_='reactive tooltip-trigger',
- attrib={'stroke-width': 3}
- ), metadata
- )
-
- # draw lines connecting whiskers to box (Q1 and Q3)
- alter(
- self.svg.line(
- parent_node,
- coords=[(left_edge + width / 2, self.view.y(quartiles[0])),
- (left_edge + width / 2, self.view.y(quartiles[1]))],
- class_='reactive tooltip-trigger',
- attrib={'stroke-width': 2}
- ), metadata
- )
- alter(
- self.svg.line(
+ alter(self.svg.line(
parent_node,
- coords=[(left_edge + width / 2, self.view.y(quartiles[4])),
- (left_edge + width / 2, self.view.y(quartiles[3]))],
+ coords=[(xs, self.view.y(whisker)),
+ (xe, self.view.y(whisker))],
class_='reactive tooltip-trigger',
- attrib={'stroke-width': 2}
- ), metadata
- )
+ attrib={'stroke-width': 3}), metadata)
+
+ # draw lines connecting whiskers to box (Q1 and Q3)
+ alter(self.svg.line(
+ parent_node,
+ coords=[(left_edge + width / 2, self.view.y(quartiles[0])),
+ (left_edge + width / 2, self.view.y(quartiles[1]))],
+ class_='reactive tooltip-trigger',
+ attrib={'stroke-width': 2}), metadata)
+ alter(self.svg.line(
+ parent_node,
+ coords=[(left_edge + width / 2, self.view.y(quartiles[4])),
+ (left_edge + width / 2, self.view.y(quartiles[3]))],
+ class_='reactive tooltip-trigger',
+ attrib={'stroke-width': 2}), metadata)
# box, bounded by Q1 and Q3
- alter(
- self.svg.node(
- parent_node,
- tag='rect',
- x=left_edge,
- y=self.view.y(quartiles[1]),
- height=self.view.y(quartiles[3]) - self.view.y(quartiles[1]),
- width=width,
- class_='subtle-fill reactive tooltip-trigger'
- ), metadata
- )
+ alter(self.svg.node(
+ parent_node,
+ tag='rect',
+ x=left_edge,
+ y=self.view.y(quartiles[1]),
+ height=self.view.y(quartiles[3]) - self.view.y(quartiles[1]),
+ width=width,
+ class_='subtle-fill reactive tooltip-trigger'), metadata)
# draw outliers
for o in outliers:
- alter(
- self.svg.node(
- parent_node,
- tag='circle',
- cx=left_edge + width / 2,
- cy=self.view.y(o),
- r=3,
- class_='subtle-fill reactive tooltip-trigger'
- ), metadata
- )
-
- return (
- left_edge + width / 2,
- self.view.y(sum(quartiles) / len(quartiles))
- )
+ alter(self.svg.node(
+ parent_node,
+ tag='circle',
+ cx=left_edge + width / 2,
+ cy=self.view.y(o),
+ r=3,
+ class_='subtle-fill reactive tooltip-trigger'), metadata)
+
+ return (left_edge + width / 2, self.view.y(
+ sum(quartiles) / len(quartiles)))
@staticmethod
def _box_points(values, mode='extremes'):
@@ -214,7 +197,6 @@ def _box_points(values, mode='extremes'):
Sincich, T. L. Statistics for Engineering and the
Sciences, 4th ed. Prentice-Hall, 1995.
"""
-
def median(seq):
n = len(seq)
if n % 2 == 0: # seq has an even length
diff --git a/pygal/graph/dot.py b/pygal/graph/dot.py
index d97d4b35..c3d8a4ef 100644
--- a/pygal/graph/dot.py
+++ b/pygal/graph/dot.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Dot chart displaying values as a grid of dots, the bigger the value
the bigger the dot
@@ -29,6 +30,7 @@
class Dot(Graph):
+
"""Dot graph class"""
def dot(self, serie, r_max):
@@ -43,8 +45,10 @@ def dot(self, serie, r_max):
log10max = log10(self._max or 1)
if value != 0:
- size = r_max * ((log10(abs(value)) - log10min) /
- (log10max - log10min))
+ size = r_max * (
+ (log10(abs(value)) - log10min) /
+ (log10max - log10min)
+ )
else:
size = 0
else:
@@ -52,25 +56,19 @@ def dot(self, serie, r_max):
metadata = serie.metadata.get(i)
dots = decorate(
- self.svg, self.svg.node(serie_node['plot'], class_="dots"),
- metadata
- )
- alter(
- self.svg.node(
- dots,
- 'circle',
- cx=x,
- cy=y,
- r=size,
- class_='dot reactive tooltip-trigger' +
- (' negative' if value < 0 else '')
- ), metadata
- )
+ self.svg,
+ self.svg.node(serie_node['plot'], class_="dots"),
+ metadata)
+ alter(self.svg.node(
+ dots, 'circle',
+ cx=x, cy=y, r=size,
+ class_='dot reactive tooltip-trigger' + (
+ ' negative' if value < 0 else '')), metadata)
val = self._format(serie, i)
self._tooltip_data(
- dots, val, x, y, 'centered', self._get_x_label(i)
- )
+ dots, val, x, y, 'centered',
+ self._get_x_label(i))
self._static_value(serie_node, val, x, y, metadata)
def _compute(self):
@@ -84,8 +82,9 @@ def _compute(self):
self._y_pos = [n / 2 for n in reversed(range(1, 2 * y_len, 2))]
for j, serie in enumerate(self.series):
- serie.points = [(self._x_pos[i], self._y_pos[j])
- for i in range(x_len)]
+ serie.points = [
+ (self._x_pos[i], self._y_pos[j])
+ for i in range(x_len)]
def _compute_y_labels(self):
if self.y_labels:
@@ -106,9 +105,9 @@ def _set_view(self):
view_class = ReverseView if self.inverse_y_axis else View
self.view = view_class(
- self.width - self.margin_box.x, self.height - self.margin_box.y,
- self._box
- )
+ self.width - self.margin_box.x,
+ self.height - self.margin_box.y,
+ self._box)
@cached_property
def _values(self):
@@ -118,16 +117,14 @@ def _values(self):
@cached_property
def _max(self):
"""Getter for the maximum series value"""
- return (
- self.range[1] if (self.range and self.range[1] is not None) else
- (max(map(abs, self._values)) if self._values else None)
- )
+ return (self.range[1] if (self.range and self.range[1] is not None)
+ else (max(map(abs, self._values)) if self._values else None))
def _plot(self):
"""Plot all dots for series"""
r_max = min(
self.view.x(1) - self.view.x(0),
- (self.view.y(0) or 0) - self.view.y(1)
- ) / (2 * 1.05)
+ (self.view.y(0) or 0) - self.view.y(1)) / (
+ 2 * 1.05)
for serie in self.series:
self.dot(serie, r_max)
diff --git a/pygal/graph/dual.py b/pygal/graph/dual.py
index f8f08c89..4f0d19e2 100644
--- a/pygal/graph/dual.py
+++ b/pygal/graph/dual.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Dual chart base. Dual means a chart with 2 scaled axis like xy"""
from pygal.graph.graph import Graph
@@ -29,12 +30,14 @@ def _value_format(self, value):
"""
Format value for dual value display.
"""
- return '%s: %s' % (self._x_format(value[0]), self._y_format(value[1]))
+ return '%s: %s' % (
+ self._x_format(value[0]),
+ self._y_format(value[1]))
def _compute_x_labels(self):
x_pos = compute_scale(
- self._box.xmin, self._box.xmax, self.logarithmic, self.order_min,
- self.min_scale, self.max_scale
+ self._box.xmin, self._box.xmax, self.logarithmic,
+ self.order_min, self.min_scale, self.max_scale
)
if self.x_labels:
self._x_labels = []
@@ -59,8 +62,7 @@ def _compute_x_labels(self):
def _compute_x_labels_major(self):
# In case of dual, x labels must adapters and so majors too
self.x_labels_major = self.x_labels_major and list(
- map(self._x_adapt, self.x_labels_major)
- )
+ map(self._x_adapt, self.x_labels_major))
super(Dual, self)._compute_x_labels_major()
def _get_x_label(self, i):
diff --git a/pygal/graph/funnel.py b/pygal/graph/funnel.py
index 23576eb2..e9f42fa4 100644
--- a/pygal/graph/funnel.py
+++ b/pygal/graph/funnel.py
@@ -24,6 +24,7 @@
class Funnel(Graph):
+
"""Funnel graph class"""
_adapters = [positive, none_to_zero]
@@ -41,27 +42,22 @@ def funnel(self, serie):
val = self._format(serie, i)
funnels = decorate(
- self.svg, self.svg.node(serie_node['plot'], class_="funnels"),
- metadata
- )
-
- alter(
- self.svg.node(
- funnels,
- 'polygon',
- points=' '.join(map(fmt, map(self.view, poly))),
- class_='funnel reactive tooltip-trigger'
- ), metadata
- )
+ self.svg,
+ self.svg.node(serie_node['plot'], class_="funnels"),
+ metadata)
+
+ alter(self.svg.node(
+ funnels, 'polygon',
+ points=' '.join(map(fmt, map(self.view, poly))),
+ class_='funnel reactive tooltip-trigger'), metadata)
# Poly center from label
x, y = self.view((
self._center(self._x_pos[serie.index]),
- sum([point[1] for point in poly]) / len(poly)
- ))
+ sum([point[1] for point in poly]) / len(poly)))
self._tooltip_data(
- funnels, val, x, y, 'centered', self._get_x_label(serie.index)
- )
+ funnels, val, x, y, 'centered',
+ self._get_x_label(serie.index))
self._static_value(serie_node, val, x, y, metadata)
def _center(self, x):
@@ -75,7 +71,7 @@ def _compute(self):
previous = [[self.zero, self.zero] for i in range(self._len)]
for i, serie in enumerate(self.series):
- y_height = -sum(serie.safe_values) / 2
+ y_height = - sum(serie.safe_values) / 2
all_x_pos = [0] + self._x_pos
serie.points = []
for j, value in enumerate(serie.values):
@@ -100,14 +96,12 @@ def _compute(self):
def _compute_x_labels(self):
self._x_labels = list(
- zip(
- self.x_labels and map(self._x_format, self.x_labels) or [
+ zip(self.x_labels and
+ map(self._x_format, self.x_labels) or [
serie.title['title']
- if isinstance(serie.title, dict) else serie.title or ''
- for serie in self.series
- ], map(self._center, self._x_pos)
- )
- )
+ if isinstance(serie.title, dict)
+ else serie.title or '' for serie in self.series],
+ map(self._center, self._x_pos)))
def _plot(self):
"""Plot the funnel"""
diff --git a/pygal/graph/gauge.py b/pygal/graph/gauge.py
index 91de4894..dd14cd89 100644
--- a/pygal/graph/gauge.py
+++ b/pygal/graph/gauge.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Gauge chart representing values as needles on a polar scale"""
from pygal.graph.graph import Graph
@@ -24,6 +25,7 @@
class Gauge(Graph):
+
"""Gauge graph class"""
needle_width = 1 / 20
@@ -36,9 +38,9 @@ def _set_view(self):
view_class = PolarThetaView
self.view = view_class(
- self.width - self.margin_box.x, self.height - self.margin_box.y,
- self._box
- )
+ self.width - self.margin_box.x,
+ self.height - self.margin_box.y,
+ self._box)
def needle(self, serie):
"""Draw a needle for each value"""
@@ -53,9 +55,9 @@ def point(x, y):
val = self._format(serie, i)
metadata = serie.metadata.get(i)
gauges = decorate(
- self.svg, self.svg.node(serie_node['plot'], class_="dots"),
- metadata
- )
+ self.svg,
+ self.svg.node(serie_node['plot'], class_="dots"),
+ metadata)
tolerance = 1.15
@@ -68,24 +70,23 @@ def point(x, y):
w = (self._box._tmax - self._box._tmin + self.view.aperture) / 4
if self.logarithmic:
- w = min(w, self._min - self._min * 10**-10)
+ w = min(w, self._min - self._min * 10 ** -10)
alter(
self.svg.node(
- gauges,
- 'path',
- d='M %s L %s A %s 1 0 1 %s Z' % (
+ gauges, 'path', d='M %s L %s A %s 1 0 1 %s Z' % (
point(.85, theta),
point(self.needle_width, theta - w),
'%f %f' % (self.needle_width, self.needle_width),
point(self.needle_width, theta + w),
),
- class_='line reactive tooltip-trigger'
- ), metadata
- )
+ class_='line reactive tooltip-trigger'),
+ metadata)
x, y = self.view((.75, theta))
- self._tooltip_data(gauges, val, x, y, xlabel=self._get_x_label(i))
+ self._tooltip_data(
+ gauges, val, x, y,
+ xlabel=self._get_x_label(i))
self._static_value(serie_node, val, x, y, metadata)
def _y_axis(self, draw_axes=True):
@@ -96,26 +97,26 @@ def _y_axis(self, draw_axes=True):
guides = self.svg.node(axis, class_='guides')
self.svg.line(
- guides, [self.view((.95, theta)),
- self.view((1, theta))],
+ guides, [self.view((.95, theta)), self.view((1, theta))],
close=True,
- class_='line'
- )
+ class_='line')
self.svg.line(
- guides, [self.view((0, theta)),
- self.view((.95, theta))],
+ guides, [self.view((0, theta)), self.view((.95, theta))],
close=True,
- class_='guide line %s' %
- ('major' if i in (0, len(self._y_labels) - 1) else '')
- )
+ class_='guide line %s' % (
+ 'major' if i in (0, len(self._y_labels) - 1)
+ else ''))
x, y = self.view((.9, theta))
- self.svg.node(guides, 'text', x=x, y=y).text = label
+ self.svg.node(
+ guides, 'text',
+ x=x,
+ y=y
+ ).text = label
self.svg.node(
- guides,
- 'title',
+ guides, 'title',
).text = self._y_format(theta)
def _x_axis(self, draw_axes=True):
@@ -132,15 +133,18 @@ def _compute(self):
self.min_ -= 1
self.max_ += 1
- self._box.set_polar_box(0, 1, self.min_, self.max_)
+ self._box.set_polar_box(
+ 0, 1,
+ self.min_,
+ self.max_)
def _compute_x_labels(self):
pass
def _compute_y_labels(self):
y_pos = compute_scale(
- self.min_, self.max_, self.logarithmic, self.order_min,
- self.min_scale, self.max_scale
+ self.min_, self.max_, self.logarithmic,
+ self.order_min, self.min_scale, self.max_scale
)
if self.y_labels:
self._y_labels = []
@@ -157,7 +161,10 @@ def _compute_y_labels(self):
self._y_labels.append((title, pos))
self.min_ = min(self.min_, min(cut(self._y_labels, 1)))
self.max_ = max(self.max_, max(cut(self._y_labels, 1)))
- self._box.set_polar_box(0, 1, self.min_, self.max_)
+ self._box.set_polar_box(
+ 0, 1,
+ self.min_,
+ self.max_)
else:
self._y_labels = list(zip(map(self._y_format, y_pos), y_pos))
diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py
index c2867dc9..f000a68d 100644
--- a/pygal/graph/graph.py
+++ b/pygal/graph/graph.py
@@ -38,10 +38,11 @@
split_title,
truncate,
)
-from pygal.view import LogView, ReverseView, View, XYLogView
+from pygal.view import LogView, ReverseView, View, XYLogView, XLogView
class Graph(PublicApi):
+
"""Graph super class containing generic common functions"""
_dual = False
@@ -55,6 +56,7 @@ def _decorate(self):
self._make_title()
self._make_x_title()
self._make_y_title()
+ self._make_y2_title()
def _axes(self):
"""Draw axes"""
@@ -63,98 +65,79 @@ def _axes(self):
def _set_view(self):
"""Assign a view to current graph"""
- if self.logarithmic:
+ if self.x_logarithmic or self.y_logarithmic or self.logarithmic:
if self._dual:
- view_class = XYLogView
+ if self.logarithmic:
+ view_class = XYLogView
+ elif self.x_logarithmic:
+ view_class = XLogView
+ elif self.y_logarithmic:
+ view_class = LogView
else:
view_class = LogView
else:
view_class = ReverseView if self.inverse_y_axis else View
self.view = view_class(
- self.width - self.margin_box.x, self.height - self.margin_box.y,
- self._box
- )
+ self.width - self.margin_box.x,
+ self.height - self.margin_box.y,
+ self._box)
def _make_graph(self):
"""Init common graph svg structure"""
self.nodes['graph'] = self.svg.node(
class_='graph %s-graph %s' % (
self.__class__.__name__.lower(),
- 'horizontal' if self.horizontal else 'vertical'
- )
- )
- self.svg.node(
- self.nodes['graph'],
- 'rect',
- class_='background',
- x=0,
- y=0,
- width=self.width,
- height=self.height
- )
+ 'horizontal' if self.horizontal else 'vertical'))
+ self.svg.node(self.nodes['graph'], 'rect',
+ class_='background',
+ x=0, y=0,
+ width=self.width,
+ height=self.height)
self.nodes['plot'] = self.svg.node(
- self.nodes['graph'],
- class_="plot",
- transform="translate(%d, %d)" %
- (self.margin_box.left, self.margin_box.top)
- )
- self.svg.node(
- self.nodes['plot'],
- 'rect',
- class_='background',
- x=0,
- y=0,
- width=self.view.width,
- height=self.view.height
- )
+ self.nodes['graph'], class_="plot",
+ transform="translate(%d, %d)" % (
+ self.margin_box.left, self.margin_box.top))
+ self.svg.node(self.nodes['plot'], 'rect',
+ class_='background',
+ x=0, y=0,
+ width=self.view.width,
+ height=self.view.height)
self.nodes['title'] = self.svg.node(
- self.nodes['graph'], class_="titles"
- )
- self.nodes['overlay'] = self.svg.node(
self.nodes['graph'],
- class_="plot overlay",
- transform="translate(%d, %d)" %
- (self.margin_box.left, self.margin_box.top)
- )
+ class_="titles")
+ self.nodes['overlay'] = self.svg.node(
+ self.nodes['graph'], class_="plot overlay",
+ transform="translate(%d, %d)" % (
+ self.margin_box.left, self.margin_box.top))
self.nodes['text_overlay'] = self.svg.node(
- self.nodes['graph'],
- class_="plot text-overlay",
- transform="translate(%d, %d)" %
- (self.margin_box.left, self.margin_box.top)
- )
+ self.nodes['graph'], class_="plot text-overlay",
+ transform="translate(%d, %d)" % (
+ self.margin_box.left, self.margin_box.top))
self.nodes['tooltip_overlay'] = self.svg.node(
- self.nodes['graph'],
- class_="plot tooltip-overlay",
- transform="translate(%d, %d)" %
- (self.margin_box.left, self.margin_box.top)
- )
+ self.nodes['graph'], class_="plot tooltip-overlay",
+ transform="translate(%d, %d)" % (
+ self.margin_box.left, self.margin_box.top))
self.nodes['tooltip'] = self.svg.node(
self.nodes['tooltip_overlay'],
transform='translate(0 0)',
style="opacity: 0",
- **{'class': 'tooltip'}
- )
+ **{'class': 'tooltip'})
- self.svg.node(
- self.nodes['tooltip'],
- 'rect',
- rx=self.tooltip_border_radius,
- ry=self.tooltip_border_radius,
- width=0,
- height=0,
- **{'class': 'tooltip-box'}
- )
+ self.svg.node(self.nodes['tooltip'], 'rect',
+ rx=self.tooltip_border_radius,
+ ry=self.tooltip_border_radius,
+ width=0, height=0,
+ **{'class': 'tooltip-box'})
self.svg.node(self.nodes['tooltip'], 'g', class_='text')
def _x_axis(self):
"""Make the x axis: labels and guides"""
if not self._x_labels or not self.show_x_labels:
return
- axis = self.svg.node(
- self.nodes['plot'],
- class_="axis x%s" % (' always_show' if self.show_x_guides else '')
- )
+ axis = self.svg.node(self.nodes['plot'], class_="axis x%s" % (
+ ' always_show' if self.show_x_guides else ''
+ ))
truncation = self.truncate_label
if not truncation:
if self.x_label_rotation or len(self._x_labels) <= 1:
@@ -162,22 +145,18 @@ def _x_axis(self):
else:
first_label_position = self.view.x(self._x_labels[0][1]) or 0
last_label_position = self.view.x(self._x_labels[-1][1]) or 0
- available_space = (last_label_position - first_label_position
- ) / len(self._x_labels) - 1
-
+ available_space = (
+ last_label_position - first_label_position) / (
+ len(self._x_labels) - 1)
truncation = reverse_text_len(
- available_space, self.style.label_font_size
- )
+ available_space, self.style.label_font_size)
truncation = max(truncation, 1)
lastlabel = self._x_labels[-1][0]
if 0 not in [label[1] for label in self._x_labels]:
- self.svg.node(
- axis,
- 'path',
- d='M%f %f v%f' % (0, 0, self.view.height),
- class_='line'
- )
+ self.svg.node(axis, 'path',
+ d='M%f %f v%f' % (0, 0, self.view.height),
+ class_='line')
lastlabel = None
for label, position in self._x_labels:
@@ -194,18 +173,18 @@ def _x_axis(self):
y = self.view.height + 5
last_guide = (self._y_2nd_labels and label == lastlabel)
self.svg.node(
- guides,
- 'path',
+ guides, 'path',
d='M%f %f v%f' % (x or 0, 0, self.view.height),
class_='%s%s%sline' % (
- 'axis ' if label == "0" else '', 'major '
- if major else '', 'guide '
- if position != 0 and not last_guide else ''
- )
- )
+ 'axis ' if label == "0" else '',
+ 'major ' if major else '',
+ 'guide ' if position != 0 and not last_guide else ''))
y += .5 * self.style.label_font_size + 5
text = self.svg.node(
- guides, 'text', x=x, y=y, class_='major' if major else ''
+ guides, 'text',
+ x=x,
+ y=y,
+ class_='major' if major else ''
)
text.text = truncate(label, truncation)
@@ -213,35 +192,29 @@ def _x_axis(self):
self.svg.node(guides, 'title').text = label
elif self._dual:
self.svg.node(
- guides,
- 'title',
+ guides, 'title',
).text = self._x_format(position)
if self.x_label_rotation:
text.attrib['transform'] = "rotate(%d %f %f)" % (
- self.x_label_rotation, x, y
- )
+ self.x_label_rotation, x, y)
if self.x_label_rotation >= 180:
- text.attrib['class'] = ' '.join((
- text.attrib['class']
- and text.attrib['class'].split(' ') or []
- ) + ['backwards'])
+ text.attrib['class'] = ' '.join(
+ (text.attrib['class'] and text.attrib['class'].split(
+ ' ') or []) + ['backwards'])
- if self._y_2nd_labels and 0 not in [label[1]
- for label in self._x_labels]:
- self.svg.node(
- axis,
- 'path',
- d='M%f %f v%f' % (self.view.width, 0, self.view.height),
- class_='line'
- )
+ if self._y_2nd_labels and 0 not in [
+ label[1] for label in self._x_labels]:
+ self.svg.node(axis, 'path',
+ d='M%f %f v%f' % (
+ self.view.width, 0, self.view.height),
+ class_='line')
if self._x_2nd_labels:
secondary_ax = self.svg.node(
- self.nodes['plot'],
- class_="axis x x2%s" %
- (' always_show' if self.show_x_guides else '')
- )
+ self.nodes['plot'], class_="axis x x2%s" % (
+ ' always_show' if self.show_x_guides else ''
+ ))
for label, position in self._x_2nd_labels:
major = label in self._x_labels_major
if not (self.show_minor_x_labels or major):
@@ -251,38 +224,37 @@ def _x_axis(self):
x = self.view.x(position)
y = -5
text = self.svg.node(
- guides, 'text', x=x, y=y, class_='major' if major else ''
+ guides, 'text',
+ x=x,
+ y=y,
+ class_='major' if major else ''
)
text.text = label
if self.x_label_rotation:
text.attrib['transform'] = "rotate(%d %f %f)" % (
- -self.x_label_rotation, x, y
- )
+ -self.x_label_rotation, x, y)
if self.x_label_rotation >= 180:
text.attrib['class'] = ' '.join((
- text.attrib['class']
- and text.attrib['class'].split(' ') or []
- ) + ['backwards'])
+ text.attrib['class'] and
+ text.attrib['class'].split(
+ ' ') or []) + ['backwards'])
def _y_axis(self):
"""Make the y axis: labels and guides"""
if not self._y_labels or not self.show_y_labels:
return
- axis = self.svg.node(
- self.nodes['plot'],
- class_="axis y%s" % (' always_show' if self.show_y_guides else '')
- )
+ axis = self.svg.node(self.nodes['plot'], class_="axis y%s" % (
+ ' always_show' if self.show_y_guides else ''
+ ))
- if (0 not in [label[1] for label in self._y_labels]
- and self.show_y_guides):
+ if (0 not in [label[1] for label in self._y_labels] and
+ self.show_y_guides):
self.svg.node(
- axis,
- 'path',
+ axis, 'path',
d='M%f %f h%f' % (
0, 0 if self.inverse_y_axis else self.view.height,
- self.view.width
- ),
+ self.view.width),
class_='line'
)
@@ -294,28 +266,23 @@ def _y_axis(self):
if not (self.show_minor_y_labels or major):
continue
- guides = self.svg.node(
- axis,
- class_='%sguides' %
- ('logarithmic ' if self.logarithmic else '')
- )
+ guides = self.svg.node(axis, class_='%sguides' % (
+ 'logarithmic ' if self.logarithmic else ''
+ ))
x = -5
y = self.view.y(position)
if not y:
continue
if self.show_y_guides:
self.svg.node(
- guides,
- 'path',
+ guides, 'path',
d='M%f %f h%f' % (0, y, self.view.width),
class_='%s%s%sline' % (
- 'axis ' if label == "0" else '', 'major '
- if major else '', 'guide ' if position != 0 else ''
- )
- )
+ 'axis ' if label == "0" else '',
+ 'major ' if major else '',
+ 'guide ' if position != 0 else ''))
text = self.svg.node(
- guides,
- 'text',
+ guides, 'text',
x=x,
y=y + .35 * self.style.label_font_size,
class_='major' if major else ''
@@ -325,20 +292,18 @@ def _y_axis(self):
if self.y_label_rotation:
text.attrib['transform'] = "rotate(%d %f %f)" % (
- self.y_label_rotation, x, y
- )
+ self.y_label_rotation, x, y)
if 90 < self.y_label_rotation < 270:
- text.attrib['class'] = ' '.join((
- text.attrib['class']
- and text.attrib['class'].split(' ') or []
- ) + ['backwards'])
+ text.attrib['class'] = ' '.join(
+ (text.attrib['class'] and text.attrib['class'].split(
+ ' ') or []) + ['backwards'])
self.svg.node(
- guides,
- 'title',
+ guides, 'title',
).text = self._y_format(position)
if self._y_2nd_labels:
- secondary_ax = self.svg.node(self.nodes['plot'], class_="axis y2")
+ secondary_ax = self.svg.node(
+ self.nodes['plot'], class_="axis y2")
for label, position in self._y_2nd_labels:
major = position in self._y_labels_major
if not (self.show_minor_y_labels or major):
@@ -348,8 +313,7 @@ def _y_axis(self):
x = self.view.width + 5
y = self.view.y(position)
text = self.svg.node(
- guides,
- 'text',
+ guides, 'text',
x=x,
y=y + .35 * self.style.label_font_size,
class_='major' if major else ''
@@ -357,13 +321,12 @@ def _y_axis(self):
text.text = label
if self.y_label_rotation:
text.attrib['transform'] = "rotate(%d %f %f)" % (
- self.y_label_rotation, x, y
- )
+ self.y_label_rotation, x, y)
if 90 < self.y_label_rotation < 270:
- text.attrib['class'] = ' '.join((
- text.attrib['class']
- and text.attrib['class'].split(' ') or []
- ) + ['backwards'])
+ text.attrib['class'] = ' '.join(
+ (text.attrib['class'] and
+ text.attrib['class'].split(
+ ' ') or []) + ['backwards'])
def _legend(self):
"""Make the legend box"""
@@ -372,20 +335,17 @@ def _legend(self):
truncation = self.truncate_legend
if self.legend_at_bottom:
x = self.margin_box.left + self.spacing
- y = (
- self.margin_box.top + self.view.height + self._x_title_height +
- self._x_labels_height + self.spacing
- )
- cols = self.legend_at_bottom_columns or ceil(sqrt(self._order)
- ) or 1
+ y = (self.margin_box.top + self.view.height +
+ self._x_title_height +
+ self._x_labels_height + self.spacing)
+ cols = self.legend_at_bottom_columns or ceil(
+ sqrt(self._order)) or 1
if not truncation:
available_space = self.view.width / cols - (
- self.legend_box_size + 5
- )
+ self.legend_box_size + 5)
truncation = reverse_text_len(
- available_space, self.style.legend_font_size
- )
+ available_space, self.style.legend_font_size)
else:
x = self.spacing
y = self.margin_box.top + self.spacing
@@ -394,10 +354,8 @@ def _legend(self):
truncation = 15
legends = self.svg.node(
- self.nodes['graph'],
- class_='legends',
- transform='translate(%d, %d)' % (x, y)
- )
+ self.nodes['graph'], class_='legends',
+ transform='translate(%d, %d)' % (x, y))
h = max(self.legend_box_size, self.style.legend_font_size)
x_step = self.view.width / cols
@@ -409,25 +367,22 @@ def _legend(self):
x = self.margin_box.left + self.view.width + self.spacing
if self._y_2nd_labels:
h, w = get_texts_box(
- cut(self._y_2nd_labels), self.style.label_font_size
- )
- x += self.spacing + max(
- w * abs(cos(rad(self.y_label_rotation))), h
- )
+ cut(self._y_2nd_labels), self.style.label_font_size)
+ x += self.spacing + max(w * abs(cos(rad(
+ self.y_label_rotation))), h)
y = self.margin_box.top + self.spacing
secondary_legends = self.svg.node(
- self.nodes['graph'],
- class_='legends',
- transform='translate(%d, %d)' % (x, y)
- )
+ self.nodes['graph'], class_='legends',
+ transform='translate(%d, %d)' % (x, y))
serie_number = -1
i = 0
- for titles, is_secondary in ((self._legends, False),
- (self._secondary_legends, True)):
+ for titles, is_secondary in (
+ (self._legends, False),
+ (self._secondary_legends, True)):
if not self.legend_at_bottom and is_secondary:
i = 0
@@ -441,11 +396,9 @@ def _legend(self):
legend = self.svg.node(
secondary_legends if is_secondary else legends,
class_='legend reactive activate-serie',
- id="activate-serie-%d" % serie_number
- )
+ id="activate-serie-%d" % serie_number)
self.svg.node(
- legend,
- 'rect',
+ legend, 'rect',
x=col * x_step,
y=1.5 * row * h + (
self.style.legend_font_size - self.legend_box_size
@@ -465,8 +418,7 @@ def _legend(self):
truncated = truncate(title, truncation)
self.svg.node(
- node,
- 'text',
+ node, 'text',
x=col * x_step + self.legend_box_size + 5,
y=1.5 * row * h + .5 * h + .3 * self.style.legend_font_size
).text = truncated
@@ -481,22 +433,19 @@ def _make_title(self):
if self._title:
for i, title_line in enumerate(self._title, 1):
self.svg.node(
- self.nodes['title'],
- 'text',
- class_='title plot_title',
+ self.nodes['title'], 'text', class_='title plot_title',
x=self.width / 2,
y=i * (self.style.title_font_size + self.spacing)
).text = title_line
def _make_x_title(self):
"""Make the X-Axis title"""
- y = (self.height - self.margin_box.bottom + self._x_labels_height)
+ y = (self.height - self.margin_box.bottom +
+ self._x_labels_height)
if self._x_title:
for i, title_line in enumerate(self._x_title, 1):
text = self.svg.node(
- self.nodes['title'],
- 'text',
- class_='title',
+ self.nodes['title'], 'text', class_='title',
x=self.margin_box.left + self.view.width / 2,
y=y + i * (self.style.title_font_size + self.spacing)
)
@@ -508,15 +457,26 @@ def _make_y_title(self):
yc = self.margin_box.top + self.view.height / 2
for i, title_line in enumerate(self._y_title, 1):
text = self.svg.node(
- self.nodes['title'],
- 'text',
- class_='title',
+ self.nodes['title'], 'text', class_='title',
x=self._legend_at_left_width,
y=i * (self.style.title_font_size + self.spacing) + yc
)
text.attrib['transform'] = "rotate(%d %f %f)" % (
- -90, self._legend_at_left_width, yc
+ -90, self._legend_at_left_width, yc)
+ text.text = title_line
+
+ def _make_y2_title(self):
+ """Make the Y-Axis title"""
+ if self._y2_title:
+ yc = self.margin_box.top + self.view.height / 2
+ for i, title_line in enumerate(self._y2_title, 1):
+ text = self.svg.node(
+ self.nodes['title'], 'text', class_='title',
+ x=self._legend_at_right_width,
+ y=i * (self.style.title_font_size + self.spacing) + yc
)
+ text.attrib['transform'] = "rotate(%d %f %f)" % (
+ 90, self._legend_at_right_width, yc)
text.text = title_line
def _interpolate(self, xs, ys):
@@ -530,19 +490,16 @@ def _interpolate(self, xs, ys):
interpolate = INTERPOLATIONS[self.interpolate]
- return list(
- interpolate(
- x, y, self.interpolation_precision,
- **self.interpolation_parameters
- )
- )
+ return list(interpolate(
+ x, y, self.interpolation_precision,
+ **self.interpolation_parameters))
def _rescale(self, points):
"""Scale for secondary"""
- return [(
- x, self._scale_diff + (y - self._scale_min_2nd) * self._scale
- if y is not None else None
- ) for x, y in points]
+ return [
+ (x, self._scale_diff + (y - self._scale_min_2nd) * self._scale
+ if y is not None else None)
+ for x, y in points]
def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None):
"""Insert in desc tags informations for the javascript tooltip"""
@@ -560,16 +517,8 @@ def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None):
if xlabel:
self.svg.node(node, 'desc', class_="x_label").text = str(xlabel)
- def _static_value(
- self,
- serie_node,
- value,
- x,
- y,
- metadata,
- align_text='left',
- classes=None
- ):
+ def _static_value(self, serie_node, value, x, y, metadata,
+ align_text='left', classes=None):
"""Write the print value"""
label = metadata and metadata.get('label')
classes = classes and [classes] or []
@@ -579,8 +528,7 @@ def _static_value(
if self.print_values:
y -= self.style.value_font_size / 2
self.svg.node(
- serie_node['text_overlay'],
- 'text',
+ serie_node['text_overlay'], 'text',
class_=' '.join(label_cls),
x=x,
y=y + self.style.value_font_size / 3
@@ -593,14 +541,11 @@ def _static_value(
val_cls.append('showable')
self.svg.node(
- serie_node['text_overlay'],
- 'text',
+ serie_node['text_overlay'], 'text',
class_=' '.join(val_cls),
x=x,
y=y + self.style.value_font_size / 3,
- attrib={
- 'text-anchor': align_text
- }
+ attrib={'text-anchor': align_text}
).text = value if self.print_zeroes or value != '0' else ''
def _points(self, x_pos):
@@ -609,7 +554,9 @@ def _points(self, x_pos):
and interpolated points if interpolate option is specified
"""
for serie in self.all_series:
- serie.points = [(x_pos[i], v) for i, v in enumerate(serie.values)]
+ serie.points = [
+ (x_pos[i], v)
+ for i, v in enumerate(serie.values)]
if serie.points and self.interpolate:
serie.interpolated = self._interpolate(x_pos, serie.values)
else:
@@ -630,8 +577,12 @@ def _compute_secondary(self):
left_range = abs(y_pos[-1] - y_pos[0])
right_range = abs(ymax - ymin) or 1
scale = right_range / ((steps - 1) or 1)
- self._y_2nd_labels = [(self._y_format(ymin + i * scale), pos)
- for i, pos in enumerate(y_pos)]
+ if not self.secondary_axis_round:
+ self._y_2nd_labels = [(self._y_format(ymin + i * scale), pos)
+ for i, pos in enumerate(y_pos)]
+ else:
+ self._y_2nd_labels = [(self._y_format(round(ymin + i * scale, self.secondary_axis_round)), pos)
+ for i, pos in enumerate(y_pos)]
self._scale = left_range / right_range
self._scale_diff = y_pos[0]
@@ -679,18 +630,33 @@ def _format(self, serie, i):
value = serie.values[i]
metadata = serie.metadata.get(i)
- kwargs = {'chart': self, 'serie': serie, 'index': i}
- formatter = ((metadata and metadata.get('formatter'))
- or serie.formatter or self.formatter
- or self._value_format)
+ kwargs = {
+ 'chart': self,
+ 'serie': serie,
+ 'index': i
+ }
+ formatter = (
+ (metadata and metadata.get('formatter')) or
+ serie.formatter or
+ self.formatter or
+ self._value_format
+ )
kwargs = filter_kwargs(formatter, kwargs)
return formatter(value, **kwargs)
def _serie_format(self, serie, value):
"""Format an independent value for the serie"""
- kwargs = {'chart': self, 'serie': serie, 'index': None}
- formatter = (serie.formatter or self.formatter or self._value_format)
+ kwargs = {
+ 'chart': self,
+ 'serie': serie,
+ 'index': None
+ }
+ formatter = (
+ serie.formatter or
+ self.formatter or
+ self._value_format
+ )
kwargs = filter_kwargs(formatter, kwargs)
return formatter(value, **kwargs)
@@ -700,27 +666,22 @@ def _compute(self):
def _compute_margin(self):
"""Compute graph margins from set texts"""
self._legend_at_left_width = 0
+ self._legend_at_right_width = self.width
for series_group in (self.series, self.secondary_series):
if self.show_legend and series_group:
h, w = get_texts_box(
- map(
- lambda x: truncate(x, self.truncate_legend or 15), [
- serie.title['title']
- if isinstance(serie.title, dict) else serie.title
- or '' for serie in series_group
- ]
- ), self.style.legend_font_size
- )
+ map(lambda x: truncate(x, self.truncate_legend or 15),
+ [serie.title['title']
+ if isinstance(serie.title, dict)
+ else serie.title or '' for serie in series_group]),
+ self.style.legend_font_size)
if self.legend_at_bottom:
h_max = max(h, self.legend_box_size)
- cols = (
- self._order // self.legend_at_bottom_columns
- if self.legend_at_bottom_columns else
- ceil(sqrt(self._order)) or 1
- )
+ cols = (self._order // self.legend_at_bottom_columns
+ if self.legend_at_bottom_columns
+ else ceil(sqrt(self._order)) or 1)
self.margin_box.bottom += self.spacing + h_max * round(
- cols - 1
- ) * 1.5 + h_max
+ cols - 1) * 1.5 + h_max
else:
if series_group is self.series:
legend_width = self.spacing + w + self.legend_box_size
@@ -728,22 +689,18 @@ def _compute_margin(self):
self._legend_at_left_width += legend_width
else:
self.margin_box.right += (
- self.spacing + w + self.legend_box_size
- )
+ self.spacing + w + self.legend_box_size)
self._x_labels_height = 0
if (self._x_labels or self._x_2nd_labels) and self.show_x_labels:
for xlabels in (self._x_labels, self._x_2nd_labels):
if xlabels:
h, w = get_texts_box(
- map(
- lambda x: truncate(x, self.truncate_label or 25),
- cut(xlabels)
- ), self.style.label_font_size
- )
+ map(lambda x: truncate(x, self.truncate_label or 25),
+ cut(xlabels)),
+ self.style.label_font_size)
self._x_labels_height = self.spacing + max(
- w * abs(sin(rad(self.x_label_rotation))), h
- )
+ w * abs(sin(rad(self.x_label_rotation))), h)
if xlabels is self._x_labels:
self.margin_box.bottom += self._x_labels_height
else:
@@ -752,32 +709,26 @@ def _compute_margin(self):
if self.x_label_rotation % 180 < 90:
self.margin_box.right = max(
w * abs(cos(rad(self.x_label_rotation))),
- self.margin_box.right
- )
+ self.margin_box.right)
else:
self.margin_box.left = max(
w * abs(cos(rad(self.x_label_rotation))),
- self.margin_box.left
- )
+ self.margin_box.left)
if self.show_y_labels:
for ylabels in (self._y_labels, self._y_2nd_labels):
if ylabels:
h, w = get_texts_box(
- cut(ylabels), self.style.label_font_size
- )
+ cut(ylabels), self.style.label_font_size)
if ylabels is self._y_labels:
self.margin_box.left += self.spacing + max(
- w * abs(cos(rad(self.y_label_rotation))), h
- )
+ w * abs(cos(rad(self.y_label_rotation))), h)
else:
self.margin_box.right += self.spacing + max(
- w * abs(cos(rad(self.y_label_rotation))), h
- )
+ w * abs(cos(rad(self.y_label_rotation))), h)
self._title = split_title(
- self.title, self.width, self.style.title_font_size
- )
+ self.title, self.width, self.style.title_font_size)
if self.title:
h, _ = get_text_box(self._title[0], self.style.title_font_size)
@@ -785,8 +736,7 @@ def _compute_margin(self):
self._x_title = split_title(
self.x_title, self.width - self.margin_box.x,
- self.style.title_font_size
- )
+ self.style.title_font_size)
self._x_title_height = 0
if self._x_title:
@@ -797,8 +747,7 @@ def _compute_margin(self):
self._y_title = split_title(
self.y_title, self.height - self.margin_box.y,
- self.style.title_font_size
- )
+ self.style.title_font_size)
self._y_title_height = 0
if self._y_title:
@@ -807,6 +756,17 @@ def _compute_margin(self):
self.margin_box.left += height
self._y_title_height = height + self.spacing
+ self._y2_title = split_title(
+ self.y2_title, self.height - self.margin_box.y,
+ self.style.title_font_size)
+
+ self._y2_title_height = 0
+ if self._y2_title:
+ h, _ = get_text_box(self._y2_title[0], self.style.title_font_size)
+ height = len(self._y2_title) * (self.spacing + h)
+ self.margin_box.right += height
+ self._y2_title_height = height + self.spacing
+
# Inner margin
if self.print_values_position == 'top':
gh = self.height - self.margin_box.y
@@ -823,16 +783,15 @@ def _confidence_interval(self, node, x, y, value, metadata):
ci['point_estimate'] = value
low, high = getattr(
- stats, 'confidence_interval_%s' % ci.get('type', 'manual')
+ stats,
+ 'confidence_interval_%s' % ci.get('type', 'manual')
)(**ci)
self.svg.confidence_interval(
- node,
- x,
+ node, x,
# Respect some charts y modifications (pyramid, stackbar)
y + (self.view.y(low) - self.view.y(value)),
- y + (self.view.y(high) - self.view.y(value))
- )
+ y + (self.view.y(high) - self.view.y(value)))
@cached_property
def _legends(self):
@@ -847,59 +806,54 @@ def _secondary_legends(self):
@cached_property
def _values(self):
"""Getter for series values (flattened)"""
- return [
- val for serie in self.series for val in serie.values
- if val is not None
- ]
+ return [val
+ for serie in self.series
+ for val in serie.values
+ if val is not None]
@cached_property
def _secondary_values(self):
"""Getter for secondary series values (flattened)"""
- return [
- val for serie in self.secondary_series for val in serie.values
- if val is not None
- ]
+ return [val
+ for serie in self.secondary_series
+ for val in serie.values
+ if val is not None]
@cached_property
def _len(self):
"""Getter for the maximum series size"""
- return max([len(serie.values) for serie in self.all_series] or [0])
+ return max([
+ len(serie.values)
+ for serie in self.all_series] or [0])
@cached_property
def _secondary_min(self):
"""Getter for the minimum series value"""
- return (
- self.secondary_range[0]
- if (self.secondary_range
- and self.secondary_range[0] is not None) else
- (min(self._secondary_values) if self._secondary_values else None)
- )
+ return (self.secondary_range[0] if (
+ self.secondary_range and self.secondary_range[0] is not None)
+ else (min(self._secondary_values)
+ if self._secondary_values else None))
@cached_property
def _min(self):
"""Getter for the minimum series value"""
- return (
- self.range[0] if (self.range and self.range[0] is not None) else
- (min(self._values) if self._values else None)
- )
+ return (self.range[0] if (self.range and self.range[0] is not None)
+ else (min(self._values)
+ if self._values else None))
@cached_property
def _max(self):
"""Getter for the maximum series value"""
- return (
- self.range[1] if (self.range and self.range[1] is not None) else
- (max(self._values) if self._values else None)
- )
+ return (self.range[1] if (self.range and self.range[1] is not None)
+ else (max(self._values) if self._values else None))
@cached_property
def _secondary_max(self):
"""Getter for the maximum series value"""
- return (
- self.secondary_range[1]
- if (self.secondary_range
- and self.secondary_range[1] is not None) else
- (max(self._secondary_values) if self._secondary_values else None)
- )
+ return (self.secondary_range[1] if (
+ self.secondary_range and self.secondary_range[1] is not None)
+ else (max(self._secondary_values)
+ if self._secondary_values else None))
@cached_property
def _order(self):
@@ -913,17 +867,13 @@ def _x_label_format_if_value(self, label):
def _compute_x_labels(self):
self._x_labels = self.x_labels and list(
- zip(
- map(self._x_label_format_if_value, self.x_labels), self._x_pos
- )
- )
+ zip(map(self._x_label_format_if_value, self.x_labels),
+ self._x_pos))
def _compute_x_labels_major(self):
if self.x_labels_major_every:
- self._x_labels_major = [
- self._x_labels[i][0] for i in
- range(0, len(self._x_labels), self.x_labels_major_every)
- ]
+ self._x_labels_major = [self._x_labels[i][0] for i in range(
+ 0, len(self._x_labels), self.x_labels_major_every)]
elif self.x_labels_major_count:
label_count = len(self._x_labels)
@@ -932,20 +882,17 @@ def _compute_x_labels_major(self):
self._x_labels_major = [label[0] for label in self._x_labels]
else:
- self._x_labels_major = [
- self._x_labels[int(
- i * (label_count - 1) / (major_count - 1)
- )][0] for i in range(major_count)
- ]
+ self._x_labels_major = [self._x_labels[
+ int(i * (label_count - 1) / (major_count - 1))][0]
+ for i in range(major_count)]
else:
self._x_labels_major = self.x_labels_major and list(
- map(self._x_label_format_if_value, self.x_labels_major)
- ) or []
+ map(self._x_label_format_if_value, self.x_labels_major)) or []
def _compute_y_labels(self):
y_pos = compute_scale(
- self._box.ymin, self._box.ymax, self.logarithmic, self.order_min,
- self.min_scale, self.max_scale
+ self._box.ymin, self._box.ymax, self.logarithmic,
+ self.order_min, self.min_scale, self.max_scale
)
if self.y_labels:
self._y_labels = []
@@ -967,10 +914,8 @@ def _compute_y_labels(self):
def _compute_y_labels_major(self):
if self.y_labels_major_every:
- self._y_labels_major = [
- self._y_labels[i][1] for i in
- range(0, len(self._y_labels), self.y_labels_major_every)
- ]
+ self._y_labels_major = [self._y_labels[i][1] for i in range(
+ 0, len(self._y_labels), self.y_labels_major_every)]
elif self.y_labels_major_count:
label_count = len(self._y_labels)
@@ -978,11 +923,9 @@ def _compute_y_labels_major(self):
if (major_count >= label_count):
self._y_labels_major = [label[1] for label in self._y_labels]
else:
- self._y_labels_major = [
- self._y_labels[int(
- i * (label_count - 1) / (major_count - 1)
- )][1] for i in range(major_count)
- ]
+ self._y_labels_major = [self._y_labels[
+ int(i * (label_count - 1) / (major_count - 1))][1]
+ for i in range(major_count)]
elif self.y_labels_major:
self._y_labels_major = list(map(self._adapt, self.y_labels_major))
@@ -1001,22 +944,19 @@ def add_squares(self, squares):
for line in range(x_lines):
_current_x += (self.width - self.margin_box.x) / squares[0]
self.svg.node(
- self.nodes['plot'],
- 'path',
+ self.nodes['plot'], 'path',
class_='bg-lines',
- d='M%s %s L%s %s' %
- (_current_x, 0, _current_x, self.height - self.margin_box.y)
- )
+ d='M%s %s L%s %s' % (
+ _current_x, 0, _current_x,
+ self.height - self.margin_box.y))
for line in range(y_lines):
_current_y += (self.height - self.margin_box.y) / squares[1]
self.svg.node(
- self.nodes['plot'],
- 'path',
+ self.nodes['plot'], 'path',
class_='bg-lines',
- d='M%s %s L%s %s' %
- (0, _current_y, self.width - self.margin_box.x, _current_y)
- )
+ d='M%s %s L%s %s' % (
+ 0, _current_y, self.width - self.margin_box.x, _current_y))
return ((self.width - self.margin_box.x) / squares[0],
(self.height - self.margin_box.y) / squares[1])
@@ -1041,6 +981,7 @@ def _has_data(self):
return any([
len([
v for a in (s[0] if is_list_like(s) else [s])
- for v in (a if is_list_like(a) else [a]) if v is not None
- ]) for s in self.raw_series
+ for v in (a if is_list_like(a) else [a])
+ if v is not None])
+ for s in self.raw_series
])
diff --git a/pygal/graph/histogram.py b/pygal/graph/histogram.py
index d3953a73..06dbef0c 100644
--- a/pygal/graph/histogram.py
+++ b/pygal/graph/histogram.py
@@ -27,6 +27,7 @@
class Histogram(Dual, Bar):
+
"""Histogram chart class"""
_series_margin = 0
@@ -38,26 +39,27 @@ def _values(self):
@cached_property
def _secondary_values(self):
"""Getter for secondary series values (flattened)"""
- return [
- val[0] for serie in self.secondary_series for val in serie.values
- if val[0] is not None
- ]
+ return [val[0]
+ for serie in self.secondary_series
+ for val in serie.values
+ if val[0] is not None]
@cached_property
def xvals(self):
"""All x values"""
- return [
- val for serie in self.all_series for dval in serie.values
- for val in dval[1:3] if val is not None
- ]
+ return [val
+ for serie in self.all_series
+ for dval in serie.values
+ for val in dval[1:3]
+ if val is not None]
@cached_property
def yvals(self):
"""All y values"""
- return [
- val[0] for serie in self.series for val in serie.values
- if val[0] is not None
- ]
+ return [val[0]
+ for serie in self.series
+ for val in serie.values
+ if val[0] is not None]
def _bar(self, serie, parent, x0, x1, y, i, zero, secondary=False):
"""Internal bar drawing function"""
@@ -70,19 +72,10 @@ def _bar(self, serie, parent, x0, x1, y, i, zero, secondary=False):
width -= 2 * series_margin
r = serie.rounded_bars * 1 if serie.rounded_bars else 0
- alter(
- self.svg.transposable_node(
- parent,
- 'rect',
- x=x,
- y=y,
- rx=r,
- ry=r,
- width=width,
- height=height,
- class_='rect reactive tooltip-trigger'
- ), serie.metadata.get(i)
- )
+ alter(self.svg.transposable_node(
+ parent, 'rect',
+ x=x, y=y, rx=r, ry=r, width=width, height=height,
+ class_='rect reactive tooltip-trigger'), serie.metadata.get(i))
return x, y, width, height
def bar(self, serie, rescale=False):
@@ -97,16 +90,15 @@ def bar(self, serie, rescale=False):
metadata = serie.metadata.get(i)
bar = decorate(
- self.svg, self.svg.node(bars, class_='histbar'), metadata
- )
+ self.svg,
+ self.svg.node(bars, class_='histbar'),
+ metadata)
val = self._format(serie, i)
bounds = self._bar(
- serie, bar, x0, x1, y, i, self.zero, secondary=rescale
- )
+ serie, bar, x0, x1, y, i, self.zero, secondary=rescale)
self._tooltip_and_print_values(
- serie_node, serie, bar, i, val, metadata, *bounds
- )
+ serie_node, serie, bar, i, val, metadata, *bounds)
def _compute(self):
"""Compute x/y min and max and x/y scale and set labels"""
diff --git a/pygal/graph/horizontal.py b/pygal/graph/horizontal.py
index ed66cc3b..9939b21a 100644
--- a/pygal/graph/horizontal.py
+++ b/pygal/graph/horizontal.py
@@ -23,6 +23,7 @@
class HorizontalGraph(Graph):
+
"""Horizontal graph mixin"""
def __init__(self, *args, **kwargs):
@@ -34,14 +35,11 @@ def _post_compute(self):
"""After computations transpose labels"""
self._x_labels, self._y_labels = self._y_labels, self._x_labels
self._x_labels_major, self._y_labels_major = (
- self._y_labels_major, self._x_labels_major
- )
+ self._y_labels_major, self._x_labels_major)
self._x_2nd_labels, self._y_2nd_labels = (
- self._y_2nd_labels, self._x_2nd_labels
- )
+ self._y_2nd_labels, self._x_2nd_labels)
self.show_y_guides, self.show_x_guides = (
- self.show_x_guides, self.show_y_guides
- )
+ self.show_x_guides, self.show_y_guides)
def _axes(self):
"""Set the _force_vertical flag when rendering axes"""
@@ -57,9 +55,9 @@ def _set_view(self):
view_class = HorizontalView
self.view = view_class(
- self.width - self.margin_box.x, self.height - self.margin_box.y,
- self._box
- )
+ self.width - self.margin_box.x,
+ self.height - self.margin_box.y,
+ self._box)
def _get_x_label(self, i):
"""Convenience function to get the x_label of a value index"""
diff --git a/pygal/graph/horizontalbar.py b/pygal/graph/horizontalbar.py
index ad9c2ffa..5e801cef 100644
--- a/pygal/graph/horizontalbar.py
+++ b/pygal/graph/horizontalbar.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Horizontal bar graph"""
from pygal.graph.bar import Bar
@@ -23,6 +24,7 @@
class HorizontalBar(HorizontalGraph, Bar):
+
"""Horizontal Bar graph"""
def _plot(self):
diff --git a/pygal/graph/horizontalline.py b/pygal/graph/horizontalline.py
index 801dada4..e13039d7 100644
--- a/pygal/graph/horizontalline.py
+++ b/pygal/graph/horizontalline.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Horizontal line graph"""
from pygal.graph.horizontal import HorizontalGraph
@@ -23,6 +24,7 @@
class HorizontalLine(HorizontalGraph, Line):
+
"""Horizontal Line graph"""
def _plot(self):
diff --git a/pygal/graph/horizontalstackedbar.py b/pygal/graph/horizontalstackedbar.py
index 29f4c64c..990361f1 100644
--- a/pygal/graph/horizontalstackedbar.py
+++ b/pygal/graph/horizontalstackedbar.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Horizontal stacked graph"""
from pygal.graph.horizontal import HorizontalGraph
@@ -23,4 +24,5 @@
class HorizontalStackedBar(HorizontalGraph, StackedBar):
+
"""Horizontal Stacked Bar graph"""
diff --git a/pygal/graph/horizontalstackedline.py b/pygal/graph/horizontalstackedline.py
index 88155230..9748d819 100644
--- a/pygal/graph/horizontalstackedline.py
+++ b/pygal/graph/horizontalstackedline.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Horizontal Stacked Line graph"""
from pygal.graph.horizontal import HorizontalGraph
@@ -23,6 +24,7 @@
class HorizontalStackedLine(HorizontalGraph, StackedLine):
+
"""Horizontal Stacked Line graph"""
def _plot(self):
diff --git a/pygal/graph/line.py b/pygal/graph/line.py
index ff9e4cdd..d9675f19 100644
--- a/pygal/graph/line.py
+++ b/pygal/graph/line.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Line chart: Display series of data as markers (dots)
connected by straight segments
@@ -26,6 +27,7 @@
class Line(Graph):
+
"""Line graph class"""
def __init__(self, *args, **kwargs):
@@ -37,19 +39,21 @@ def __init__(self, *args, **kwargs):
def _values(self):
"""Getter for series values (flattened)"""
return [
- val[1] for serie in self.series for val in
- (serie.interpolated if self.interpolate else serie.points)
- if val[1] is not None and (not self.logarithmic or val[1] > 0)
- ]
+ val[1]
+ for serie in self.series
+ for val in (serie.interpolated
+ if self.interpolate else serie.points)
+ if val[1] is not None and (not self.logarithmic or val[1] > 0)]
@cached_property
def _secondary_values(self):
"""Getter for secondary series values (flattened)"""
return [
- val[1] for serie in self.secondary_series for val in
- (serie.interpolated if self.interpolate else serie.points)
- if val[1] is not None and (not self.logarithmic or val[1] > 0)
- ]
+ val[1]
+ for serie in self.secondary_series
+ for val in (serie.interpolated
+ if self.interpolate else serie.points)
+ if val[1] is not None and (not self.logarithmic or val[1] > 0)]
def _fill(self, values):
"""Add extra values to fill the line"""
@@ -74,12 +78,12 @@ def _fill(self, values):
"Invalid value ({}) for config key "
"'missing_value_fill_truncation';"
" Use 'x', 'y' or 'either'".format(
- self.missing_value_fill_truncation
- )
- )
+ self.missing_value_fill_truncation))
end -= 1
- return ([(values[0][0], zero)] + values + [(values[end][0], zero)])
+ return ([(values[0][0], zero)] +
+ values +
+ [(values[end][0], zero)])
def line(self, serie, rescale=False):
"""Draw the line serie"""
@@ -96,9 +100,9 @@ def line(self, serie, rescale=False):
if self.logarithmic:
if points[i][1] is None or points[i][1] <= 0:
continue
- if (serie.show_only_major_dots and self.x_labels
- and i < len(self.x_labels)
- and self.x_labels[i] not in self._x_labels_major):
+ if (serie.show_only_major_dots and
+ self.x_labels and i < len(self.x_labels) and
+ self.x_labels[i] not in self._x_labels_major):
continue
metadata = serie.metadata.get(i)
@@ -110,33 +114,25 @@ def line(self, serie, rescale=False):
classes = ' '.join(classes)
self._confidence_interval(
- serie_node['overlay'], x, y, serie.values[i], metadata
- )
+ serie_node['overlay'], x, y, serie.values[i], metadata)
dots = decorate(
self.svg,
self.svg.node(serie_node['overlay'], class_="dots"),
- metadata
- )
+ metadata)
val = self._format(serie, i)
- alter(
- self.svg.transposable_node(
- dots,
- 'circle',
- cx=x,
- cy=y,
- r=serie.dots_size,
- class_='dot reactive tooltip-trigger'
- ), metadata
- )
+ alter(self.svg.transposable_node(
+ dots, 'circle', cx=x, cy=y, r=serie.dots_size,
+ class_='dot reactive tooltip-trigger'), metadata)
self._tooltip_data(
- dots, val, x, y, xlabel=self._get_x_label(i)
- )
+ dots, val, x, y,
+ xlabel=self._get_x_label(i))
self._static_value(
- serie_node, val, x + self.style.value_font_size,
- y + self.style.value_font_size, metadata
- )
+ serie_node, val,
+ x + self.style.value_font_size,
+ y + self.style.value_font_size,
+ metadata)
if serie.stroke:
if self.interpolate:
@@ -159,12 +155,12 @@ def line(self, serie, rescale=False):
# emit current subsequence
sequences.append(cur_sequence)
cur_sequence = []
- elif y is None: # just discard
+ elif y is None: # just discard
continue
else:
- cur_sequence.append((x, y)) # append the element
+ cur_sequence.append((x, y)) # append the element
- if len(cur_sequence) > 0: # emit last possible sequence
+ if len(cur_sequence) > 0: # emit last possible sequence
sequences.append(cur_sequence)
else:
# plain vanilla rendering
@@ -177,12 +173,9 @@ def line(self, serie, rescale=False):
del seq[seq.index(ele)]
for seq in sequences:
self.svg.line(
- serie_node['plot'],
- seq,
- close=self._self_close,
+ serie_node['plot'], seq, close=self._self_close,
class_='line reactive' +
- (' nofill' if not serie.fill else '')
- )
+ (' nofill' if not serie.fill else ''))
def _compute(self):
"""Compute y min and max and y scale and set labels"""
diff --git a/pygal/graph/map.py b/pygal/graph/map.py
index 51d92fdb..308b522b 100644
--- a/pygal/graph/map.py
+++ b/pygal/graph/map.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
pygal contains no map but a base class to create extension
see the pygal_maps_world package to get an exemple.
@@ -28,6 +29,7 @@
class BaseMap(Graph):
+
"""Base class for maps"""
_dual = True
@@ -35,10 +37,10 @@ class BaseMap(Graph):
@cached_property
def _values(self):
"""Getter for series values (flattened)"""
- return [
- val[1] for serie in self.series for val in serie.values
- if val[1] is not None
- ]
+ return [val[1]
+ for serie in self.series
+ for val in serie.values
+ if val[1] is not None]
def enumerate_values(self, serie):
"""Hook to replace default enumeration on values"""
@@ -54,8 +56,7 @@ def _value_format(self, value):
"""
return '%s: %s' % (
self.area_names.get(self.adapt_code(value[0]), '?'),
- self._y_format(value[1])
- )
+ self._y_format(value[1]))
def _plot(self):
"""Insert a map in the chart and apply data on it"""
@@ -64,9 +65,8 @@ def _plot(self):
map.set('height', str(self.view.height))
for i, serie in enumerate(self.series):
- safe_vals = list(
- filter(lambda x: x is not None, cut(serie.values, 1))
- )
+ safe_vals = list(filter(
+ lambda x: x is not None, cut(serie.values, 1)))
if not safe_vals:
continue
min_ = min(safe_vals)
@@ -81,9 +81,9 @@ def _plot(self):
ratio = .3 + .7 * (value - min_) / (max_ - min_)
areae = map.findall(
- ".//*[@class='%s%s %s map-element']" %
- (self.area_prefix, area_code, self.kind)
- )
+ ".//*[@class='%s%s %s map-element']" % (
+ self.area_prefix, area_code,
+ self.kind))
if not areae:
continue
diff --git a/pygal/graph/pie.py b/pygal/graph/pie.py
index 867d3d78..39cd9bfd 100644
--- a/pygal/graph/pie.py
+++ b/pygal/graph/pie.py
@@ -29,6 +29,7 @@
class Pie(Graph):
+
"""Pie graph class"""
_adapters = [positive, none_to_zero]
@@ -59,8 +60,9 @@ def slice(self, serie, start_angle, total):
val = self._format(serie, i)
metadata = serie.metadata.get(i)
slice_ = decorate(
- self.svg, self.svg.node(slices, class_="slice"), metadata
- )
+ self.svg,
+ self.svg.node(slices, class_="slice"),
+ metadata)
if dual:
small_radius = radius * .9
big_radius = radius
@@ -68,21 +70,17 @@ def slice(self, serie, start_angle, total):
big_radius = radius * .9
small_radius = radius * serie.inner_radius
- alter(
- self.svg.slice(
- serie_node, slice_, big_radius, small_radius, angle,
- start_angle, center, val, i, metadata
- ), metadata
- )
+ alter(self.svg.slice(
+ serie_node, slice_, big_radius, small_radius,
+ angle, start_angle, center, val, i, metadata), metadata)
start_angle += angle
if dual:
val = self._serie_format(serie, sum(serie.values))
- self.svg.slice(
- serie_node, self.svg.node(slices,
- class_="big_slice"), radius * .9, 0,
- serie_angle, original_start_angle, center, val, i, metadata
- )
+ self.svg.slice(serie_node,
+ self.svg.node(slices, class_="big_slice"),
+ radius * .9, 0, serie_angle,
+ original_start_angle, center, val, i, metadata)
return serie_angle
def _compute_x_labels(self):
diff --git a/pygal/graph/public.py b/pygal/graph/public.py
index 5c4a1828..6c0b0815 100644
--- a/pygal/graph/public.py
+++ b/pygal/graph/public.py
@@ -26,6 +26,7 @@
class PublicApi(BaseGraph):
+
"""Chart public functions"""
def add(self, title, values, **kwargs):
@@ -50,8 +51,7 @@ def render(self, is_unicode=False, **kwargs):
"""Render the graph, and return the svg string"""
self.setup(**kwargs)
svg = self.svg.render(
- is_unicode=is_unicode, pretty_print=self.pretty_print
- )
+ is_unicode=is_unicode, pretty_print=self.pretty_print)
self.teardown()
return svg
@@ -96,16 +96,16 @@ def render_django_response(self, **kwargs):
"""Render the graph, and return a Django response"""
from django.http import HttpResponse
return HttpResponse(
- self.render(**kwargs), content_type='image/svg+xml'
- )
+ self.render(**kwargs), content_type='image/svg+xml')
def render_data_uri(self, **kwargs):
"""Output a base 64 encoded data uri"""
# Force protocol as data uri have none
kwargs.setdefault('force_uri_protocol', 'https')
return "data:image/svg+xml;charset=utf-8;base64,%s" % (
- base64.b64encode(self.render(**kwargs)
- ).decode('utf-8').replace('\n', '')
+ base64.b64encode(
+ self.render(**kwargs)
+ ).decode('utf-8').replace('\n', '')
)
def render_to_file(self, filename, **kwargs):
@@ -117,8 +117,7 @@ def render_to_png(self, filename=None, dpi=72, **kwargs):
"""Render the graph, convert it to png and write it to filename"""
import cairosvg
return cairosvg.svg2png(
- bytestring=self.render(**kwargs), write_to=filename, dpi=dpi
- )
+ bytestring=self.render(**kwargs), write_to=filename, dpi=dpi)
def render_sparktext(self, relative_to=None):
"""Make a mini text sparkline from chart"""
@@ -142,9 +141,8 @@ def render_sparktext(self, relative_to=None):
divisions = len(bars) - 1
for value in values:
- chart += bars[int(
- divisions * (value - relative_to) / (vmax - relative_to)
- )]
+ chart += bars[int(divisions *
+ (value - relative_to) / (vmax - relative_to))]
return chart
def render_sparkline(self, **kwargs):
diff --git a/pygal/graph/pyramid.py b/pygal/graph/pyramid.py
index 206299d6..14d93893 100644
--- a/pygal/graph/pyramid.py
+++ b/pygal/graph/pyramid.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Pyramid chart: Stacked bar chart containing only positive values divided by two
axes, generally gender for age pyramid.
@@ -27,6 +28,7 @@
class VerticalPyramid(StackedBar):
+
"""Vertical Pyramid graph class"""
_adapters = [positive]
@@ -38,37 +40,26 @@ def _value_format(self, value):
def _get_separated_values(self, secondary=False):
"""Separate values between odd and even series stacked"""
series = self.secondary_series if secondary else self.series
- positive_vals = map(
- sum,
- zip(
- *[
- serie.safe_values for index, serie in enumerate(series)
- if index % 2
- ]
- )
- )
- negative_vals = map(
- sum,
- zip(
- *[
- serie.safe_values for index, serie in enumerate(series)
- if not index % 2
- ]
- )
- )
+ positive_vals = map(sum, zip(
+ *[serie.safe_values
+ for index, serie in enumerate(series)
+ if index % 2]))
+ negative_vals = map(sum, zip(
+ *[serie.safe_values
+ for index, serie in enumerate(series)
+ if not index % 2]))
return list(positive_vals), list(negative_vals)
def _compute_box(self, positive_vals, negative_vals):
"""Compute Y min and max"""
max_ = max(
max(positive_vals or [self.zero]),
- max(negative_vals or [self.zero])
- )
+ max(negative_vals or [self.zero]))
if self.range and self.range[0] is not None:
self._box.ymin = self.range[0]
else:
- self._box.ymin = -max_
+ self._box.ymin = - max_
if self.range and self.range[1] is not None:
self._box.ymax = self.range[1]
@@ -78,15 +69,16 @@ def _compute_box(self, positive_vals, negative_vals):
def _pre_compute_secondary(self, positive_vals, negative_vals):
"""Compute secondary y min and max"""
self._secondary_max = max(max(positive_vals), max(negative_vals))
- self._secondary_min = -self._secondary_max
+ self._secondary_min = - self._secondary_max
def _bar(self, serie, parent, x, y, i, zero, secondary=False):
"""Internal stacking bar drawing function"""
if serie.index % 2:
y = -y
- return super(VerticalPyramid,
- self)._bar(serie, parent, x, y, i, zero, secondary)
+ return super(VerticalPyramid, self)._bar(
+ serie, parent, x, y, i, zero, secondary)
class Pyramid(HorizontalGraph, VerticalPyramid):
+
"""Horizontal Pyramid graph class like the one used by age pyramid"""
diff --git a/pygal/graph/radar.py b/pygal/graph/radar.py
index 80b638cc..4d229dd1 100644
--- a/pygal/graph/radar.py
+++ b/pygal/graph/radar.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Radar chart: As known as kiviat chart or spider chart is a polar line chart
useful for multivariate observation.
@@ -30,6 +31,7 @@
class Radar(Line):
+
"""Rada graph class"""
_adapters = [positive, none_to_zero]
@@ -47,9 +49,8 @@ def _fill(self, values):
def _values(self):
"""Getter for series values (flattened)"""
if self.interpolate:
- return [
- val[0] for serie in self.series for val in serie.interpolated
- ]
+ return [val[0] for serie in self.series
+ for val in serie.interpolated]
else:
return super(Line, self)._values
@@ -61,20 +62,18 @@ def _set_view(self):
view_class = PolarView
self.view = view_class(
- self.width - self.margin_box.x, self.height - self.margin_box.y,
- self._box
- )
+ self.width - self.margin_box.x,
+ self.height - self.margin_box.y,
+ self._box)
def _x_axis(self, draw_axes=True):
"""Override x axis to make it polar"""
if not self._x_labels or not self.show_x_labels:
return
- axis = self.svg.node(
- self.nodes['plot'],
- class_="axis x web%s" %
- (' always_show' if self.show_x_guides else '')
- )
+ axis = self.svg.node(self.nodes['plot'], class_="axis x web%s" % (
+ ' always_show' if self.show_x_guides else ''
+ ))
format_ = lambda x: '%f %f' % x
center = self.view((0, 0))
r = self._rmax
@@ -90,37 +89,32 @@ def _x_axis(self, draw_axes=True):
end = self.view((r, theta))
self.svg.node(
- guides,
- 'path',
+ guides, 'path',
d='M%s L%s' % (format_(center), format_(end)),
- class_='%s%sline' %
- ('axis ' if label == "0" else '', 'major ' if major else '')
- )
+ class_='%s%sline' % (
+ 'axis ' if label == "0" else '',
+ 'major ' if major else ''))
r_txt = (1 - self._box.__class__.margin) * self._box.ymax
pos_text = self.view((r_txt, theta))
text = self.svg.node(
- guides,
- 'text',
+ guides, 'text',
x=pos_text[0],
y=pos_text[1],
- class_='major' if major else ''
- )
+ class_='major' if major else '')
text.text = truncate(label, truncation)
if text.text != label:
self.svg.node(guides, 'title').text = label
else:
self.svg.node(
- guides,
- 'title',
+ guides, 'title',
).text = self._x_format(theta)
- angle = -theta + pi / 2
+ angle = - theta + pi / 2
if cos(angle) < 0:
angle -= pi
text.attrib['transform'] = 'rotate(%f %s)' % (
- self.x_label_rotation or deg(angle), format_(pos_text)
- )
+ self.x_label_rotation or deg(angle), format_(pos_text))
def _y_axis(self, draw_axes=True):
"""Override y axis to make it polar"""
@@ -133,32 +127,31 @@ def _y_axis(self, draw_axes=True):
major = r in self._y_labels_major
if not (self.show_minor_y_labels or major):
continue
- guides = self.svg.node(
- axis,
- class_='%sguides' %
- ('logarithmic ' if self.logarithmic else '')
- )
+ guides = self.svg.node(axis, class_='%sguides' % (
+ 'logarithmic ' if self.logarithmic else ''
+ ))
if self.show_y_guides:
self.svg.line(
guides, [self.view((r, theta)) for theta in self._x_pos],
close=True,
- class_='%sguide line' % ('major ' if major else '')
- )
+ class_='%sguide line' % (
+ 'major ' if major else ''))
x, y = self.view((r, self._x_pos[0]))
x -= 5
text = self.svg.node(
- guides, 'text', x=x, y=y, class_='major' if major else ''
+ guides, 'text',
+ x=x,
+ y=y,
+ class_='major' if major else ''
)
text.text = label
if self.y_label_rotation:
- text.attrib[
- 'transform'
- ] = "rotate(%d %f %f)" % (self.y_label_rotation, x, y)
+ text.attrib['transform'] = "rotate(%d %f %f)" % (
+ self.y_label_rotation, x, y)
self.svg.node(
- guides,
- 'title',
+ guides, 'title',
).text = self._y_format(r)
def _compute(self):
@@ -166,20 +159,19 @@ def _compute(self):
delta = 2 * pi / self._len if self._len else 0
self._x_pos = [.5 * pi + i * delta for i in range(self._len + 1)]
for serie in self.all_series:
- serie.points = [(v, self._x_pos[i])
- for i, v in enumerate(serie.values)]
+ serie.points = [
+ (v, self._x_pos[i])
+ for i, v in enumerate(serie.values)]
if self.interpolate:
- extended_x_pos = ([.5 * pi - delta] + self._x_pos)
- extended_vals = (serie.values[-1:] + serie.values)
+ extended_x_pos = (
+ [.5 * pi - delta] + self._x_pos)
+ extended_vals = (serie.values[-1:] +
+ serie.values)
serie.interpolated = list(
- map(
- tuple,
- map(
- reversed,
- self._interpolate(extended_x_pos, extended_vals)
- )
- )
- )
+ map(tuple,
+ map(reversed,
+ self._interpolate(
+ extended_x_pos, extended_vals))))
# x labels space
self._box.margin *= 2
diff --git a/pygal/graph/solidgauge.py b/pygal/graph/solidgauge.py
index 5057d15c..d0230694 100644
--- a/pygal/graph/solidgauge.py
+++ b/pygal/graph/solidgauge.py
@@ -16,6 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
+
"""
Solid Guage
For each series a solid guage is shown on the plot area.
@@ -27,21 +29,24 @@
class SolidGauge(Graph):
+
def gaugify(self, serie, squares, sq_dimensions, current_square):
serie_node = self.svg.serie(serie)
if self.half_pie:
start_angle = 3 * pi / 2
- center = ((current_square[1] * sq_dimensions[0]) -
- (sq_dimensions[0] / 2.),
- (current_square[0] * sq_dimensions[1]) -
- (sq_dimensions[1] / 4))
+ center = (
+ (current_square[1] * sq_dimensions[0]) - (
+ sq_dimensions[0] / 2.),
+ (current_square[0] * sq_dimensions[1]) - (
+ sq_dimensions[1] / 4))
end_angle = pi / 2
else:
start_angle = 0
- center = ((current_square[1] * sq_dimensions[0]) -
- (sq_dimensions[0] / 2.),
- (current_square[0] * sq_dimensions[1]) -
- (sq_dimensions[1] / 2.))
+ center = (
+ (current_square[1] * sq_dimensions[0]) - (
+ sq_dimensions[0] / 2.),
+ (current_square[0] * sq_dimensions[1]) - (
+ sq_dimensions[1] / 2.))
end_angle = 2 * pi
max_value = serie.metadata.get(0, {}).get('max_value', 100)
@@ -50,8 +55,7 @@ def gaugify(self, serie, squares, sq_dimensions, current_square):
self.svg.gauge_background(
serie_node, start_angle, center, radius, small_radius, end_angle,
- self.half_pie, self._serie_format(serie, max_value)
- )
+ self.half_pie, self._serie_format(serie, max_value))
sum_ = 0
for i, value in enumerate(serie.values):
@@ -67,30 +71,27 @@ def gaugify(self, serie, squares, sq_dimensions, current_square):
metadata = serie.metadata.get(i)
gauge_ = decorate(
- self.svg, self.svg.node(serie_node['plot'], class_="gauge"),
- metadata
- )
+ self.svg,
+ self.svg.node(serie_node['plot'], class_="gauge"),
+ metadata)
alter(
self.svg.solid_gauge(
- serie_node, gauge_, radius, small_radius, angle,
- start_angle, center, val, i, metadata, self.half_pie,
- end_angle, self._serie_format(serie, max_value)
- ), metadata
- )
+ serie_node, gauge_, radius, small_radius,
+ angle, start_angle, center, val, i, metadata,
+ self.half_pie, end_angle,
+ self._serie_format(serie, max_value)),
+ metadata)
start_angle += angle
sum_ += value
x, y = center
self.svg.node(
- serie_node['text_overlay'],
- 'text',
+ serie_node['text_overlay'], 'text',
class_='value gauge-sum',
x=x,
y=y + self.style.value_font_size / 3,
- attrib={
- 'text-anchor': 'middle'
- }
+ attrib={'text-anchor': 'middle'}
).text = self._serie_format(serie, sum_)
def _compute_x_labels(self):
@@ -106,7 +107,8 @@ def _plot(self):
for index, serie in enumerate(self.series):
current_square = self._current_square(squares, index)
- self.gaugify(serie, squares, sq_dimensions, current_square)
+ self.gaugify(
+ serie, squares, sq_dimensions, current_square)
def _squares(self):
@@ -146,5 +148,4 @@ def _current_square(self, squares, index):
else:
return tuple(current_square)
raise Exception(
- 'Something went wrong with the current square assignment.'
- )
+ 'Something went wrong with the current square assignment.')
diff --git a/pygal/graph/stackedbar.py b/pygal/graph/stackedbar.py
index 82f83f59..be9560f1 100644
--- a/pygal/graph/stackedbar.py
+++ b/pygal/graph/stackedbar.py
@@ -26,6 +26,7 @@
class StackedBar(Bar):
+
"""Stacked Bar graph class"""
_adapters = [none_to_zero]
@@ -34,14 +35,15 @@ def _get_separated_values(self, secondary=False):
"""Separate values between positives and negatives stacked"""
series = self.secondary_series if secondary else self.series
transposed = list(zip(*[serie.values for serie in series]))
- positive_vals = [
- sum([val for val in vals if val is not None and val >= self.zero])
- for vals in transposed
- ]
- negative_vals = [
- sum([val for val in vals if val is not None and val < self.zero])
- for vals in transposed
- ]
+ positive_vals = [sum([
+ val for val in vals
+ if val is not None and val >= self.zero])
+ for vals in transposed]
+ negative_vals = [sum([
+ val
+ for val in vals
+ if val is not None and val < self.zero])
+ for vals in transposed]
return positive_vals, negative_vals
def _compute_box(self, positive_vals, negative_vals):
@@ -50,26 +52,22 @@ def _compute_box(self, positive_vals, negative_vals):
self._box.ymin = self.range[0]
else:
self._box.ymin = negative_vals and min(
- min(negative_vals), self.zero
- ) or self.zero
+ min(negative_vals), self.zero) or self.zero
if self.range and self.range[1] is not None:
self._box.ymax = self.range[1]
else:
self._box.ymax = positive_vals and max(
- max(positive_vals), self.zero
- ) or self.zero
+ max(positive_vals), self.zero) or self.zero
def _compute(self):
"""Compute y min and max and y scale and set labels"""
positive_vals, negative_vals = self._get_separated_values()
if self.logarithmic:
- positive_vals = list(
- filter(lambda x: x > self.zero, positive_vals)
- )
- negative_vals = list(
- filter(lambda x: x > self.zero, negative_vals)
- )
+ positive_vals = list(filter(
+ lambda x: x > self.zero, positive_vals))
+ negative_vals = list(filter(
+ lambda x: x > self.zero, negative_vals))
self._compute_box(positive_vals, negative_vals)
positive_vals = positive_vals or [self.zero]
@@ -96,25 +94,21 @@ def _compute(self):
def _pre_compute_secondary(self, positive_vals, negative_vals):
"""Compute secondary y min and max"""
- self._secondary_min = (
- negative_vals and min(min(negative_vals), self.zero)
- ) or self.zero
- self._secondary_max = (
- positive_vals and max(max(positive_vals), self.zero)
- ) or self.zero
+ self._secondary_min = (negative_vals and min(
+ min(negative_vals), self.zero)) or self.zero
+ self._secondary_max = (positive_vals and max(
+ max(positive_vals), self.zero)) or self.zero
def _bar(self, serie, parent, x, y, i, zero, secondary=False):
"""Internal stacking bar drawing function"""
if secondary:
- cumulation = (
- self.secondary_negative_cumulation
- if y < self.zero else self.secondary_positive_cumulation
- )
+ cumulation = (self.secondary_negative_cumulation
+ if y < self.zero else
+ self.secondary_positive_cumulation)
else:
- cumulation = (
- self.negative_cumulation
- if y < self.zero else self.positive_cumulation
- )
+ cumulation = (self.negative_cumulation
+ if y < self.zero else
+ self.positive_cumulation)
zero = cumulation[i]
cumulation[i] = zero + y
if zero == 0:
@@ -137,16 +131,9 @@ def _bar(self, serie, parent, x, y, i, zero, secondary=False):
height = self.view.y(zero) - y
r = serie.rounded_bars * 1 if serie.rounded_bars else 0
self.svg.transposable_node(
- parent,
- 'rect',
- x=x,
- y=y,
- rx=r,
- ry=r,
- width=width,
- height=height,
- class_='rect reactive tooltip-trigger'
- )
+ parent, 'rect',
+ x=x, y=y, rx=r, ry=r, width=width, height=height,
+ class_='rect reactive tooltip-trigger')
return x, y, width, height
def _plot(self):
diff --git a/pygal/graph/stackedline.py b/pygal/graph/stackedline.py
index 02bfdb04..4b7b78d7 100644
--- a/pygal/graph/stackedline.py
+++ b/pygal/graph/stackedline.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
Stacked Line chart: Like a line chart but with all lines stacking
on top of the others. Used along fill=True option.
@@ -26,6 +27,7 @@
class StackedLine(Line):
+
"""Stacked Line graph class"""
_adapters = [none_to_zero]
@@ -41,11 +43,15 @@ def _value_format(self, value, serie, index):
"""
sum_ = serie.points[index][1]
if serie in self.series and (
- self.stack_from_top
- and self.series.index(serie) == self._order - 1
- or not self.stack_from_top and self.series.index(serie) == 0):
+ self.stack_from_top and
+ self.series.index(serie) == self._order - 1 or
+ not self.stack_from_top and
+ self.series.index(serie) == 0):
return super(StackedLine, self)._value_format(value)
- return '%s (+%s)' % (self._y_format(sum_), self._y_format(value))
+ return '%s (+%s)' % (
+ self._y_format(sum_),
+ self._y_format(value)
+ )
def _fill(self, values):
"""Add extra values to fill the line"""
@@ -65,8 +71,9 @@ def _points(self, x_pos):
accumulation = [0] * self._len
for serie in series_group[::-1 if self.stack_from_top else 1]:
accumulation = list(map(sum, zip(accumulation, serie.values)))
- serie.points = [(x_pos[i], v)
- for i, v in enumerate(accumulation)]
+ serie.points = [
+ (x_pos[i], v)
+ for i, v in enumerate(accumulation)]
if serie.points and self.interpolate:
serie.interpolated = self._interpolate(x_pos, accumulation)
else:
diff --git a/pygal/graph/time.py b/pygal/graph/time.py
index ddd7795d..3da15a6a 100644
--- a/pygal/graph/time.py
+++ b/pygal/graph/time.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
XY time extensions: handle convertion of date, time, datetime, timedelta
into float for xy plot and back to their type for display
@@ -66,20 +67,21 @@ def timedelta_to_seconds(x):
def time_to_seconds(x):
"""Convert a time in a seconds sum"""
if isinstance(x, time):
- return ((((x.hour * 60) + x.minute) * 60 + x.second) * 10**6 +
- x.microsecond) / 10**6
+ return ((
+ ((x.hour * 60) + x.minute) * 60 + x.second
+ ) * 10 ** 6 + x.microsecond) / 10 ** 6
if isinstance(x, str):
return x
# Clamp to valid time
- return x and max(0, min(x, 24 * 3600 - 10**-6))
+ return x and max(0, min(x, 24 * 3600 - 10 ** -6))
def seconds_to_time(x):
"""Convert a number of second into a time"""
- t = int(x * 10**6)
- ms = t % 10**6
- t = t // 10**6
+ t = int(x * 10 ** 6)
+ ms = t % 10 ** 6
+ t = t // 10 ** 6
s = t % 60
t = t // 60
m = t % 60
@@ -89,6 +91,7 @@ def seconds_to_time(x):
class DateTimeLine(XY):
+
"""DateTime abscissa xy graph class"""
_x_adapters = [datetime_to_timestamp, date_to_datetime]
@@ -96,29 +99,27 @@ class DateTimeLine(XY):
@property
def _x_format(self):
"""Return the value formatter for this graph"""
-
def datetime_to_str(x):
dt = datetime.utcfromtimestamp(x)
return self.x_value_formatter(dt)
-
return datetime_to_str
class DateLine(DateTimeLine):
+
"""Date abscissa xy graph class"""
@property
def _x_format(self):
"""Return the value formatter for this graph"""
-
def date_to_str(x):
d = datetime.utcfromtimestamp(x).date()
return self.x_value_formatter(d)
-
return date_to_str
class TimeLine(DateTimeLine):
+
"""Time abscissa xy graph class"""
_x_adapters = [positive, time_to_seconds, datetime_to_time]
@@ -126,15 +127,14 @@ class TimeLine(DateTimeLine):
@property
def _x_format(self):
"""Return the value formatter for this graph"""
-
def date_to_str(x):
t = seconds_to_time(x)
return self.x_value_formatter(t)
-
return date_to_str
class TimeDeltaLine(XY):
+
"""TimeDelta abscissa xy graph class"""
_x_adapters = [timedelta_to_seconds]
@@ -142,7 +142,6 @@ class TimeDeltaLine(XY):
@property
def _x_format(self):
"""Return the value formatter for this graph"""
-
def timedelta_to_str(x):
td = timedelta(seconds=x)
return self.x_value_formatter(td)
diff --git a/pygal/graph/treemap.py b/pygal/graph/treemap.py
index 59d00509..cc78facf 100644
--- a/pygal/graph/treemap.py
+++ b/pygal/graph/treemap.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Treemap chart: Visualize data using nested recangles"""
from pygal.adapters import none_to_zero, positive
@@ -24,6 +25,7 @@
class Treemap(Graph):
+
"""Treemap graph class"""
_adapters = [positive, none_to_zero]
@@ -39,26 +41,31 @@ def _rect(self, serie, serie_node, rects, val, x, y, w, h, i):
val = self._format(serie, i)
rect = decorate(
- self.svg, self.svg.node(rects, class_="rect"), metadata
- )
+ self.svg,
+ self.svg.node(rects, class_="rect"),
+ metadata)
alter(
self.svg.node(
- rect,
- 'rect',
+ rect, 'rect',
x=rx,
y=ry,
width=rw,
height=rh,
- class_='rect reactive tooltip-trigger'
- ), metadata
- )
+ class_='rect reactive tooltip-trigger'),
+ metadata)
self._tooltip_data(
- rect, val, rx + rw / 2, ry + rh / 2, 'centered',
- self._get_x_label(i)
- )
- self._static_value(serie_node, val, rx + rw / 2, ry + rh / 2, metadata)
+ rect, val,
+ rx + rw / 2,
+ ry + rh / 2,
+ 'centered',
+ self._get_x_label(i))
+ self._static_value(
+ serie_node, val,
+ rx + rw / 2,
+ ry + rh / 2,
+ metadata)
def _binary_tree(self, data, total, x, y, w, h, parent=None):
if total == 0:
@@ -72,11 +79,10 @@ def _binary_tree(self, data, total, x, y, w, h, parent=None):
datum = data[0]
serie_node = self.svg.serie(datum)
self._binary_tree(
- list(enumerate(datum.values)), total, x, y, w, h, (
- datum, serie_node,
- self.svg.node(serie_node['plot'], class_="rects")
- )
- )
+ list(enumerate(datum.values)),
+ total, x, y, w, h,
+ (datum, serie_node,
+ self.svg.node(serie_node['plot'], class_="rects")))
return
midpoint = total / 2
@@ -102,16 +108,16 @@ def _binary_tree(self, data, total, x, y, w, h, parent=None):
if h > w:
y_pivot = pivot_pct * h
- self._binary_tree(half1, half1_sum, x, y, w, y_pivot, parent)
self._binary_tree(
- half2, half2_sum, x, y + y_pivot, w, h - y_pivot, parent
- )
+ half1, half1_sum, x, y, w, y_pivot, parent)
+ self._binary_tree(
+ half2, half2_sum, x, y + y_pivot, w, h - y_pivot, parent)
else:
x_pivot = pivot_pct * w
- self._binary_tree(half1, half1_sum, x, y, x_pivot, h, parent)
self._binary_tree(
- half2, half2_sum, x + x_pivot, y, w - x_pivot, h, parent
- )
+ half1, half1_sum, x, y, x_pivot, h, parent)
+ self._binary_tree(
+ half2, half2_sum, x + x_pivot, y, w - x_pivot, h, parent)
def _compute_x_labels(self):
pass
@@ -128,7 +134,7 @@ def _plot(self):
gh = self.height - self.margin_box.y
self.view.box.xmin = self.view.box.ymin = x = y = 0
- self.view.box.xmax = w = (total * gw / gh)**.5
+ self.view.box.xmax = w = (total * gw / gh) ** .5
self.view.box.ymax = h = total / w
self.view.box.fix()
diff --git a/pygal/graph/xy.py b/pygal/graph/xy.py
index 7d532eb2..3fb19261 100644
--- a/pygal/graph/xy.py
+++ b/pygal/graph/xy.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""
XY Line graph: Plot a set of couple data points (x, y) connected by
straight segments.
@@ -29,6 +30,7 @@
class XY(Line, Dual):
+
"""XY Line graph class"""
_x_adapters = []
@@ -36,42 +38,38 @@ class XY(Line, Dual):
@cached_property
def xvals(self):
"""All x values"""
- return [
- val[0] for serie in self.all_series for val in serie.values
- if val[0] is not None
- ]
+ return [val[0]
+ for serie in self.all_series
+ for val in serie.values
+ if val[0] is not None]
@cached_property
def yvals(self):
"""All y values"""
- return [
- val[1] for serie in self.series for val in serie.values
- if val[1] is not None
- ]
+ return [val[1]
+ for serie in self.series
+ for val in serie.values
+ if val[1] is not None]
@cached_property
def _min(self):
"""Getter for the minimum series value"""
- return (
- self.range[0] if (self.range and self.range[0] is not None) else
- (min(self.yvals) if self.yvals else None)
- )
+ return (self.range[0] if (self.range and self.range[0] is not None)
+ else (min(self.yvals) if self.yvals else None))
@cached_property
def _max(self):
"""Getter for the maximum series value"""
- return (
- self.range[1] if (self.range and self.range[1] is not None) else
- (max(self.yvals) if self.yvals else None)
- )
+ return (self.range[1] if (self.range and self.range[1] is not None)
+ else (max(self.yvals) if self.yvals else None))
def _compute(self):
"""Compute x/y min and max and x/y scale and set labels"""
if self.xvals:
if self.xrange:
- x_adapter = reduce(compose, self._x_adapters) if getattr(
- self, '_x_adapters', None
- ) else ident
+ x_adapter = reduce(
+ compose, self._x_adapters) if getattr(
+ self, '_x_adapters', None) else ident
xmin = x_adapter(self.xrange[0])
xmax = x_adapter(self.xrange[1])
@@ -98,24 +96,18 @@ def _compute(self):
for serie in self.all_series:
serie.points = serie.values
if self.interpolate:
- vals = list(
- zip(
- *sorted(
- filter(lambda t: None not in t, serie.points),
- key=lambda x: x[0]
- )
- )
- )
+ vals = list(zip(*sorted(
+ filter(lambda t: None not in t,
+ serie.points), key=lambda x: x[0])))
serie.interpolated = self._interpolate(vals[0], vals[1])
if self.interpolate:
- self.xvals = [
- val[0] for serie in self.all_series
- for val in serie.interpolated
- ]
- self.yvals = [
- val[1] for serie in self.series for val in serie.interpolated
- ]
+ self.xvals = [val[0]
+ for serie in self.all_series
+ for val in serie.interpolated]
+ self.yvals = [val[1]
+ for serie in self.series
+ for val in serie.interpolated]
if self.xvals:
xmin = min(self.xvals)
xmax = max(self.xvals)
diff --git a/pygal/interpolate.py b/pygal/interpolate.py
index 58bc39a5..6a10dda0 100644
--- a/pygal/interpolate.py
+++ b/pygal/interpolate.py
@@ -102,9 +102,8 @@ def cubic_interpolate(x, y, precision=250, **kwargs):
yield x[i] + X, a[i] + b[i] * X + c[i] * X2 + d[i] * X3
-def hermite_interpolate(
- x, y, precision=250, type='cardinal', c=None, b=None, t=None
-):
+def hermite_interpolate(x, y, precision=250,
+ type='cardinal', c=None, b=None, t=None):
"""
Interpolate x, y using the hermite method.
See https://en.wikipedia.org/wiki/Cubic_Hermite_spline
@@ -133,9 +132,11 @@ def hermite_interpolate(
c = 0
if type == 'finite_difference':
for i in range(1, n):
- m[i] = w[i] = .5 * ((y[i + 1] - y[i]) / (x[i + 1] - x[i]) +
- (y[i] - y[i - 1]) / (x[i] - x[i - 1])
- ) if x[i + 1] - x[i] and x[i] - x[i - 1] else 0
+ m[i] = w[i] = .5 * (
+ (y[i + 1] - y[i]) / (x[i + 1] - x[i]) +
+ (y[i] - y[i - 1]) / (
+ x[i] - x[i - 1])
+ ) if x[i + 1] - x[i] and x[i] - x[i - 1] else 0
elif type == 'kochanek_bartels':
c = c or 0
@@ -150,9 +151,9 @@ def hermite_interpolate(
if type == 'cardinal':
c = c or 0
for i in range(1, n):
- m[i] = w[i] = (1 - c) * (y[i + 1] - y[i - 1]) / (
- x[i + 1] - x[i - 1]
- ) if x[i + 1] - x[i - 1] else 0
+ m[i] = w[i] = (1 - c) * (
+ y[i + 1] - y[i - 1]) / (
+ x[i + 1] - x[i - 1]) if x[i + 1] - x[i - 1] else 0
def p(i, x_):
t = (x_ - x[i]) / delta_x[i]
@@ -161,13 +162,13 @@ def p(i, x_):
h00 = 2 * t3 - 3 * t2 + 1
h10 = t3 - 2 * t2 + t
- h01 = -2 * t3 + 3 * t2
+ h01 = - 2 * t3 + 3 * t2
h11 = t3 - t2
- return (
- h00 * y[i] + h10 * m[i] * delta_x[i] + h01 * y[i + 1] +
- h11 * w[i + 1] * delta_x[i]
- )
+ return (h00 * y[i] +
+ h10 * m[i] * delta_x[i] +
+ h01 * y[i + 1] +
+ h11 * w[i + 1] * delta_x[i])
for i in range(n + 1):
yield x[i], y[i]
@@ -238,6 +239,7 @@ def trigonometric_interpolate(x, y, precision=250, **kwargs):
'trigonometric': trigonometric_interpolate
}
+
if __name__ == '__main__':
from pygal import XY
points = [(.1, 7), (.3, -4), (.6, 10), (.9, 8), (1.4, 3), (1.7, 1)]
@@ -247,32 +249,16 @@ def trigonometric_interpolate(x, y, precision=250, **kwargs):
xy.add('cubic', cubic_interpolate(*zip(*points)))
xy.add('lagrange', lagrange_interpolate(*zip(*points)))
xy.add('trigonometric', trigonometric_interpolate(*zip(*points)))
- xy.add(
- 'hermite catmul_rom',
- hermite_interpolate(*zip(*points), type='catmul_rom')
- )
- xy.add(
- 'hermite finite_difference',
- hermite_interpolate(*zip(*points), type='finite_difference')
- )
- xy.add(
- 'hermite cardinal -.5',
- hermite_interpolate(*zip(*points), type='cardinal', c=-.5)
- )
- xy.add(
- 'hermite cardinal .5',
- hermite_interpolate(*zip(*points), type='cardinal', c=.5)
- )
- xy.add(
- 'hermite kochanek_bartels .5 .75 -.25',
- hermite_interpolate(
- *zip(*points), type='kochanek_bartels', c=.5, b=.75, t=-.25
- )
- )
- xy.add(
- 'hermite kochanek_bartels .25 -.75 .5',
- hermite_interpolate(
- *zip(*points), type='kochanek_bartels', c=.25, b=-.75, t=.5
- )
- )
+ xy.add('hermite catmul_rom', hermite_interpolate(
+ *zip(*points), type='catmul_rom'))
+ xy.add('hermite finite_difference', hermite_interpolate(
+ *zip(*points), type='finite_difference'))
+ xy.add('hermite cardinal -.5', hermite_interpolate(
+ *zip(*points), type='cardinal', c=-.5))
+ xy.add('hermite cardinal .5', hermite_interpolate(
+ *zip(*points), type='cardinal', c=.5))
+ xy.add('hermite kochanek_bartels .5 .75 -.25', hermite_interpolate(
+ *zip(*points), type='kochanek_bartels', c=.5, b=.75, t=-.25))
+ xy.add('hermite kochanek_bartels .25 -.75 .5', hermite_interpolate(
+ *zip(*points), type='kochanek_bartels', c=.25, b=-.75, t=.5))
xy.render_in_browser()
diff --git a/pygal/maps/__init__.py b/pygal/maps/__init__.py
index 4a1f243c..28ea40f9 100644
--- a/pygal/maps/__init__.py
+++ b/pygal/maps/__init__.py
@@ -16,4 +16,5 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Maps extensions namespace module"""
diff --git a/pygal/serie.py b/pygal/serie.py
index 46ea65ea..a5b70c42 100644
--- a/pygal/serie.py
+++ b/pygal/serie.py
@@ -22,6 +22,7 @@
class Serie(object):
+
"""Serie class containing title, values and the graph serie index"""
def __init__(self, index, values, config, metadata=None):
diff --git a/pygal/state.py b/pygal/state.py
index e85a7a55..7570a845 100644
--- a/pygal/state.py
+++ b/pygal/state.py
@@ -16,12 +16,14 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Class holding state during render"""
from pygal.util import merge
class State(object):
+
"""
Class containing config values
overriden by chart values
diff --git a/pygal/stats.py b/pygal/stats.py
index b49809af..55d03b7a 100644
--- a/pygal/stats.py
+++ b/pygal/stats.py
@@ -35,18 +35,15 @@ def ppf(x, n):
# http://eprints.maths.ox.ac.uk/184/1/tdist.pdf
raise ImportError(
'You must have scipy installed to use t-student '
- 'when sample_size is below 30'
- )
+ 'when sample_size is below 30')
return norm_ppf(x)
-
# According to http://sphweb.bumc.bu.edu/otlt/MPH-Modules/BS/
# BS704_Confidence_Intervals/BS704_Confidence_Intervals_print.html
def confidence_interval_continuous(
- point_estimate, stddev, sample_size, confidence=.95, **kwargs
-):
+ point_estimate, stddev, sample_size, confidence=.95, **kwargs):
"""Continuous confidence interval from sample size and standard error"""
alpha = ppf((confidence + 1) / 2, sample_size - 1)
@@ -55,13 +52,8 @@ def confidence_interval_continuous(
def confidence_interval_dichotomous(
- point_estimate,
- sample_size,
- confidence=.95,
- bias=False,
- percentage=True,
- **kwargs
-):
+ point_estimate, sample_size, confidence=.95, bias=False,
+ percentage=True, **kwargs):
"""Dichotomous confidence interval from sample size and maybe a bias"""
alpha = ppf((confidence + 1) / 2, sample_size - 1)
p = point_estimate
diff --git a/pygal/style.py b/pygal/style.py
index 9b4b3914..80aeb98e 100644
--- a/pygal/style.py
+++ b/pygal/style.py
@@ -27,6 +27,7 @@
class Style(object):
+
"""Styling class containing colors for the css generation"""
plot_background = 'rgba(255, 255, 255, 1)'
@@ -35,9 +36,11 @@ class Style(object):
foreground = 'rgba(0, 0, 0, .87)'
foreground_strong = 'rgba(0, 0, 0, 1)'
foreground_subtle = 'rgba(0, 0, 0, .54)'
+ guide_color = 'rgba(158, 158, 158, 1)'
# Monospaced font is highly encouraged
- font_family = ('Consolas, "Liberation Mono", Menlo, Courier, monospace')
+ font_family = (
+ 'Consolas, "Liberation Mono", Menlo, Courier, monospace')
label_font_family = None
major_label_font_family = None
@@ -60,18 +63,12 @@ class Style(object):
# Guide line dash array style
guide_stroke_dasharray = '4,4'
major_guide_stroke_dasharray = '6,6'
- guide_stroke_color = 'black'
- major_guide_stroke_color = 'black'
opacity = '.7'
opacity_hover = '.8'
stroke_opacity = '.8'
- stroke_width = '1'
stroke_opacity_hover = '.9'
- stroke_width_hover = '4'
-
- dot_opacity = '1'
transition = '150ms'
colors = (
@@ -114,36 +111,33 @@ def __init__(self, **kwargs):
elif fn.startswith('googlefont:'):
setattr(self, name, fn.replace('googlefont:', ''))
self._google_fonts.add(
- getattr(self, name).split(',')[0].strip()
- )
+ getattr(self, name).split(',')[0].strip())
def get_colors(self, prefix, len_):
"""Get the css color list"""
-
def color(tupl):
"""Make a color css"""
return ((
'%s.color-{0}, %s.color-{0} a:visited {{\n'
' stroke: {1};\n'
' fill: {1};\n'
- '}}\n'
- ) % (prefix, prefix)).format(*tupl)
+ '}}\n') % (prefix, prefix)).format(*tupl)
def value_color(tupl):
"""Make a value color css"""
return ((
'%s .text-overlay .color-{0} text {{\n'
' fill: {1};\n'
- '}}\n'
- ) % (prefix, )).format(*tupl)
+ '}}\n') % (prefix,)).format(*tupl)
def ci_color(tupl):
"""Make a value color css"""
if not tupl[1]:
return ''
- return (('%s .color-{0} .ci {{\n'
- ' stroke: {1};\n'
- '}}\n') % (prefix, )).format(*tupl)
+ return ((
+ '%s .color-{0} .ci {{\n'
+ ' stroke: {1};\n'
+ '}}\n') % (prefix,)).format(*tupl)
if len(self.colors) < len_:
missing = len_ - len(self.colors)
@@ -166,17 +160,13 @@ def ci_color(tupl):
if i < len(self.value_colors) and self.value_colors[i] is not None:
value_colors.append(self.value_colors[i])
else:
- value_colors.append(
- 'white' if is_foreground_light(colors[i]) else 'black'
- )
-
- return '\n'.join(
- chain(
- map(color, enumerate(colors)),
- map(value_color, enumerate(value_colors)),
- map(ci_color, enumerate(self.ci_colors))
- )
- )
+ value_colors.append('white' if is_foreground_light(
+ colors[i]) else 'black')
+
+ return '\n'.join(chain(
+ map(color, enumerate(colors)),
+ map(value_color, enumerate(value_colors)),
+ map(ci_color, enumerate(self.ci_colors))))
def to_dict(self):
"""Convert instance to a serializable mapping."""
@@ -193,6 +183,7 @@ def to_dict(self):
class DarkStyle(Style):
+
"""A dark style (old default)"""
background = 'black'
@@ -204,13 +195,14 @@ class DarkStyle(Style):
opacity_hover = '.4'
transition = '250ms'
colors = (
- '#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe', '#899ca1',
- '#f8f8f2', '#bf4646', '#516083', '#f92672', '#82b414', '#fd971f',
- '#56c2d6', '#808384', '#8c54fe', '#465457'
- )
+ '#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe',
+ '#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672',
+ '#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe',
+ '#465457')
class LightStyle(Style):
+
"""A light style"""
background = 'white'
@@ -218,13 +210,13 @@ class LightStyle(Style):
foreground = 'rgba(0, 0, 0, 0.7)'
foreground_strong = 'rgba(0, 0, 0, 0.9)'
foreground_subtle = 'rgba(0, 0, 0, 0.5)'
- colors = (
- '#242424', '#9f6767', '#92ac68', '#d0d293', '#9aacc3', '#bb77a4',
- '#77bbb5', '#777777'
- )
+ colors = ('#242424', '#9f6767', '#92ac68',
+ '#d0d293', '#9aacc3', '#bb77a4',
+ '#77bbb5', '#777777')
class NeonStyle(DarkStyle):
+
"""Similar to DarkStyle but with more opacity and effects"""
opacity = '.1'
@@ -233,6 +225,7 @@ class NeonStyle(DarkStyle):
class CleanStyle(Style):
+
"""A rather clean style"""
background = 'transparent'
@@ -242,11 +235,11 @@ class CleanStyle(Style):
foreground_subtle = 'rgba(0, 0, 0, 0.5)'
colors = (
'rgb(12,55,149)', 'rgb(117,38,65)', 'rgb(228,127,0)', 'rgb(159,170,0)',
- 'rgb(149,12,12)'
- )
+ 'rgb(149,12,12)')
class DarkSolarizedStyle(Style):
+
"""Dark solarized popular theme"""
background = '#073642'
@@ -258,12 +251,12 @@ class DarkSolarizedStyle(Style):
opacity_hover = '.9'
transition = '500ms ease-in'
colors = (
- '#b58900', '#cb4b16', '#dc322f', '#d33682', '#6c71c4', '#268bd2',
- '#2aa198', '#859900'
- )
+ '#b58900', '#cb4b16', '#dc322f', '#d33682',
+ '#6c71c4', '#268bd2', '#2aa198', '#859900')
class LightSolarizedStyle(DarkSolarizedStyle):
+
"""Light solarized popular theme"""
background = '#fdf6e3'
@@ -274,6 +267,7 @@ class LightSolarizedStyle(DarkSolarizedStyle):
class RedBlueStyle(Style):
+
"""A red and blue theme"""
background = lighten('#e6e7e9', 7)
@@ -284,13 +278,13 @@ class RedBlueStyle(Style):
opacity = '.6'
opacity_hover = '.9'
colors = (
- '#d94e4c', '#e5884f', '#39929a', lighten('#d94e4c', 10),
- darken('#39929a', 15), lighten('#e5884f', 17), darken('#d94e4c', 10),
- '#234547'
- )
+ '#d94e4c', '#e5884f', '#39929a',
+ lighten('#d94e4c', 10), darken('#39929a', 15), lighten('#e5884f', 17),
+ darken('#d94e4c', 10), '#234547')
class LightColorizedStyle(Style):
+
"""A light colorized style"""
background = '#f8f8f8'
@@ -302,13 +296,13 @@ class LightColorizedStyle(Style):
opacity_hover = '.9'
transition = '250ms ease-in'
colors = (
- '#fe9592', '#534f4c', '#3ac2c0', '#a2a7a1', darken('#fe9592', 15),
- lighten('#534f4c', 15), lighten('#3ac2c0', 15), lighten('#a2a7a1', 15),
- lighten('#fe9592', 15), darken('#3ac2c0', 10)
- )
+ '#fe9592', '#534f4c', '#3ac2c0', '#a2a7a1',
+ darken('#fe9592', 15), lighten('#534f4c', 15), lighten('#3ac2c0', 15),
+ lighten('#a2a7a1', 15), lighten('#fe9592', 15), darken('#3ac2c0', 10))
class DarkColorizedStyle(Style):
+
"""A dark colorized style"""
background = darken('#3a2d3f', 5)
@@ -322,11 +316,11 @@ class DarkColorizedStyle(Style):
colors = (
'#c900fe', '#01b8fe', '#59f500', '#ff00e4', '#f9fa00',
darken('#c900fe', 20), darken('#01b8fe', 15), darken('#59f500', 20),
- darken('#ff00e4', 15), lighten('#f9fa00', 20)
- )
+ darken('#ff00e4', 15), lighten('#f9fa00', 20))
class TurquoiseStyle(Style):
+
"""A turquoise style"""
background = darken('#1b8088', 15)
@@ -338,12 +332,13 @@ class TurquoiseStyle(Style):
opacity_hover = '.9'
transition = '250ms ease-in'
colors = (
- '#93d2d9', '#ef940f', '#8C6243', '#fff', darken('#93d2d9', 20),
- lighten('#ef940f', 15), lighten('#8c6243', 15), '#1b8088'
- )
+ '#93d2d9', '#ef940f', '#8C6243', '#fff',
+ darken('#93d2d9', 20), lighten('#ef940f', 15),
+ lighten('#8c6243', 15), '#1b8088')
class LightGreenStyle(Style):
+
"""A light green style"""
background = lighten('#f3f3f3', 3)
@@ -357,11 +352,11 @@ class LightGreenStyle(Style):
colors = (
'#7dcf30', '#247fab', lighten('#7dcf30', 10), '#ccc',
darken('#7dcf30', 15), '#ddd', lighten('#247fab', 10),
- darken('#247fab', 15)
- )
+ darken('#247fab', 15))
class DarkGreenStyle(Style):
+
"""A dark green style"""
background = darken('#251e01', 3)
@@ -374,11 +369,11 @@ class DarkGreenStyle(Style):
transition = '250ms ease-in'
colors = (
'#adde09', '#6e8c06', '#4a5e04', '#fcd202', '#C1E34D',
- lighten('#fcd202', 25)
- )
+ lighten('#fcd202', 25))
class DarkGreenBlueStyle(Style):
+
"""A dark green and blue style"""
background = '#000'
@@ -389,14 +384,13 @@ class DarkGreenBlueStyle(Style):
opacity = '.55'
opacity_hover = '.9'
transition = '250ms ease-in'
- colors = (
- lighten('#34B8F7', 15), '#7dcf30', '#247fab', darken('#7dcf30', 10),
- lighten('#247fab', 10), lighten('#7dcf30', 10), darken('#247fab', 10),
- '#fff'
- )
+ colors = (lighten('#34B8F7', 15), '#7dcf30', '#247fab',
+ darken('#7dcf30', 10), lighten('#247fab', 10),
+ lighten('#7dcf30', 10), darken('#247fab', 10), '#fff')
class BlueStyle(Style):
+
"""A blue style"""
background = darken('#f8f8f8', 3)
@@ -410,11 +404,11 @@ class BlueStyle(Style):
colors = (
'#00b2f0', '#43d9be', '#0662ab', darken('#00b2f0', 20),
lighten('#43d9be', 20), lighten('#7dcf30', 10), darken('#0662ab', 15),
- '#ffd541', '#7dcf30', lighten('#00b2f0', 15), darken('#ffd541', 20)
- )
+ '#ffd541', '#7dcf30', lighten('#00b2f0', 15), darken('#ffd541', 20))
class SolidColorStyle(Style):
+
"""A light style with strong colors"""
background = '#FFFFFF'
@@ -426,32 +420,30 @@ class SolidColorStyle(Style):
opacity_hover = '.9'
transition = '400ms ease-in'
colors = (
- '#FF9900', '#DC3912', '#4674D1', '#109618', '#990099', '#0099C6',
- '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499'
- )
-
-
-styles = {
- 'default': DefaultStyle,
- 'dark': DarkStyle,
- 'light': LightStyle,
- 'neon': NeonStyle,
- 'clean': CleanStyle,
- 'light_red_blue': RedBlueStyle,
- 'dark_solarized': DarkSolarizedStyle,
- 'light_solarized': LightSolarizedStyle,
- 'dark_colorized': DarkColorizedStyle,
- 'light_colorized': LightColorizedStyle,
- 'turquoise': TurquoiseStyle,
- 'green': LightGreenStyle,
- 'dark_green': DarkGreenStyle,
- 'dark_green_blue': DarkGreenBlueStyle,
- 'blue': BlueStyle,
- 'solid_color': SolidColorStyle
-}
+ '#FF9900', '#DC3912', '#4674D1', '#109618', '#990099',
+ '#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499')
+
+
+styles = {'default': DefaultStyle,
+ 'dark': DarkStyle,
+ 'light': LightStyle,
+ 'neon': NeonStyle,
+ 'clean': CleanStyle,
+ 'light_red_blue': RedBlueStyle,
+ 'dark_solarized': DarkSolarizedStyle,
+ 'light_solarized': LightSolarizedStyle,
+ 'dark_colorized': DarkColorizedStyle,
+ 'light_colorized': LightColorizedStyle,
+ 'turquoise': TurquoiseStyle,
+ 'green': LightGreenStyle,
+ 'dark_green': DarkGreenStyle,
+ 'dark_green_blue': DarkGreenBlueStyle,
+ 'blue': BlueStyle,
+ 'solid_color': SolidColorStyle}
class ParametricStyleBase(Style):
+
"""Parametric Style base class for all the parametric operations"""
_op = None
@@ -498,30 +490,35 @@ def modifier(index):
class LightenStyle(ParametricStyleBase):
+
"""Create a style by lightening the given color"""
_op = 'lighten'
class DarkenStyle(ParametricStyleBase):
+
"""Create a style by darkening the given color"""
_op = 'darken'
class SaturateStyle(ParametricStyleBase):
+
"""Create a style by saturating the given color"""
_op = 'saturate'
class DesaturateStyle(ParametricStyleBase):
+
"""Create a style by desaturating the given color"""
_op = 'desaturate'
class RotateStyle(ParametricStyleBase):
+
"""Create a style by rotating the given color"""
_op = 'rotate'
diff --git a/pygal/svg.py b/pygal/svg.py
index 0ff66664..b3eefe35 100644
--- a/pygal/svg.py
+++ b/pygal/svg.py
@@ -44,6 +44,7 @@
class Svg(object):
+
"""Svg related methods"""
ns = 'http://www.w3.org/2000/svg'
@@ -58,9 +59,16 @@ def __init__(self, graph):
self.id = ''
self.processing_instructions = []
if etree.lxml:
- attrs = {'nsmap': {None: self.ns, 'xlink': self.xlink_ns}}
+ attrs = {
+ 'nsmap': {
+ None: self.ns,
+ 'xlink': self.xlink_ns
+ }
+ }
else:
- attrs = {'xmlns': self.ns}
+ attrs = {
+ 'xmlns': self.ns
+ }
if hasattr(etree, 'register_namespace'):
etree.register_namespace('xlink', self.xlink_ns)
else:
@@ -96,8 +104,8 @@ def add_styles(self):
if self.graph.style._google_fonts:
auto_css.append(
- '//fonts.googleapis.com/css?family=%s' %
- quote_plus('|'.join(self.graph.style._google_fonts))
+ '//fonts.googleapis.com/css?family=%s' % quote_plus(
+ '|'.join(self.graph.style._google_fonts))
)
for css in auto_css + list(self.graph.css):
@@ -108,7 +116,8 @@ def add_styles(self):
css = css[len('file://'):]
if not os.path.exists(css):
- css = os.path.join(os.path.dirname(__file__), 'css', css)
+ css = os.path.join(
+ os.path.dirname(__file__), 'css', css)
with io.open(css, encoding='utf-8') as f:
css_text = template(
@@ -116,8 +125,7 @@ def add_styles(self):
style=self.graph.style,
colors=colors,
strokes=strokes,
- id=self.id
- )
+ id=self.id)
if css_text is not None:
if not self.graph.pretty_print:
@@ -130,8 +138,7 @@ def add_styles(self):
etree.PI('xml-stylesheet', 'href="%s"' % css)
)
self.node(
- self.defs, 'style', type='text/css'
- ).text = '\n'.join(all_css)
+ self.defs, 'style', type='text/css').text = '\n'.join(all_css)
def add_scripts(self):
"""Add the js to the svg"""
@@ -141,9 +148,8 @@ def get_js_dict():
return dict(
(k, getattr(self.graph.state, k))
for k in dir(self.graph.config)
- if not k.startswith('_') and hasattr(self.graph.state, k)
- and not hasattr(getattr(self.graph.state, k), '__call__')
- )
+ if not k.startswith('_') and hasattr(self.graph.state, k) and
+ not hasattr(getattr(self.graph.state, k), '__call__'))
def json_default(o):
if isinstance(o, (datetime, date)):
@@ -156,8 +162,7 @@ def json_default(o):
# Config adds
dct['legends'] = [
l.get('title') if isinstance(l, dict) else l
- for l in self.graph._legends + self.graph._secondary_legends
- ]
+ for l in self.graph._legends + self.graph._secondary_legends]
common_js = 'window.pygal = window.pygal || {};'
common_js += 'window.pygal.config = window.pygal.config || {};'
@@ -190,7 +195,7 @@ def in_attrib_and_number(key):
for pos, dim in (('x', 'width'), ('y', 'height')):
if in_attrib_and_number(dim) and attrib[dim] < 0:
- attrib[dim] = -attrib[dim]
+ attrib[dim] = - attrib[dim]
if in_attrib_and_number(pos):
attrib[pos] = attrib[pos] - attrib[dim]
@@ -203,8 +208,8 @@ def in_attrib_and_number(key):
attrib[key.rstrip('_')] = attrib[key]
del attrib[key]
elif key == 'href':
- attrib[etree.QName('http://www.w3.org/1999/xlink',
- key)] = attrib[key]
+ attrib[etree.QName(
+ 'http://www.w3.org/1999/xlink', key)] = attrib[key]
del attrib[key]
return etree.SubElement(parent, tag, attrib)
@@ -229,17 +234,16 @@ def serie(self, serie):
return dict(
plot=self.node(
self.graph.nodes['plot'],
- class_='series serie-%d color-%d' % (serie.index, serie.index)
- ),
+ class_='series serie-%d color-%d' % (
+ serie.index, serie.index)),
overlay=self.node(
self.graph.nodes['overlay'],
- class_='series serie-%d color-%d' % (serie.index, serie.index)
- ),
+ class_='series serie-%d color-%d' % (
+ serie.index, serie.index)),
text_overlay=self.node(
self.graph.nodes['text_overlay'],
- class_='series serie-%d color-%d' % (serie.index, serie.index)
- )
- )
+ class_='series serie-%d color-%d' % (
+ serie.index, serie.index)))
def line(self, node, coords, close=False, **kwargs):
"""Draw a svg line"""
@@ -258,54 +262,47 @@ def line(self, node, coords, close=False, **kwargs):
coord_format = lambda xy: '%f %f' % xy
origin = coord_format(coords[origin_index])
- line = ' '.join([
- coord_format(c) for c in coords[origin_index + 1:] if None not in c
- ])
- return self.node(node, 'path', d=root % (origin, line), **kwargs)
+ line = ' '.join([coord_format(c)
+ for c in coords[origin_index + 1:]
+ if None not in c])
+ return self.node(
+ node, 'path', d=root % (origin, line), **kwargs)
def slice(
- self, serie_node, node, radius, small_radius, angle, start_angle,
- center, val, i, metadata
- ):
+ self, serie_node, node, radius, small_radius,
+ angle, start_angle, center, val, i, metadata):
"""Draw a pie slice"""
if angle == 2 * pi:
angle = nearly_2pi
if angle > 0:
- to = [
- coord_abs_project(center, radius, start_angle),
- coord_abs_project(center, radius, start_angle + angle),
- coord_abs_project(center, small_radius, start_angle + angle),
- coord_abs_project(center, small_radius, start_angle)
- ]
+ to = [coord_abs_project(center, radius, start_angle),
+ coord_abs_project(center, radius, start_angle + angle),
+ coord_abs_project(center, small_radius, start_angle + angle),
+ coord_abs_project(center, small_radius, start_angle)]
rv = self.node(
- node,
- 'path',
+ node, 'path',
d='M%s A%s 0 %d 1 %s L%s A%s 0 %d 0 %s z' % (
- to[0], coord_dual(radius), int(angle > pi), to[1], to[2],
- coord_dual(small_radius), int(angle > pi), to[3]
- ),
- class_='slice reactive tooltip-trigger'
- )
+ to[0],
+ coord_dual(radius), int(angle > pi), to[1],
+ to[2],
+ coord_dual(small_radius), int(angle > pi), to[3]),
+ class_='slice reactive tooltip-trigger')
else:
rv = None
- x, y = coord_diff(
- center,
- coord_project((radius + small_radius) / 2, start_angle + angle / 2)
- )
+ x, y = coord_diff(center, coord_project(
+ (radius + small_radius) / 2, start_angle + angle / 2))
self.graph._tooltip_data(
- node, val, x, y, "centered", self.graph._x_labels
- and self.graph._x_labels[i][0]
- )
+ node, val, x, y, "centered",
+ self.graph._x_labels and self.graph._x_labels[i][0])
if angle >= 0.3: # 0.3 radians is about 17 degrees
self.graph._static_value(serie_node, val, x, y, metadata)
return rv
def gauge_background(
self, serie_node, start_angle, center, radius, small_radius,
- end_angle, half_pie, max_value
- ):
+ end_angle, half_pie, max_value):
if end_angle == 2 * pi:
end_angle = nearly_2pi
@@ -314,45 +311,37 @@ def gauge_background(
coord_abs_project(center, radius, start_angle),
coord_abs_project(center, radius, end_angle),
coord_abs_project(center, small_radius, end_angle),
- coord_abs_project(center, small_radius, start_angle)
- ]
+ coord_abs_project(center, small_radius, start_angle)]
self.node(
- serie_node['plot'],
- 'path',
+ serie_node['plot'], 'path',
d='M%s A%s 0 1 1 %s L%s A%s 0 1 0 %s z' % (
- to_shade[0], coord_dual(radius), to_shade[1], to_shade[2],
- coord_dual(small_radius), to_shade[3]
- ),
- class_='gauge-background reactive'
- )
+ to_shade[0],
+ coord_dual(radius),
+ to_shade[1],
+ to_shade[2],
+ coord_dual(small_radius),
+ to_shade[3]),
+ class_='gauge-background reactive')
if half_pie:
begin_end = [
coord_diff(
center,
coord_project(
- radius - (radius - small_radius) / 2, start_angle
- )
- ),
+ radius - (radius - small_radius) / 2, start_angle)),
coord_diff(
center,
coord_project(
- radius - (radius - small_radius) / 2, end_angle
- )
- )
- ]
+ radius - (radius - small_radius) / 2, end_angle))]
pos = 0
for i in begin_end:
self.node(
- serie_node['plot'],
- 'text',
+ serie_node['plot'], 'text',
class_='y-{} bound reactive'.format(pos),
x=i[0],
y=i[1] + 10,
- attrib={
- 'text-anchor': 'middle'
- }
+ attrib={'text-anchor': 'middle'}
).text = '{}'.format(0 if pos == 0 else max_value)
pos += 1
else:
@@ -360,21 +349,22 @@ def gauge_background(
# Correct text vertical alignment
middle_radius -= .1 * (radius - small_radius)
to_labels = [
- coord_abs_project(center, middle_radius, 0),
- coord_abs_project(center, middle_radius, nearly_2pi)
+ coord_abs_project(
+ center, middle_radius, 0),
+ coord_abs_project(
+ center, middle_radius, nearly_2pi)
]
self.node(
- self.defs,
- 'path',
- id='valuePath-%s%s' % center,
- d='M%s A%s 0 1 1 %s' %
- (to_labels[0], coord_dual(middle_radius), to_labels[1])
- )
- text_ = self.node(serie_node['text_overlay'], 'text')
+ self.defs, 'path', id='valuePath-%s%s' % center,
+ d='M%s A%s 0 1 1 %s' % (
+ to_labels[0],
+ coord_dual(middle_radius),
+ to_labels[1]
+ ))
+ text_ = self.node(
+ serie_node['text_overlay'], 'text')
self.node(
- text_,
- 'textPath',
- class_='max-value reactive',
+ text_, 'textPath', class_='max-value reactive',
attrib={
'href': '#valuePath-%s%s' % center,
'startOffset': '99%',
@@ -383,42 +373,40 @@ def gauge_background(
).text = max_value
def solid_gauge(
- self, serie_node, node, radius, small_radius, angle, start_angle,
- center, val, i, metadata, half_pie, end_angle, max_value
- ):
+ self, serie_node, node, radius, small_radius,
+ angle, start_angle, center, val, i, metadata, half_pie, end_angle,
+ max_value):
"""Draw a solid gauge slice and background slice"""
if angle == 2 * pi:
angle = nearly_2pi
if angle > 0:
- to = [
- coord_abs_project(center, radius, start_angle),
- coord_abs_project(center, radius, start_angle + angle),
- coord_abs_project(center, small_radius, start_angle + angle),
- coord_abs_project(center, small_radius, start_angle)
- ]
+ to = [coord_abs_project(center, radius, start_angle),
+ coord_abs_project(center, radius, start_angle + angle),
+ coord_abs_project(center, small_radius, start_angle + angle),
+ coord_abs_project(center, small_radius, start_angle)]
self.node(
- node,
- 'path',
+ node, 'path',
d='M%s A%s 0 %d 1 %s L%s A%s 0 %d 0 %s z' % (
- to[0], coord_dual(radius), int(angle > pi), to[1], to[2],
- coord_dual(small_radius), int(angle > pi), to[3]
- ),
- class_='slice reactive tooltip-trigger'
- )
+ to[0],
+ coord_dual(radius),
+ int(angle > pi),
+ to[1],
+ to[2],
+ coord_dual(small_radius),
+ int(angle > pi),
+ to[3]),
+ class_='slice reactive tooltip-trigger')
else:
return
- x, y = coord_diff(
- center,
- coord_project((radius + small_radius) / 2, start_angle + angle / 2)
- )
+ x, y = coord_diff(center, coord_project(
+ (radius + small_radius) / 2, start_angle + angle / 2))
self.graph._static_value(serie_node, val, x, y, metadata, 'middle')
self.graph._tooltip_data(
- node, val, x, y, "centered", self.graph._x_labels
- and self.graph._x_labels[i][0]
- )
+ node, val, x, y, "centered",
+ self.graph._x_labels and self.graph._x_labels[i][0])
def confidence_interval(self, node, x, low, high, width=7):
if self.graph.horizontal:
@@ -435,17 +423,12 @@ def confidence_interval(self, node, x, low, high, width=7):
ci = self.node(node, class_="ci")
self.node(
- ci,
- 'path',
- d="M%s L%s M%s L%s M%s L%s L%s M%s L%s" % tuple(
- map(
- fmt, (
- top, shr(top), top, shl(top), top, bottom, shr(bottom),
- bottom, shl(bottom)
- )
- )
- ),
- class_='nofill reactive'
+ ci, 'path', d="M%s L%s M%s L%s M%s L%s L%s M%s L%s" % tuple(
+ map(fmt, (
+ top, shr(top), top, shl(top), top,
+ bottom, shr(bottom), bottom, shl(bottom)
+ ))
+ ), class_='nofill reactive'
)
def pre_render(self):
@@ -453,28 +436,26 @@ def pre_render(self):
self.add_styles()
self.add_scripts()
self.root.set(
- 'viewBox', '0 0 %d %d' % (self.graph.width, self.graph.height)
- )
+ 'viewBox', '0 0 %d %d' % (self.graph.width, self.graph.height))
if self.graph.explicit_size:
self.root.set('width', str(self.graph.width))
self.root.set('height', str(self.graph.height))
def draw_no_data(self):
"""Write the no data text to the svg"""
- no_data = self.node(
- self.graph.nodes['text_overlay'],
- 'text',
- x=self.graph.view.width / 2,
- y=self.graph.view.height / 2,
- class_='no_data'
- )
+ no_data = self.node(self.graph.nodes['text_overlay'], 'text',
+ x=self.graph.view.width / 2,
+ y=self.graph.view.height / 2,
+ class_='no_data')
no_data.text = self.graph.no_data_text
def render(self, is_unicode=False, pretty_print=False):
"""Last thing to do before rendering"""
for f in self.graph.xml_filters:
self.root = f(self.root)
- args = {'encoding': 'utf-8'}
+ args = {
+ 'encoding': 'utf-8'
+ }
svg = b''
if etree.lxml:
@@ -484,12 +465,14 @@ def render(self, is_unicode=False, pretty_print=False):
svg = b"\n"
if not self.graph.disable_xml_declaration:
- svg += b'\n'.join([
- etree.tostring(pi, **args)
- for pi in self.processing_instructions
- ])
+ svg += b'\n'.join(
+ [etree.tostring(
+ pi, **args)
+ for pi in self.processing_instructions]
+ )
- svg += etree.tostring(self.root, **args)
+ svg += etree.tostring(
+ self.root, **args)
if self.graph.disable_xml_declaration or is_unicode:
svg = svg.decode('utf-8')
@@ -497,17 +480,16 @@ def render(self, is_unicode=False, pretty_print=False):
def get_strokes(self):
"""Return a css snippet containing all stroke style options"""
-
def stroke_dict_to_css(stroke, i=None):
"""Return a css style for the given option"""
- css = [
- '%s.series%s {\n' %
- (self.id, '.serie-%d' % i if i is not None else '')
- ]
- for key in ('width', 'linejoin', 'linecap', 'dasharray',
- 'dashoffset'):
+ css = ['%s.series%s {\n' % (
+ self.id, '.serie-%d' % i if i is not None else '')]
+ for key in (
+ 'width', 'linejoin', 'linecap',
+ 'dasharray', 'dashoffset'):
if stroke.get(key):
- css.append(' stroke-%s: %s;\n' % (key, stroke[key]))
+ css.append(' stroke-%s: %s;\n' % (
+ key, stroke[key]))
css.append('}')
return '\n'.join(css)
@@ -520,9 +502,5 @@ def stroke_dict_to_css(stroke, i=None):
for secondary_serie in self.graph.secondary_series:
if secondary_serie.stroke_style is not None:
- css.append(
- stroke_dict_to_css(
- secondary_serie.stroke_style, secondary_serie.index
- )
- )
+ css.append(stroke_dict_to_css(secondary_serie.stroke_style, secondary_serie.index))
return '\n'.join(css)
diff --git a/pygal/table.py b/pygal/table.py
index d58b807e..5c51a13f 100644
--- a/pygal/table.py
+++ b/pygal/table.py
@@ -30,6 +30,7 @@
class HTML(object):
+
"""Lower case adapter of lxml builder"""
def __getattribute__(self, attr):
@@ -38,6 +39,7 @@ def __getattribute__(self, attr):
class Table(object):
+
"""Table generator class"""
_dual = None
@@ -133,23 +135,33 @@ def render(self, total=False, transpose=False, style=False):
if thead:
parts.append(
html.thead(
- *[html.tr(*[html.th(_(col)) for col in r]) for r in thead]
+ *[html.tr(
+ *[html.th(_(col)) for col in r]
+ ) for r in thead]
)
)
if tbody:
parts.append(
html.tbody(
- *[html.tr(*[html.td(_(col)) for col in r]) for r in tbody]
+ *[html.tr(
+ *[html.td(_(col)) for col in r]
+ ) for r in tbody]
)
)
if tfoot:
parts.append(
html.tfoot(
- *[html.tr(*[html.th(_(col)) for col in r]) for r in tfoot]
+ *[html.tr(
+ *[html.th(_(col)) for col in r]
+ ) for r in tfoot]
)
)
- table = tostring(html.table(*parts, **attrs))
+ table = tostring(
+ html.table(
+ *parts, **attrs
+ )
+ )
if style:
if style is True:
css = '''
@@ -185,9 +197,9 @@ def render(self, total=False, transpose=False, style=False):
'''
else:
css = style
- table = tostring(
- html.style(template(css, **attrs), scoped='scoped')
- ) + table
+ table = tostring(html.style(
+ template(css, **attrs),
+ scoped='scoped')) + table
table = table.decode('utf-8')
self.chart.teardown()
return table
diff --git a/pygal/test/__init__.py b/pygal/test/__init__.py
index 78e94826..cfe761c6 100644
--- a/pygal/test/__init__.py
+++ b/pygal/test/__init__.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Pygal test package"""
from decimal import Decimal
@@ -27,9 +28,12 @@
def get_data(i):
"""Return sample test data for an index"""
- return [[(-1, 1), (2, 0), (0, 4)], [(0, 1), (None, 2), (3, 2)],
- [(-3, 3), (1, 3), (1, 1)], [(1, 1), (Decimal('1.'), 1),
- (1, 1)], [(3, 2), (2, 1), (1., 1)]][i]
+ return [
+ [(-1, 1), (2, 0), (0, 4)],
+ [(0, 1), (None, 2), (3, 2)],
+ [(-3, 3), (1, 3), (1, 1)],
+ [(1, 1), (Decimal('1.'), 1), (1, 1)],
+ [(3, 2), (2, 1), (1., 1)]][i]
def adapt(chart, data):
@@ -49,5 +53,7 @@ def adapt(chart, data):
def make_data(chart, datas):
"""Add sample data to the test chart"""
for i, data in enumerate(datas):
- chart.add(data[0], adapt(chart, data[1]), secondary=bool(i % 2))
+ chart.add(data[0],
+ adapt(chart, data[1]),
+ secondary=bool(i % 2))
return chart
diff --git a/pygal/test/conftest.py b/pygal/test/conftest.py
index 5b2c3087..21e2801d 100644
--- a/pygal/test/conftest.py
+++ b/pygal/test/conftest.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""pytest fixtures"""
import sys
@@ -53,6 +54,8 @@ def pytest_generate_tests(metafunc):
metafunc.parametrize("Chart", pygal.CHARTS)
if "datas" in metafunc.fixturenames:
metafunc.parametrize(
- "datas", [[("Serie %d" % i, get_data(i)) for i in range(s)]
- for s in (5, 1, 0)]
- )
+ "datas",
+ [
+ [("Serie %d" % i, get_data(i)) for i in range(s)]
+ for s in (5, 1, 0)
+ ])
diff --git a/pygal/test/test_bar.py b/pygal/test/test_bar.py
index ca74663c..631990b0 100644
--- a/pygal/test/test_bar.py
+++ b/pygal/test/test_bar.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Bar chart related tests"""
from pygal import Bar
diff --git a/pygal/test/test_box.py b/pygal/test/test_box.py
index e4d8c96c..48b42f58 100644
--- a/pygal/test/test_box.py
+++ b/pygal/test/test_box.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Box chart related tests"""
from pygal.graph.box import Box
@@ -25,8 +26,7 @@ def test_quartiles():
"""Test box points for the 1.5IQR computation method"""
a = [-2.0, 3.0, 4.0, 5.0, 8.0] # odd test data
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- a, mode='1.5IQR'
- )
+ a, mode='1.5IQR')
assert q1 == 7.0 / 4.0
assert q2 == 4.0
@@ -36,22 +36,19 @@ def test_quartiles():
b = [1.0, 4.0, 6.0, 8.0] # even test data
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- b, mode='1.5IQR'
- )
+ b, mode='1.5IQR')
assert q2 == 5.0
c = [2.0, None, 4.0, 6.0, None] # odd with None elements
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- c, mode='1.5IQR'
- )
+ c, mode='1.5IQR')
assert q2 == 4.0
d = [4]
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- d, mode='1.5IQR'
- )
+ d, mode='1.5IQR')
assert q0 == 4
assert q1 == 4
@@ -64,8 +61,7 @@ def test_quartiles_min_extremes():
"""Test box points for the extremes computation method"""
a = [-2.0, 3.0, 4.0, 5.0, 8.0] # odd test data
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- a, mode='extremes'
- )
+ a, mode='extremes')
assert q1 == 7.0 / 4.0
assert q2 == 4.0
@@ -75,22 +71,19 @@ def test_quartiles_min_extremes():
b = [1.0, 4.0, 6.0, 8.0] # even test data
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- b, mode='extremes'
- )
+ b, mode='extremes')
assert q2 == 5.0
c = [2.0, None, 4.0, 6.0, None] # odd with None elements
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- c, mode='extremes'
- )
+ c, mode='extremes')
assert q2 == 4.0
d = [4]
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- d, mode='extremes'
- )
+ d, mode='extremes')
assert q0 == 4
assert q1 == 4
@@ -103,16 +96,14 @@ def test_quartiles_tukey():
"""Test box points for the tukey computation method"""
a = [] # empty data
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- a, mode='tukey'
- )
+ a, mode='tukey')
assert min_s == q0 == q1 == q2 == q3 == q4 == 0
assert outliers == []
# https://en.wikipedia.org/wiki/Quartile example 1
b = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- b, mode='tukey'
- )
+ b, mode='tukey')
assert min_s == q0 == 6
assert q1 == 20.25
assert q2 == 40
@@ -123,8 +114,7 @@ def test_quartiles_tukey():
# previous test with added outlier 75
c = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49, 75]
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- c, mode='tukey'
- )
+ c, mode='tukey')
assert min_s == q0 == 6
assert q1 == 25.5
assert q2 == (40 + 41) / 2.0
@@ -135,8 +125,7 @@ def test_quartiles_tukey():
# one more outlier, 77
c = [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49, 75, 77]
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- c, mode='tukey'
- )
+ c, mode='tukey')
assert min_s == q0 == 6
assert q1 == 30.75
assert q2 == 41
@@ -148,14 +137,11 @@ def test_quartiles_tukey():
def test_quartiles_stdev():
"""Test box points for the stdev computation method"""
- a = [
- 35, 42, 35, 41, 36, 6, 12, 51, 33, 27, 46, 36, 44, 53, 75, 46, 16, 51,
- 45, 29, 25, 26, 54, 61, 27, 40, 23, 34, 51, 37
- ]
+ a = [35, 42, 35, 41, 36, 6, 12, 51, 33, 27, 46, 36, 44, 53, 75, 46, 16,
+ 51, 45, 29, 25, 26, 54, 61, 27, 40, 23, 34, 51, 37]
SD = 14.67
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- a, mode='stdev'
- )
+ a, mode='stdev')
assert min_s == min(a)
assert max_s == max(a)
assert q2 == 36.5
@@ -165,8 +151,7 @@ def test_quartiles_stdev():
b = [5] # test for posible zero division
(min_s, q0, q1, q2, q3, q4, max_s), outliers = Box._box_points(
- b, mode='stdev'
- )
+ b, mode='stdev')
assert min_s == q0 == q1 == q2 == q3 == q4 == max_s == b[0]
assert outliers == []
diff --git a/pygal/test/test_colors.py b/pygal/test/test_colors.py
index 3d09c870..97fe7c1b 100644
--- a/pygal/test/test_colors.py
+++ b/pygal/test/test_colors.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Color utility functions tests"""
from __future__ import division
diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py
index 62b29e1a..6a5f9a52 100644
--- a/pygal/test/test_config.py
+++ b/pygal/test/test_config.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Various config options tested on one chart type or more"""
from tempfile import NamedTemporaryFile
@@ -77,8 +78,7 @@ def test_config_behaviours():
fill=True,
pretty_print=True,
no_prefix=True,
- x_labels=['a', 'b', 'c']
- )
+ x_labels=['a', 'b', 'c'])
line2.add('_', [1, 2, 3])
l2 = line2.render()
assert l1 == l2
@@ -118,7 +118,6 @@ 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
@@ -142,7 +141,6 @@ 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
@@ -167,7 +165,6 @@ 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
@@ -203,7 +200,7 @@ class LineConfig(Config):
def test_logarithmic():
"""Test logarithmic option"""
line = Line(logarithmic=True)
- line.add('_', [1, 10**10, 1])
+ line.add('_', [1, 10 ** 10, 1])
q = line.render_pyquery()
assert len(q(".axis.x")) == 0
assert len(q(".axis.y")) == 1
@@ -249,7 +246,7 @@ def test_logarithmic_bad_interpolation():
def test_logarithmic_big_scale():
"""Test logarithmic option with a large range of value"""
line = Line(logarithmic=True)
- line.add('_', [10**-10, 10**10, 1])
+ line.add('_', [10 ** -10, 10 ** 10, 1])
q = line.render_pyquery()
assert len(q(".y.axis .guides")) == 21
@@ -270,7 +267,7 @@ def test_value_formatter():
def test_logarithmic_small_scale():
"""Test logarithmic with a small range of values"""
line = Line(logarithmic=True)
- line.add('_', [1 + 10**10, 3 + 10**10, 2 + 10**10])
+ line.add('_', [1 + 10 ** 10, 3 + 10 ** 10, 2 + 10 ** 10])
q = line.render_pyquery()
assert len(q(".y.axis .guides")) == 11
@@ -278,18 +275,16 @@ def test_logarithmic_small_scale():
def test_human_readable():
"""Test human readable option"""
line = Line()
- line.add('_', [10**4, 10**5, 23 * 10**4])
+ line.add('_', [10 ** 4, 10 ** 5, 23 * 10 ** 4])
q = line.render_pyquery()
- assert q(".axis.y text").map(texts) == list(
- map(str, range(20000, 240000, 20000))
- )
+ assert q(".axis.y text").map(texts) == list(map(
+ str, range(20000, 240000, 20000)))
line.value_formatter = formatters.human_readable
q = line.render_pyquery()
- assert q(".axis.y text").map(texts) == list(
- map(lambda x: '%dk' % x, range(20, 240, 20))
- )
+ assert q(".axis.y text").map(texts) == list(map(
+ lambda x: '%dk' % x, range(20, 240, 20)))
def test_show_legend():
@@ -327,8 +322,9 @@ def test_no_data():
def test_include_x_axis(Chart):
"""Test x axis inclusion option"""
chart = Chart()
- if Chart in (Pie, Treemap, Radar, Funnel, Dot, Gauge, Histogram, Box,
- SolidGauge) or issubclass(Chart, BaseMap):
+ if Chart in (
+ Pie, Treemap, Radar, Funnel, Dot, Gauge, Histogram, Box, SolidGauge
+ ) or issubclass(Chart, BaseMap):
return
if not chart._dual:
data = 100, 200, 150
@@ -338,8 +334,7 @@ def test_include_x_axis(Chart):
q = chart.render_pyquery()
# Ghost thing
yaxis = ".axis.%s .guides text" % (
- 'y' if not getattr(chart, 'horizontal', False) else 'x'
- )
+ 'y' if not getattr(chart, 'horizontal', False) else 'x')
if not isinstance(chart, Bar):
assert '0' not in q(yaxis).map(texts)
else:
@@ -413,11 +408,9 @@ def test_legend_at_bottom(Chart):
def test_x_y_title(Chart):
"""Test x title and y title options"""
- chart = Chart(
- title='I Am A Title',
- x_title="I am a x title",
- y_title="I am a y title"
- )
+ chart = Chart(title='I Am A Title',
+ x_title="I am a x title",
+ y_title="I am a y title")
chart.add('1', [4, -5, 123, 59, 38])
chart.add('2', [89, 0, 8, .12, 8])
q = chart.render_pyquery()
@@ -426,7 +419,9 @@ def test_x_y_title(Chart):
def test_range(Chart):
"""Test y label major option"""
- if Chart in (Pie, Treemap, Dot, SolidGauge) or issubclass(Chart, BaseMap):
+ if Chart in (
+ Pie, Treemap, Dot, SolidGauge
+ ) or issubclass(Chart, BaseMap):
return
chart = Chart()
chart.range = (0, 100)
@@ -441,10 +436,11 @@ def test_range(Chart):
def test_x_label_major(Chart):
"""Test x label major option"""
- if Chart in (Pie, Treemap, Funnel, Dot, Gauge, Histogram, Box, SolidGauge,
- Pyramid, DateTimeLine, TimeLine, DateLine,
- TimeDeltaLine) or issubclass(
- Chart, (BaseMap, Dual, HorizontalGraph)):
+ if Chart in (
+ Pie, Treemap, Funnel, Dot, Gauge, Histogram, Box, SolidGauge,
+ Pyramid, DateTimeLine, TimeLine, DateLine,
+ TimeDeltaLine
+ ) or issubclass(Chart, (BaseMap, Dual, HorizontalGraph)):
return
chart = Chart()
chart.add('test', range(12))
@@ -485,10 +481,13 @@ def test_x_label_major(Chart):
def test_y_label_major(Chart):
"""Test y label major option"""
- if Chart in (Pie, Treemap, Funnel, Dot, Gauge, Histogram, Box, SolidGauge,
- HorizontalBar, HorizontalStackedBar, HorizontalStackedLine,
- HorizontalLine, Pyramid, DateTimeLine, TimeLine, DateLine,
- TimeDeltaLine) or issubclass(Chart, BaseMap):
+ if Chart in (
+ Pie, Treemap, Funnel, Dot, Gauge, Histogram, Box, SolidGauge,
+ HorizontalBar, HorizontalStackedBar,
+ HorizontalStackedLine, HorizontalLine,
+ Pyramid, DateTimeLine, TimeLine, DateLine,
+ TimeDeltaLine
+ ) or issubclass(Chart, BaseMap):
return
chart = Chart()
data = range(12)
@@ -583,7 +582,7 @@ def test_classes(Chart):
chart = Chart(classes=(Ellipsis, ))
assert chart.render_pyquery().attr('class') == 'pygal-chart'
- chart = Chart(classes=('graph', ))
+ chart = Chart(classes=('graph',))
assert chart.render_pyquery().attr('class') == 'graph'
chart = Chart(classes=('pygal-chart', 'graph'))
diff --git a/pygal/test/test_date.py b/pygal/test/test_date.py
index c801e090..fc18d26d 100644
--- a/pygal/test/test_date.py
+++ b/pygal/test/test_date.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Date related charts tests"""
from datetime import date, datetime, time, timedelta, timezone
@@ -28,100 +29,137 @@
def test_date():
"""Test a simple dateline"""
date_chart = DateLine(truncate_label=1000)
- date_chart.add(
- 'dates', [(date(2013, 1, 2), 300), (date(2013, 1, 12), 412),
- (date(2013, 2, 2), 823), (date(2013, 2, 22), 672)]
- )
+ date_chart.add('dates', [
+ (date(2013, 1, 2), 300),
+ (date(2013, 1, 12), 412),
+ (date(2013, 2, 2), 823),
+ (date(2013, 2, 22), 672)
+ ])
q = date_chart.render_pyquery()
- dates = list(map(lambda t: t.split(' ')[0], q(".axis.x text").map(texts)))
- assert dates == ['2013-01-12', '2013-01-24', '2013-02-04', '2013-02-16']
+
+ assert list(
+ map(lambda t: t.split(' ')[0],
+ q(".axis.x text").map(texts))) == [
+ '2013-01-12',
+ '2013-01-24',
+ '2013-02-04',
+ '2013-02-16']
def test_time():
"""Test a simple timeline"""
time_chart = TimeLine(truncate_label=1000)
- time_chart.add(
- 'times', [(time(1, 12, 29), 2), (time(21, 2, 29), 10),
- (time(12, 30, 59), 7)]
- )
+ time_chart.add('times', [
+ (time(1, 12, 29), 2),
+ (time(21, 2, 29), 10),
+ (time(12, 30, 59), 7)
+ ])
q = time_chart.render_pyquery()
- dates = list(map(lambda t: t.split(' ')[0], q(".axis.x text").map(texts)))
- assert dates == [
- '02:46:40', '05:33:20', '08:20:00', '11:06:40', '13:53:20', '16:40:00',
- '19:26:40'
- ]
+
+ assert list(
+ map(lambda t: t.split(' ')[0],
+ q(".axis.x text").map(texts))) == [
+ '02:46:40',
+ '05:33:20',
+ '08:20:00',
+ '11:06:40',
+ '13:53:20',
+ '16:40:00',
+ '19:26:40']
def test_datetime():
"""Test a simple datetimeline"""
datetime_chart = DateTimeLine(truncate_label=1000)
- datetime_chart.add(
- 'datetimes',
- [(datetime(2013, 1, 2, 1, 12, 29), 300),
- (datetime(2013, 1, 12, 21, 2, 29), 412),
- (datetime(2013, 2, 2, 12, 30, 59), 823), (datetime(2013, 2, 22), 672)]
- )
+ datetime_chart.add('datetimes', [
+ (datetime(2013, 1, 2, 1, 12, 29), 300),
+ (datetime(2013, 1, 12, 21, 2, 29), 412),
+ (datetime(2013, 2, 2, 12, 30, 59), 823),
+ (datetime(2013, 2, 22), 672)
+ ])
q = datetime_chart.render_pyquery()
- dates = list(map(lambda t: t.split(' ')[0], q(".axis.x text").map(texts)))
- assert dates == [
- '2013-01-12T14:13:20', '2013-01-24T04:00:00', '2013-02-04T17:46:40',
- '2013-02-16T07:33:20'
- ]
+
+ assert list(
+ map(lambda t: t.split(' ')[0],
+ q(".axis.x text").map(texts))) == [
+ '2013-01-12T14:13:20',
+ '2013-01-24T04:00:00',
+ '2013-02-04T17:46:40',
+ '2013-02-16T07:33:20']
def test_timedelta():
"""Test a simple timedeltaline"""
timedelta_chart = TimeDeltaLine(truncate_label=1000)
- timedelta_chart.add(
- 'timedeltas', [
- (timedelta(seconds=1), 10),
- (timedelta(weeks=1), 50),
- (timedelta(hours=3, seconds=30), 3),
- (timedelta(microseconds=12112), .3),
- ]
- )
+ timedelta_chart.add('timedeltas', [
+ (timedelta(seconds=1), 10),
+ (timedelta(weeks=1), 50),
+ (timedelta(hours=3, seconds=30), 3),
+ (timedelta(microseconds=12112), .3),
+ ])
q = timedelta_chart.render_pyquery()
- assert list(t for t in q(".axis.x text").map(texts) if t != '0:00:00') == [
- '1 day, 3:46:40', '2 days, 7:33:20', '3 days, 11:20:00',
- '4 days, 15:06:40', '5 days, 18:53:20', '6 days, 22:40:00'
- ]
+ assert list(
+ t for t in q(".axis.x text").map(texts) if t != '0:00:00'
+ ) == [
+ '1 day, 3:46:40',
+ '2 days, 7:33:20',
+ '3 days, 11:20:00',
+ '4 days, 15:06:40',
+ '5 days, 18:53:20',
+ '6 days, 22:40:00']
def test_date_xrange():
"""Test dateline with xrange"""
datey = DateLine(truncate_label=1000)
- datey.add(
- 'dates', [(date(2013, 1, 2), 300), (date(2013, 1, 12), 412),
- (date(2013, 2, 2), 823), (date(2013, 2, 22), 672)]
- )
+ datey.add('dates', [
+ (date(2013, 1, 2), 300),
+ (date(2013, 1, 12), 412),
+ (date(2013, 2, 2), 823),
+ (date(2013, 2, 22), 672)
+ ])
datey.xrange = (date(2013, 1, 1), date(2013, 3, 1))
q = datey.render_pyquery()
- dates = list(map(lambda t: t.split(' ')[0], q(".axis.x text").map(texts)))
- assert dates == [
- '2013-01-01', '2013-01-12', '2013-01-24', '2013-02-04', '2013-02-16',
- '2013-02-27'
- ]
+ assert list(
+ map(lambda t: t.split(' ')[0],
+ q(".axis.x text").map(texts))) == [
+ '2013-01-01',
+ '2013-01-12',
+ '2013-01-24',
+ '2013-02-04',
+ '2013-02-16',
+ '2013-02-27']
def test_date_labels():
"""Test dateline with xrange"""
datey = DateLine(truncate_label=1000)
- datey.add(
- 'dates', [(date(2013, 1, 2), 300), (date(2013, 1, 12), 412),
- (date(2013, 2, 2), 823), (date(2013, 2, 22), 672)]
- )
-
- datey.x_labels = [date(2013, 1, 1), date(2013, 2, 1), date(2013, 3, 1)]
+ datey.add('dates', [
+ (date(2013, 1, 2), 300),
+ (date(2013, 1, 12), 412),
+ (date(2013, 2, 2), 823),
+ (date(2013, 2, 22), 672)
+ ])
+
+ datey.x_labels = [
+ date(2013, 1, 1),
+ date(2013, 2, 1),
+ date(2013, 3, 1)
+ ]
q = datey.render_pyquery()
- dates = list(map(lambda t: t.split(' ')[0], q(".axis.x text").map(texts)))
- assert dates == ['2013-01-01', '2013-02-01', '2013-03-01']
+ assert list(
+ map(lambda t: t.split(' ')[0],
+ q(".axis.x text").map(texts))) == [
+ '2013-01-01',
+ '2013-02-01',
+ '2013-03-01']
def test_utc_timestamping():
diff --git a/pygal/test/test_formatters.py b/pygal/test/test_formatters.py
index 5f6be66d..9e649154 100644
--- a/pygal/test/test_formatters.py
+++ b/pygal/test/test_formatters.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Test formatters"""
from pygal import formatters
diff --git a/pygal/test/test_graph.py b/pygal/test/test_graph.py
index eee71133..ebff381c 100644
--- a/pygal/test/test_graph.py
+++ b/pygal/test/test_graph.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Generate tests for different chart types with different data"""
import io
@@ -79,44 +80,26 @@ def test_metadata(Chart):
"""Test metadata values"""
chart = Chart()
v = range(7)
- if Chart in (pygal.Box, ):
+ if Chart in (pygal.Box,):
return # summary charts cannot display per-value metadata
elif Chart == pygal.XY:
v = list(map(lambda x: (x, x + 1), v))
elif issubclass(Chart, BaseMap):
- v = [(k, i) for i, k in enumerate(Chart.x_labels)
- if k not in ['oecd', 'nafta', 'eur']]
-
- chart.add(
- 'Serie with metadata', [
- v[0], {
- 'value': v[1]
- }, {
- 'value': v[2],
- 'label': 'Three'
- }, {
- 'value': v[3],
- 'xlink': 'http://4.example.com/'
- }, {
- 'value': v[4],
- 'xlink': 'http://5.example.com/',
- 'label': 'Five'
- }, {
- 'value': v[5],
- 'xlink': {
- 'href': 'http://6.example.com/'
- },
- 'label': 'Six'
- }, {
- 'value': v[6],
- 'xlink': {
- 'href': 'http://7.example.com/',
- 'target': '_blank'
- },
- 'label': 'Seven'
- }
- ]
- )
+ v = [(k, i) for i, k in enumerate(Chart.x_labels) if k not in [
+ 'oecd', 'nafta', 'eur']]
+
+ chart.add('Serie with metadata', [
+ v[0],
+ {'value': v[1]},
+ {'value': v[2], 'label': 'Three'},
+ {'value': v[3], 'xlink': 'http://4.example.com/'},
+ {'value': v[4], 'xlink': 'http://5.example.com/', 'label': 'Five'},
+ {'value': v[5], 'xlink': {
+ 'href': 'http://6.example.com/'}, 'label': 'Six'},
+ {'value': v[6], 'xlink': {
+ 'href': 'http://7.example.com/',
+ 'target': '_blank'}, 'label': 'Seven'}
+ ])
q = chart.render_pyquery()
for md in ('Three', 'Five', 'Seven'):
assert md in cut(q('desc'), 'text')
@@ -353,49 +336,31 @@ def test_labels_with_links(Chart):
chart = Chart()
# link on chart and label
chart.add({
- 'title': 'Red',
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Red'
- }
+ 'title': 'Red', 'xlink': {'href': 'http://en.wikipedia.org/wiki/Red'}
}, [{
'value': 2,
'label': 'This is red',
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Red'
- }
- }])
+ 'xlink': {'href': 'http://en.wikipedia.org/wiki/Red'}}])
# link on chart only
- chart.add(
- 'Green', [{
- 'value': 4,
- 'label': 'This is green',
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Green',
- 'target': '_top'
- }
- }]
- )
+ chart.add('Green', [{
+ 'value': 4,
+ 'label': 'This is green',
+ 'xlink': {
+ 'href': 'http://en.wikipedia.org/wiki/Green',
+ 'target': '_top'}}])
# link on label only opens in new tab
- chart.add({
- 'title': 'Yellow',
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Yellow',
- 'target': '_blank'
- }
- }, 7)
+ chart.add({'title': 'Yellow', 'xlink': {
+ 'href': 'http://en.wikipedia.org/wiki/Yellow',
+ 'target': '_blank'}}, 7)
# link on chart only
- chart.add(
- 'Blue', [{
- 'value': 5,
- 'xlink': {
- 'href': 'http://en.wikipedia.org/wiki/Blue',
- 'target': '_blank'
- }
- }]
- )
+ chart.add('Blue', [{
+ 'value': 5,
+ 'xlink': {
+ 'href': 'http://en.wikipedia.org/wiki/Blue',
+ 'target': '_blank'}}])
# link on label and chart with diffrent behaviours
chart.add({
@@ -406,9 +371,7 @@ def test_labels_with_links(Chart):
'label': 'This is violet',
'xlink': {
'href': 'http://en.wikipedia.org/wiki/Violet_(color)',
- 'target': '_self'
- }
- }])
+ 'target': '_self'}}])
q = chart.render_pyquery()
links = q('a')
@@ -428,7 +391,9 @@ def test_secondary(Chart):
chart = Chart()
rng = [83, .12, -34, 59]
chart.add('First serie', rng)
- chart.add('Secondary serie', map(lambda x: x * 2, rng), secondary=True)
+ chart.add('Secondary serie',
+ map(lambda x: x * 2, rng),
+ secondary=True)
assert chart.render_pyquery()
@@ -446,8 +411,7 @@ def test_long_title(Chart, datas):
"'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."
- )
+ "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/test/test_histogram.py b/pygal/test/test_histogram.py
index d3813f91..04793a0e 100644
--- a/pygal/test/test_histogram.py
+++ b/pygal/test/test_histogram.py
@@ -16,15 +16,22 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Histogram chart related tests"""
+
from pygal import Histogram
def test_histogram():
"""Simple histogram test"""
hist = Histogram()
- hist.add('1', [(2, 0, 1), (4, 1, 3), (3, 3.5, 5), (1.5, 5, 10)])
+ hist.add('1', [
+ (2, 0, 1),
+ (4, 1, 3),
+ (3, 3.5, 5),
+ (1.5, 5, 10)
+ ])
hist.add('2', [(2, 2, 8)], secondary=True)
q = hist.render_pyquery()
assert len(q('.rect')) == 5
diff --git a/pygal/test/test_interpolate.py b/pygal/test/test_interpolate.py
index 3ef3b87c..a2eda9c8 100644
--- a/pygal/test/test_interpolate.py
+++ b/pygal/test/test_interpolate.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Interpolations tests"""
from pygal.test import make_data
@@ -69,71 +70,44 @@ def test_hermite(Chart, datas):
def test_hermite_finite(Chart, datas):
"""Test hermite finite difference interpolation"""
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={'type': 'finite_difference'}
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={'type': 'finite_difference'})
chart = make_data(chart, datas)
assert chart.render()
def test_hermite_cardinal(Chart, datas):
"""Test hermite cardinal interpolation"""
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={
- 'type': 'cardinal',
- 'c': .75
- }
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={'type': 'cardinal', 'c': .75})
chart = make_data(chart, datas)
assert chart.render()
def test_hermite_catmull_rom(Chart, datas):
"""Test hermite catmull rom interpolation"""
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={'type': 'catmull_rom'}
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={'type': 'catmull_rom'})
chart = make_data(chart, datas)
assert chart.render()
def test_hermite_kochanek_bartels(Chart, datas):
"""Test hermite kochanek bartels interpolation"""
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={
- 'type': 'kochanek_bartels',
- 'b': -1,
- 'c': 1,
- 't': 1
- }
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={
+ 'type': 'kochanek_bartels', 'b': -1, 'c': 1, 't': 1})
chart = make_data(chart, datas)
assert chart.render()
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={
- 'type': 'kochanek_bartels',
- 'b': -1,
- 'c': -8,
- 't': 0
- }
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={
+ 'type': 'kochanek_bartels', 'b': -1, 'c': -8, 't': 0})
chart = make_data(chart, datas)
assert chart.render()
- chart = Chart(
- interpolate='hermite',
- interpolation_parameters={
- 'type': 'kochanek_bartels',
- 'b': 0,
- 'c': 10,
- 't': -1
- }
- )
+ chart = Chart(interpolate='hermite',
+ interpolation_parameters={
+ 'type': 'kochanek_bartels', 'b': 0, 'c': 10, 't': -1})
chart = make_data(chart, datas)
assert chart.render()
diff --git a/pygal/test/test_line.py b/pygal/test/test_line.py
index 7a66475f..c1022320 100644
--- a/pygal/test/test_line.py
+++ b/pygal/test/test_line.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Line chart related tests"""
from __future__ import division
@@ -44,13 +45,11 @@ def test_simple_line():
assert len(q(".y.axis .guides")) == 13
assert len(q(".dots")) == 3 * 13
assert q(".axis.x text").map(texts) == [
- '-30', '-25', '-20', '-15', '-10', '-5', '0', '5', '10', '15', '20',
- '25', '30'
- ]
+ '-30', '-25', '-20', '-15', '-10', '-5',
+ '0', '5', '10', '15', '20', '25', '30']
assert q(".axis.y text").map(texts) == [
- '-1.2', '-1', '-0.8', '-0.6', '-0.4', '-0.2', '0', '0.2', '0.4', '0.6',
- '0.8', '1', '1.2'
- ]
+ '-1.2', '-1', '-0.8', '-0.6', '-0.4', '-0.2',
+ '0', '0.2', '0.4', '0.6', '0.8', '1', '1.2']
assert q(".title").text() == 'cos sin and cos - sin'
assert q(".legend text").map(texts) == ['test1', 'test2', 'test3']
@@ -104,8 +103,7 @@ def test_not_equal_x_labels():
assert len(q(".dots")) == 100
assert len(q(".axis.x")) == 1
assert q(".axis.x text").map(texts) == [
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'
- ]
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
def test_int_x_labels():
@@ -118,8 +116,7 @@ def test_int_x_labels():
assert len(q(".dots")) == 100
assert len(q(".axis.x")) == 1
assert q(".axis.x text").map(texts) == [
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'
- ]
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
def test_only_major_dots_every():
@@ -151,7 +148,7 @@ def test_only_major_dots_count():
def test_only_major_dots():
"""Test major dots with specified major labels"""
- line = Line(show_only_major_dots=True, )
+ line = Line(show_only_major_dots=True,)
line.add('test', range(12))
line.x_labels = map(str, range(12))
line.x_labels_major = ['1', '5', '11']
@@ -164,7 +161,9 @@ def test_line_secondary():
line = Line()
rng = [8, 12, 23, 73, 39, 57]
line.add('First serie', rng)
- line.add('Secondary serie', map(lambda x: x * 2, rng), secondary=True)
+ line.add('Secondary serie',
+ map(lambda x: x * 2, rng),
+ secondary=True)
line.title = "One serie"
q = line.render_pyquery()
assert len(q(".axis.x")) == 0
diff --git a/pygal/test/test_line_log_none_max_solved.py b/pygal/test/test_line_log_none_max_solved.py
index 72b25777..41e72f8f 100644
--- a/pygal/test/test_line_log_none_max_solved.py
+++ b/pygal/test/test_line_log_none_max_solved.py
@@ -4,7 +4,6 @@
# in the Log graph will be max or not (issue #309)
from __future__ import division
-
from pygal import Line
chart = Line(title='test', logarithmic=True)
diff --git a/pygal/test/test_maps.py b/pygal/test/test_maps.py
index ed069586..99e65208 100644
--- a/pygal/test/test_maps.py
+++ b/pygal/test/test_maps.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Map plugins tests are imported here"""
from importlib_metadata import entry_points
diff --git a/pygal/test/test_pie.py b/pygal/test/test_pie.py
index 3dfbef1a..2bca0191 100644
--- a/pygal/test/test_pie.py
+++ b/pygal/test/test_pie.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Donut chart related tests"""
from pygal import Pie
diff --git a/pygal/test/test_serie_config.py b/pygal/test/test_serie_config.py
index ec85990f..f8072a2b 100644
--- a/pygal/test/test_serie_config.py
+++ b/pygal/test/test_serie_config.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Test per serie configuration"""
from pygal import Line
diff --git a/pygal/test/test_sparktext.py b/pygal/test/test_sparktext.py
index c9260810..91d576fa 100644
--- a/pygal/test/test_sparktext.py
+++ b/pygal/test/test_sparktext.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Test sparktext rendering"""
from pygal import Bar, Line
diff --git a/pygal/test/test_stacked.py b/pygal/test/test_stacked.py
index d4b37324..bb2f706d 100644
--- a/pygal/test/test_stacked.py
+++ b/pygal/test/test_stacked.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Stacked chart related tests"""
from pygal import StackedLine
@@ -28,8 +29,7 @@ def test_stacked_line():
stacked.add('ten_twelve', [10, 12])
q = stacked.render_pyquery()
assert set([v.text for v in q("desc.value")]) == set(
- ('1', '2', '11 (+10)', '14 (+12)')
- )
+ ('1', '2', '11 (+10)', '14 (+12)'))
def test_stacked_line_reverse():
@@ -39,8 +39,7 @@ def test_stacked_line_reverse():
stacked.add('ten_twelve', [10, 12])
q = stacked.render_pyquery()
assert set([v.text for v in q("desc.value")]) == set(
- ('11 (+1)', '14 (+2)', '10', '12')
- )
+ ('11 (+1)', '14 (+2)', '10', '12'))
def test_stacked_line_log():
@@ -50,8 +49,7 @@ def test_stacked_line_log():
stacked.add('ten_twelve', [10, 12])
q = stacked.render_pyquery()
assert set([v.text for v in q("desc.value")]) == set(
- ('1', '2', '11 (+10)', '14 (+12)')
- )
+ ('1', '2', '11 (+10)', '14 (+12)'))
def test_stacked_line_interpolate():
@@ -61,5 +59,4 @@ def test_stacked_line_interpolate():
stacked.add('ten_twelve', [10, 12])
q = stacked.render_pyquery()
assert set([v.text for v in q("desc.value")]) == set(
- ('1', '2', '11 (+10)', '14 (+12)')
- )
+ ('1', '2', '11 (+10)', '14 (+12)'))
diff --git a/pygal/test/test_style.py b/pygal/test/test_style.py
index 72778d8f..eb18acad 100644
--- a/pygal/test/test_style.py
+++ b/pygal/test/test_style.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Style related tests"""
from pygal import Line
@@ -45,9 +46,8 @@ def test_parametric_styles():
def test_parametric_styles_with_parameters():
"""Test a parametric style with parameters"""
- line = Line(
- style=RotateStyle('#de3804', step=12, max_=180, base_style=LightStyle)
- )
+ line = Line(style=RotateStyle(
+ '#de3804', step=12, max_=180, base_style=LightStyle))
line.add('_', [1, 2, 3])
line.x_labels = 'abc'
assert line.render()
diff --git a/pygal/test/test_table.py b/pygal/test/test_table.py
index 5ed31e7e..4d7eb400 100644
--- a/pygal/test/test_table.py
+++ b/pygal/test/test_table.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Box chart related tests"""
from pyquery import PyQuery as pq
diff --git a/pygal/test/test_util.py b/pygal/test/test_util.py
index 21f71181..36be5a90 100644
--- a/pygal/test/test_util.py
+++ b/pygal/test/test_util.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Utility functions tests"""
@@ -81,12 +82,13 @@ def test_format():
class Object(object):
pass
-
obj = Object()
obj.a = 1
obj.b = True
obj.c = '3'
- assert template('foo {{ o.a }} {{o.b}}-{{o.c}}', o=obj) == 'foo 1 True-3'
+ assert template(
+ 'foo {{ o.a }} {{o.b}}-{{o.c}}',
+ o=obj) == 'foo 1 True-3'
def test_truncate():
@@ -119,29 +121,28 @@ def test_minify_css():
'''
assert minify_css(css) == (
'.title{font-family:sans;font-size:12}'
- '.legends .legend text{font-family:monospace;font-size:14}'
- )
+ '.legends .legend text{font-family:monospace;font-size:14}')
def test_majorize():
"""Test majorize function"""
assert majorize(()) == []
- assert majorize((0, )) == []
+ assert majorize((0,)) == []
assert majorize((0, 1)) == []
assert majorize((0, 1, 2)) == []
assert majorize((-1, 0, 1, 2)) == [0]
assert majorize((0, .1, .2, .3, .4, .5, .6, .7, .8, .9, 1)) == [0, .5, 1]
assert majorize((0, .2, .4, .6, .8, 1)) == [0, 1]
assert majorize((-.4, -.2, 0, .2, .4, .6, .8, 1)) == [0, 1]
- assert majorize((-1, -.8, -.6, -.4, -.2, 0, .2, .4, .6, .8,
- 1)) == [-1, 0, 1]
+ assert majorize(
+ (-1, -.8, -.6, -.4, -.2, 0, .2, .4, .6, .8, 1)) == [-1, 0, 1]
assert majorize((0, .2, .4, .6, .8, 1, 1.2, 1.4, 1.6)) == [0, 1]
assert majorize((0, .2, .4, .6, .8, 1, 1.2, 1.4, 1.6, 1.8, 2)) == [0, 1, 2]
- assert majorize((0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110,
- 120)) == [0, 50, 100]
- assert majorize((
- 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36
- )) == [0, 10, 20, 30]
+ assert majorize(
+ (0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120)) == [0, 50, 100]
+ assert majorize(
+ (0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 30, 32, 34, 36)) == [0, 10, 20, 30]
assert majorize((0, 1, 2, 3, 4, 5)) == [0, 5]
assert majorize((-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5)) == [-5, 0, 5]
assert majorize((-5, 5, -4, 4, 0, 1, -1, 3, -2, 2, -3)) == [-5, 0, 5]
diff --git a/pygal/test/test_view.py b/pygal/test/test_view.py
index a6cabe88..db325ad6 100644
--- a/pygal/test/test_view.py
+++ b/pygal/test/test_view.py
@@ -16,6 +16,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
+
"""View related tests"""
# TODO
diff --git a/pygal/test/test_xml_filters.py b/pygal/test/test_xml_filters.py
index 5834281a..86aff3c0 100644
--- a/pygal/test/test_xml_filters.py
+++ b/pygal/test/test_xml_filters.py
@@ -16,12 +16,14 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Xml filter tests"""
from pygal import Bar
class ChangeBarsXMLFilter(object):
+
"""xml filter that insert a subplot"""
def __init__(self, a, b):
@@ -30,9 +32,8 @@ def __init__(self, a, b):
def __call__(self, T):
"""Apply the filter on the tree"""
- subplot = Bar(
- legend_at_bottom=True, explicit_size=True, width=800, height=150
- )
+ subplot = Bar(legend_at_bottom=True, explicit_size=True,
+ width=800, height=150)
subplot.add("Difference", self.data)
subplot = subplot.render_tree()
subplot = subplot.findall("g")[0]
@@ -54,9 +55,8 @@ def test_xml_filters_round_trip():
def test_xml_filters_change_bars():
"""Test the use a xml filter"""
- plot = Bar(
- legend_at_bottom=True, explicit_size=True, width=800, height=600
- )
+ plot = Bar(legend_at_bottom=True, explicit_size=True,
+ width=800, height=600)
A = [60, 75, 80, 78, 83, 90]
B = [92, 87, 81, 73, 68, 55]
plot.add("A", A)
@@ -64,5 +64,5 @@ def test_xml_filters_change_bars():
plot.add_xml_filter(ChangeBarsXMLFilter(A, B))
q = plot.render_tree()
assert len(q.findall("g")) == 2
- assert q.findall("g")[1].attrib["transform"
- ] == "translate(0,150), scale(1,0.75)"
+ assert q.findall("g")[1].attrib[
+ "transform"] == "translate(0,150), scale(1,0.75)"
diff --git a/pygal/test/utils.py b/pygal/test/utils.py
index 835f073e..da6a3bf7 100644
--- a/pygal/test/utils.py
+++ b/pygal/test/utils.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Tests helpers"""
from pyquery import PyQuery as pq
diff --git a/pygal/util.py b/pygal/util.py
index 42b58afa..c7476e27 100644
--- a/pygal/util.py
+++ b/pygal/util.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Various utility functions"""
from __future__ import division
@@ -39,10 +40,10 @@ def majorize(values):
return []
values_step = sorted_values[1] - sorted_values[0]
full_range = sorted_values[-1] - sorted_values[0]
- step = 10**int(log10(full_range))
+ step = 10 ** int(log10(full_range))
if step == values_step:
step *= 10
- step_factor = 10**(int(log10(step)) + 1)
+ step_factor = 10 ** (int(log10(step)) + 1)
if round(step * step_factor) % (round(values_step * step_factor) or 1):
# TODO: Find lower common multiple instead
step *= values_step
@@ -51,8 +52,7 @@ def majorize(values):
elif full_range >= 5 * step:
step *= 5
major_values = [
- value for value in values if value / step == round(value / step)
- ]
+ value for value in values if value / step == round(value / step)]
return [value for value in sorted_values if value in major_values]
@@ -65,8 +65,9 @@ def round_to_int(number, precision):
def round_to_float(number, precision):
"""Round a float to a precision"""
- rounded = Decimal(str(floor((number + precision / 2) // precision))
- ) * Decimal(str(precision))
+ rounded = Decimal(
+ str(floor((number + precision / 2) // precision))
+ ) * Decimal(str(precision))
return float(rounded)
@@ -98,11 +99,15 @@ def deg(radiants):
def _swap_curly(string):
"""Swap single and double curly brackets"""
- return (
- string.replace('{{ ', '{{').replace('{{', '\x00').replace('{', '{{')
- .replace('\x00', '{').replace(' }}', '}}').replace('}}', '\x00')
- .replace('}', '}}').replace('\x00', '}')
- )
+ return (string
+ .replace('{{ ', '{{')
+ .replace('{{', '\x00')
+ .replace('{', '{{')
+ .replace('\x00', '{')
+ .replace(' }}', '}}')
+ .replace('}}', '\x00')
+ .replace('}', '}}')
+ .replace('\x00', '}'))
def template(string, **kwargs):
@@ -131,21 +136,24 @@ def compute_logarithmic_scale(min_, max_, min_scale, max_scale):
detail /= 2
for order in range(min_order, max_order + 1):
for i in range(int(detail)):
- tick = (10 * i / detail or 1) * 10**order
+ tick = (10 * i / detail or 1) * 10 ** order
tick = round_to_scale(tick, tick)
if min_ <= tick <= max_ and tick not in positions:
positions.append(tick)
return positions
-def compute_scale(min_, max_, logarithmic, order_min, min_scale, max_scale):
+def compute_scale(
+ min_, max_, logarithmic, order_min,
+ min_scale, max_scale):
"""Compute an optimal scale between min and max"""
if min_ == 0 and max_ == 0:
return [0]
if max_ - min_ == 0:
return [min_]
if logarithmic:
- log_scale = compute_logarithmic_scale(min_, max_, min_scale, max_scale)
+ log_scale = compute_logarithmic_scale(
+ min_, max_, min_scale, max_scale)
if log_scale:
return log_scale
# else we fallback to normal scalling
@@ -154,10 +162,10 @@ def compute_scale(min_, max_, logarithmic, order_min, min_scale, max_scale):
if order_min is not None and order < order_min:
order = order_min
else:
- while ((max_ - min_) / (10**order) < min_scale
- and (order_min is None or order > order_min)):
+ while ((max_ - min_) / (10 ** order) < min_scale and
+ (order_min is None or order > order_min)):
order -= 1
- step = float(10**order)
+ step = float(10 ** order)
while (max_ - min_) / step > max_scale:
step *= 2.
positions = []
@@ -212,7 +220,8 @@ def decorate(svg, node, metadata):
if 'color' in metadata:
color = metadata.pop('color')
- node.attrib['style'] = 'fill: %s; stroke: %s' % (color, color)
+ node.attrib['style'] = 'fill: %s; stroke: %s' % (
+ color, color)
if 'style' in metadata:
node.attrib['style'] = metadata.pop('style')
@@ -228,8 +237,7 @@ def alter(node, metadata):
"""Override nodes attributes from metadata node mapping"""
if node is not None and metadata and 'node' in metadata:
node.attrib.update(
- dict((k, str(v)) for k, v in metadata['node'].items())
- )
+ dict((k, str(v)) for k, v in metadata['node'].items()))
def truncate(string, index):
@@ -241,6 +249,7 @@ def truncate(string, index):
# # Stolen partly from brownie http://packages.python.org/Brownie/
class cached_property(object):
+
"""Memoize a property"""
def __init__(self, getter, doc=None):
diff --git a/pygal/view.py b/pygal/view.py
index 68d0a2c2..9360a584 100644
--- a/pygal/view.py
+++ b/pygal/view.py
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see .
+
"""Projection and bounding helpers"""
from __future__ import division
@@ -24,6 +25,7 @@
class Margin(object):
+
"""Class reprensenting a margin (top, right, left, bottom)"""
def __init__(self, top, right, bottom, left):
@@ -45,6 +47,7 @@ def y(self):
class Box(object):
+
"""Chart boundings"""
margin = .02
@@ -144,6 +147,7 @@ def fix(self, with_margin=True):
class View(object):
+
"""Projection base class"""
def __init__(self, width, height, box):
@@ -163,9 +167,8 @@ def y(self, y):
"""Project y"""
if y is None:
return None
- return (
- self.height - self.height * (y - self.box.ymin) / self.box.height
- )
+ return (self.height - self.height *
+ (y - self.box.ymin) / self.box.height)
def __call__(self, xy):
"""Project x and y"""
@@ -174,6 +177,7 @@ def __call__(self, xy):
class ReverseView(View):
+
"""Same as view but reversed vertically"""
def y(self, y):
@@ -184,6 +188,7 @@ def y(self, y):
class HorizontalView(View):
+
"""Same as view but transposed"""
def __init__(self, width, height, box):
@@ -214,6 +219,7 @@ def y(self, y):
class PolarView(View):
+
"""Polar projection for pie like graphs"""
def __call__(self, rhotheta):
@@ -221,11 +227,12 @@ def __call__(self, rhotheta):
if None in rhotheta:
return None, None
rho, theta = rhotheta
- return super(PolarView,
- self).__call__((rho * cos(theta), rho * sin(theta)))
+ return super(PolarView, self).__call__(
+ (rho * cos(theta), rho * sin(theta)))
class PolarLogView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box):
@@ -233,8 +240,7 @@ def __init__(self, width, height, box):
super(PolarLogView, self).__init__(width, height, box)
if not hasattr(box, '_rmin') or not hasattr(box, '_rmax'):
raise Exception(
- 'Box must be set with set_polar_box for polar charts'
- )
+ 'Box must be set with set_polar_box for polar charts')
self.log10_rmax = log10(self.box._rmax)
self.log10_rmin = log10(self.box._rmin)
@@ -250,13 +256,14 @@ def __call__(self, rhotheta):
if rho == 0:
return super(PolarLogView, self).__call__((0, 0))
rho = (self.box._rmax - self.box._rmin) * (
- log10(rho) - self.log10_rmin
- ) / (self.log10_rmax - self.log10_rmin)
- return super(PolarLogView,
- self).__call__((rho * cos(theta), rho * sin(theta)))
+ log10(rho) - self.log10_rmin) / (
+ self.log10_rmax - self.log10_rmin)
+ return super(PolarLogView, self).__call__(
+ (rho * cos(theta), rho * sin(theta)))
class PolarThetaView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3):
@@ -265,8 +272,7 @@ def __init__(self, width, height, box, aperture=pi / 3):
self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
raise Exception(
- 'Box must be set with set_polar_box for polar charts'
- )
+ 'Box must be set with set_polar_box for polar charts')
def __call__(self, rhotheta):
"""Project rho and theta"""
@@ -274,14 +280,15 @@ def __call__(self, rhotheta):
return None, None
rho, theta = rhotheta
start = 3 * pi / 2 + self.aperture / 2
- theta = start + (2 * pi - self.aperture) * (theta - self.box._tmin) / (
- self.box._tmax - self.box._tmin
- )
- return super(PolarThetaView,
- self).__call__((rho * cos(theta), rho * sin(theta)))
+ theta = start + (2 * pi - self.aperture) * (
+ theta - self.box._tmin) / (
+ self.box._tmax - self.box._tmin)
+ return super(PolarThetaView, self).__call__(
+ (rho * cos(theta), rho * sin(theta)))
class PolarThetaLogView(View):
+
"""Logarithmic polar projection"""
def __init__(self, width, height, box, aperture=pi / 3):
@@ -290,8 +297,7 @@ def __init__(self, width, height, box, aperture=pi / 3):
self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
raise Exception(
- 'Box must be set with set_polar_box for polar charts'
- )
+ 'Box must be set with set_polar_box for polar charts')
self.log10_tmax = log10(self.box._tmax) if self.box._tmax > 0 else 0
self.log10_tmin = log10(self.box._tmin) if self.box._tmin > 0 else 0
if self.log10_tmin == self.log10_tmax:
@@ -306,19 +312,20 @@ def __call__(self, rhotheta):
if theta == 0:
return super(PolarThetaLogView, self).__call__((0, 0))
theta = self.box._tmin + (self.box._tmax - self.box._tmin) * (
- log10(theta) - self.log10_tmin
- ) / (self.log10_tmax - self.log10_tmin)
+ log10(theta) - self.log10_tmin) / (
+ self.log10_tmax - self.log10_tmin)
start = 3 * pi / 2 + self.aperture / 2
- theta = start + (2 * pi - self.aperture) * (theta - self.box._tmin) / (
- self.box._tmax - self.box._tmin
- )
+ theta = start + (2 * pi - self.aperture) * (
+ theta - self.box._tmin) / (
+ self.box._tmax - self.box._tmin)
- return super(PolarThetaLogView,
- self).__call__((rho * cos(theta), rho * sin(theta)))
+ return super(PolarThetaLogView, self).__call__(
+ (rho * cos(theta), rho * sin(theta)))
class LogView(View):
+
"""Y Logarithmic projection"""
# Do not want to call the parent here
@@ -337,13 +344,13 @@ def y(self, y):
"""Project y"""
if y is None or y <= 0 or self.log10_ymax - self.log10_ymin == 0:
return 0
- return (
- self.height - self.height * (log10(y) - self.log10_ymin) /
- (self.log10_ymax - self.log10_ymin)
- )
+ return (self.height - self.height *
+ (log10(y) - self.log10_ymin) / (
+ self.log10_ymax - self.log10_ymin))
class XLogView(View):
+
"""X logarithmic projection"""
# Do not want to call the parent here
@@ -360,13 +367,13 @@ def x(self, x):
"""Project x"""
if x is None or x <= 0 or self.log10_xmax - self.log10_xmin == 0:
return None
- return (
- self.width * (log10(x) - self.log10_xmin) /
- (self.log10_xmax - self.log10_xmin)
- )
+ return (self.width *
+ (log10(x) - self.log10_xmin) /
+ (self.log10_xmax - self.log10_xmin))
class XYLogView(XLogView, LogView):
+
"""X and Y logarithmic projection"""
def __init__(self, width, height, box):
@@ -382,6 +389,7 @@ def __init__(self, width, height, box):
class HorizontalLogView(XLogView):
+
"""Transposed Logarithmic projection"""
# Do not want to call the parent here