Browse Source

Add gauge

pull/8/head
Florian Mounier 13 years ago
parent
commit
6ede0d580b
  1. 8
      demo/simple_test.py
  2. 6
      pygal/__init__.py
  3. 2
      pygal/config.py
  4. 1
      pygal/css/graph.css
  5. 4
      pygal/graph/base.py
  6. 122
      pygal/graph/gauge.py
  7. 13
      pygal/graph/pie.py

8
demo/simple_test.py

@ -26,6 +26,13 @@ lnk = lambda v, l=None: {'value': v, 'xlink': 'javascript:alert("Test %s")' % v,
t_start = time.time() t_start = time.time()
gauge = Gauge()
gauge.range = [-10, 10]
gauge.add('Need l', [2.3, 5.12])
# gauge.add('No', [99, -99])
gauge.render_to_file('out-gauge.svg')
pyramid = Pyramid() pyramid = Pyramid()
pyramid.x_labels = ['0-25', '25-45', '45-65', '65+'] pyramid.x_labels = ['0-25', '25-45', '45-65', '65+']
@ -168,6 +175,7 @@ pie.add('test3', [24, 10, 32])
pie.add('test4', [20, lnk(18), 9]) pie.add('test4', [20, lnk(18), 9])
pie.add('test5', [17, 5, 10]) pie.add('test5', [17, 5, 10])
pie.add('test6', [None, None, 10]) pie.add('test6', [None, None, 10])
# pie.included_js = [] # pie.included_js = []
# pie.external_js = [ # pie.external_js = [
# 'http://localhost:7575/svg.jquery.js', # 'http://localhost:7575/svg.jquery.js',

6
pygal/__init__.py

@ -26,11 +26,12 @@ __version__ = '0.9.22'
from pygal.config import Config from pygal.config import Config
from pygal.graph.bar import Bar from pygal.graph.bar import Bar
from pygal.graph.dot import Dot from pygal.graph.dot import Dot
from pygal.graph.funnel import Funnel
from pygal.graph.gauge import Gauge
from pygal.graph.horizontal import HorizontalBar from pygal.graph.horizontal import HorizontalBar
from pygal.graph.horizontal import HorizontalStackedBar from pygal.graph.horizontal import HorizontalStackedBar
from pygal.graph.line import Line from pygal.graph.line import Line
from pygal.graph.pie import Pie from pygal.graph.pie import Pie
from pygal.graph.funnel import Funnel
from pygal.graph.pyramid import Pyramid from pygal.graph.pyramid import Pyramid
from pygal.graph.radar import Radar from pygal.graph.radar import Radar
from pygal.graph.stackedbar import StackedBar from pygal.graph.stackedbar import StackedBar
@ -41,8 +42,9 @@ from pygal.graph.xy import XY
#: List of all chart types #: List of all chart types
CHARTS = [ CHARTS = [
Bar, Bar,
Funnel,
Dot, Dot,
Funnel,
Gauge,
HorizontalBar, HorizontalBar,
HorizontalStackedBar, HorizontalStackedBar,
Line, Line,

2
pygal/config.py

@ -87,6 +87,8 @@ class Config(object):
interpolate = None interpolate = None
#: Number of interpolated points between two values #: Number of interpolated points between two values
interpolation_precision = 250 interpolation_precision = 250
#: Explicitly specify min and max of values (ie: (0, 100))
range = None
#: Set the ordinate zero value (for filling) #: Set the ordinate zero value (for filling)
zero = 0 zero = 0
#: Text to display when no data is given #: Text to display when no data is given

1
pygal/css/graph.css

@ -72,6 +72,7 @@ text.no_data {
.axis.y .guides:hover .guide.line, .axis.y .guides:hover .guide.line,
.line-graph .axis.x .guides:hover .guide.line, .line-graph .axis.x .guides:hover .guide.line,
.gauge-graph .axis.x .guides:hover .guide.line,
.stackedline-graph .axis.x .guides:hover .guide.line, .stackedline-graph .axis.x .guides:hover .guide.line,
.xy-graph .axis.x .guides:hover .guide.line { .xy-graph .axis.x .guides:hover .guide.line {
opacity: 1; opacity: 1;

4
pygal/graph/base.py

@ -128,12 +128,12 @@ class BaseGraph(object):
@cached_property @cached_property
def _min(self): def _min(self):
"""Getter for the minimum series value""" """Getter for the minimum series value"""
return min(self._values) return (self.range and self.range[0]) or min(self._values)
@cached_property @cached_property
def _max(self): def _max(self):
"""Getter for the maximum series value""" """Getter for the maximum series value"""
return max(self._values) return (self.range and self.range[1]) or max(self._values)
def _draw(self): def _draw(self):
"""Draw all the things""" """Draw all the things"""

122
pygal/graph/gauge.py

@ -0,0 +1,122 @@
# -*- 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/>.
"""
Gauge chart
"""
from __future__ import division
from pygal.util import decorate, compute_scale
from pygal.view import PolarView
from pygal.graph.graph import Graph
from math import pi
class Gauge(Graph):
"""Gauge graph"""
def _set_view(self):
self.view = PolarView(
self.width - self.margin.x,
self.height - self.margin.y,
self._box)
def arc_pos(self, value):
aperture = pi / 3
if value > self._max:
return (3 * pi - aperture / 2) / 2
if value < self._min:
return (3 * pi + aperture / 2) / 2
start = 3 * pi / 2 + aperture / 2
return start + (2 * pi - aperture) * (
value - self.min_) / (self.max_ - self.min_)
def needle(self, serie_node, serie,):
thickness = .05
for i, value in enumerate(serie.values):
theta = self.arc_pos(value)
fmt = lambda x: '%f %f' % x
value = self._format(serie.values[i])
metadata = serie.metadata[i]
gauges = decorate(
self.svg,
self.svg.node(serie_node['plot'], class_="dots"),
metadata)
self.svg.node(gauges, 'polygon', points=' '.join([
fmt(self.view((0, 0))),
fmt(self.view((.75, theta + thickness))),
fmt(self.view((.8, theta))),
fmt(self.view((.75, theta - thickness)))]),
class_='line reactive tooltip-trigger')
x, y = self.view((.75, theta))
self._tooltip_data(gauges, value, x, y)
self._static_value(serie_node, value, x, y)
def _x_axis(self, draw_axes=True):
if not self._x_labels:
return
axis = self.svg.node(self.nodes['plot'], class_="axis x gauge")
for i, (label, pos) in enumerate(self._x_labels):
guides = self.svg.node(axis, class_='guides')
theta = self.arc_pos(pos)
self.svg.line(
guides, [self.view((.95, theta)), self.view((1, theta))],
close=True,
class_='line')
self.svg.line(
guides, [self.view((0, theta)), self.view((.95, theta))],
close=True,
class_='guide line %s' % ('major'
if i in (0, len(self._x_labels) - 1) else ''))
x, y = self.view((.9, theta))
self.svg.node(guides, 'text',
x=x,
y=y
).text = label
def _y_axis(self, draw_axes=True):
axis = self.svg.node(self.nodes['plot'], class_="axis y gauge")
x, y = self.view((0, 0))
self.svg.node(axis, 'circle', cx=x, cy=y, r=4)
def _compute(self):
self._box.xmin = -1
self._box.ymin = -1
self.min_ = self._min
self.max_ = self._max
if self.max_ - self.min_ == 0:
self.min_ -= 1
self.max_ += 1
x_pos = compute_scale(
self.min_, self.max_, self.logarithmic
)
self._x_labels = zip(map(self._format, x_pos), x_pos)
def _plot(self):
for serie in self.series:
self.needle(
self._serie(serie.index), serie)

13
pygal/graph/pie.py

@ -35,6 +35,7 @@ class Pie(Graph):
def slice(self, serie_node, start_angle, serie, total): def slice(self, serie_node, start_angle, serie, total):
"""Make a serie slice""" """Make a serie slice"""
dual = self._len > 1 and not len(self.series) == 1
slices = self.svg.node(serie_node['plot'], class_="slices") slices = self.svg.node(serie_node['plot'], class_="slices")
serie_angle = 0 serie_angle = 0
@ -53,18 +54,20 @@ class Pie(Graph):
self.svg, self.svg,
self.svg.node(slices, class_="slice"), self.svg.node(slices, class_="slice"),
metadata) metadata)
if len(serie.values) > 1: if dual:
small_radius = radius * .9 small_radius = radius * .9
big_radius = radius
else: else:
radius = radius * .9 big_radius = radius * .9
small_radius = 0 small_radius = 0
self.svg.slice(serie_node, self.svg.slice(
slice_, radius, small_radius, angle, start_angle, center, val) serie_node, slice_, big_radius, small_radius,
angle, start_angle, center, val)
start_angle += angle start_angle += angle
total_perc += perc total_perc += perc
if len(serie.values) > 1: if dual:
val = '{0:.2%}'.format(total_perc) val = '{0:.2%}'.format(total_perc)
self.svg.slice(serie_node, self.svg.slice(serie_node,
self.svg.node(slices, class_="big_slice"), self.svg.node(slices, class_="big_slice"),

Loading…
Cancel
Save