Browse Source

Truncate long texts + config option

pull/8/head
Florian Mounier 13 years ago
parent
commit
f285ac2809
  1. 21
      demo/simple_test.py
  2. 4
      pygal/config.py
  3. 9
      pygal/graph/base.py
  4. 28
      pygal/graph/graph.py
  5. 12
      pygal/test/test_util.py
  6. 16
      pygal/util.py

21
demo/simple_test.py

@ -18,10 +18,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
import time
import sys
from math import cos, sin
from subprocess import call
from pygal import *
from pygal.style import *
from math import cos, sin
lnk = lambda v, l=None: {'value': v, 'xlink': 'javascript:alert("Test %s")' % v, 'label': l}
t_start = time.time()
@ -151,12 +153,13 @@ line.interpolation_precision = 200
line.render_to_file('out-line.svg')
stackedline = StackedLine(fill=True)
stackedline.add('test1', [1, 3, 2, None, 2, 13, 2, 5, 8])
stackedline.add('test1u euirset uriets ur itseruie uriset uriest u', [1, 3, 2, None, 2, 13, 2, 5, 8])
stackedline.add('test2', [4, 1, 1, 3, 12, 3])
stackedline.add('test3', [9, 3, 2, lnk(10, '!'), 8, 2])
stackedline.x_labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
stackedline.title = "Stackedline test"
# stackedline.interpolate = "cubic"
stackedline.x_labels = ['aaisaruistarsitauritaria', 'bsruie trstie rusiet ruetsru', 'cuasitaruisrnauciuestuirue uiretsu iersut irasui', 'd', 'e', 'f', 'g']
stackedline.title = "Stackedline ine uisernuis enuir esnue sunres nuise nuires uinsre auin ruist arusit arst ierustie ruiset uriest uirest test"
# stackedline.interpolate = "cubic"xb
stackedline.render_to_file('out-stackedline.svg')
xy = XY(Config(fill=True, style=NeonStyle, interpolate='cubic'))
@ -201,4 +204,10 @@ radar.title = "Radar test"
radar.render_to_file('out-radar.svg')
call(['wsreload', '--url', "file:///*/*/kozea/pygal/*"])
print "Ok (%dms)" % (1000 * (time.time() - t_start))
if '-t' in sys.argv:
import pytest
pytest.main('')

4
pygal/config.py

@ -104,6 +104,10 @@ class Config(object):
disable_xml_declaration = False
#: Write width and height attributes
explicit_size = False
#: Legend string length truncation threshold
truncate_legend = 15
#: Label string length truncation threshold (None = auto)
truncate_label = None
def __init__(self, **kwargs):
"""Can be instanciated with config kwargs"""

9
pygal/graph/base.py

@ -23,9 +23,10 @@ Base for pygal charts
from __future__ import division
import io
from pygal.serie import Serie, Value, PositiveValue
from pygal.serie import Serie, Value
from pygal.view import Margin, Box
from pygal.util import get_text_box, get_texts_box, cut, rad, humanize
from pygal.util import (
get_text_box, get_texts_box, cut, rad, humanize, truncate)
from pygal.svg import Svg
from pygal.config import Config
from pygal.util import cached_property
@ -85,7 +86,9 @@ class BaseGraph(object):
"""Compute graph margins from set texts"""
if self.show_legend:
h, w = get_texts_box(
cut(self.series, 'title'), self.legend_font_size)
map(lambda x: truncate(x, self.truncate_legend),
cut(self.series, 'title')),
self.legend_font_size)
self.margin.right += 10 + w + self.legend_box_size
if self.title:

28
pygal/graph/graph.py

@ -25,7 +25,7 @@ from __future__ import division
from pygal.interpolate import interpolation
from pygal.graph.base import BaseGraph
from pygal.view import View, LogView
from pygal.util import is_major
from pygal.util import is_major, truncate, reverse_text_len
from math import isnan, pi
@ -104,6 +104,18 @@ class Graph(BaseGraph):
return
axis = self.svg.node(self.nodes['plot'], class_="axis x")
truncation = self.truncate_label
if not truncation:
if self.x_label_rotation:
truncation = 25
else:
first_label_position = self.view.x(self._x_labels[0][1])
last_label_position = self.view.x(self._x_labels[-1][1])
available_space = (
last_label_position - first_label_position) / (
len(self._x_labels) - 1)
truncation = int(
reverse_text_len(available_space, self.label_font_size))
if 0 not in [label[1] for label in self._x_labels] and draw_axes:
self.svg.node(axis, 'path',
@ -123,7 +135,9 @@ class Graph(BaseGraph):
x=x,
y=y + .5 * self.label_font_size + 5
)
text.text = label
text.text = truncate(label, truncation)
if text.text != label:
self.svg.node(guides, 'title').text = label
if self.x_label_rotation:
text.attrib['transform'] = "rotate(%d %f %f)" % (
self.x_label_rotation, x, y)
@ -183,7 +197,8 @@ class Graph(BaseGraph):
width=self.legend_box_size,
height=self.legend_box_size,
class_="color-%d reactive" % i
).text = title
)
truncated = truncate(title, self.truncate_legend)
# Serious magical numbers here
self.svg.node(
legend, 'text',
@ -191,7 +206,9 @@ class Graph(BaseGraph):
y=1.5 * i * self.legend_box_size
+ .5 * self.legend_box_size
+ .3 * self.legend_font_size
).text = title
).text = truncated
if truncated != title:
self.svg.node(legend, 'title').text = title
def _title(self):
"""Make the title"""
@ -199,7 +216,8 @@ class Graph(BaseGraph):
self.svg.node(self.nodes['graph'], 'text', class_='title',
x=self.margin.left + self.view.width / 2,
y=self.title_font_size + 10
).text = self.title
).text = truncate(self.title, int(reverse_text_len(
self.width, self.title_font_size)))
def _serie(self, serie):
"""Make serie node"""

12
pygal/test/test_util.py

@ -18,7 +18,7 @@
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
from pygal.util import (
round_to_int, round_to_float, _swap_curly, template, humanize,
is_major)
is_major, truncate)
from pytest import raises
@ -111,3 +111,13 @@ def test_is_major():
assert is_major(n)
for n in (2, 10002., 100000.0003, -200, -0.0005):
assert not is_major(n)
def test_truncate():
assert truncate('1234567890', 50) == '1234567890'
assert truncate('1234567890', 5) == u'1234…'
assert truncate('1234567890', 1) == u''
assert truncate('1234567890', 9) == u'12345678…'
assert truncate('1234567890', 10) == '1234567890'
assert truncate('1234567890', 0) == '1234567890'
assert truncate('1234567890', -1) == '1234567890'

16
pygal/util.py

@ -175,9 +175,14 @@ def compute_scale(min_, max_, logarithmic=False, min_scale=4, max_scale=20):
return positions
def text_len(lenght, fs):
def text_len(length, fs):
"""Approximation of text width"""
return length * 0.6 * fs
def reverse_text_len(width, fs):
"""Approximation of text length"""
return lenght * 0.6 * fs
return width / (0.6 * fs)
def get_text_box(text, fs):
@ -217,6 +222,13 @@ def cycle_fill(short_list, max_len):
return short_list
def truncate(string, index):
"""Truncate a string at index and add ..."""
if len(string) > index and index > 0:
string = string[:index - 1] + u''
return string
# Stolen from brownie http://packages.python.org/Brownie/
class cached_property(object):
"""Optimize a static property"""

Loading…
Cancel
Save