From 579a6f91d540af5b6f5fffdbdfcdc1603669e428 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Wed, 15 Jul 2015 18:12:28 +0200 Subject: [PATCH] Styles are now all classes and parametric styles are explicit. WIP on new default theme based on material design --- demo/moulinrouge/__init__.py | 21 +- demo/moulinrouge/templates/index.jinja2 | 2 +- docs/changelog.rst | 2 + docs/documentation/custom_styles.rst | 4 +- docs/ext/pygal_sphinx_directives.py | 4 +- pygal/css/base.css | 5 + pygal/css/style.css | 37 +- pygal/graph/base.py | 2 + pygal/graph/graph.py | 2 +- pygal/graph/time.py | 4 +- pygal/style.py | 585 ++++++++++++++---------- pygal/util.py | 2 + 12 files changed, 393 insertions(+), 277 deletions(-) diff --git a/demo/moulinrouge/__init__.py b/demo/moulinrouge/__init__.py index 377e72a..b3b3b0d 100644 --- a/demo/moulinrouge/__init__.py +++ b/demo/moulinrouge/__init__.py @@ -30,6 +30,13 @@ import random import pickle +def get(type): + from importlib import import_module + module = '.'.join(type.split('.')[:-1]) + name = type.split('.')[-1] + return getattr(import_module(module), name) + + def random_label(): chars = string.ascii_letters + string.digits + u' àéèçêâäëï' return ''.join( @@ -98,10 +105,7 @@ def create_app(): @app.route("/svg///") def svg(type, series, config): - module = '.'.join(type.split('.')[:-1]) - name = type.split('.')[-1] - from importlib import import_module - graph = getattr(import_module(module), name)( + graph = get(type)( pickle.loads(b64decode(str(config)))) for title, values in pickle.loads(b64decode(str(series))): graph.add(title, values) @@ -109,7 +113,7 @@ def create_app(): @app.route("/table///") def table(type, series, config): - graph = getattr(pygal, type)(pickle.loads(b64decode(str(config)))) + graph = get(type)(pickle.loads(b64decode(str(config)))) for title, values in pickle.loads(b64decode(str(series))): graph.add(title, values) return graph.render_table() @@ -186,6 +190,7 @@ def create_app(): else: style = parametric_styles[style]( color, base_style=styles[base_style or 'default']) + xy_series = _random(data, order) other_series = [] for title, values in xy_series: @@ -234,7 +239,7 @@ def create_app(): config.title = "%d rotation" % angle config.x_labels = labels config.x_label_rotation = angle - svgs.append({'type': 'Bar', + svgs.append({'type': 'pygal.Bar', 'series': series, 'config': b64encode(pickle.dumps(config))}) @@ -258,7 +263,7 @@ def create_app(): for interpolation in 'quadratic', 'cubic', 'lagrange', 'trigonometric': config.title = "%s interpolation" % interpolation config.interpolate = interpolation - svgs.append({'type': 'StackedLine', + svgs.append({'type': 'pygal.StackedLine', 'series': series, 'config': b64encode(pickle.dumps(config))}) @@ -282,7 +287,7 @@ def create_app(): config.title = "Hermite interpolation with params %r" % params config.interpolate = 'hermite' config.interpolation_parameters = params - svgs.append({'type': 'StackedLine', + svgs.append({'type': 'pygal.StackedLine', 'series': series, 'config': b64encode(pickle.dumps(config))}) diff --git a/demo/moulinrouge/templates/index.jinja2 b/demo/moulinrouge/templates/index.jinja2 index a0aa6e1..2797d94 100644 --- a/demo/moulinrouge/templates/index.jinja2 +++ b/demo/moulinrouge/templates/index.jinja2 @@ -43,7 +43,7 @@

Parametric Styles

    {% for color in parametric_colors %} -
  • +
  • {% for style in parametric_styles %}
  • {{ style }} for {{ color }} diff --git a/docs/changelog.rst b/docs/changelog.rst index 7726c29..2d73d54 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -28,6 +28,8 @@ Changelog * Remove stroke style in style and set it as a global / serie configuration. * Fix None values in tables * Fix timezones in DateTimeLine +* Rename in Style foreground_light as foreground_strong +* Rename in Style foreground_dark as foreground_subtle 1.7.0 ===== diff --git a/docs/documentation/custom_styles.rst b/docs/documentation/custom_styles.rst index d8996ae..3a79914 100644 --- a/docs/documentation/custom_styles.rst +++ b/docs/documentation/custom_styles.rst @@ -16,8 +16,8 @@ You can instantiate the ``Style`` class with some customizations for quick styli background='transparent', plot_background='transparent', foreground='#53E89B', - foreground_light='#53A0E8', - foreground_dark='#630C0D', + foreground_strong='#53A0E8', + foreground_subtle='#630C0D', opacity='.6', opacity_hover='.9', transition='400ms ease-in', diff --git a/docs/ext/pygal_sphinx_directives.py b/docs/ext/pygal_sphinx_directives.py index ab8e8ac..4b05fde 100644 --- a/docs/ext/pygal_sphinx_directives.py +++ b/docs/ext/pygal_sphinx_directives.py @@ -33,8 +33,8 @@ pygal.config.Config.style.value = pygal.style.RotateStyle( background='#fcfcfc', plot_background='#ffffff', foreground='#707070', - foreground_light='#404040', - foreground_dark='#a0a0a0', + foreground_strong='#404040', + foreground_subtle='#a0a0a0', opacity='.8', opacity_hover='.9', transition='400ms ease-in') diff --git a/pygal/css/base.css b/pygal/css/base.css index 238a931..0af06af 100644 --- a/pygal/css/base.css +++ b/pygal/css/base.css @@ -22,6 +22,11 @@ * Font-sizes from config, override with care */ +{{ id }}.graph { + -webkit-user-select: none; + -webkit-font-smoothing: antialiased; +} + {{ id }}.title { font-family: {{ style.font_family }}; font-size: {{ font_sizes.title }}; diff --git a/pygal/css/style.css b/pygal/css/style.css index e8c0ec7..cf9c1e3 100644 --- a/pygal/css/style.css +++ b/pygal/css/style.css @@ -48,11 +48,11 @@ } {{ id }}text.no_data { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.title { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.legends .legend text { @@ -60,15 +60,15 @@ } {{ id }}.legends .legend:hover text { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.axis .line { - stroke: {{ style.foreground_light }}; + stroke: {{ style.foreground_strong }}; } {{ id }}.axis .guide.line { - stroke: {{ style.foreground_dark }}; + stroke: {{ style.foreground_subtle }}; } {{ id }}.axis .major.line { @@ -76,18 +76,18 @@ } {{ id }}.axis text.major { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.axis.y .guides:hover .guide.line, {{ id }}.line-graph .axis.x .guides:hover .guide.line, {{ id }}.stackedline-graph .axis.x .guides:hover .guide.line, {{ id }}.xy-graph .axis.x .guides:hover .guide.line { - stroke: {{ style.foreground_light }}; + stroke: {{ style.foreground_strong }}; } {{ id }}.axis .guides:hover text { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.reactive { @@ -101,30 +101,29 @@ } {{ id }}.series text { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.tooltip rect { fill: {{ style.plot_background }}; - stroke: {{ style.foreground_light }}; - -webkit-transition: opacity 250ms; - -moz-transition: opacity 250ms; - transition: opacity 250ms; + stroke: {{ style.foreground_strong }}; + -webkit-transition: opacity {{ style.transition }}; + -moz-transition: opacity {{ style.transition }}; + transition: opacity {{ style.transition }}; } {{ id }}.tooltip text { - fill: {{ style.foreground_light }}; + fill: {{ style.foreground_strong }}; } {{ id }}.map-element { fill: {{ style.foreground }}; - stroke: {{ style.foreground_dark }} !important; + stroke: {{ style.foreground_subtle }} !important; opacity: .9; stroke-width: 3; - -webkit-transition: 250ms; - -moz-transition: 250ms; - -o-transition: 250ms; - transition: 250ms; + -webkit-transition: opacity {{ style.transition }}; + -moz-transition: opacity {{ style.transition }}; + transition: opacity {{ style.transition }}; } {{ id }}.map-element:hover { diff --git a/pygal/graph/base.py b/pygal/graph/base.py index 2625fa9..7b3abf8 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -182,6 +182,8 @@ class BaseGraph(object): if getattr(self, 'y_labels', None) is not None: self.y_labels = list(self.y_labels) self.state = State(self, **kwargs) + if isinstance(self.style, type): + self.style = self.style() self.series = self.prepare_values( self.raw_series) or [] self.secondary_series = self.prepare_values( diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 59649a6..971f1dc 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -732,7 +732,7 @@ class Graph(PublicApi): return any([ len([v for a in ( s[1] if is_list_like(s) else [s]) - for v in (a if self._dual else [a]) + 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/time.py b/pygal/graph/time.py index 69054ca..648b0c2 100644 --- a/pygal/graph/time.py +++ b/pygal/graph/time.py @@ -69,7 +69,9 @@ def time_to_seconds(x): return (( ((x.hour * 60) + x.minute) * 60 + x.second ) * 10 ** 6 + x.microsecond) / 10 ** 6 - return x + + # Clamp to valid time + return max(0, min(x, 24 * 3600 - 10 ** -6)) def seconds_to_time(x): diff --git a/pygal/style.py b/pygal/style.py index 102da10..9aa3695 100644 --- a/pygal/style.py +++ b/pygal/style.py @@ -22,41 +22,48 @@ from __future__ import division from pygal.util import cycle_fill from pygal import colors from pygal.colors import darken, lighten -import sys class Style(object): """Styling class containing colors for the css generation""" - def __init__( - self, - background='black', - plot_background='#111', - foreground='#999', - foreground_light='#eee', - foreground_dark='#555', - font_family='monospace', # Monospaced font is highly encouraged - opacity='.8', - opacity_hover='.9', - transition='250ms', - colors=( - '#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe', - - '#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672', - '#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe', - '#465457')): + plot_background = 'rgba(255, 255, 255, 1)' + background = 'rgba(249, 249, 249, 1)' + foreground = 'rgba(0, 0, 0, .87)' + foreground_strong = 'rgba(0, 0, 0, 1)' + foreground_subtle = 'rgba(0, 0, 0, .54)' + # Monospaced font is highly encouraged + font_family = 'Consolas, "Liberation Mono", Menlo, Courier, ' + 'monospace' + opacity = '.7' + opacity_hover = '.8' + transition = '150ms' + colors = ( + '#F44336', # 0 + '#3F51B5', # 4 + '#009688', # 8 + '#FFC107', # 13 + '#FF5722', # 15 + '#9C27B0', # 2 + '#03A9F4', # 6 + '#8BC34A', # 10 + '#FF9800', # 14 + '#E91E63', # 1 + '#2196F3', # 5 + '#4CAF50', # 9 + '#FFEB3B', # 12 + '#673AB7', # 3 + '#00BCD4', # 7 + '#CDDC39', # 11 + '#795548', # 16 + '#9E9E9E', # 17 + '#607D8B', # 18 + ) + + def __init__(self, **kwargs): """Create the style""" - self.background = background - self.plot_background = plot_background - self.foreground = foreground - self.foreground_light = foreground_light - self.foreground_dark = foreground_dark - self.font_family = font_family - self.opacity = opacity - self.opacity_hover = opacity_hover - self.transition = transition - self.colors = colors + self.__dict__.update(kwargs) def get_colors(self, prefix, len_): """Get the css color list""" @@ -86,194 +93,249 @@ class Style(object): return config -DefaultStyle = Style(opacity_hover='.4', opacity='.8') +DefaultStyle = Style -LightStyle = Style( - background='white', - plot_background='rgba(0, 0, 255, 0.1)', - foreground='rgba(0, 0, 0, 0.7)', - foreground_light='rgba(0, 0, 0, 0.9)', - foreground_dark='rgba(0, 0, 0, 0.5)', - colors=('#242424', '#9f6767', '#92ac68', - '#d0d293', '#9aacc3', '#bb77a4', - '#77bbb5', '#777777')) +class DarkStyle(Style): + """A dark style (old default)""" -NeonStyle = Style( - opacity='.1', - opacity_hover='.75', - transition='1s ease-out') + background = 'black' + plot_background = '#111' + foreground = '#999' + foreground_strong = '#eee' + foreground_subtle = '#555' + opacity = '.8' + opacity_hover = '.4' + transition = '250ms' + colors = ( + '#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe', + '#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672', + '#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe', + '#465457') -CleanStyle = Style( - background='transparent', - plot_background='rgba(240, 240, 240, 0.7)', - foreground='rgba(0, 0, 0, 0.9)', - foreground_light='rgba(0, 0, 0, 0.9)', - foreground_dark='rgba(0, 0, 0, 0.5)', - colors=( +class LightStyle(Style): + + """A light style""" + + background = 'white' + plot_background = 'rgba(0, 0, 255, 0.1)' + 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') + + +class NeonStyle(DarkStyle): + + """Similar to DarkStyle but with more opacity and effects""" + + opacity = '.1' + opacity_hover = '.75' + transition = '1s ease-out' + + +class CleanStyle(Style): + + """A rather clean style""" + + background = 'transparent' + plot_background = 'rgba(240, 240, 240, 0.7)' + foreground = 'rgba(0, 0, 0, 0.9)' + foreground_strong = 'rgba(0, 0, 0, 0.9)' + 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)')) - - -solarized_colors = ( - '#b58900', '#cb4b16', '#dc322f', '#d33682', - '#6c71c4', '#268bd2', '#2aa198', '#859900') - - -DarkSolarizedStyle = Style( - background='#073642', - plot_background='#002b36', - foreground='#839496', - foreground_light='#fdf6e3', - foreground_dark='#657b83', - opacity='.66', - opacity_hover='.9', - transition='500ms ease-in', - colors=solarized_colors) - - -LightSolarizedStyle = Style( - background='#fdf6e3', - plot_background='#eee8d5', - foreground='#657b83', - foreground_light='#073642', - foreground_dark='#073642', - opacity='.6', - opacity_hover='.9', - transition='500ms ease-in', - colors=solarized_colors) - - -RedBlueStyle = Style( - background=lighten('#e6e7e9', 7), - plot_background=lighten('#e6e7e9', 10), - foreground='rgba(0, 0, 0, 0.9)', - foreground_light='rgba(0, 0, 0, 0.9)', - foreground_dark='rgba(0, 0, 0, 0.5)', - opacity='.6', - opacity_hover='.9', - colors=( + 'rgb(149,12,12)') + + +class DarkSolarizedStyle(Style): + + """Dark solarized popular theme""" + + background = '#073642' + plot_background = '#002b36' + foreground = '#839496' + foreground_strong = '#fdf6e3' + foreground_subtle = '#657b83' + opacity = '.66' + opacity_hover = '.9' + transition = '500ms ease-in' + colors = ( + '#b58900', '#cb4b16', '#dc322f', '#d33682', + '#6c71c4', '#268bd2', '#2aa198', '#859900') + + +class LightSolarizedStyle(DarkSolarizedStyle): + + """Light solarized popular theme""" + + background = '#fdf6e3' + plot_background = '#eee8d5' + foreground = '#657b83' + foreground_strong = '#073642' + foreground_subtle = '#073642' + + +class RedBlueStyle(Style): + + """A red and blue theme""" + + background = lighten('#e6e7e9', 7) + plot_background = lighten('#e6e7e9', 10) + foreground = 'rgba(0, 0, 0, 0.9)' + foreground_strong = 'rgba(0, 0, 0, 0.9)' + foreground_subtle = 'rgba(0, 0, 0, 0.5)' + opacity = '.6' + opacity_hover = '.9' + colors = ( '#d94e4c', '#e5884f', '#39929a', lighten('#d94e4c', 10), darken('#39929a', 15), lighten('#e5884f', 17), - darken('#d94e4c', 10), '#234547')) - - -LightColorizedStyle = Style( - background='#f8f8f8', - plot_background=lighten('#f8f8f8', 3), - foreground='#333', - foreground_light='#666', - foreground_dark='rgba(0, 0 , 0, 0.5)', - opacity='.5', - opacity_hover='.9', - transition='250ms ease-in', - colors=( + darken('#d94e4c', 10), '#234547') + + +class LightColorizedStyle(Style): + + """A light colorized style""" + + background = '#f8f8f8' + plot_background = lighten('#f8f8f8', 3) + foreground = '#333' + foreground_strong = '#666' + foreground_subtle = 'rgba(0, 0 , 0, 0.5)' + opacity = '.5' + 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))) - - -DarkColorizedStyle = Style( - background=darken('#3a2d3f', 5), - plot_background=lighten('#3a2d3f', 2), - foreground='rgba(255, 255, 255, 0.9)', - foreground_light='rgba(255, 255, 255, 0.9)', - foreground_dark='rgba(255, 255 , 255, 0.5)', - opacity='.2', - opacity_hover='.7', - transition='250ms ease-in', - colors=( + lighten('#a2a7a1', 15), lighten('#fe9592', 15), darken('#3ac2c0', 10)) + + +class DarkColorizedStyle(Style): + + """A dark colorized style""" + + background = darken('#3a2d3f', 5) + plot_background = lighten('#3a2d3f', 2) + foreground = 'rgba(255, 255, 255, 0.9)' + foreground_strong = 'rgba(255, 255, 255, 0.9)' + foreground_subtle = 'rgba(255, 255 , 255, 0.5)' + opacity = '.2' + opacity_hover = '.7' + transition = '250ms ease-in' + colors = ( '#c900fe', '#01b8fe', '#59f500', '#ff00e4', '#f9fa00', darken('#c900fe', 20), darken('#01b8fe', 15), darken('#59f500', 20), - darken('#ff00e4', 15), lighten('#f9fa00', 20))) - - -TurquoiseStyle = Style( - background=darken('#1b8088', 15), - plot_background=darken('#1b8088', 17), - foreground='rgba(255, 255, 255, 0.9)', - foreground_light='rgba(255, 255, 255, 0.9)', - foreground_dark='rgba(255, 255 , 255, 0.5)', - opacity='.5', - opacity_hover='.9', - transition='250ms ease-in', - colors=( + darken('#ff00e4', 15), lighten('#f9fa00', 20)) + + +class TurquoiseStyle(Style): + + """A turquoise style""" + + background = darken('#1b8088', 15) + plot_background = darken('#1b8088', 17) + foreground = 'rgba(255, 255, 255, 0.9)' + foreground_strong = 'rgba(255, 255, 255, 0.9)' + foreground_subtle = 'rgba(255, 255 , 255, 0.5)' + opacity = '.5' + opacity_hover = '.9' + transition = '250ms ease-in' + colors = ( '#93d2d9', '#ef940f', '#8C6243', '#fff', darken('#93d2d9', 20), lighten('#ef940f', 15), - lighten('#8c6243', 15), '#1b8088')) - - -LightGreenStyle = Style( - background=lighten('#f3f3f3', 3), - plot_background='#fff', - foreground='#333333', - foreground_light='#666', - foreground_dark='#222222', - opacity='.5', - opacity_hover='.9', - transition='250ms ease-in', - colors=( + lighten('#8c6243', 15), '#1b8088') + + +class LightGreenStyle(Style): + + """A light green style""" + + background = lighten('#f3f3f3', 3) + plot_background = '#fff' + foreground = '#333333' + foreground_strong = '#666' + foreground_subtle = '#222222' + opacity = '.5' + opacity_hover = '.9' + transition = '250ms ease-in' + colors = ( '#7dcf30', '#247fab', lighten('#7dcf30', 10), '#ccc', darken('#7dcf30', 15), '#ddd', lighten('#247fab', 10), - darken('#247fab', 15))) - - -DarkGreenStyle = Style( - background=darken('#251e01', 3), - plot_background=darken('#251e01', 1), - foreground='rgba(255, 255, 255, 0.9)', - foreground_light='rgba(255, 255, 255, 0.9)', - foreground_dark='rgba(255, 255, 255, 0.6)', - opacity='.6', - opacity_hover='.9', - transition='250ms ease-in', - colors=( + darken('#247fab', 15)) + + +class DarkGreenStyle(Style): + + """A dark green style""" + + background = darken('#251e01', 3) + plot_background = darken('#251e01', 1) + foreground = 'rgba(255, 255, 255, 0.9)' + foreground_strong = 'rgba(255, 255, 255, 0.9)' + foreground_subtle = 'rgba(255, 255, 255, 0.6)' + opacity = '.6' + opacity_hover = '.9' + transition = '250ms ease-in' + colors = ( '#adde09', '#6e8c06', '#4a5e04', '#fcd202', '#C1E34D', - lighten('#fcd202', 25))) - - -DarkGreenBlueStyle = Style( - background='#000', - plot_background=lighten('#000', 8), - foreground='rgba(255, 255, 255, 0.9)', - foreground_light='rgba(255, 255, 255, 0.9)', - foreground_dark='rgba(255, 255, 255, 0.6)', - 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')) - - -BlueStyle = Style( - background=darken('#f8f8f8', 3), - plot_background='#f8f8f8', - foreground='rgba(0, 0, 0, 0.9)', - foreground_light='rgba(0, 0, 0, 0.9)', - foreground_dark='rgba(0, 0, 0, 0.6)', - opacity='.5', - opacity_hover='.9', - transition='250ms ease-in', - colors=( + lighten('#fcd202', 25)) + + +class DarkGreenBlueStyle(Style): + + """A dark green and blue style""" + + background = '#000' + plot_background = lighten('#000', 8) + foreground = 'rgba(255, 255, 255, 0.9)' + foreground_strong = 'rgba(255, 255, 255, 0.9)' + foreground_subtle = 'rgba(255, 255, 255, 0.6)' + 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') + + +class BlueStyle(Style): + + """A blue style""" + + background = darken('#f8f8f8', 3) + plot_background = '#f8f8f8' + foreground = 'rgba(0, 0, 0, 0.9)' + foreground_strong = 'rgba(0, 0, 0, 0.9)' + foreground_subtle = 'rgba(0, 0, 0, 0.6)' + opacity = '.5' + opacity_hover = '.9' + transition = '250ms ease-in' + 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""" -SolidColorStyle = Style( - background='#FFFFFF', - plot_background='#FFFFFF', - foreground='#000000', - foreground_light='#000000', - foreground_dark='#828282', - opacity='.8', - opacity_hover='.9', - transition='400ms ease-in', - colors=('#FF9900', '#DC3912', '#4674D1', '#109618', '#990099', - '#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499')) + background = '#FFFFFF' + plot_background = '#FFFFFF' + foreground = '#000000' + foreground_strong = '#000000' + foreground_subtle = '#828282' + opacity = '.8' + opacity_hover = '.9' + transition = '400ms ease-in' + colors = ( + '#FF9900', '#DC3912', '#4674D1', '#109618', '#990099', + '#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499') styles = {'default': DefaultStyle, @@ -293,55 +355,92 @@ styles = {'default': DefaultStyle, 'solid_color': SolidColorStyle} -parametric_styles = {} -for op in ('lighten', 'darken', 'saturate', 'desaturate', 'rotate'): - name = op.capitalize() + 'Style' +class ParametricStyleBase(Style): - def get_style_for(op_name): + """Parametric Style base class for all the parametric operations""" + + _op = None + + def __init__(self, color, step=10, max_=None, base_style=None, **kwargs): """ - Return a callable that returns a Style instance - for the given operation + Initialization of the parametric style. + + This takes several parameters: + * a `step` which correspond on how many colors will be needed + * a `max_` which defines the maximum amplitude of the color effect + * a `base_style` which will be taken as default for everything + except colors + * any keyword arguments setting other style parameters """ - operation = getattr(colors, op_name) - - def parametric_style(color, step=10, max_=None, base_style=None, - **kwargs): - """ - Generate a parametric Style instance of the parametric operation - This takes several parameters: - * a `step` which correspond on how many colors will be needed - * a `max_` which defines the maximum amplitude of the color effect - * a `base_style` which will be taken as default for everything - except colors - * any keyword arguments setting other style parameters - """ - if max_ is None: - violency = { - 'darken': 50, - 'lighten': 50, - 'saturate': 100, - 'desaturate': 100, - 'rotate': 360 - } - max__ = violency[op_name] - else: - max__ = max_ - - def modifier(index): - percent = max__ * index / (step - 1) - return operation(color, percent) - - colors = list(map(modifier, range(0, max(2, step)))) - - if base_style is None: - return Style(colors=colors, **kwargs) - opts = dict(base_style.__dict__) - opts.update({'colors': colors}) - opts.update(kwargs) - return Style(**opts) - - return parametric_style - - style = get_style_for(op) - parametric_styles[name] = style - setattr(sys.modules[__name__], name, style) + + if self._op is None: + raise RuntimeError('ParametricStyle is not instanciable') + + defaults = {} + if base_style is not None: + if isinstance(base_style, type): + base_style = base_style() + defaults.update(base_style.to_dict()) + defaults.update(kwargs) + + super(ParametricStyleBase, self).__init__(**defaults) + + if max_ is None: + violency = { + 'darken': 50, + 'lighten': 50, + 'saturate': 100, + 'desaturate': 100, + 'rotate': 360 + } + max_ = violency[self._op] + + def modifier(index): + percent = max_ * index / (step - 1) + return getattr(colors, self._op)(color, percent) + + self.colors = list(map(modifier, range(0, max(2, step)))) + + +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' + + +parametric_styles = { + 'lighten': LightenStyle, + 'darken': DarkenStyle, + 'saturate': SaturateStyle, + 'desaturate': DesaturateStyle, + 'rotate': RotateStyle +} diff --git a/pygal/util.py b/pygal/util.py index 1fa96f0..a8fc5d9 100644 --- a/pygal/util.py +++ b/pygal/util.py @@ -37,6 +37,8 @@ def humanize(number): """Format a number to engineer scale""" if is_list_like(number): return', '.join(map(humanize, number)) + if number is None: + return u('∅') order = number and int(floor(log(abs(number)) / log(1000))) human_readable = ORDERS.split(" ")[int(order > 0)] if order == 0 or order > len(human_readable):