Browse Source

Compatibility with python 2.6 and 3.2. Yay!

pull/8/head
Florian Mounier 13 years ago
parent
commit
4e3d585334
  1. 1
      .gitignore
  2. 7
      pygal/graph/bar.py
  3. 32
      pygal/graph/base.py
  4. 19
      pygal/graph/graph.py
  5. 5
      pygal/graph/line.py
  6. 5
      pygal/graph/pie.py
  7. 7
      pygal/graph/radar.py
  8. 3
      pygal/graph/stackedbar.py
  9. 3
      pygal/graph/xy.py
  10. 14
      pygal/svg.py
  11. 4
      pygal/test/test_config.py
  12. 5
      pygal/test/test_graph.py
  13. 7
      pygal/test/test_line.py
  14. 13
      pygal/util.py
  15. 7
      pygal/view.py
  16. 2
      setup.py
  17. 8
      tox.ini

1
.gitignore vendored

@ -1,3 +1,4 @@
*.svg
.livereload
dist
.tox

7
pygal/graph/bar.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.graph import Graph
from pygal.util import swap, ident
@ -78,7 +79,7 @@ class Bar(Graph):
class_='rect reactive tooltip-trigger')
self.svg.node(bar, 'desc', class_="values").text = val
tooltip_positions = map(
str, (x + bar_inner_width / 2., y + height / 2.))
str, (x + bar_inner_width / 2, y + height / 2))
self.svg.node(bar, 'desc',
class_="x centered"
).text = tooltip_positions[int(self._horizontal)]
@ -103,14 +104,14 @@ class Bar(Graph):
self._box.ymin = min(min(self._values), self.zero)
self._box.ymax = max(max(self._values), self.zero)
x_step = len(self.series[0].values)
x_pos = [x / float(x_step) for x in range(x_step + 1)
x_pos = [x / x_step for x in range(x_step + 1)
] if x_step > 1 else [0, 1] # Center if only one value
y_pos = self._compute_scale(self._box.ymin, self._box.ymax,
) if not self.y_labels else map(float, self.y_labels)
self._x_ranges = zip(x_pos, x_pos[1:])
self._x_labels = self.x_labels and zip(self.x_labels, [
sum(x_range) / 2. for x_range in self._x_ranges])
sum(x_range) / 2 for x_range in self._x_ranges])
self._y_labels = zip(map(self.format, y_pos), y_pos)
def _plot(self):

32
pygal/graph/base.py

@ -1,3 +1,23 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012 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 __future__ import division
import io
from pygal.serie import Serie
from pygal.view import Margin, Box
from pygal.util import round_to_scale, cut, rad, humanize
@ -44,7 +64,7 @@ class BaseGraph(object):
detail /= 2
for order in range(min_order, max_order + 1):
for i in range(int(detail)):
tick = (10 * i / detail or 1.) * 10 ** order
tick = (10 * i / detail or 1) * 10 ** order
tick = round_to_scale(tick, tick)
if min_ <= tick <= max_ and tick not in positions:
positions.append(tick)
@ -61,7 +81,7 @@ class BaseGraph(object):
return log_scale
# else we fallback to normal scalling
order = round(log10(max(abs(min_), abs(max_)))) - 1
while (max_ - min_) / float(10 ** order) < min_scale:
while (max_ - min_) / (10 ** order) < min_scale:
order -= 1
step = float(10 ** order)
while (max_ - min_) / step > max_scale:
@ -159,9 +179,9 @@ class BaseGraph(object):
else:
self.svg._pre_render(True)
def render(self):
def render(self, is_unicode=False):
self._render()
return self.svg.render()
return self.svg.render(is_unicode=is_unicode)
def render_tree(self):
self._render()
@ -180,8 +200,8 @@ class BaseGraph(object):
return Response(self.render(), mimetype='image/svg+xml')
def render_to_file(self, filename):
with open(filename, 'w') as f:
f.write(self.render())
with io.open(filename, 'w', encoding='utf-8') as f:
f.write(self.render(is_unicode=True))
def render_to_png(self, filename):
import cairosvg

19
pygal/graph/graph.py

@ -1,3 +1,22 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012 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 __future__ import division
from pygal.graph.base import BaseGraph
from pygal.view import View, LogView
from pygal.util import is_major

5
pygal/graph/line.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.graph import Graph
from pygal.util import cached_property
from pygal.interpolate import interpolation
@ -55,9 +56,9 @@ class Line(Graph):
continue
classes = []
if x > self.view.width / 2.:
if x > self.view.width / 2:
classes.append('left')
if y > self.view.height / 2.:
if y > self.view.height / 2:
classes.append('top')
classes = ' '.join(classes)

5
pygal/graph/pie.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.graph import Graph
from math import cos, sin, pi
project = lambda rho, alpha: (
@ -30,7 +31,7 @@ class Pie(Graph):
def slice(self, serie_node, start_angle, angle, perc,
small=False):
val = '{:.2%}'.format(perc)
val = '{0:.2%}'.format(perc)
slices = self.svg.node(serie_node['plot'], class_="slices")
slice_ = self.svg.node(slices, class_="slice")
center = ((self.width - self.margin.x) / 2.,
@ -64,7 +65,7 @@ class Pie(Graph):
self.svg.node(slice_, 'desc', class_="value").text = val
tooltip_position = map(
str, diff(center, project(
(r + small_r) / 2., start_angle + angle / 2.)))
(r + small_r) / 2, start_angle + angle / 2)))
self.svg.node(slice_, 'desc',
class_="x centered").text = tooltip_position[0]
self.svg.node(slice_, 'desc',

7
pygal/graph/radar.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.line import Line
from pygal.view import PolarView
from pygal.util import deg, cached_property
@ -67,7 +68,7 @@ class Radar(Line):
y=pos_text[1]
)
text.text = label
angle = - theta + pi / 2.
angle = - theta + pi / 2
if cos(angle) < 0:
angle -= pi
text.attrib['transform'] = 'rotate(%f %s)' % (
@ -92,7 +93,7 @@ class Radar(Line):
).text = label
def _compute(self):
delta = 2 * pi / float(self._len)
delta = 2 * pi / self._len
self._x_pos = [.5 * pi + i * delta for i in range(self._len + 1)]
for serie in self.series:
vals = list(serie.values)
@ -112,7 +113,7 @@ class Radar(Line):
interpolate = interpolation(
extended_x_pos, extended_vals, kind=self.interpolate)
serie.interpolated = []
p = float(self.interpolation_precision)
p = self.interpolation_precision
for s in range(int(p + 1)):
x = .5 * pi + 2 * pi * (s / p)
serie.interpolated.append((float(interpolate(x)), x))

3
pygal/graph/stackedbar.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.bar import Bar
@ -35,7 +36,7 @@ class StackedBar(Bar):
min(min(negative_vals), 0), max(max(positive_vals), 0))
self._length = len(self.series[0].values)
x_pos = [x / float(self._length)
x_pos = [x / self._length
for x in range(self._length + 1)
] if self._length > 1 else [0, 1] # Center if only one value
y_pos = self._compute_scale(self._box.ymin, self._box.ymax

3
pygal/graph/xy.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal.graph.line import Line
from pygal.interpolate import interpolation
from math import isnan
@ -48,7 +49,7 @@ class XY(Line):
serie_xmax = max(vals[0])
serie.interpolated = []
r = (max(xvals) - xmin)
p = float(self.interpolation_precision)
p = self.interpolation_precision
for s in range(int(p + 1)):
x = xmin + r * (s / p)
if (serie_xmin <= x <= serie_xmax and not

14
pygal/svg.py

@ -16,6 +16,8 @@
#
# 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 __future__ import division
import io
import os
from lxml import etree
from pygal.util import template
@ -44,23 +46,23 @@ class Svg(object):
def add_style(self, css):
style = self.node(self.defs, 'style', type='text/css')
with open(css) as f:
with io.open(css, encoding='utf-8') as f:
templ = template(
f.read(),
style=self.graph.style,
font_sizes=self.graph.font_sizes(),
hidden='y' if self.graph._horizontal else 'x')
style.text = templ.decode('utf-8')
style.text = templ
def add_script(self, js):
script = self.node(self.defs, 'script', type='text/javascript')
with open(js) as f:
with io.open(js, encoding='utf-8') as f:
templ = template(
f.read(),
font_sizes=self.graph.font_sizes(False),
animation_steps=self.graph.animation_steps
)
script.text = templ.decode('utf-8')
script.text = templ
def node(self, parent=None, tag='g', attrib=None, **extras):
if parent is None:
@ -119,11 +121,11 @@ class Svg(object):
class_='no_data')
no_data.text = self.graph.no_data_text
def render(self):
def render(self, is_unicode=False):
svg = etree.tostring(
self.root, pretty_print=True,
xml_declaration=not self.graph.disable_xml_declaration,
encoding='utf-8')
if self.graph.disable_xml_declaration:
if self.graph.disable_xml_declaration or is_unicode:
svg = svg.decode('utf-8')
return svg

4
pygal/test/test_config.py

@ -34,6 +34,10 @@ def test_logarithmic():
def test_logarithmic_bad_interpolation():
try:
import scipy
except ImportError:
return
line = Line(logarithmic=True, interpolate='cubic')
line.add('_', [.001, .00000001, 1])
q = line.render_pyquery()

5
pygal/test/test_graph.py

@ -48,6 +48,11 @@ def test_render_to_file():
def test_render_to_png():
try:
import cairosvg
except ImportError:
return
file_name = '/tmp/test_graph.png'
if os.path.exists(file_name):
os.remove(file_name)

7
pygal/test/test_line.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from pygal import Line
from pygal.test.utils import texts
from math import cos, sin
@ -24,9 +25,9 @@ from math import cos, sin
def test_simple_line():
line = Line()
rng = range(-30, 31, 5)
line.add('test1', [cos(x / 10.) for x in rng])
line.add('test2', [sin(x / 10.) for x in rng])
line.add('test3', [cos(x / 10.) - sin(x / 10.) for x in rng])
line.add('test1', [cos(x / 10) for x in rng])
line.add('test2', [sin(x / 10) for x in rng])
line.add('test3', [cos(x / 10) - sin(x / 10) for x in rng])
line.x_labels = map(str, rng)
line.title = "cos sin and cos - sin"
q = line.render_pyquery()

13
pygal/util.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from decimal import Decimal
from math import floor, pi, log, log10
ORDERS = u"yzafpnµm kMGTPEZY"
@ -29,9 +30,9 @@ def humanize(number):
order = number and int(floor(log(abs(number)) / log(1000)))
human_readable = ORDERS.split(" ")[int(order > 0)]
if order == 0 or order > len(human_readable):
return float_format(number / float(1000 ** int(order)))
return float_format(number / (1000 ** int(order)))
return (
float_format(number / float(1000 ** int(order))) +
float_format(number / (1000 ** int(order))) +
human_readable[int(order) - int(order > 0)])
@ -42,13 +43,13 @@ def is_major(number):
def round_to_int(number, precision):
precision = int(precision)
rounded = (int(number) + precision / 2) / precision * precision
rounded = (int(number) + precision / 2) // precision * precision
return rounded
def round_to_float(number, precision):
rounded = Decimal(
floor((number + precision / 2) / precision)) * Decimal(str(precision))
rounded = Decimal(str(floor((number + precision / 2) // precision))
) * Decimal(str(precision))
return float(rounded)
@ -67,7 +68,7 @@ def cut(list_, index=0):
def rad(deg):
return pi * deg / 180.
return pi * deg / 180
def deg(deg):

7
pygal/view.py

@ -16,6 +16,7 @@
#
# 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 __future__ import division
from math import sin, cos, log10
@ -79,13 +80,13 @@ class View(object):
def x(self, x):
if x == None:
return None
return self.width * (x - self.box.xmin) / float(self.box.width)
return self.width * (x - self.box.xmin) / self.box.width
def y(self, y):
if y == None:
return None
return (self.height - self.height *
(y - self.box.ymin) / float(self.box.height))
(y - self.box.ymin) / self.box.height)
def __call__(self, xy):
x, y = xy
@ -118,4 +119,4 @@ class LogView(View):
return None
return (self.height - self.height *
(log10(y) - self.log10_ymin)
/ float(self.log10_ymax - self.log10_ymin))
/ (self.log10_ymax - self.log10_ymin))

2
setup.py

@ -42,6 +42,7 @@ setup(
tests_require=["pytest", "pyquery", "flask", "cairosvg"],
package_data={'pygal': ['css/*', 'js/*']},
install_requires=['lxml'],
use_2to3=True,
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Console",
@ -50,4 +51,5 @@ setup(
"GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Topic :: Multimedia :: Graphics :: Presentation"])

8
tox.ini

@ -0,0 +1,8 @@
[tox]
envlist = py26,py27,py32
[testenv]
changedir=demo
commands=py.test --pyargs pygal []
deps =
pytest
pyquery
Loading…
Cancel
Save