Browse Source

Work on cabaret

pull/8/head
Florian Mounier 12 years ago
parent
commit
9986362ec2
  1. 17
      demo/cabaret/__init__.py
  2. 6
      demo/cabaret/static/css.css
  3. 44
      demo/cabaret/static/js.js
  4. 4
      demo/cabaret/templates/_layout.jinja2
  5. 20
      demo/cabaret/templates/index.jinja2
  6. 14
      demo/moulinrouge/tests.py
  7. 266
      pygal/config.py
  8. 2
      pygal/css/style.css

17
demo/cabaret/__init__.py

@ -19,7 +19,7 @@
from flask import Flask, render_template, request
from pygal import CHARTS_BY_NAME
from pygal.graph import CHARTS_NAMES
from pygal.config import Config
from pygal.config import CONFIG_ITEMS
from pygal.style import styles
from json import loads
@ -31,17 +31,22 @@ def create_app():
@app.route("/")
def index():
configs = Config()._items
return render_template(
'index.jinja2', charts_names=CHARTS_NAMES, configs=dict(configs),
'index.jinja2', charts_names=CHARTS_NAMES, configs=CONFIG_ITEMS,
styles_names=styles.keys())
@app.route("/svg", methods=('POST',))
def svg():
values = request.values
chart = CHARTS_BY_NAME[values['type']](
disable_xml_declaration=True,
style=styles[values['style']], **loads(values['opts']))
config = loads(values['opts'])
config['disable_xml_declaration'] = True
config['style'] = styles[values['style']]
config['js'] = []
for item in CONFIG_ITEMS:
value = config.get(item.name, None)
if value:
config[item.name] = item.coerce(value)
chart = CHARTS_BY_NAME[values['type']](**config)
for title, vals in loads(values['vals']).items():
chart.add(title, vals)
return chart.render_response()

6
demo/cabaret/static/css.css

@ -7,12 +7,16 @@ body {
padding: 9px 0;
}
figure {
margin: 0;
}
figure svg {
width: 100%;
background: none;
}
* {
textarea {
-webkit-transition: all 500ms ease-out;
-moz-transition: all 500ms ease-out;
transition: all 500ms ease-out;

44
demo/cabaret/static/js.js

@ -8,6 +8,9 @@ function resend() {
$('.c-opts').each(function() {
var $this = $(this),
val = $this.val();
if($this.attr('type') == 'checkbox') {
val = $this.is(":checked");
}
if(val) {
opts[$this.attr('id').replace('c-', '')] = val;
}
@ -23,49 +26,22 @@ function resend() {
},
dataType: 'html'
}).done(function (data) {
$fig.find('svg').remove();
$fig.prepend(data);
// $fig.find('div').get(0).innerHTML = data;
$fig.find('div').html(data);
init_svg($fig.find('svg').get(0));
$('textarea').css({'-webkit-box-shadow': ''});
}).fail(function () {
$('textarea').css({'-webkit-box-shadow': 'inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6)'});
});
// $embed.remove();
// $fig.prepend(
// $('<embed>')
// .attr({
// src: src,
// type: 'image/svg+xml'
// })
// );
}
$(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
})
);
})
);
$('#type').on('change', resend);
$('#data').on('input', resend);
$('#style').on('change', resend);
$('.c-opts').on('input', resend);
$('.c-opts:not([type=checkbox])').on('input', resend);
$('.c-opts[type=checkbox]').on('change', resend);
$('label.tt').tooltip({ placement: 'right' });
$('input.tt').tooltip({ placement: 'top' });
resend();
});

4
demo/cabaret/templates/_layout.jinja2

@ -2,8 +2,10 @@
<html>
<head>
<title>Cabaret - Online pygal graph generator</title>
<script type="text/javascript" src="{{ url_for('static', filename='components/jquery/jquery.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='components/jquery/jquery.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='components/bootstrap/docs/assets/js/bootstrap.min.js') }}"></script>
<script type="text/javascript" src="https://raw.github.com/Kozea/pygal.js/master/svg.jquery.js"></script>
<script type="text/javascript" src="https://raw.github.com/Kozea/pygal.js/master/pygal-tooltips.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='components/bootstrap/docs/assets/css/bootstrap.css') }}" type="text/css" />
<link rel="stylesheet" href="{{ url_for('static', filename='components/bootstrap/docs/assets/css/bootstrap-responsive.css') }}" type="text/css" />

20
demo/cabaret/templates/index.jinja2

@ -27,11 +27,24 @@
</select>
</li>
<li class="divider"></li>
{% for key, value in configs.items() %}
{% for key in configs if key.name not in ['js', 'css'] %}
{% set doc = 'title="' + key.doc + ' ' + key.subdoc + '"' %}
<li>
<label class="nav-header" for="c-{{ key }}">{{ key }}</label>
<div class="controls">
<input type="text" id="c-{{ key }}" class="c-opts" placeholder="{{ value }}">
{% if key.is_boolean %}
<label class="nav-header checkbox tt" for="c-{{ key.name }}" {{ doc }}>
<input type="checkbox" id="c-{{ key.name }}" class="c-opts tt" {{ 'checked' if key.value }} {{ doc }} />{{ key.name }}
</label>
{% else %}
<label class="nav-header tt" for="c-{{ key.name }}" {{ doc }}> {{ key.name }}</label>
{% if key.is_numeric %}
<input type="number" id="c-{{ key.name }}" class="c-opts tt" value="{{ key.value or ''}}" {{ doc }} />
{% elif key.is_string %}
<input type="text" id="c-{{ key.name }}" class="c-opts tt" value="{{ key.value or ''}}" {{ doc }} />
{% elif key.is_list %}
<input type="text" id="c-{{ key.name }}" class="c-opts tt list-of-{{ key.subtype}}" placeholder="value1, value2, ..." value="{{ key.value or ''}}" {{ doc }} />
{% endif %}
{% endif %}
</div>
</li>
{% endfor %}
@ -40,6 +53,7 @@
{% block section %}
<figure>
<div></div>
<figcaption></figcaption>
</figure>
{% endblock section %}

14
demo/moulinrouge/tests.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
from pygal import Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, CHARTS_BY_NAME
from pygal import Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, CHARTS_BY_NAME, Config
from pygal.style import styles
@ -111,4 +111,16 @@ def get_test_routes(app):
stacked.add('2', [4, 5, 6])
return stacked.render_response()
@app.route('/test/config')
def test_config():
class LolConfig(Config):
js = ['http://l:2343/svg.jquery.js',
'http://l:2343/pygal-tooltips.js']
stacked = StackedBar(LolConfig())
stacked.add('1', [1, 2, 3])
stacked.add('2', [4, 5, 6])
return stacked.render_response()
return filter(lambda x: x.startswith('test'), locals())

266
pygal/config.py

@ -21,101 +21,184 @@
Config module with all options
"""
from copy import deepcopy
from pygal.style import DefaultStyle
from pygal.style import Style, DefaultStyle
class FontSizes(object):
"""Container for font sizes"""
CONFIG_ITEMS = []
class Key(object):
def __init__(self, default_value, type_, doc, subdoc="", subtype=None):
self.value = default_value
self.type = type_
self.doc = doc
self.subdoc = subdoc
self.subtype = subtype
self.name = "Unbound"
CONFIG_ITEMS.append(self)
@property
def is_boolean(self):
return self.type == bool
@property
def is_numeric(self):
return self.type == int
@property
def is_string(self):
return self.type == str
@property
def is_list(self):
return self.type == list
def coerce(self, value):
if self.type == Style:
return value
elif self.type == list:
return self.type(
map(
self.subtype, map(
lambda x: x.strip(), value.split(','))))
return self.type(value)
class MetaConfig(type):
def __new__(mcs, classname, bases, classdict):
for k, v in classdict.items():
if isinstance(v, Key):
v.name = k
return type.__new__(mcs, classname, bases, classdict)
class Config(object):
"""Class holding config values"""
#: Graph width
width = 800
#: Graph height
height = 600
#: Display values in human readable format (ie: 12.4M)
human_readable = False
#: Display values in logarithmic scale
logarithmic = False
#: Minimum order of scale, defaults to None
order_min = None
#: List of css file, can be an absolute file path or an external link
css = ('style.css', 'graph.css') # Relative path to pygal css
#: List of js file, can be a filepath or an external link
js = (
'https://raw.github.com/Kozea/pygal.js/master/svg.jquery.js',
'https://raw.github.com/Kozea/pygal.js/master/pygal-tooltips.js'
)
#: Style holding values injected in css
style = DefaultStyle
#: Various font sizes
label_font_size = 10
value_font_size = 8
tooltip_font_size = 20
title_font_size = 16
legend_font_size = 14
#: Specify labels rotation angles in degrees
x_label_rotation = 0
y_label_rotation = 0
#: Set to false to remove legend
show_legend = True
#: Set to true to position legend at bottom
legend_at_bottom = False
#: Set to false to remove dots
show_dots = True
#: Size of legend boxes
legend_box_size = 12
#: X labels, must have same len than data.
#: Leave it to None to disable x labels display.
x_labels = None
#: You can specify explicit y labels (must be list(int))
y_labels = None
#: Graph title
#: Leave it to None to disable title.
title = None
#: Set this to the desired radius in px
rounded_bars = False
#: Always include x axis
include_x_axis = False
#: Fill areas under lines
fill = False
#: Line dots (set it to false to get a scatter plot)
stroke = True
#: Interpolation, this requires scipy module
#: May be any of 'linear', 'nearest', 'zero', 'slinear', 'quadratic,
#: 'cubic', 'krogh', 'barycentric', 'univariate',
#: or an integer specifying the order
#: of the spline interpolator
interpolate = None
#: Number of interpolated points between two values
interpolation_precision = 250
#: Explicitly specify min and max of values (ie: (0, 100))
range = None
#: Set the ordinate zero value (for filling)
zero = 0
#: Text to display when no data is given
no_data_text = "No data"
#: Print values when graph is in non interactive mode
print_values = True
#: Print zeroes when graph is in non interactive mode
print_zeroes = False
#: Animate tooltip steps (0 disable animation)
animation_steps = 0
#: Don't write xml declaration and return unicode instead of string
#: (usefull for writing output in html)
disable_xml_declaration = False
#: Write width and height attributes
explicit_size = False
#: Legend string length truncation threshold (None = auto)
truncate_legend = None
#: Label string length truncation threshold (None = auto)
truncate_label = None
#: Pretty print the svg
pretty_print = False
#: If True don't try to adapt / filter wrong values
strict = False
__metaclass__ = MetaConfig
width = Key(800, int, "Graph width")
height = Key(600, int, "Graph height")
human_readable = Key(
False, bool, "Display values in human readable format",
"(ie: 12.4M)")
logarithmic = Key(False, bool, "Display values in logarithmic scale")
order_min = Key(None, int, "Minimum order of scale, defaults to None")
css = Key(
('style.css', 'graph.css'), list,
"List of css file",
"It can be an absolute file path or an external link",
str)
js = Key(
('https://raw.github.com/Kozea/pygal.js/master/svg.jquery.js',
'https://raw.github.com/Kozea/pygal.js/master/pygal-tooltips.js'),
list, "List of js file",
"It can be a filepath or an external link",
str)
style = Key(DefaultStyle, Style, "Style holding values injected in css")
label_font_size = Key(10, int, "Label font size")
value_font_size = Key(8, int, "Value font size")
tooltip_font_size = Key(20, int, "Tooltip font size")
title_font_size = Key(16, int, "Title font size")
legend_font_size = Key(14, int, "Legend font size")
x_label_rotation = Key(
0, int, "Specify x labels rotation angles", "in degrees")
y_label_rotation = Key(
0, int, "Specify y labels rotation angles", "in degrees")
show_legend = Key(True, bool, "Set to false to remove legend")
legend_at_bottom = Key(
False, bool, "Set to true to position legend at bottom")
show_dots = Key(True, bool, "Set to false to remove dots")
legend_box_size = Key(12, int, "Size of legend boxes")
x_labels = Key(
None, list,
"X labels, must have same len than data.",
"Leave it to None to disable x labels display.",
str)
y_labels = Key(
None, list,
"You can specify explicit y labels",
"(must be list(int))", int)
title = Key(
None, str, "Graph title.", "Leave it to None to disable title.")
rounded_bars = Key(False, bool, "Set this to the desired radius in px")
include_x_axis = Key(False, bool, "Always include x axis")
fill = Key(False, bool, "Fill areas under lines")
stroke = Key(
True, bool, "Line dots (set it to false to get a scatter plot)")
interpolate = Key(
None, str, "Interpolation, this requires scipy module",
"May be any of 'linear', 'nearest', 'zero', 'slinear', 'quadratic,"
"'cubic', 'krogh', 'barycentric', 'univariate',"
"or an integer specifying the order"
"of the spline interpolator")
interpolation_precision = Key(
250, int, "Number of interpolated points between two values")
range = Key(
None, list, "Explicitly specify min and max of values",
"(ie: (0, 100))", int)
zero = Key(
0, int, "Set the ordinate zero value", "(for filling)")
no_data_text = Key(
"No data", str, "Text to display when no data is given")
print_values = Key(
True, bool, "Print values when graph is in non interactive mode")
print_zeroes = Key(
False, bool, "Print zeroes when graph is in non interactive mode")
disable_xml_declaration = Key(
False, bool,
"Don't write xml declaration and return str instead of string",
"usefull for writing output directly in html")
explicit_size = Key(False, bool, "Write width and height attributes")
truncate_legend = Key(
None, int, "Legend string length truncation threshold (None = auto)")
truncate_label = Key(
None, int, "Label string length truncation threshold (None = auto)")
pretty_print = Key(False, bool, "Pretty print the svg")
strict = Key(
False, bool, "If True don't try to adapt / filter wrong values")
def __init__(self, **kwargs):
"""Can be instanciated with config kwargs"""
@ -124,6 +207,8 @@ class Config(object):
if (k not in self.__dict__ and not
k.startswith('_') and not
hasattr(v, '__call__')):
if isinstance(v, Key):
v = v.value
setattr(self, k, v)
self.css = list(self.css)
@ -158,16 +243,9 @@ class Config(object):
value = getattr(self, attr)
if hasattr(value, 'to_dict'):
config[attr] = value.to_dict()
elif attr != '_items' and not hasattr(value, '__call__'):
elif not hasattr(value, '__call__'):
config[attr] = value
return config
def copy(self):
return deepcopy(self)
@property
def _items(self):
return filter(
lambda x:
not x[0].startswith('_') and not hasattr(x[1], '__call__'),
[(k, getattr(self, k)) for k in dir(self) if k != '_items'])

2
pygal/css/style.css

@ -26,7 +26,7 @@ svg {
background-color: {{ style.background }};
}
svg * {
svg path, svg line, svg rect, svg circle {
-webkit-transition: {{ style.transition }};
-moz-transition: {{ style.transition }};
transition: {{ style.transition }};

Loading…
Cancel
Save