diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index 6927a95..97c05d6 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -581,14 +581,16 @@ def get_test_routes(app): @app.route('/test/stroke_config') def test_stroke_config(): - line = Line() + line = Line(stroke_style={'width': .5}) line.add('test_no_line', range(12), stroke=False) - line.add('test', reversed(range(12))) - line.add('test_no_dots', [5] * 12, show_dots=False) + line.add('test', reversed(range(12)), stroke_style={'width': 3}) + line.add('test_no_dots', [5] * 12, show_dots=False, + stroke_style={'width': 2, 'dasharray': '12, 31'}) line.add('test_big_dots', [ randint(1, 12) for _ in range(12)], dots_size=5) line.add('test_fill', [ - randint(1, 3) for _ in range(12)], fill=True) + randint(1, 3) for _ in range(12)], fill=True, + stroke_style={'width': 5, 'dasharray': '4, 12, 7, 20'}) line.x_labels = [ 'lol', 'lol1', 'lol2', 'lol3', 'lol4', 'lol5', diff --git a/docs/changelog.rst b/docs/changelog.rst index ccc86ad..6127574 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -25,6 +25,7 @@ Changelog * mode option has been renamed to a less generic name: box_mode * fix stack_from_top for stacked lines * Add flake8 test to py.test in tox +* Remove stroke style in style and set it as a global / serie configuration. 1.7.0 ===== diff --git a/docs/documentation/configuration/rendering.rst b/docs/documentation/configuration/rendering.rst index 31e4aca..225f3f2 100644 --- a/docs/documentation/configuration/rendering.rst +++ b/docs/documentation/configuration/rendering.rst @@ -69,6 +69,18 @@ You can change the dot size chart.add('line', [.0002, .0005, .00035]) +stroke_style +------------ + +It is possible to set a default style for lines with the ``stroke_style`` dictionary. + +.. pygal-code:: + + chart = pygal.Line(stroke_style={'width': 5, 'dasharray': '3, 6', 'linecap': 'round', 'linejoin': 'round'}) + chart.add('line', [.0002, .0005, .00035]) + + + show_x_guides ------------- diff --git a/docs/documentation/configuration/serie.rst b/docs/documentation/configuration/serie.rst index e675391..b89753d 100644 --- a/docs/documentation/configuration/serie.rst +++ b/docs/documentation/configuration/serie.rst @@ -92,6 +92,16 @@ dots_size chart.add('line', [.0004, .0009, .001], dots_size=12) +stroke_style +------------ + +.. pygal-code:: + + chart = pygal.Line() + chart.add('line', [.0002, .0005, .00035], stroke_style={'width': 5, 'dasharray': '3, 6', 'linecap': 'round', 'linejoin': 'round'}) + chart.add('line', [.0004, .0009, .001], stroke_style={'width': 2, 'dasharray': '3, 6, 12, 24'}) + + rounded_bars ~~~~~~~~~~~~ diff --git a/pygal/config.py b/pygal/config.py index 5acb6f4..6384d0c 100644 --- a/pygal/config.py +++ b/pygal/config.py @@ -155,6 +155,10 @@ class CommonConfig(BaseConfig): 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' and 'dasharray'") + rounded_bars = Key( None, int, "Look", "Set this to the desired radius in px (for Bar-like charts)") diff --git a/pygal/css/style.css b/pygal/css/style.css index 134bfe8..ca71cd1 100644 --- a/pygal/css/style.css +++ b/pygal/css/style.css @@ -99,13 +99,6 @@ fill-opacity: {{ style.opacity_hover }}; } -{{ id }}.series { - stroke-width: {{ style.stroke_width }}; - stroke-linejoin: {{ style.stroke_style }}; - stroke-linecap: {{ style.stroke_style }}; - stroke-dasharray: {{ style.stroke_dasharray }}; -} - {{ id }}.series text { fill: {{ style.foreground_light }}; } @@ -140,4 +133,6 @@ {{ colors }} +{{ strokes }} + diff --git a/pygal/style.py b/pygal/style.py index 6cfdbe8..0d33641 100644 --- a/pygal/style.py +++ b/pygal/style.py @@ -24,9 +24,6 @@ from pygal.util import cycle_fill from pygal import colors from pygal.colors import darken, lighten import sys -import re - -re_dasharray_delimiters = re.compile(r'[\.|,|x|\||\- ]+', re.I) class Style(object): @@ -41,12 +38,10 @@ class Style(object): font_family='monospace', # Monospaced font is highly encouraged opacity='.8', opacity_hover='.9', - stroke_width='1', - stroke_style='round', - stroke_dasharray=None, transition='250ms', colors=( '#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe', + '#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672', '#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe', '#465457')): @@ -58,35 +53,9 @@ class Style(object): self.font_family = font_family self.opacity = opacity self.opacity_hover = opacity_hover - self.stroke_width = stroke_width - self.stroke_style = stroke_style - self.stroke_dasharray = stroke_dasharray self.transition = transition self.colors = colors - self.validate_stroke_values() - - def validate_stroke_values(self): - # stroke_width - self.stroke_width = float(self.stroke_width) - - # stroke_style - self.stroke_style = self.stroke_style.lower().strip() - if self.stroke_style not in ['round', 'bevel', 'miter']: - self.stroke_style = 'round' - - # stroke_dasharray - if self.stroke_dasharray is None: - self.stroke_dasharray = 'none' - elif isinstance(self.stroke_dasharray, (list, tuple)): - self.stroke_dasharray = '%d,%d' % self.stroke_dasharray - elif isinstance(self.stroke_dasharray, str): - self.stroke_dasharray = re.sub( - re_dasharray_delimiters, ',', self.stroke_dasharray) - else: - raise ValueError( - 'stroke_dasharray not in proper form: tuple(int, int)') - def get_colors(self, prefix, len_): """Get the css color list""" diff --git a/pygal/svg.py b/pygal/svg.py index 5b7b2ed..e701b27 100644 --- a/pygal/svg.py +++ b/pygal/svg.py @@ -81,7 +81,8 @@ class Svg(object): def add_styles(self): """Add the css to the svg""" - colors = self.graph.style.get_colors(self.id) + colors = self.graph.style.get_colors(self.id, self.graph._order) + strokes = self.get_strokes() all_css = [] for css in ['base.css'] + list(self.graph.css): if '://' in css: @@ -111,6 +112,7 @@ class Svg(object): f.read(), style=self.graph.style, colors=colors, + strokes=strokes, font_sizes=fs, id=self.id) if not self.graph.pretty_print: @@ -310,3 +312,22 @@ class Svg(object): if self.graph.disable_xml_declaration or is_unicode: svg = svg.decode('utf-8') return svg + + def get_strokes(self): + def stroke_dict_to_css(stroke, i=None): + css = ['%s.series%s {\n' % ( + self.id, '.serie-%d' % i if i is not None else '')] + for key in ('width', 'linejoin', 'linecap', 'dasharray'): + if stroke.get(key): + css.append(' stroke-%s: %s;\n' % ( + key, stroke[key])) + css.append('}') + return '\n'.join(css) + + css = [] + if self.graph.stroke_style is not None: + css.append(stroke_dict_to_css(self.graph.stroke_style)) + for serie in self.graph.series: + if serie.stroke_style is not None: + css.append(stroke_dict_to_css(serie.stroke_style, serie.index)) + return '\n'.join(css) diff --git a/pygal/test/test_style.py b/pygal/test/test_style.py index 502e0d3..cce464b 100644 --- a/pygal/test/test_style.py +++ b/pygal/test/test_style.py @@ -43,38 +43,3 @@ def test_parametric_styles_with_parameters(): line.add('_', [1, 2, 3]) line.x_labels = 'abc' assert line.render() - - -def test_stroke_style(): - s = Style(stroke_style='round') - assert s.stroke_style == 'round' - s = Style(stroke_style='bevel') - assert s.stroke_style == 'bevel' - s = Style(stroke_style='miter') - assert s.stroke_style == 'miter' - s = Style(stroke_style='rounded') - assert s.stroke_style == 'round' - s = Style(stroke_style='invalid derp') - assert s.stroke_style == 'round' - - -def test_stroke_dasharray(): - s = Style(stroke_dasharray=(0, 0)) - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray=(.5, .5)) - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray=(.9, .9)) - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray=(1.9, 1.9)) - assert s.stroke_dasharray == '1,1' - - -def test_stroke_dasharray_input_types(): - s = Style(stroke_dasharray=(0, 0)) - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray='0,0') - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray='0x0') - assert s.stroke_dasharray == '0,0' - s = Style(stroke_dasharray='0 0') - assert s.stroke_dasharray == '0,0'