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 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 V 1.7.0
Remove DateY and replace it by real XY datetime, date, time and timedelta support. (#188) 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) not_zero, positive, decimal_to_float)
from functools import reduce from functools import reduce
from uuid import uuid4 from uuid import uuid4
import io
class BaseGraph(object): class BaseGraph(object):
@ -86,8 +87,6 @@ class BaseGraph(object):
def prepare_values(self, raw, offset=0): def prepare_values(self, raw, offset=0):
"""Prepare the values to start with sane values""" """Prepare the values to start with sane values"""
from pygal import Worldmap, FrenchMapDepartments, Histogram 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( if self.zero == 0 and isinstance(
self, (Worldmap, FrenchMapDepartments)): self, (Worldmap, FrenchMapDepartments)):
self.zero = 1 self.zero = 1
@ -169,17 +168,22 @@ class BaseGraph(object):
values.append(value) values.append(value)
serie_config = SerieConfig() serie_config = SerieConfig()
serie_config(**{k: v for k, v in self.state.__dict__.items() serie_config(**dict((k, v) for k, v in self.state.__dict__.items()
if k in dir(serie_config)}) if k in dir(serie_config)))
serie_config(**serie_config_kwargs) serie_config(**serie_config_kwargs)
series.append( series.append(
Serie(offset + len(series), Serie(offset + len(series),
title, values, serie_config, metadata)) title, values, serie_config, metadata))
return series return series
def setup(self): def setup(self, **kwargs):
"""Init the graph""" """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.series = self.prepare_values(
self.raw_series) or [] self.raw_series) or []
self.secondary_series = self.prepare_values( self.secondary_series = self.prepare_values(
@ -191,7 +195,7 @@ class BaseGraph(object):
self._x_2nd_labels = None self._x_2nd_labels = None
self._y_2nd_labels = None self._y_2nd_labels = None
self.nodes = {} self.nodes = {}
self.margin = Margin( self.margin_box = Margin(
self.margin_top or self.margin, self.margin_top or self.margin,
self.margin_right or self.margin, self.margin_right or self.margin,
self.margin_bottom or self.margin, self.margin_bottom or self.margin,
@ -217,7 +221,7 @@ class BaseGraph(object):
def render(self, is_unicode=False, **kwargs): def render(self, is_unicode=False, **kwargs):
"""Render the graph, and return the svg string""" """Render the graph, and return the svg string"""
self.setup() self.setup(**kwargs)
svg = self.svg.render( svg = self.svg.render(
is_unicode=is_unicode, pretty_print=self.pretty_print) is_unicode=is_unicode, pretty_print=self.pretty_print)
self.teardown() self.teardown()
@ -225,9 +229,11 @@ class BaseGraph(object):
def render_tree(self): def render_tree(self):
"""Render the graph, and return (l)xml etree""" """Render the graph, and return (l)xml etree"""
self.setup()
svg = self.svg.root svg = self.svg.root
for f in self.xml_filters: for f in self.xml_filters:
svg = f(svg) svg = f(svg)
self.teardown()
return svg return svg
def render_table(self, **kwargs): def render_table(self, **kwargs):
@ -264,7 +270,7 @@ class BaseGraph(object):
def render_to_file(self, filename, **kwargs): def render_to_file(self, filename, **kwargs):
"""Render the graph, and write it to filename""" """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)) f.write(self.render(is_unicode=True, **kwargs))
def render_to_png(self, filename=None, dpi=72, **kwargs): def render_to_png(self, filename=None, dpi=72, **kwargs):
@ -312,7 +318,7 @@ class BaseGraph(object):
explicit_size=True explicit_size=True
) )
spark_options.update(kwargs) spark_options.update(kwargs)
return self.make_instance(spark_options).render() return self.render(**spark_options)
def _repr_svg_(self): def _repr_svg_(self):
"""Display svg in IPython notebook""" """Display svg in IPython notebook"""
@ -320,3 +326,4 @@ class BaseGraph(object):
def _repr_png_(self): def _repr_png_(self):
"""Display png in IPython notebook""" """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 view_class = PolarThetaView
self.view = view_class( self.view = view_class(
self.width - self.margin.x, self.width - self.margin_box.x,
self.height - self.margin.y, self.height - self.margin_box.y,
self._box) self._box)
def needle(self, serie): def needle(self, serie):

56
pygal/graph/graph.py

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

4
pygal/graph/horizontal.py

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

8
pygal/graph/pie.py

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

4
pygal/graph/radar.py

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

4
pygal/graph/treemap.py

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

3
pygal/state.py

@ -23,6 +23,7 @@ Class holding state during render
class State(object): class State(object):
def __init__(self, graph): def __init__(self, graph, **kwargs):
self.__dict__.update(**graph.config.__dict__) self.__dict__.update(**graph.config.__dict__)
self.__dict__.update(**graph.__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') common_script = self.node(self.defs, 'script', type='text/javascript')
def get_js_dict(): def get_js_dict():
return dict((k, getattr(self.graph, k)) for k in dir(self) return dict((k, getattr(self.graph.state, k))
if not k.startswith('_') and for k in dir(self.graph.config)
k in dir(self.graph.config)) if not k.startswith('_') and not hasattr(
getattr(self.graph.config, k), '__call__'))
def json_default(o): def json_default(o):
if isinstance(o, (datetime, date)): if isinstance(o, (datetime, date)):
return o.isoformat() return o.isoformat()
if hasattr(o, 'to_dict'): if hasattr(o, 'to_dict'):
o = o.to_dict() return o.to_dict()
print(o)
return json.JSONEncoder().default(o) return json.JSONEncoder().default(o)
common_script.text = " = ".join( 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 pygal.util import template
from lxml.html import builder, tostring from lxml.html import builder, tostring
import uuid import uuid
@ -32,18 +31,17 @@ class HTML(object):
return getattr(builder, attr.upper()) return getattr(builder, attr.upper())
class Table(BaseGraph): class Table(object):
_dual = None _dual = None
def __init__(self, chart, series, secondary_series, uuid, xml_filters): def __init__(self, chart):
"Init the table" "Init the table"
self.uuid = uuid self.chart = chart
self.series = series or []
self.secondary_series = secondary_series or []
self.xml_filters = xml_filters or []
self.__dict__.update(chart.state)
def render(self, total=False, transpose=False, style=False): def render(self, total=False, transpose=False, style=False):
self.chart.setup()
ln = self.chart._len
fmt = self.chart._format
html = HTML() html = HTML()
attrs = {} attrs = {}
@ -54,22 +52,22 @@ class Table(BaseGraph):
_ = lambda x: x if x is not None else '' _ = lambda x: x if x is not None else ''
if self.x_labels: if self.chart.x_labels:
labels = [None] + list(self.x_labels) labels = [None] + list(self.chart.x_labels)
if len(labels) < self._len: if len(labels) < ln:
labels += [None] * (self._len + 1 - len(labels)) labels += [None] * (ln + 1 - len(labels))
if len(labels) > self._len + 1: if len(labels) > ln + 1:
labels = labels[:self._len + 1] labels = labels[:ln + 1]
table.append(labels) table.append(labels)
if total: if total:
if len(table): if len(table):
table[0].append('Total') table[0].append('Total')
else: else:
table.append([None] * (self._len + 1) + ['Total']) table.append([None] * (ln + 1) + ['Total'])
acc = [0] * (self._len + 1) acc = [0] * (ln + 1)
for i, serie in enumerate(self.series): for i, serie in enumerate(self.chart.series):
row = [serie.title] row = [serie.title]
if total: if total:
sum_ = 0 sum_ = 0
@ -77,18 +75,18 @@ class Table(BaseGraph):
if total: if total:
acc[j] += value acc[j] += value
sum_ += value sum_ += value
row.append(self._format(value)) row.append(fmt(value))
if total: if total:
acc[-1] += sum_ acc[-1] += sum_
row.append(self._format(sum_)) row.append(fmt(sum_))
table.append(row) table.append(row)
width = self._len + 1 width = ln + 1
if total: if total:
width += 1 width += 1
table.append(['Total']) table.append(['Total'])
for val in acc: for val in acc:
table[-1].append(self._format(val)) table[-1].append(fmt(val))
# Align values # Align values
len_ = max([len(r) for r in table] or [0]) len_ = max([len(r) for r in table] or [0])
@ -105,7 +103,7 @@ class Table(BaseGraph):
tbody = [] tbody = []
tfoot = [] 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 # There's always series title but not always x_labels
thead = [table[0]] thead = [table[0]]
tbody = table[1:] tbody = table[1:]
@ -185,6 +183,7 @@ class Table(BaseGraph):
table = tostring(html.style( table = tostring(html.style(
template(css, **attrs), template(css, **attrs),
scoped='scoped')) + table scoped='scoped')) + table
if self.disable_xml_declaration: if self.chart.disable_xml_declaration:
table = table.decode('utf-8') table = table.decode('utf-8')
self.chart.teardown()
return table return table

Loading…
Cancel
Save