Browse Source

Fix js options and 2.6

2.0.0
Florian Mounier 10 years ago
parent
commit
55caf8ce49
  1. 2
      CHANGELOG
  2. 27
      pygal/graph/base.py
  3. 4
      pygal/graph/gauge.py
  4. 56
      pygal/graph/graph.py
  5. 4
      pygal/graph/horizontal.py
  6. 8
      pygal/graph/pie.py
  7. 4
      pygal/graph/radar.py
  8. 4
      pygal/graph/treemap.py
  9. 3
      pygal/state.py
  10. 10
      pygal/svg.py
  11. 45
      pygal/table.py

2
CHANGELOG

@ -1,5 +1,5 @@
V 2.0.0 UNRELEASED
Rework the ghost mechanism to come back to a more object oriented behavior (Maybe)
Rework the ghost mechanism to come back to a more object oriented behavior, storing all state in a state object which is created every render.
V 1.7.0
Remove DateY and replace it by real XY datetime, date, time and timedelta support. (#188)

27
pygal/graph/base.py

@ -34,6 +34,7 @@ from pygal.adapters import (
not_zero, positive, decimal_to_float)
from functools import reduce
from uuid import uuid4
import io
class BaseGraph(object):
@ -86,8 +87,6 @@ class BaseGraph(object):
def prepare_values(self, raw, offset=0):
"""Prepare the values to start with sane values"""
from pygal import Worldmap, FrenchMapDepartments, Histogram
if self.x_labels is not None:
self.x_labels = list(map(to_unicode, self.x_labels))
if self.zero == 0 and isinstance(
self, (Worldmap, FrenchMapDepartments)):
self.zero = 1
@ -169,17 +168,22 @@ class BaseGraph(object):
values.append(value)
serie_config = SerieConfig()
serie_config(**{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),
title, values, serie_config, metadata))
return series
def setup(self):
def setup(self, **kwargs):
"""Init the graph"""
self.state = State(self)
# Keep labels in case of map
if getattr(self, 'x_labels', None) is not None:
self.x_labels = list(map(to_unicode, self.x_labels))
if getattr(self, 'y_labels', None) is not None:
self.y_labels = list(self.y_labels)
self.state = State(self, **kwargs)
self.series = self.prepare_values(
self.raw_series) or []
self.secondary_series = self.prepare_values(
@ -191,7 +195,7 @@ class BaseGraph(object):
self._x_2nd_labels = None
self._y_2nd_labels = None
self.nodes = {}
self.margin = Margin(
self.margin_box = Margin(
self.margin_top or self.margin,
self.margin_right or self.margin,
self.margin_bottom or self.margin,
@ -217,7 +221,7 @@ class BaseGraph(object):
def render(self, is_unicode=False, **kwargs):
"""Render the graph, and return the svg string"""
self.setup()
self.setup(**kwargs)
svg = self.svg.render(
is_unicode=is_unicode, pretty_print=self.pretty_print)
self.teardown()
@ -225,9 +229,11 @@ class BaseGraph(object):
def render_tree(self):
"""Render the graph, and return (l)xml etree"""
self.setup()
svg = self.svg.root
for f in self.xml_filters:
svg = f(svg)
self.teardown()
return svg
def render_table(self, **kwargs):
@ -264,7 +270,7 @@ class BaseGraph(object):
def render_to_file(self, filename, **kwargs):
"""Render the graph, and write it to filename"""
with open(filename, 'w', encoding='utf-8') as f:
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):
@ -312,7 +318,7 @@ class BaseGraph(object):
explicit_size=True
)
spark_options.update(kwargs)
return self.make_instance(spark_options).render()
return self.render(**spark_options)
def _repr_svg_(self):
"""Display svg in IPython notebook"""
@ -320,3 +326,4 @@ class BaseGraph(object):
def _repr_png_(self):
"""Display png in IPython notebook"""
return self.render_to_png()

4
pygal/graph/gauge.py

@ -37,8 +37,8 @@ class Gauge(Graph):
view_class = PolarThetaView
self.view = view_class(
self.width - self.margin.x,
self.height - self.margin.y,
self.width - self.margin_box.x,
self.height - self.margin_box.y,
self._box)
def needle(self, serie):

56
pygal/graph/graph.py

@ -63,8 +63,8 @@ class Graph(BaseGraph):
view_class = View
self.view = view_class(
self.width - self.margin.x,
self.height - self.margin.y,
self.width - self.margin_box.x,
self.height - self.margin_box.y,
self._box)
def _make_graph(self):
@ -81,7 +81,7 @@ class Graph(BaseGraph):
self.nodes['plot'] = self.svg.node(
self.nodes['graph'], class_="plot",
transform="translate(%d, %d)" % (
self.margin.left, self.margin.top))
self.margin_box.left, self.margin_box.top))
self.svg.node(self.nodes['plot'], 'rect',
class_='background',
x=0, y=0,
@ -93,15 +93,15 @@ class Graph(BaseGraph):
self.nodes['overlay'] = self.svg.node(
self.nodes['graph'], class_="plot overlay",
transform="translate(%d, %d)" % (
self.margin.left, self.margin.top))
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.left, self.margin.top))
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.left, self.margin.top))
self.margin_box.left, self.margin_box.top))
self.nodes['tooltip'] = self.svg.node(
self.nodes['tooltip_overlay'],
transform='translate(0 0)',
@ -279,8 +279,8 @@ class Graph(BaseGraph):
return
truncation = self.truncate_legend
if self.legend_at_bottom:
x = self.margin.left + self.spacing
y = (self.margin.top + self.view.height +
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(
@ -293,7 +293,7 @@ class Graph(BaseGraph):
available_space, self.legend_font_size)
else:
x = self.spacing
y = self.margin.top + self.spacing
y = self.margin_box.top + self.spacing
cols = 1
if not truncation:
truncation = 15
@ -321,13 +321,13 @@ class Graph(BaseGraph):
enumerate(zip(self._secondary_legends, repeat(True)))))
# draw secondary axis on right
x = self.margin.left + self.view.width + self.spacing
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.label_font_size)
x += self.spacing + max(w * cos(rad(self.y_label_rotation)), h)
y = self.margin.top + self.spacing
y = self.margin_box.top + self.spacing
secondary_legends = self.svg.node(
self.nodes['graph'], class_='legends',
@ -383,13 +383,13 @@ class Graph(BaseGraph):
def _make_x_title(self):
"""Make the X-Axis title"""
y = (self.height - self.margin.bottom +
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',
x=self.margin.left + self.view.width / 2,
x=self.margin_box.left + self.view.width / 2,
y=y + i * (self.title_font_size + self.spacing)
)
text.text = title_line
@ -397,7 +397,7 @@ class Graph(BaseGraph):
def _make_y_title(self):
"""Make the Y-Axis title"""
if self._y_title:
yc = self.margin.top + self.view.height / 2
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',
@ -524,15 +524,15 @@ class Graph(BaseGraph):
cols = (self._order // self.legend_at_bottom_columns
if self.legend_at_bottom_columns
else ceil(sqrt(self._order)) or 1)
self.margin.bottom += self.spacing + h_max * round(
self.margin_box.bottom += self.spacing + h_max * round(
cols - 1) * 1.5 + h_max
else:
if series_group is self.series:
legend_width = self.spacing + w + self.legend_box_size
self.margin.left += legend_width
self.margin_box.left += legend_width
self._legend_at_left_width += legend_width
else:
self.margin.right += (
self.margin_box.right += (
self.spacing + w + self.legend_box_size)
self._x_labels_height = 0
@ -546,13 +546,13 @@ class Graph(BaseGraph):
self._x_labels_height = self.spacing + max(
w * sin(rad(self.x_label_rotation)), h)
if xlabels is self._x_labels:
self.margin.bottom += self._x_labels_height
self.margin_box.bottom += self._x_labels_height
else:
self.margin.top += self._x_labels_height
self.margin_box.top += self._x_labels_height
if self.x_label_rotation:
self.margin.right = max(
self.margin_box.right = max(
w * cos(rad(self.x_label_rotation)),
self.margin.right)
self.margin_box.right)
if self.show_y_labels:
for ylabels in (self._y_labels, self._y_2nd_labels):
@ -560,10 +560,10 @@ class Graph(BaseGraph):
h, w = get_texts_box(
cut(ylabels), self.label_font_size)
if ylabels is self._y_labels:
self.margin.left += self.spacing + max(
self.margin_box.left += self.spacing + max(
w * cos(rad(self.y_label_rotation)), h)
else:
self.margin.right += self.spacing + max(
self.margin_box.right += self.spacing + max(
w * cos(rad(self.y_label_rotation)), h)
self._title = split_title(
@ -571,27 +571,27 @@ class Graph(BaseGraph):
if self.title:
h, _ = get_text_box(self._title[0], self.title_font_size)
self.margin.top += len(self._title) * (self.spacing + h)
self.margin_box.top += len(self._title) * (self.spacing + h)
self._x_title = split_title(
self.x_title, self.width - self.margin.x, self.title_font_size)
self.x_title, self.width - self.margin_box.x, self.title_font_size)
self._x_title_height = 0
if self._x_title:
h, _ = get_text_box(self._x_title[0], self.title_font_size)
height = len(self._x_title) * (self.spacing + h)
self.margin.bottom += height
self.margin_box.bottom += height
self._x_title_height = height + self.spacing
self._y_title = split_title(
self.y_title, self.height - self.margin.y,
self.y_title, self.height - self.margin_box.y,
self.title_font_size)
self._y_title_height = 0
if self._y_title:
h, _ = get_text_box(self._y_title[0], self.title_font_size)
height = len(self._y_title) * (self.spacing + h)
self.margin.left += height
self.margin_box.left += height
self._y_title_height = height + self.spacing
@cached_property

4
pygal/graph/horizontal.py

@ -48,6 +48,6 @@ class HorizontalGraph(Graph):
view_class = HorizontalView
self.view = view_class(
self.width - self.margin.x,
self.height - self.margin.y,
self.width - self.margin_box.x,
self.height - self.margin_box.y,
self._box)

8
pygal/graph/pie.py

@ -43,11 +43,11 @@ class Pie(Graph):
total_perc = 0
original_start_angle = start_angle
if self.half_pie:
center = ((self.width - self.margin.x) / 2.,
(self.height - self.margin.y) / 1.25)
center = ((self.width - self.margin_box.x) / 2.,
(self.height - self.margin_box.y) / 1.25)
else:
center = ((self.width - self.margin.x) / 2.,
(self.height - self.margin.y) / 2.)
center = ((self.width - self.margin_box.x) / 2.,
(self.height - self.margin_box.y) / 2.)
radius = min(center)
for i, val in enumerate(serie.values):

4
pygal/graph/radar.py

@ -60,8 +60,8 @@ class Radar(Line):
view_class = PolarView
self.view = view_class(
self.width - self.margin.x,
self.height - self.margin.y,
self.width - self.margin_box.x,
self.height - self.margin_box.y,
self._box)
def _x_axis(self, draw_axes=True):

4
pygal/graph/treemap.py

@ -118,8 +118,8 @@ class Treemap(Graph):
if total == 0:
return
gw = self.width - self.margin.x
gh = self.height - self.margin.y
gw = self.width - self.margin_box.x
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

3
pygal/state.py

@ -23,6 +23,7 @@ Class holding state during render
class State(object):
def __init__(self, graph):
def __init__(self, graph, **kwargs):
self.__dict__.update(**graph.config.__dict__)
self.__dict__.update(**graph.__dict__)
self.__dict__.update(**kwargs)

10
pygal/svg.py

@ -124,16 +124,16 @@ class Svg(object):
common_script = self.node(self.defs, 'script', type='text/javascript')
def get_js_dict():
return dict((k, getattr(self.graph, k)) for k in dir(self)
if not k.startswith('_') and
k in dir(self.graph.config))
return dict((k, getattr(self.graph.state, k))
for k in dir(self.graph.config)
if not k.startswith('_') and not hasattr(
getattr(self.graph.config, k), '__call__'))
def json_default(o):
if isinstance(o, (datetime, date)):
return o.isoformat()
if hasattr(o, 'to_dict'):
o = o.to_dict()
print(o)
return o.to_dict()
return json.JSONEncoder().default(o)
common_script.text = " = ".join(

45
pygal/table.py

@ -21,7 +21,6 @@ Table maker
"""
from pygal.graph.base import BaseGraph
from pygal.util import template
from lxml.html import builder, tostring
import uuid
@ -32,18 +31,17 @@ class HTML(object):
return getattr(builder, attr.upper())
class Table(BaseGraph):
class Table(object):
_dual = None
def __init__(self, chart, series, secondary_series, uuid, xml_filters):
def __init__(self, chart):
"Init the table"
self.uuid = uuid
self.series = series or []
self.secondary_series = secondary_series or []
self.xml_filters = xml_filters or []
self.__dict__.update(chart.state)
self.chart = chart
def render(self, total=False, transpose=False, style=False):
self.chart.setup()
ln = self.chart._len
fmt = self.chart._format
html = HTML()
attrs = {}
@ -54,22 +52,22 @@ class Table(BaseGraph):
_ = lambda x: x if x is not None else ''
if self.x_labels:
labels = [None] + list(self.x_labels)
if len(labels) < self._len:
labels += [None] * (self._len + 1 - len(labels))
if len(labels) > self._len + 1:
labels = labels[:self._len + 1]
if self.chart.x_labels:
labels = [None] + list(self.chart.x_labels)
if len(labels) < ln:
labels += [None] * (ln + 1 - len(labels))
if len(labels) > ln + 1:
labels = labels[:ln + 1]
table.append(labels)
if total:
if len(table):
table[0].append('Total')
else:
table.append([None] * (self._len + 1) + ['Total'])
acc = [0] * (self._len + 1)
table.append([None] * (ln + 1) + ['Total'])
acc = [0] * (ln + 1)
for i, serie in enumerate(self.series):
for i, serie in enumerate(self.chart.series):
row = [serie.title]
if total:
sum_ = 0
@ -77,18 +75,18 @@ class Table(BaseGraph):
if total:
acc[j] += value
sum_ += value
row.append(self._format(value))
row.append(fmt(value))
if total:
acc[-1] += sum_
row.append(self._format(sum_))
row.append(fmt(sum_))
table.append(row)
width = self._len + 1
width = ln + 1
if total:
width += 1
table.append(['Total'])
for val in acc:
table[-1].append(self._format(val))
table[-1].append(fmt(val))
# Align values
len_ = max([len(r) for r in table] or [0])
@ -105,7 +103,7 @@ class Table(BaseGraph):
tbody = []
tfoot = []
if not transpose or self.x_labels:
if not transpose or self.chart.x_labels:
# There's always series title but not always x_labels
thead = [table[0]]
tbody = table[1:]
@ -185,6 +183,7 @@ class Table(BaseGraph):
table = tostring(html.style(
template(css, **attrs),
scoped='scoped')) + table
if self.disable_xml_declaration:
if self.chart.disable_xml_declaration:
table = table.decode('utf-8')
self.chart.teardown()
return table

Loading…
Cancel
Save