mirror of https://github.com/Kozea/pygal.git
Florian Mounier
10 years ago
20 changed files with 659 additions and 765 deletions
@ -1,220 +0,0 @@
|
||||
# -*- coding: utf-8 -*- |
||||
# This file is part of pygal |
||||
# |
||||
# A python svg graph plotting library |
||||
# Copyright © 2012-2014 Kozea |
||||
# |
||||
# This library is free software: you can redistribute it and/or modify it under |
||||
# the terms of the GNU Lesser General Public License as published by the Free |
||||
# Software Foundation, either version 3 of the License, or (at your option) any |
||||
# later version. |
||||
# |
||||
# This library is distributed in the hope that it will be useful, but WITHOUT |
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
||||
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
||||
# details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with pygal. If not, see <http://www.gnu.org/licenses/>. |
||||
""" |
||||
Ghost container |
||||
|
||||
It is used to delegate rendering to real objects but keeping config in place |
||||
|
||||
""" |
||||
|
||||
from __future__ import division |
||||
import io |
||||
import sys |
||||
from pygal._compat import u, is_list_like |
||||
from pygal.graph import CHARTS_NAMES |
||||
from pygal.config import Config, CONFIG_ITEMS |
||||
from pygal.util import prepare_values |
||||
from uuid import uuid4 |
||||
|
||||
|
||||
class ChartCollection(object): |
||||
pass |
||||
|
||||
|
||||
REAL_CHARTS = { |
||||
'DateY': 'pygal.graph.time.DateY', |
||||
'DateTimeLine': 'pygal.graph.time.DateTimeLine', |
||||
'DateLine': 'pygal.graph.time.DateLine', |
||||
'TimeLine': 'pygal.graph.time.TimeLine', |
||||
'TimeDeltaLine': 'pygal.graph.time.TimeDeltaLine' |
||||
} |
||||
|
||||
for NAME in CHARTS_NAMES: |
||||
if NAME in REAL_CHARTS: |
||||
mod_name = 'pygal.graph.time' |
||||
else: |
||||
mod_name = 'pygal.graph.%s' % NAME.lower() |
||||
|
||||
__import__(mod_name) |
||||
mod = sys.modules[mod_name] |
||||
chart = getattr(mod, NAME) |
||||
if issubclass(chart, ChartCollection): |
||||
for name, chart in chart.__dict__.items(): |
||||
if name.startswith('_'): |
||||
continue |
||||
REAL_CHARTS['%s_%s' % (NAME, name)] = chart |
||||
else: |
||||
REAL_CHARTS[NAME] = chart |
||||
|
||||
|
||||
class Ghost(object): |
||||
|
||||
def __init__(self, config=None, **kwargs): |
||||
"""Init config""" |
||||
name = self.__class__.__name__ |
||||
self.cls = REAL_CHARTS[name] |
||||
self.uuid = str(uuid4()) |
||||
if config and isinstance(config, type): |
||||
config = config() |
||||
|
||||
if config: |
||||
config = config.copy() |
||||
else: |
||||
config = Config() |
||||
|
||||
config(**kwargs) |
||||
self.config = config |
||||
self.raw_series = [] |
||||
self.raw_series2 = [] |
||||
self.xml_filters = [] |
||||
|
||||
def add(self, title, values, **kwargs): |
||||
"""Add a serie to this graph""" |
||||
if not is_list_like(values) and not isinstance(values, dict): |
||||
values = [values] |
||||
if kwargs.get('secondary', False): |
||||
self.raw_series2.append((title, values, kwargs)) |
||||
else: |
||||
self.raw_series.append((title, values, kwargs)) |
||||
|
||||
def add_xml_filter(self, callback): |
||||
self.xml_filters.append(callback) |
||||
|
||||
def make_series(self, series, offset=0): |
||||
return prepare_values(series, self.config, self.cls, offset) |
||||
|
||||
def make_instance(self, overrides=None): |
||||
for conf_key in CONFIG_ITEMS: |
||||
if conf_key.is_list: |
||||
if getattr(self, conf_key.name, None): |
||||
setattr(self, conf_key.name, |
||||
list(getattr(self, conf_key.name))) |
||||
|
||||
self.config(**self.__dict__) |
||||
self.config.__dict__.update(overrides or {}) |
||||
series = self.make_series(self.raw_series) |
||||
secondary_series = self.make_series( |
||||
self.raw_series2, len(series or [])) |
||||
self._last__inst = self.cls( |
||||
self.config, series, secondary_series, self.uuid, |
||||
self.xml_filters) |
||||
return self._last__inst |
||||
|
||||
# Rendering |
||||
def render(self, is_unicode=False, **kwargs): |
||||
return (self |
||||
.make_instance(overrides=kwargs) |
||||
.render(is_unicode=is_unicode)) |
||||
|
||||
def render_tree(self, **kwargs): |
||||
return self.make_instance(overrides=kwargs).render_tree() |
||||
|
||||
def render_table(self, **kwargs): |
||||
# Import here to avoid lxml import |
||||
try: |
||||
from pygal.table import Table |
||||
except ImportError: |
||||
raise ImportError('You must install lxml to use render table') |
||||
real_cls, self.cls = self.cls, Table |
||||
rv = self.make_instance().render(**kwargs) |
||||
self.cls = real_cls |
||||
return rv |
||||
|
||||
def render_pyquery(self): |
||||
"""Render the graph, and return a pyquery wrapped tree""" |
||||
from pyquery import PyQuery as pq |
||||
return pq(self.render(), parser='html') |
||||
|
||||
def render_in_browser(self, **kwargs): |
||||
"""Render the graph, open it in your browser with black magic""" |
||||
try: |
||||
from lxml.html import open_in_browser |
||||
except ImportError: |
||||
raise ImportError('You must install lxml to use render in browser') |
||||
open_in_browser(self.render_tree(**kwargs), encoding='utf-8') |
||||
|
||||
def render_response(self, **kwargs): |
||||
"""Render the graph, and return a Flask response""" |
||||
from flask import Response |
||||
return Response(self.render(**kwargs), mimetype='image/svg+xml') |
||||
|
||||
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') |
||||
|
||||
def render_to_file(self, filename, **kwargs): |
||||
"""Render the graph, and write it to filename""" |
||||
with io.open(filename, 'w', encoding='utf-8') as f: |
||||
f.write(self.render(is_unicode=True, **kwargs)) |
||||
|
||||
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) |
||||
|
||||
def render_sparktext(self, relative_to=None): |
||||
"""Make a mini text sparkline from chart""" |
||||
bars = u('▁▂▃▄▅▆▇█') |
||||
if len(self.raw_series) == 0: |
||||
return u('') |
||||
values = list(self.raw_series[0][1]) |
||||
if len(values) == 0: |
||||
return u('') |
||||
|
||||
chart = u('') |
||||
values = list(map(lambda x: max(x, 0), values)) |
||||
|
||||
vmax = max(values) |
||||
if relative_to is None: |
||||
relative_to = min(values) |
||||
|
||||
if (vmax - relative_to) == 0: |
||||
chart = bars[0] * len(values) |
||||
return chart |
||||
|
||||
divisions = len(bars) - 1 |
||||
for value in values: |
||||
chart += bars[int(divisions * |
||||
(value - relative_to) / (vmax - relative_to))] |
||||
return chart |
||||
|
||||
def render_sparkline(self, **kwargs): |
||||
spark_options = dict( |
||||
width=200, |
||||
height=50, |
||||
show_dots=False, |
||||
show_legend=False, |
||||
show_x_labels=False, |
||||
show_y_labels=False, |
||||
spacing=0, |
||||
margin=5, |
||||
explicit_size=True |
||||
) |
||||
spark_options.update(kwargs) |
||||
return self.make_instance(spark_options).render() |
||||
|
||||
def _repr_svg_(self): |
||||
"""Display svg in IPython notebook""" |
||||
return self.render(disable_xml_declaration=True) |
||||
|
||||
def _repr_png_(self): |
||||
"""Display png in IPython notebook""" |
||||
return self.render_to_png() |
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*- |
||||
# This file is part of pygal |
||||
# |
||||
# A python svg graph plotting library |
||||
# Copyright © 2012-2014 Kozea |
||||
# |
||||
# This library is free software: you can redistribute it and/or modify it under |
||||
# the terms of the GNU Lesser General Public License as published by the Free |
||||
# Software Foundation, either version 3 of the License, or (at your option) any |
||||
# later version. |
||||
# |
||||
# This library is distributed in the hope that it will be useful, but WITHOUT |
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
||||
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
||||
# details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with pygal. If not, see <http://www.gnu.org/licenses/>. |
||||
""" |
||||
Class holding state during render |
||||
|
||||
""" |
||||
|
||||
|
||||
class State(object): |
||||
def __init__(self, graph): |
||||
self.__dict__.update(**graph.config.__dict__) |
||||
self.__dict__.update(**graph.__dict__) |
Loading…
Reference in new issue