Browse Source

Add legend at bottom option

pull/8/head
Florian Mounier 13 years ago
parent
commit
4ff8fe3b62
  1. 14
      demo/simple_test.py
  2. 6
      pygal/config.py
  3. 14
      pygal/graph/base.py
  4. 47
      pygal/graph/graph.py
  5. 2
      pygal/util.py

14
demo/simple_test.py

@ -172,12 +172,14 @@ xy.title = "XY test"
xy.render_to_file('out-xy.svg')
pie = Pie(Config(style=NeonStyle))
pie.add('test', [lnk(11, 'Foo'), {'value': 8, 'label': 'Few'}, 21])
pie.add('test2', [lnk(29), None, 9])
pie.add('test3', [24, 10, 32])
pie.add('test4', [20, lnk(18), 9])
pie.add('test5', [17, 5, 10])
pie.add('test6', [None, None, 10])
# pie.add('test', [lnk(11, 'Foo'), {'value': 8, 'label': 'Few'}, 21])
# pie.add('test2', [lnk(29), None, 9])
# pie.add('test3', [24, 10, 32])
# pie.add('test4', [20, lnk(18), 9])
# pie.add('test5', [17, 5, 10])
# pie.add('test6', [None, None, 10])
for i in range(30):
pie.add(str(i) + '!' * i, [i, 30 - i])
# pie.included_js = []
# pie.external_js = [

6
pygal/config.py

@ -60,7 +60,7 @@ class Config(object):
#: Set to false to remove legend
show_legend = True
#: Set to true to position legend at bottom
legend_at_bottom = True
legend_at_bottom = False
#: Set to false to remove dots
show_dots = True
#: Size of legend boxes
@ -106,8 +106,8 @@ class Config(object):
disable_xml_declaration = False
#: Write width and height attributes
explicit_size = False
#: Legend string length truncation threshold
truncate_legend = 15
#: Legend string length truncation threshold (None = auto)
truncate_legend = None
#: Label string length truncation threshold (None = auto)
truncate_label = None

14
pygal/graph/base.py

@ -30,7 +30,7 @@ from pygal.util import (
from pygal.svg import Svg
from pygal.config import Config
from pygal.util import cached_property
from math import sin, cos
from math import sin, cos, sqrt
class BaseGraph(object):
@ -86,10 +86,13 @@ class BaseGraph(object):
"""Compute graph margins from set texts"""
if self.show_legend:
h, w = get_texts_box(
map(lambda x: truncate(x, self.truncate_legend),
map(lambda x: truncate(x, self.truncate_legend or 15),
cut(self.series, 'title')),
self.legend_font_size)
self.margin.right += 10 + w + self.legend_box_size
if self.legend_at_bottom:
self.margin.bottom += 10 + h * int(sqrt(len(self.series)))
else:
self.margin.right += 10 + w + self.legend_box_size
if self.title:
h, w = get_text_box(self.title, self.title_font_size)
@ -98,12 +101,15 @@ class BaseGraph(object):
if self._x_labels:
h, w = get_texts_box(
cut(self._x_labels), self.label_font_size)
self.margin.bottom += 10 + max(
self._x_labels_height = 10 + max(
w * sin(rad(self.x_label_rotation)), h)
self.margin.bottom += self._x_labels_height
if self.x_label_rotation:
self.margin.right = max(
w * cos(rad(self.x_label_rotation)),
self.margin.right)
else:
self._x_labels_height = 0
if self._y_labels:
h, w = get_texts_box(
cut(self._y_labels), self.label_font_size)

47
pygal/graph/graph.py

@ -26,7 +26,7 @@ from pygal.interpolate import interpolation
from pygal.graph.base import BaseGraph
from pygal.view import View, LogView
from pygal.util import is_major, truncate, reverse_text_len
from math import isnan, pi
from math import isnan, pi, sqrt, floor, ceil
class Graph(BaseGraph):
@ -181,29 +181,50 @@ class Graph(BaseGraph):
"""Make the legend box"""
if not self.show_legend:
return
truncation = self.truncate_legend
if self.legend_at_bottom:
x = self.margin.left + 10
y = (self.margin.top + self.view.height +
self._x_labels_height + 10)
cols = ceil(sqrt(len(self.series)))
if not truncation:
available_space = self.width / cols - (
self.legend_box_size + 5)
truncation = int(reverse_text_len(
available_space, self.legend_font_size))
else:
x = self.margin.left + self.view.width + 10
y = self.margin.top + 10
cols = 1
if not truncation:
truncation = 15
legends = self.svg.node(
self.nodes['graph'], class_='legends',
transform='translate(%d, %d)' % (
self.margin.left + self.view.width + 10,
self.margin.top + 10))
transform='translate(%d, %d)' % (x, y))
x_step = self.width / cols
for i, title in enumerate(self._legends):
col = i % cols
row = i // cols
legend = self.svg.node(
legends, class_='legend reactive activate-serie',
id="activate-serie-%d" % i)
self.svg.node(
legend, 'rect',
x=0,
y=1.5 * i * self.legend_box_size,
x=col * x_step,
y=1.5 * row * self.legend_box_size,
width=self.legend_box_size,
height=self.legend_box_size,
class_="color-%d reactive" % i
class_="color-%d reactive" % (i % 16)
)
truncated = truncate(title, self.truncate_legend)
truncated = truncate(title, truncation)
# Serious magical numbers here
self.svg.node(
legend, 'text',
x=self.legend_box_size + 5,
y=1.5 * i * self.legend_box_size
x=col * x_step + self.legend_box_size + 5,
y=1.5 * row * self.legend_box_size
+ .5 * self.legend_box_size
+ .3 * self.legend_font_size
).text = truncated
@ -224,13 +245,13 @@ class Graph(BaseGraph):
return dict(
plot=self.svg.node(
self.nodes['plot'],
class_='series serie-%d color-%d' % (serie, serie)),
class_='series serie-%d color-%d' % (serie, serie % 16)),
overlay=self.svg.node(
self.nodes['overlay'],
class_='series serie-%d color-%d' % (serie, serie)),
class_='series serie-%d color-%d' % (serie, serie % 16)),
text_overlay=self.svg.node(
self.nodes['text_overlay'],
class_='series serie-%d color-%d' % (serie, serie)))
class_='series serie-%d color-%d' % (serie, serie % 16)))
def _interpolate(self, ys, xs,
polar=False, xy=False, xy_xmin=None, xy_rng=None):

2
pygal/util.py

@ -217,7 +217,7 @@ def cycle_fill(short_list, max_len):
"""Fill a list to max_len using a cycle of it"""
short_list = list(short_list)
list_cycle = cycle(short_list)
while len(short_list) < 16:
while len(short_list) < max_len:
short_list.append(list_cycle.next())
return short_list

Loading…
Cancel
Save