Browse Source

Start working on a render_table method to generate a HTML table with the chart values.

pull/130/head
Florian Mounier 11 years ago
parent
commit
30a1660e4f
  1. 1
      CHANGELOG
  2. 52
      demo/moulinrouge/__init__.py
  3. 84
      demo/moulinrouge/static/js.js
  4. 55
      demo/moulinrouge/templates/index.jinja2
  5. 18
      demo/moulinrouge/templates/table.jinja2
  6. 7
      pygal/ghost.py
  7. 110
      pygal/table.py
  8. 32
      pygal/test/test_table.py

1
CHANGELOG

@ -1,6 +1,7 @@
V 1.5.0 UNRELEASED
Add per serie configuration
Add half pie (thanks philt2001)
Add render_table (WIP)
V 1.4.6
Add support for \n separated multiline titles (thanks sirlark)

52
demo/moulinrouge/__init__.py

@ -96,6 +96,13 @@ def create_app():
graph.add(title, values)
return graph.render_response()
@app.route("/table/<type>/<series>/<config>")
def table(type, series, config):
graph = getattr(pygal, type)(pickle.loads(b64decode(str(config))))
for title, values in pickle.loads(b64decode(str(series))):
graph.add(title, values)
return graph.render_table()
@app.route("/sparkline/<style>")
@app.route("/sparkline/parameric/<style>/<color>")
def sparkline(style, color=None):
@ -109,6 +116,51 @@ def create_app():
return Response(
line.render_sparkline(height=40), mimetype='image/svg+xml')
@app.route("/with/table/<type>")
def with_table(type):
chart = pygal.StackedBar(
disable_xml_declaration=True,
x_label_rotation=35)
chart.title = (
'What Linux distro do you primarily use'
' on your server computers? (Desktop'
' users vs Server Users)')
if type == 'series':
chart.add('Debian', [1775, 82])
chart.add('Ubuntu', [1515, 80])
chart.add('CentOS', [807, 60])
chart.add('Arch Linux', [549, 12])
chart.add('Red Hat Enterprise Linux', [247, 10])
chart.add('Gentoo', [129, 7])
chart.add('Fedora', [91, 6])
chart.add('Amazon Linux', [60, 0])
chart.add('OpenSUSE', [58, 0])
chart.add('Slackware', [50, 3])
chart.add('Xubuntu', [38, 1])
chart.add('Rasbian', [33, 4])
chart.add('SUSE Linux Enterprise Server', [33, 1])
chart.add('Linux Mint', [30, 4])
chart.add('Scientific Linux', [32, 0])
chart.add('Other', [187, 5])
elif type == 'labels':
chart.x_labels = [
'Debian', 'Ubuntu', 'CentOS', 'Arch Linux',
'Red Hat Enterprise Linux', 'Gentoo', 'Fedora', 'Amazon Linux',
'OpenSUSE', 'Slackware', 'Xubuntu', 'Rasbian',
'SUSE Linux Enterprise Server', 'Linux Mint',
'Scientific Linux', 'Other']
chart.add('Desktop Users', [
1775, 1515, 807, 549, 247, 129, 91, 60, 58, 50, 38, 33, 33,
30, 32, 187
])
chart.add('Server Users', [
82, 80, 60, 12, 10, 7, 6, 0, 0, 3, 1, 4, 1, 4, 0, 5
])
return render_template('table.jinja2', chart=chart)
@app.route("/all")
@app.route("/all/<style>")
@app.route("/all/<style>/<color>")

84
demo/moulinrouge/static/js.js

@ -1,24 +1,64 @@
$(function () {
$('figure figcaption').append(
$('<button>')
.text('⟳')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src,
type: 'image/svg+xml',
width: w,
height: h
})
);
})
);
$('figure figcaption').append(
$('<button>')
.text('⟳')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src,
type: 'image/svg+xml',
width: w,
height: h
})
);
}),
$('<button>')
.text('📄')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src.replace('/svg/', '/table/'),
type: 'text/html',
width: w,
height: h
})
);
}),
$('<button>')
.text('📈')
.click(function() {
var $fig, $embed, w, h, src;
$fig = $(this).closest('figure');
$embed = $fig.find('embed');
w = $embed.width();
h = $embed.height();
src = $embed.attr('src');
$embed.remove();
$fig.prepend(
$('<embed>')
.attr({
src: src.replace('/table/', '/svg/'),
type: 'text/html',
width: w,
height: h
})
);
})
);
});

55
demo/moulinrouge/templates/index.jinja2

@ -2,27 +2,14 @@
{% block section %}
<h1>Moulin Rouge</h1>
<ul>
<li>Styles</li>
{% for style in styles %}
<li>
<embed src="{{ url_for('sparkline', style=style) }}" type="image/svg+xml" />
<a href="{{ url_for('all', style=style) }}">{{ style | title }}</a>
</li>
{% endfor %}
<li>Parametric Styles</li>
{% for color in parametric_colors %}
<li><embed src="{{ url_for('sparkline', style='RotateStyle', color=color) }}" type="image/svg+xml" /></li>
{% for style in parametric_styles %}
<li>
<a href="{{ url_for('all', style=style, color=color) }}">{{ style }} for {{ color }}</a>
</li>
{% endfor %}
{% endfor %}
</ul>
<a href="{{ url_for('interpolation') }}">Interpolation</a>
<a href="{{ url_for('rotation') }}">Rotations test</a>
<a href="{{ url_for('raw_svgs') }}">Raw svgs</a>
{% for kind in ('series', 'labels') %}
<a href="{{ url_for('with_table', type=kind) }}">Chart with HTML table ({{ kind }})</a>
{% endfor %}
<hr />
<h3>Quick tests</h3>
<dl>
@ -30,14 +17,38 @@
{% if link.endswith('_for') %}
<dd>{{ link.replace('test_', '').replace('_for', '').replace('_', ' ') | title }}
<small>
{% for chart_name in charts_name %}
<a href="{{ url_for(link, chart=chart_name) }}">{{ chart_name | title }}</a>
{% endfor %}
{% for chart_name in charts_name %}
<a href="{{ url_for(link, chart=chart_name) }}">{{ chart_name | title }}</a>
{% endfor %}
</small>
</dd>
{% else %}
<dd><a href="{{ url_for(link) }}">{{ link.replace('test_', '').replace('_', ' ') | title }}</a></dd>
{% endif %}
{% endif %}
{% endfor %}
</dl>
<hr />
<h3>Styles</h3>
<ul>
{% for style in styles %}
<li>
<embed src="{{ url_for('sparkline', style=style) }}" type="image/svg+xml" />
<a href="{{ url_for('all', style=style) }}">{{ style | title }}</a>
</li>
{% endfor %}
</ul>
<hr />
<h3>Parametric Styles</h3>
<ul>
{% for color in parametric_colors %}
<li><embed src="{{ url_for('sparkline', style='RotateStyle', color=color) }}" type="image/svg+xml" /></li>
{% for style in parametric_styles %}
<li>
<a href="{{ url_for('all', style=style, color=color) }}">{{ style }} for {{ color }}</a>
</li>
{% endfor %}
{% endfor %}
</ul>
{% endblock section %}

18
demo/moulinrouge/templates/table.jinja2

@ -0,0 +1,18 @@
{% extends '_layout.jinja2' %}
{% block section %}
<h3>Normal</h3>
{{ chart.render_table() }}
<h3>Total</h3>
{{ chart.render_table(total=True) }}
<h3>Transposed</h3>
{{ chart.render_table(transpose=True) }}
<h3>Transposed + total</h3>
{{ chart.render_table(transpose=True, total=True) }}
<h3>Chart</h3>
{{ chart.render() }}
{% endblock section %}

7
pygal/ghost.py

@ -30,6 +30,7 @@ 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 pygal.table import Table
from uuid import uuid4
@ -113,6 +114,12 @@ class Ghost(object):
def render_tree(self):
return self.make_instance().render_tree()
def render_table(self, **kwargs):
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

110
pygal/table.py

@ -0,0 +1,110 @@
# -*- 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/>.
"""
Table maker
"""
from pygal.graph.base import BaseGraph
from lxml.html import builder, tostring
class HTML(object):
def __getattribute__(self, attr):
return getattr(builder, attr.upper())
class Table(BaseGraph):
_dual = None
def __init__(self, config, series, secondary_series, uuid, xml_filters):
"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(config.to_dict())
self.config = config
def render(self, total=False, transpose=False):
html = HTML()
table = []
_ = lambda x: x if x is not None else ''
table.append([None])
labels = []
if self.x_labels:
labels += self.x_labels
if len(labels) < self._len:
labels += [None] * (self._len - len(labels))
if len(labels) > self._len:
labels = labels[:self._len]
for label in labels:
table[0].append(label)
if total:
table[0].append('Total')
acc = [0] * (self._len + 1)
for i, serie in enumerate(self.series):
row = [serie.title]
if total:
sum_ = 0
for j, value in enumerate(serie.values):
if total:
acc[j] += value
sum_ += value
row.append(self._format(value))
if total:
acc[-1] += sum_
row.append(self._format(sum_))
table.append(row)
width = self._len + 1
if total:
width += 1
table.append(['Total'])
for val in acc:
table[-1].append(self._format(val))
# Align values
len_ = max([len(r) for r in table] or [0])
for i, row in enumerate(table[:]):
len_ = len(row)
if len_ < width:
table[i] = row + [None] * (width - len_)
if not transpose:
table = list(zip(*table))
table = tostring(
html.table(
html.tbody(
*[html.tr(
*[html.td(_(col)) for col in r]
) for r in table]
)
)
)
if self.disable_xml_declaration:
table = table.decode('utf-8')
return table

32
pygal/test/test_table.py

@ -0,0 +1,32 @@
# -*- 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/>.
from pygal import Pie
from pyquery import PyQuery as pq
def test_pie_table():
chart = Pie(inner_radius=.3, pretty_print=True)
chart.title = 'Browser usage in February 2012 (in %)'
chart.add('IE', 19.5)
chart.add('Firefox', 36.6)
chart.add('Chrome', 36.3)
chart.add('Safari', 4.5)
chart.add('Opera', 2.3)
q = pq(chart.render_table())
assert len(q('table')) == 1
Loading…
Cancel
Save