Browse Source

Fix a lot of label related things

pull/264/head
Florian Mounier 10 years ago
parent
commit
494d46613a
  1. 2
      demo/moulinrouge/__init__.py
  2. 8
      demo/moulinrouge/tests.py
  3. 7
      docs/api/pygal.adapters.rst
  4. 3
      pygal/adapters.py
  5. 9
      pygal/graph/bar.py
  6. 2
      pygal/graph/base.py
  7. 22
      pygal/graph/box.py
  8. 8
      pygal/graph/dot.py
  9. 4
      pygal/graph/dual.py
  10. 6
      pygal/graph/funnel.py
  11. 7
      pygal/graph/gauge.py
  12. 15
      pygal/graph/graph.py
  13. 9
      pygal/graph/histogram.py
  14. 6
      pygal/graph/horizontal.py
  15. 7
      pygal/graph/line.py
  16. 2
      pygal/graph/map.py
  17. 4
      pygal/graph/pie.py
  18. 12
      pygal/graph/radar.py
  19. 6
      pygal/graph/stackedbar.py
  20. 4
      pygal/graph/time.py
  21. 15
      pygal/graph/treemap.py
  22. 8
      pygal/svg.py

2
demo/moulinrouge/__init__.py

@ -213,7 +213,7 @@ def create_app():
else:
config.x_labels = [random_label() for i in range(data)]
svgs.append({'type': type,
'series': xy_series if type == 'XY' else other_series,
'series': xy_series if chart._dual else other_series,
'config': b64encode(pickle.dumps(config))})
return render_template('svgs.jinja2',

8
demo/moulinrouge/tests.py

@ -376,8 +376,8 @@ def get_test_routes(app):
chart.x_label_rotation = 25
chart.y_label_rotation = 50
chart.add('1', [30, 20, -2])
chart.add(10 * '1b', [-4, 50, 6], secondary=True)
chart.add(10 * '2b', [None, 10, 20], secondary=True)
chart.add('1b', [-4, 50, 6], secondary=True)
chart.add('2b', [None, 10, 20], secondary=True)
chart.add('2', [8, 21, -0])
chart.add('3', [None, 20, 10])
chart.add('3b', [-1, 2, -3], secondary=True)
@ -395,6 +395,8 @@ def get_test_routes(app):
@app.route('/test/box')
def test_box():
chart = Box()
chart.js = ('http://l:2343/2.0.x/pygal-tooltips.js',)
chart.box_mode = '1.5IQR'
chart.add('One', [15, 8, 2, -12, 9, 23])
chart.add('Two', [5, 8, 2, -9, 23, 12])
chart.add('Three', [8, -2, 12, -5, 9, 3])
@ -408,6 +410,7 @@ def get_test_routes(app):
stacked = StackedLine(stack_from_top=True, logarithmic=True)
stacked.add('1', [1, 2])
stacked.add('2', [10, 12])
stacked.x_labels = ['a', 'b', 'c', 'd']
return stacked.render_response()
@app.route('/test/stacked/reverse')
@ -415,7 +418,6 @@ def get_test_routes(app):
stacked = StackedBar(stack_from_top=True)
stacked.add('1', [1, 2, 3])
stacked.add('2', [4, 5, 6])
stacked.x_labels = ['a', 'b', 'c']
return stacked.render_response()
@app.route('/test/show_dots')

7
docs/api/pygal.adapters.rst

@ -1,7 +0,0 @@
pygal.adapters module
=====================
.. automodule:: pygal.adapters
:members:
:undoc-members:
:show-inheritance:

3
pygal/adapters.py

@ -18,12 +18,15 @@
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""Value adapters to use when a chart doesn't accept all value types"""
from decimal import Decimal
from pygal._compat import is_str
def positive(x):
"""Return zero if value is negative"""
if x is None:
return
if is_str(x):
return x
if x < 0:
return 0
return x

9
pygal/graph/bar.py

@ -88,7 +88,8 @@ class Bar(Graph):
x_center, y_center = self._bar(
serie, bar, x, y, i, self.zero, secondary=rescale)
self._tooltip_data(
bar, val, x_center, y_center, classes="centered")
bar, val, x_center, y_center, "centered",
self._get_x_label(i))
self._static_value(serie_node, val, x_center, y_center)
def _compute(self):
@ -98,11 +99,13 @@ class Bar(Graph):
if self._max:
self._box.ymax = max(self._max, self.zero)
x_pos = [
self._x_pos = [
x / self._len for x in range(self._len + 1)
] if self._len > 1 else [0, 1] # Center if only one value
self._points(x_pos)
self._points(self._x_pos)
self._x_pos = [(i + .5) / self._len for i in range(self._len)]
def _compute_secondary(self):
"""Compute parameters for secondary series rendering"""

2
pygal/graph/base.py

@ -149,6 +149,8 @@ class BaseGraph(object):
value = (None, None, None)
elif not is_list_like(value):
value = (value, self.zero, self.zero)
elif len(value) == 2:
value = (1, value[0], value[1])
value = list(map(adapter, value))
elif self._dual:
if value is None:

22
pygal/graph/box.py

@ -50,14 +50,16 @@ class Box(Graph):
def format_maybe_quartile(x):
if is_list_like(x):
if self.box_mode == "extremes":
return 'Min: %s Q1: %s Q2: %s Q3: %s Max: %s' \
% tuple(map(sup, x[1:6]))
return (
'Min: %s\nQ1 : %s\nQ2 : %s\nQ3 : %s\nMax: %s' %
tuple(map(sup, x[1:6])))
elif self.box_mode in ["tukey", "stdev", "pstdev"]:
return 'Min: %s Lower Whisker: %s Q1: %s Q2: %s Q3: %s '\
'Upper Whisker: %s Max: %s' % tuple(map(sup, x))
return (
'Min: %s\nLower Whisker: %s\nQ1: %s\nQ2: %s\nQ3: %s\n'
'Upper Whisker: %s\nMax: %s' % tuple(map(sup, x)))
elif self.box_mode == '1.5IQR':
# 1.5IQR mode
return 'Q1: %s Q2: %s Q3: %s' % tuple(map(sup, x[2:5]))
return 'Q1: %s\nQ2: %s\nQ3: %s' % tuple(map(sup, x[2:5]))
else:
return sup(x)
return format_maybe_quartile
@ -71,15 +73,14 @@ class Box(Graph):
serie.values, serie.outliers = \
self._box_points(serie.values, self.box_mode)
self._x_pos = [
(i + .5) / self._order for i in range(self._order)]
if self._min:
self._box.ymin = min(self._min, self.zero)
if self._max:
self._box.ymax = max(self._max, self.zero)
def _compute_x_labels(self):
self._x_labels = self.x_labels and list(zip(self.x_labels, [
(i + .5) / self._order for i in range(self._order)]))
def _plot(self):
"""Plot the series data"""
for serie in self.series:
@ -108,7 +109,8 @@ class Box(Graph):
x_center, y_center = self._draw_box(
box, serie.values[1:6], serie.outliers, serie.index, metadata)
self._tooltip_data(box, val, x_center, y_center, classes="centered")
self._tooltip_data(box, val, x_center, y_center, "centered",
self._get_x_label(serie.index))
self._static_value(serie_node, val, x_center, y_center)
def _draw_box(self, parent_node, quartiles, outliers, box_index, metadata):

8
pygal/graph/dot.py

@ -68,7 +68,9 @@ class Dot(Graph):
' negative' if value < 0 else '')), metadata)
value = self._format(value)
self._tooltip_data(dots, value, x, y, classes='centered')
self._tooltip_data(
dots, value, x, y, 'centered',
self._get_x_label(i))
self._static_value(serie_node, value, x, y)
def _compute(self):
@ -86,10 +88,6 @@ class Dot(Graph):
(self._x_pos[i], self._y_pos[j])
for i in range(x_len)]
def _compute_x_labels(self):
self._x_labels = self.x_labels and list(
zip(self.x_labels, self._x_pos))
def _compute_y_labels(self):
self._y_labels = list(zip(
self.y_labels or [

4
pygal/graph/dual.py

@ -51,3 +51,7 @@ class Dual(Graph):
else:
self._x_labels = list(zip(map(self._x_format, x_pos), x_pos))
def _get_x_label(self, i):
"""Convenience function to get the x_label of a value index"""
return

6
pygal/graph/funnel.py

@ -53,11 +53,13 @@ class Funnel(Graph):
points=' '.join(map(fmt, map(self.view, poly))),
class_='funnel reactive tooltip-trigger'), metadata)
x, y = self.view((
# Poly center from label
x, y = self.view((
self._center(self._x_pos[serie.index]),
sum([point[1] for point in poly]) / len(poly)))
self._tooltip_data(funnels, value, x, y, classes='centered')
self._tooltip_data(
funnels, value, x, y, 'centered',
self._get_x_label(serie.index))
self._static_value(serie_node, value, x, y)
def _center(self, x):

7
pygal/graph/gauge.py

@ -86,7 +86,9 @@ class Gauge(Graph):
metadata)
x, y = self.view((.75, theta))
self._tooltip_data(gauges, value, x, y)
self._tooltip_data(
gauges, value, x, y,
xlabel=self._get_x_label(i))
self._static_value(serie_node, value, x, y)
def _y_axis(self, draw_axes=True):
@ -138,6 +140,9 @@ class Gauge(Graph):
self.min_,
self.max_)
def _compute_x_labels(self):
pass
def _compute_y_labels(self):
y_pos = compute_scale(
self.min_, self.max_, self.logarithmic,

15
pygal/graph/graph.py

@ -429,7 +429,7 @@ class Graph(PublicApi):
if y is not None else None)
for x, y in points]
def _tooltip_data(self, node, value, x, y, classes=None):
def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None):
"""Insert in desc tags informations for the javascript tooltip"""
self.svg.node(node, 'desc', class_="value").text = value
if classes is None:
@ -444,6 +444,9 @@ class Graph(PublicApi):
class_="x " + classes).text = str(x)
self.svg.node(node, 'desc',
class_="y " + classes).text = str(y)
if xlabel:
self.svg.node(node, 'desc',
class_="x_label").text = str(xlabel)
def _static_value(self, serie_node, value, x, y):
"""Write the print value"""
@ -499,6 +502,12 @@ class Graph(PublicApi):
"""Hook called after compute and before margin computations and plot"""
pass
def _get_x_label(self, i):
"""Convenience function to get the x_label of a value index"""
if not self.x_labels or not self._x_labels or len(self._x_labels) <= i:
return
return self._x_labels[i][0]
@property
def all_series(self):
"""Getter for all series (nomal and secondary)"""
@ -716,8 +725,8 @@ class Graph(PublicApi):
)
def _compute_x_labels(self):
self._x_labels = self.x_labels and list(zip(self.x_labels, [
(i + .5) / self._len for i in range(self._len)]))
self._x_labels = self.x_labels and list(
zip(self.x_labels, self._x_pos))
def _compute_y_labels(self):
y_pos = compute_scale(

9
pygal/graph/histogram.py

@ -22,16 +22,14 @@ as bars of varying width.
"""
from __future__ import division
from pygal._compat import is_list_like
from pygal.graph.dual import Dual
from pygal.util import (
swap, ident, compute_scale, decorate, cached_property, alter)
from pygal.util import alter, cached_property, decorate, ident, swap
class Histogram(Dual):
"""Histogram chart class"""
_series_margin = 0
@cached_property
@ -97,7 +95,8 @@ class Histogram(Dual):
x_center, y_center = self._bar(
serie, bar, x0, x1, y, i, self.zero, secondary=rescale)
self._tooltip_data(
bar, val, x_center, y_center, classes="centered")
bar, val, x_center, y_center, "centered",
self._get_x_label(i))
self._static_value(serie_node, val, x_center, y_center)
def _compute(self):

6
pygal/graph/horizontal.py

@ -53,3 +53,9 @@ class HorizontalGraph(Graph):
self.width - self.margin_box.x,
self.height - self.margin_box.y,
self._box)
def _get_x_label(self, i):
"""Convenience function to get the x_label of a value index"""
if not self.x_labels or not self._y_labels or len(self._y_labels) <= i:
return
return self._y_labels[i][0]

7
pygal/graph/line.py

@ -120,7 +120,8 @@ class Line(Graph):
dots, 'circle', cx=x, cy=y, r=serie.dots_size,
class_='dot reactive tooltip-trigger'), metadata)
self._tooltip_data(
dots, val, x, y)
dots, val, x, y,
xlabel=self._get_x_label(i))
self._static_value(
serie_node, val,
x + self.style.value_font_size,
@ -141,11 +142,11 @@ class Line(Graph):
def _compute(self):
"""Compute y min and max and y scale and set labels"""
# X Labels
x_pos = [
self._x_pos = [
x / (self._len - 1) for x in range(self._len)
] if self._len != 1 else [.5] # Center if only one value
self._points(x_pos)
self._points(self._x_pos)
if self.include_x_axis:
# Y Label

2
pygal/graph/map.py

@ -121,7 +121,7 @@ class BaseMap(Graph):
alter(node, metadata)
value = self._get_value((area_code, value))
self._tooltip_data(area, value, 0, 0, classes='auto')
self._tooltip_data(area, value, 0, 0, 'auto')
self.nodes['plot'].append(map)

4
pygal/graph/pie.py

@ -82,7 +82,7 @@ class Pie(Graph):
alter(self.svg.slice(
serie_node, slice_, big_radius, small_radius,
angle, start_angle, center, val), metadata)
angle, start_angle, center, val, i), metadata)
start_angle += angle
total_perc += perc
@ -91,7 +91,7 @@ class Pie(Graph):
self.svg.slice(serie_node,
self.svg.node(slices, class_="big_slice"),
radius * .9, 0, serie_angle,
original_start_angle, center, val)
original_start_angle, center, val, i)
return serie_angle
def _compute_x_labels(self):

12
pygal/graph/radar.py

@ -39,7 +39,6 @@ class Radar(Line):
def __init__(self, *args, **kwargs):
"""Init custom vars"""
self._x_pos = None
self._rmax = None
super(Radar, self).__init__(*args, **kwargs)
@ -168,14 +167,14 @@ class Radar(Line):
def _compute(self):
"""Compute r min max and labels position"""
delta = 2 * pi / self._len if self._len else 0
x_pos = [.5 * pi + i * delta for i in range(self._len + 1)]
self._x_pos = [.5 * pi + i * delta for i in range(self._len + 1)]
for serie in self.all_series:
serie.points = [
(v, x_pos[i])
(v, self._x_pos[i])
for i, v in enumerate(serie.values)]
if self.interpolate:
extended_x_pos = (
[.5 * pi - delta] + x_pos)
[.5 * pi - delta] + self._x_pos)
extended_vals = (serie.values[-1:] +
serie.values)
serie.interpolated = list(
@ -190,11 +189,6 @@ class Radar(Line):
self._rmax = self._max or 1
self._box.set_polar_box(self._rmin, self._rmax)
self._self_close = True
self._x_pos = x_pos
def _compute_x_labels(self):
self._x_labels = self.x_labels and list(
zip(self.x_labels, self._x_pos))
def _compute_y_labels(self):
y_pos = compute_scale(

6
pygal/graph/stackedbar.py

@ -65,11 +65,11 @@ class StackedBar(Bar):
positive_vals = positive_vals or [self.zero]
negative_vals = negative_vals or [self.zero]
x_pos = [
self._x_pos = [
x / self._len for x in range(self._len + 1)
] if self._len > 1 else [0, 1] # Center if only one value
self._points(x_pos)
self._points(self._x_pos)
self.negative_cumulation = [0] * self._len
self.positive_cumulation = [0] * self._len
@ -82,6 +82,8 @@ class StackedBar(Bar):
self.secondary_positive_cumulation = [0] * self._len
self._pre_compute_secondary(positive_vals, negative_vals)
self._x_pos = [(i + .5) / self._len for i in range(self._len)]
def _pre_compute_secondary(self, positive_vals, negative_vals):
"""Compute secondary y min and max"""
self._secondary_min = (negative_vals and min(

4
pygal/graph/time.py

@ -25,7 +25,7 @@ into float for xy plot and back to their type for display
from pygal.adapters import positive
from pygal.graph.xy import XY
from datetime import datetime, date, time, timedelta
from pygal._compat import timestamp, total_seconds
from pygal._compat import timestamp, total_seconds, is_str
def datetime_to_timestamp(x):
@ -70,6 +70,8 @@ def time_to_seconds(x):
((x.hour * 60) + x.minute) * 60 + x.second
) * 10 ** 6 + x.microsecond) / 10 ** 6
if is_str(x):
return x
# Clamp to valid time
return x and max(0, min(x, 24 * 3600 - 10 ** -6))

15
pygal/graph/treemap.py

@ -55,11 +55,14 @@ class Treemap(Graph):
class_='rect reactive tooltip-trigger'),
metadata)
self._tooltip_data(rect, value,
self._tooltip_data(
rect, value,
rx + rw / 2,
ry + rh / 2,
classes='centered')
self._static_value(serie_node, value,
'centered',
self._get_x_label(i))
self._static_value(
serie_node, value,
rx + rw / 2,
ry + rh / 2)
@ -115,6 +118,12 @@ class Treemap(Graph):
self._binary_tree(
half2, half2_sum, x + x_pivot, y, w - x_pivot, h, parent)
def _compute_x_labels(self):
pass
def _compute_y_labels(self):
pass
def _plot(self):
total = sum(map(sum, map(lambda x: x.values, self.series)))
if total == 0:

8
pygal/svg.py

@ -143,7 +143,7 @@ class Svg(object):
dct = get_js_dict()
# Config adds
dct['legends'] = self.graph._legends
dct['legends'] = self.graph._legends + self.graph._secondary_legends
common_script.text = " = ".join(
("window.config", json.dumps(
@ -239,7 +239,7 @@ class Svg(object):
def slice(
self, serie_node, node, radius, small_radius,
angle, start_angle, center, val):
angle, start_angle, center, val, i):
"""Draw a pie slice"""
project = lambda rho, alpha: (
rho * sin(-alpha), rho * cos(-alpha))
@ -274,7 +274,9 @@ class Svg(object):
x, y = diff(center, project(
(radius + small_radius) / 2, start_angle + angle / 2))
self.graph._tooltip_data(node, val, x, y, classes="centered")
self.graph._tooltip_data(
node, val, x, y, "centered",
self.graph._x_labels and self.graph._x_labels[i][0])
if angle >= 0.3: # 0.3 radians is about 17 degrees
self.graph._static_value(serie_node, val, x, y)
return rv

Loading…
Cancel
Save