Browse Source

Removed extraneous space per PEP-8

pull/8/head
jaraco 17 years ago
parent
commit
ba31107e08
  1. 78
      lib/SVG/Bar.py
  2. 6
      lib/SVG/Pie.py
  3. 258
      lib/SVG/Plot.py
  4. 62
      lib/SVG/TimeSeries.py
  5. 482
      lib/SVG/__init__.py
  6. 34
      test/testing.py

78
lib/SVG/Bar.py

@ -2,9 +2,9 @@
from SVG import Graph
from itertools import chain
__all__ = ( 'VerticalBar', 'HorizontalBar' )
__all__ = ('VerticalBar', 'HorizontalBar')
class Bar( Graph ):
class Bar(Graph):
"A superclass for bar-style graphs. Do not instantiate directly."
# gap between bars
@ -17,49 +17,49 @@ class Bar( Graph ):
scale_divisions = None
def __init__( self, fields, *args, **kargs ):
def __init__(self, fields, *args, **kargs):
self.fields = fields
super( Bar, self ).__init__( *args, **kargs )
super(Bar, self).__init__(*args, **kargs)
# adapted from Plot
def get_data_values( self ):
def get_data_values(self):
min_value, max_value, scale_division = self.data_range()
result = tuple( float_range( min_value, max_value + scale_division, scale_division ) )
result = tuple(float_range(min_value, max_value + scale_division, scale_division))
if self.scale_integers:
result = map(int, result)
return result
# adapted from plot (very much like calling data_range('y'))
def data_range( self ):
min_value = self.data_min( )
max_value = self.data_max( )
def data_range(self):
min_value = self.data_min()
max_value = self.data_max()
range = max_value - min_value
data_pad = range / 20.0 or 10
scale_range = ( max_value + data_pad ) - min_value
scale_range = (max_value + data_pad) - min_value
scale_division = self.scale_divisions or ( scale_range / 10.0 )
scale_division = self.scale_divisions or (scale_range / 10.0)
if self.scale_integers:
scale_division = round(scale_division) or 1
return min_value, max_value, scale_division
def get_field_labels( self ):
def get_field_labels(self):
return self.fields
def get_data_labels( self ):
return map( str, self.get_data_values() )
def get_data_labels(self):
return map(str, self.get_data_values())
def data_max( self ):
return max( chain( *map( lambda set: set['data'], self.data ) ) )
def data_max(self):
return max(chain(*map(lambda set: set['data'], self.data)))
# above is same as
# return max( map( lambda set: max( set['data'] ), self.data ) )
# return max(map(lambda set: max(set['data']), self.data))
def data_min( self ):
def data_min(self):
if not getattr(self, 'min_scale_value') is None: return self.min_scale_value
min_value = min( chain( *map( lambda set: set['data'], self.data ) ) )
min_value = min( min_value, 0 )
min_value = min(chain(*map(lambda set: set['data'], self.data)))
min_value = min(min_value, 0)
return min_value
def get_bar_gap(self, field_size):
@ -71,7 +71,7 @@ class Bar( Graph ):
bar_gap = int(self.bar_gap) * bar_gap
return bar_gap
def get_css( self ):
def get_css(self):
return """\
/* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
.key1,.fill1{
@ -148,14 +148,14 @@ class Bar( Graph ):
}
"""
def float_range( start = 0, stop = None, step = 1 ):
def float_range(start = 0, stop = None, step = 1):
"Much like the built-in function range, but accepts floats"
while start < stop:
yield float( start )
yield float(start)
start += step
class VerticalBar( Bar ):
class VerticalBar(Bar):
""" # === Create presentation quality SVG bar graphs easily
#
# = Synopsis
@ -210,34 +210,34 @@ class VerticalBar( Bar ):
"""
top_align = top_font = 1
def get_x_labels( self ):
def get_x_labels(self):
return self.get_field_labels()
def get_y_labels( self ):
def get_y_labels(self):
return self.get_data_labels()
def x_label_offset( self, width ):
def x_label_offset(self, width):
return width / 2.0
def draw_data( self ):
def draw_data(self):
min_value = self.data_min()
unit_size = (float(self.graph_height) - self.font_size*2*self.top_font)
unit_size /= (max(self.get_data_values()) - min(self.get_data_values()) )
unit_size /= (max(self.get_data_values()) - min(self.get_data_values()))
bar_gap = self.get_bar_gap(self.get_field_width())
bar_width = self.get_field_width() - bar_gap
if self.stack == 'side':
bar_width /= len( self.data )
bar_width /= len(self.data)
x_mod = ( self.graph_width - bar_gap )/2
x_mod = (self.graph_width - bar_gap)/2
if self.stack == 'side':
x_mod -= bar_width/2
bottom = self.graph_height
for field_count, field in enumerate( self.fields ):
for dataset_count, dataset in enumerate( self.data ):
for field_count, field in enumerate(self.fields):
for dataset_count, dataset in enumerate(self.data):
# cases (assume 0 = +ve):
# value min length
# +ve +ve value - min
@ -247,22 +247,22 @@ class VerticalBar( Bar ):
left = self.get_field_width() * field_count
length = ( abs(value) - max( min_value, 0 ) ) * unit_size
length = (abs(value) - max(min_value, 0)) * unit_size
# top is 0 if value is negative
top = bottom - (( max(value,0) - min_value ) * unit_size )
top = bottom - ((max(value,0) - min_value) * unit_size)
if self.stack == 'side':
left += bar_width * dataset_count
rect = self._create_element( 'rect', {
rect = self._create_element('rect', {
'x': str(left),
'y': str(top),
'width': str(bar_width),
'height': str(length),
'class': 'fill%s' % (dataset_count+1),
} )
self.graph.appendChild( rect )
})
self.graph.appendChild(rect)
self.make_datapoint_text( left + bar_width/2.0, top-6, value )
self.make_datapoint_text(left + bar_width/2.0, top-6, value)
class HorizontalBar(Bar):
rotate_y_labels = True

6
lib/SVG/Pie.py

@ -182,7 +182,7 @@ class Pie(SVG.Graph):
xoff = (self.width - diameter) / 2
yoff = (self.height - self.border_bottom - diameter)
yoff -= 10 * int(self.show_shadow)
transform = 'translate( %(xoff)s %(yoff)s )' % vars()
transform = 'translate(%(xoff)s %(yoff)s)' % vars()
self.graph.setAttribute('transform', transform)
wedge_text_pad = 5
@ -253,14 +253,14 @@ class Pie(SVG.Graph):
if self.expanded or (self.expand_greatest and value == max_value):
tx = (math.sin(radians) * self.expand_gap)
ty = -(math.cos(radians) * self.expand_gap)
translate = "translate( %(tx)s %(ty)s )" % vars()
translate = "translate(%(tx)s %(ty)s)" % vars()
wedge.setAttribute('transform', translate)
clear.setAttribute('transform', translate)
if self.show_shadow:
shadow_tx = self.shadow_offset + tx
shadow_ty = self.shadow_offset + ty
translate = 'translate( %(shadow_tx)s %(shadow_ty)s )' % vars()
translate = 'translate(%(shadow_tx)s %(shadow_ty)s)' % vars()
shadow.setAttribute('transform', translate)
if self.show_data_labels and value != 0:

258
lib/SVG/Plot.py

@ -2,17 +2,17 @@
import SVG
from itertools import izip, count, chain
def get_pairs( i ):
i = iter( i )
def get_pairs(i):
i = iter(i)
while True: yield i.next(), i.next()
def float_range( start = 0, stop = None, step = 1 ):
def float_range(start = 0, stop = None, step = 1):
"Much like the built-in function range, but accepts floats"
while start < stop:
yield float( start )
yield float(start)
start += step
class Plot( SVG.Graph ):
class Plot(SVG.Graph):
"""=== For creating SVG plots of scalar data
= Synopsis
@ -77,8 +77,8 @@ class Plot( SVG.Graph ):
Unlike the other types of charts, data sets must contain x,y pairs:
[ 1, 2 ] # A data set with 1 point: (1,2)
[ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
[1, 2] # A data set with 1 point: (1,2)
[1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
= See also
@ -136,196 +136,196 @@ class Plot( SVG.Graph ):
would cause the graph to attempt to generate labels stepped by 2; EG:
0,2,4,6,8..."""
def fget( self ):
return getattr( self, '_scale_x_divisions', None )
def fset( self, val ):
def fget(self):
return getattr(self, '_scale_x_divisions', None)
def fset(self, val):
self._scale_x_divisions = val
return property(**locals())
def validate_data( self, data ):
if len( data['data'] ) % 2 != 0:
def validate_data(self, data):
if len(data['data']) % 2 != 0:
raise "Expecting x,y pairs for data points for %s." % self.__class__.__name__
def process_data( self, data ):
pairs = list( get_pairs( data['data'] ) )
def process_data(self, data):
pairs = list(get_pairs(data['data']))
pairs.sort()
data['data'] = zip( *pairs )
data['data'] = zip(*pairs)
def calculate_left_margin( self ):
super( Plot, self ).calculate_left_margin()
label_left = len( str( self.get_x_labels()[0] ) ) / 2 * self.font_size * 0.6
self.border_left = max( label_left, self.border_left )
def calculate_left_margin(self):
super(Plot, self).calculate_left_margin()
label_left = len(str(self.get_x_labels()[0])) / 2 * self.font_size * 0.6
self.border_left = max(label_left, self.border_left)
def calculate_right_margin( self ):
super( Plot, self ).calculate_right_margin()
label_right = len( str( self.get_x_labels()[-1] ) ) / 2 * self.font_size * 0.6
self.border_right = max( label_right, self.border_right )
def calculate_right_margin(self):
super(Plot, self).calculate_right_margin()
label_right = len(str(self.get_x_labels()[-1])) / 2 * self.font_size * 0.6
self.border_right = max(label_right, self.border_right)
def data_max( self, axis ):
data_index = getattr( self, '%s_data_index' % axis )
max_value = max( chain( *map( lambda set: set['data'][data_index], self.data ) ) )
def data_max(self, axis):
data_index = getattr(self, '%s_data_index' % axis)
max_value = max(chain(*map(lambda set: set['data'][data_index], self.data)))
# above is same as
#max_value = max( map( lambda set: max( set['data'][data_index] ), self.data ) )
spec_max = getattr( self, 'max_%s_value' % axis )
max_value = max( max_value, spec_max )
#max_value = max(map(lambda set: max(set['data'][data_index]), self.data))
spec_max = getattr(self, 'max_%s_value' % axis)
max_value = max(max_value, spec_max)
return max_value
def data_min( self, axis ):
data_index = getattr( self, '%s_data_index' % axis )
min_value = min( chain( *map( lambda set: set['data'][data_index], self.data ) ) )
spec_min = getattr( self, 'min_%s_value' % axis )
def data_min(self, axis):
data_index = getattr(self, '%s_data_index' % axis)
min_value = min(chain(*map(lambda set: set['data'][data_index], self.data)))
spec_min = getattr(self, 'min_%s_value' % axis)
if spec_min is not None:
min_value = min( min_value, spec_min )
min_value = min(min_value, spec_min)
return min_value
x_data_index = 0
y_data_index = 1
def data_range( self, axis ):
side = { 'x': 'right', 'y': 'top' }[axis]
def data_range(self, axis):
side = {'x': 'right', 'y': 'top'}[axis]
min_value = self.data_min( axis )
max_value = self.data_max( axis )
min_value = self.data_min(axis)
max_value = self.data_max(axis)
range = max_value - min_value
side_pad = range / 20.0 or 10
scale_range = ( max_value + side_pad ) - min_value
scale_range = (max_value + side_pad) - min_value
scale_division = getattr( self, 'scale_%s_divisions' % axis ) or ( scale_range / 10.0 )
scale_division = getattr(self, 'scale_%s_divisions' % axis) or (scale_range / 10.0)
if getattr( self, 'scale_%s_integers' % axis ):
if getattr(self, 'scale_%s_integers' % axis):
scale_division = scale_division.round() or 1
return min_value, max_value, scale_division
def x_range( self ): return self.data_range( 'x' )
def y_range( self ): return self.data_range( 'y' )
def get_data_values( self, axis ):
min_value, max_value, scale_division = self.data_range( axis )
return tuple( float_range( *self.data_range( axis ) ) )
def get_x_values( self ): return self.get_data_values( 'x' )
def get_y_values( self ): return self.get_data_values( 'y' )
def get_x_labels( self ):
return map( str, self.get_x_values() )
def get_y_labels( self ):
return map( str, self.get_y_values() )
def field_size( self, axis ):
size = { 'x': 'width', 'y': 'height' }[axis]
side = { 'x': 'right', 'y': 'top' }[axis]
values = getattr( self, 'get_%s_values' % axis )()
max_d = self.data_max( axis )
dx = float( max_d - values[-1] ) / ( values[-1] - values[-2] )
graph_size = getattr( self, 'graph_%s' % size )
side_font = getattr( self, '%s_font' % side )
side_align = getattr( self, '%s_align' % side )
result = ( float( graph_size ) - self.font_size*2*side_font ) / \
( len( values ) + dx - side_align )
def x_range(self): return self.data_range('x')
def y_range(self): return self.data_range('y')
def get_data_values(self, axis):
min_value, max_value, scale_division = self.data_range(axis)
return tuple(float_range(*self.data_range(axis)))
def get_x_values(self): return self.get_data_values('x')
def get_y_values(self): return self.get_data_values('y')
def get_x_labels(self):
return map(str, self.get_x_values())
def get_y_labels(self):
return map(str, self.get_y_values())
def field_size(self, axis):
size = {'x': 'width', 'y': 'height'}[axis]
side = {'x': 'right', 'y': 'top'}[axis]
values = getattr(self, 'get_%s_values' % axis)()
max_d = self.data_max(axis)
dx = float(max_d - values[-1]) / (values[-1] - values[-2])
graph_size = getattr(self, 'graph_%s' % size)
side_font = getattr(self, '%s_font' % side)
side_align = getattr(self, '%s_align' % side)
result = (float(graph_size) - self.font_size*2*side_font) / \
(len(values) + dx - side_align)
return result
def field_width( self ): return self.field_size( 'x' )
def field_height( self ): return self.field_size( 'y' )
def field_width(self): return self.field_size('x')
def field_height(self): return self.field_size('y')
def draw_data( self ):
def draw_data(self):
self.load_transform_parameters()
for line, data in izip( count(1), self.data ):
for line, data in izip(count(1), self.data):
x_start, y_start = self.transform_output_coordinates(
( data['data'][self.x_data_index][0],
data['data'][self.y_data_index][0] )
(data['data'][self.x_data_index][0],
data['data'][self.y_data_index][0])
)
data_points = zip( *data['data'] )
graph_points = self.get_graph_points( data_points )
lpath = self.get_lpath( graph_points )
data_points = zip(*data['data'])
graph_points = self.get_graph_points(data_points)
lpath = self.get_lpath(graph_points)
if self.area_fill:
graph_height = self.graph_height
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M%(x_start)f %(graph_height)f %(lpath)s V%(graph_height)f Z' % vars(),
'class': 'fill%(line)d' % vars() } )
self.graph.appendChild( path )
'class': 'fill%(line)d' % vars()})
self.graph.appendChild(path)
if self.draw_lines_between_points:
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M%(x_start)f %(y_start)f %(lpath)s' % vars(),
'class': 'line%(line)d' % vars() } )
self.graph.appendChild( path )
self.draw_data_points( line, data_points, graph_points )
self._draw_constant_lines( )
'class': 'line%(line)d' % vars()})
self.graph.appendChild(path)
self.draw_data_points(line, data_points, graph_points)
self._draw_constant_lines()
del self.__transform_parameters
def add_constant_line( self, value, label = None, style = None ):
self.constant_lines = getattr( self, 'constant_lines', [] )
self.constant_lines.append( ( value, label, style ) )
def add_constant_line(self, value, label = None, style = None):
self.constant_lines = getattr(self, 'constant_lines', [])
self.constant_lines.append((value, label, style))
def _draw_constant_lines( self ):
if hasattr( self, 'constant_lines' ):
map( self.__draw_constant_line, self.constant_lines )
def _draw_constant_lines(self):
if hasattr(self, 'constant_lines'):
map(self.__draw_constant_line, self.constant_lines)
def __draw_constant_line( self, ( value, label, style ) ):
def __draw_constant_line(self, (value, label, style)):
"Draw a constant line on the y-axis with the label"
start = self.transform_output_coordinates( ( 0, value ) )[1]
start = self.transform_output_coordinates((0, value))[1]
stop = self.graph_width
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M 0 %(start)s h%(stop)s' % vars(),
'class': 'constantLine' } )
'class': 'constantLine'})
if style:
path['style'] = style
self.graph.appendChild( path )
text = self._create_element( 'text', {
'x': str( 2 ),
'y': str( start - 2 ),
'class': 'constantLine' } )
text.appendChild( self._doc.createTextNode( label ) )
self.graph.appendChild( text )
def load_transform_parameters( self ):
self.graph.appendChild(path)
text = self._create_element('text', {
'x': str(2),
'y': str(start - 2),
'class': 'constantLine'})
text.appendChild(self._doc.createTextNode(label))
self.graph.appendChild(text)
def load_transform_parameters(self):
"Cache the parameters necessary to transform x & y coordinates"
x_min, x_max, x_div = self.x_range()
y_min, y_max, y_div = self.y_range()
x_step = ( float( self.graph_width ) - self.font_size*2 ) / \
( x_max - x_min )
y_step = ( float( self.graph_height ) - self.font_size*2 ) / \
( y_max - y_min )
self.__transform_parameters = dict( vars() )
x_step = (float(self.graph_width) - self.font_size*2) / \
(x_max - x_min)
y_step = (float(self.graph_height) - self.font_size*2) / \
(y_max - y_min)
self.__transform_parameters = dict(vars())
del self.__transform_parameters['self']
def get_graph_points( self, data_points ):
return map( self.transform_output_coordinates, data_points )
def get_graph_points(self, data_points):
return map(self.transform_output_coordinates, data_points)
def get_lpath( self, points ):
points = map( lambda p: "%f %f" % p, points )
return 'L' + ' '.join( points )
def get_lpath(self, points):
points = map(lambda p: "%f %f" % p, points)
return 'L' + ' '.join(points)
def transform_output_coordinates( self, (x,y) ):
def transform_output_coordinates(self, (x,y)):
x_min = self.__transform_parameters['x_min']
x_step = self.__transform_parameters['x_step']
y_min = self.__transform_parameters['y_min']
y_step = self.__transform_parameters['y_step']
#locals().update( self.__transform_parameters )
#vars().update( self.__transform_parameters )
x = ( x - x_min ) * x_step
y = self.graph_height - ( y - y_min ) * y_step
#locals().update(self.__transform_parameters)
#vars().update(self.__transform_parameters)
x = (x - x_min) * x_step
y = self.graph_height - (y - y_min) * y_step
return x,y
def draw_data_points( self, line, data_points, graph_points ):
def draw_data_points(self, line, data_points, graph_points):
if not self.show_data_points \
and not self.show_data_values: return
for ((dx,dy),(gx,gy)) in izip( data_points, graph_points ):
for ((dx,dy),(gx,gy)) in izip(data_points, graph_points):
if self.show_data_points:
circle = self._create_element( 'circle', {
'cx': str( gx ),
'cy': str( gy ),
circle = self._create_element('circle', {
'cx': str(gx),
'cy': str(gy),
'r': '2.5',
'class': 'dataPoint%(line)s' % vars() } )
self.graph.appendChild( circle )
'class': 'dataPoint%(line)s' % vars()})
self.graph.appendChild(circle)
if self.show_data_values:
self.add_popup( gx, gy, self.format( dx, dy ) )
self.make_datapoint_text( gx, gy-6, dy )
self.add_popup(gx, gy, self.format(dx, dy))
self.make_datapoint_text(gx, gy-6, dy)
def format( self, x, y ):
def format(self, x, y):
return '(%0.2f, %0.2f)' % (x,y)
def get_css( self ):
def get_css(self):
return """/* default line styles */
.line1{
fill: none;

62
lib/SVG/TimeSeries.py

@ -9,7 +9,7 @@ from time import mktime
import datetime
fromtimestamp = datetime.datetime.fromtimestamp
class Plot( SVG.Plot.Plot ):
class Plot(SVG.Plot.Plot):
"""=== For creating SVG plots of scalar temporal data
= Synopsis
@ -23,7 +23,7 @@ class Plot( SVG.Plot.Plot ):
"5/1/02", 14, "3/1/95", 6, "8/1/91", 12, "12/1/87", 6,
"5/1/84", 17, "10/1/80", 12]
graph = SVG::Graph::TimeSeries.new( {
graph = SVG::Graph::TimeSeries.new({
:width => 640,
:height => 480,
:graph_title => title,
@ -75,8 +75,8 @@ class Plot( SVG.Plot.Plot ):
Unlike the other types of charts, data sets must contain x,y pairs:
[ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
[ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
["12:30", 2] # A data set with 1 point: ("12:30",2)
["01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# ("14:20",6)
Note that multiple data sets within the same chart can differ in length,
@ -118,10 +118,10 @@ class Plot( SVG.Plot.Plot ):
will cause the chart to try to divide the X axis up into segments of
two week periods."""
def add_data( self, data ):
def add_data(self, data):
"""Add data to the plot.
d1 = [ "12:30", 2 ] # A data set with 1 point: ("12:30",2)
d2 = [ "01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
d1 = ["12:30", 2] # A data set with 1 point: ("12:30",2)
d2 = ["01:00",2, "14:20",6] # A data set with 2 points: ("01:00",2) and
# ("14:20",6)
graph.add_data(
:data => d1,
@ -134,49 +134,49 @@ class Plot( SVG.Plot.Plot ):
Note that the data must be in time,value pairs, and that the date format
may be any date that is parseable by ParseDate."""
super( Plot, self ).add_data( data )
super(Plot, self).add_data(data)
def process_data( self, data ):
super( Plot, self ).process_data( data )
def process_data(self, data):
super(Plot, self).process_data(data)
# the date should be in the first element, so parse it out
data['data'][0] = map( self.parse_date, data['data'][0] )
data['data'][0] = map(self.parse_date, data['data'][0])
_min_x_value = SVG.Plot.Plot.min_x_value
def get_min_x_value( self ):
def get_min_x_value(self):
return self._min_x_value
def set_min_x_value( self, date ):
self._min_x_value = self.parse_date( date )
min_x_value = property( get_min_x_value, set_min_x_value )
def set_min_x_value(self, date):
self._min_x_value = self.parse_date(date)
min_x_value = property(get_min_x_value, set_min_x_value)
def format( self, x, y ):
return fromtimestamp( x ).strftime( self.popup_format )
def format(self, x, y):
return fromtimestamp(x).strftime(self.popup_format)
def get_x_labels( self ):
return map( lambda t: fromtimestamp( t ).strftime( self.x_label_format ), self.get_x_values() )
def get_x_labels(self):
return map(lambda t: fromtimestamp(t).strftime(self.x_label_format), self.get_x_values())
def get_x_values( self ):
def get_x_values(self):
result = self.get_x_timescale_division_values()
if result: return result
return tuple( SVG.Plot.float_range( *self.x_range() ) )
return tuple(SVG.Plot.float_range(*self.x_range()))
def get_x_timescale_division_values( self ):
def get_x_timescale_division_values(self):
if not self.timescale_divisions: return
min, max, scale_division = self.x_range()
m = re.match( '(?P<amount>\d+) ?(?P<division_units>days|weeks|months|years|hours|minutes|seconds)?', self.timescale_divisions )
m = re.match('(?P<amount>\d+) ?(?P<division_units>days|weeks|months|years|hours|minutes|seconds)?', self.timescale_divisions)
# copy amount and division_units into the local namespace
division_units = m.groupdict()['division_units'] or 'days'
amount = int( m.groupdict()['amount'] )
amount = int(m.groupdict()['amount'])
if not amount: return
delta = relativedelta( **{ division_units: amount } )
result = tuple( self.get_time_range( min, max, delta ) )
delta = relativedelta(**{division_units: amount})
result = tuple(self.get_time_range(min, max, delta))
return result
def get_time_range( self, start, stop, delta ):
start, stop = map( fromtimestamp, (start, stop ) )
def get_time_range(self, start, stop, delta):
start, stop = map(fromtimestamp, (start, stop))
current = start
while current <= stop:
yield mktime( current.timetuple() )
yield mktime(current.timetuple())
current += delta
def parse_date( self, date_string ):
return mktime( parse( date_string ).timetuple() )
def parse_date(self, date_string):
return mktime(parse(date_string).timetuple())

482
lib/SVG/__init__.py

@ -13,13 +13,13 @@ try:
except ImportError:
__have_zlib = False
def sort_multiple( arrays ):
def sort_multiple(arrays):
"sort multiple lists (of equal size) using the first list for the sort keys"
tuples = zip( *arrays )
tuples = zip(*arrays)
tuples.sort()
return zip( *tuples )
return zip(*tuples)
class Graph( object ):
class Graph(object):
"""=== Base object for generating SVG Graphs
== Synopsis
@ -112,17 +112,17 @@ Copyright © 2008 Jason R. Coombs
top_align = top_font = right_align = right_font = 0
def __init__( self, config = {} ):
def __init__(self, config = {}):
"""Initialize the graph object with the graph settings."""
if self.__class__ is Graph:
raise NotImplementedError, "Graph is an abstract base class"
self.load_config( config )
self.load_config(config)
self.clear_data()
def load_config( self, config ):
self.__dict__.update( config )
def load_config(self, config):
self.__dict__.update(config)
def add_data( self, conf ):
def add_data(self, conf):
"""This method allows you do add data to the graph object.
It can be called several times to add more data sets in.
@ -130,31 +130,31 @@ Copyright © 2008 Jason R. Coombs
>>> graph.add_data({
... 'data': data_sales_02,
... 'title': 'Sales 2002'
... })
...})
"""
self.validate_data( conf )
self.process_data( conf )
self.data.append( conf )
self.validate_data(conf)
self.process_data(conf)
self.data.append(conf)
def validate_data( self, conf ):
def validate_data(self, conf):
try:
assert( isinstance( conf['data'], ( tuple, list ) ) )
assert(isinstance(conf['data'], (tuple, list)))
except TypeError, e:
raise TypeError, "conf should be dictionary with 'data' and other items"
except AssertionError:
if not hasattr( conf['data'], '__iter__' ):
if not hasattr(conf['data'], '__iter__'):
raise TypeError, "conf['data'] should be tuple or list or iterable"
def process_data( self, data ): pass
def process_data(self, data): pass
def clear_data( self ):
def clear_data(self):
"""This method removes all data from the object so that you can
reuse it to create a new graph but with the same config options.
>>> graph.clear_data()"""
self.data = []
def burn( self ):
def burn(self):
"""This method processes the template with the data and
config which has been set and returns the resulting SVG.
@ -162,25 +162,25 @@ Copyright © 2008 Jason R. Coombs
been added to the graph object.
Ex: graph.burn()"""
if not self.data: raise ValueError( "No data available" )
if not self.data: raise ValueError("No data available")
if hasattr( self, 'calculations' ): self.calculations()
if hasattr(self, 'calculations'): self.calculations()
self.start_svg()
self.calculate_graph_dimensions()
self.foreground = self._create_element( "g" )
self.foreground = self._create_element("g")
self.draw_graph()
self.draw_titles()
self.draw_legend()
self.draw_data()
self.graph.appendChild( self.foreground )
self.graph.appendChild(self.foreground)
self.style()
data = self._doc.toprettyxml()
if hasattr( self, 'compress' ) and self.compress:
if hasattr(self, 'compress') and self.compress:
if __have_zlib:
data = zlib.compress( data )
data = zlib.compress(data)
else:
data += '<!-- Python zlib not available for SVGZ -->'
@ -188,7 +188,7 @@ Copyright © 2008 Jason R. Coombs
KEY_BOX_SIZE = 12
def calculate_left_margin( self ):
def calculate_left_margin(self):
"""Override this (and call super) to change the margin to the left
of the plot area. Results in border_left being set."""
bl = 7
@ -196,32 +196,32 @@ Copyright © 2008 Jason R. Coombs
if self.rotate_y_labels:
max_y_label_height_px = self.y_label_font_size
else:
label_lengths = map( len, self.get_y_labels() )
max_y_label_len = max( label_lengths )
label_lengths = map(len, self.get_y_labels())
max_y_label_len = max(label_lengths)
max_y_label_height_px = 0.6 * max_y_label_len * self.y_label_font_size
if self.show_y_labels: bl += max_y_label_height_px
if self.stagger_y_labels: bl += max_y_label_height_px + 10
if self.show_y_title: bl += self.y_title_font_size + 5
self.border_left = bl
def max_y_label_width_px( self ):
def max_y_label_width_px(self):
"""Calculates the width of the widest Y label. This will be the
character height if the Y labels are rotated."""
if self.rotate_y_labels:
return self.font_size
def calculate_right_margin( self ):
def calculate_right_margin(self):
"""Override this (and call super) to change the margin to the right
of the plot area. Results in border_right being set."""
br = 7
if self.key and self.key_position == 'right':
max_key_len = max( map( len, self.keys() ) )
max_key_len = max(map(len, self.keys()))
br += max_key_len * self.key_font_size * 0.6
br += self.KEY_BOX_SIZE
br += 10 # Some padding around the box
self.border_right = br
def calculate_top_margin( self ):
def calculate_top_margin(self):
"""Override this (and call super) to change the margin to the top
of the plot area. Results in border_top being set."""
self.border_top = 5
@ -229,259 +229,259 @@ Copyright © 2008 Jason R. Coombs
self.border_top += 5
if self.show_graph_subtitle: self.border_top += self.subtitle_font_size
def add_popup( self, x, y, label ):
def add_popup(self, x, y, label):
"Adds pop-up point information to a graph."
txt_width = len( label ) * self.font_size * 0.6 + 10
txt_width = len(label) * self.font_size * 0.6 + 10
tx = x + [5,-5][int(x+txt_width > self.width)]
t = self._create_element( 'text' )
t = self._create_element('text')
anchor = ['start', 'end'][x+txt_width > self.width]
style = 'fill: #000; text-anchor: %s;' % anchor
id = 'label-%s' % label
attributes = { 'x': str( tx ),
'y': str( y - self.font_size ),
attributes = {'x': str(tx),
'y': str(y - self.font_size),
'visibility': 'hidden',
'style': style,
'text': label,
'id': id
}
map( lambda a: t.setAttribute( *a ), attributes.items() )
self.foreground.appendChild( t )
map(lambda a: t.setAttribute(*a), attributes.items())
self.foreground.appendChild(t)
visibility = "document.getElementById(%s).setAttribute('visibility', %%s )" % id
t = self._create_element( 'circle' )
attributes = { 'cx': str( x ),
'cy': str( y ),
visibility = "document.getElementById(%s).setAttribute('visibility', %%s)" % id
t = self._create_element('circle')
attributes = {'cx': str(x),
'cy': str(y),
'r': 10,
'style': 'opacity: 0;',
'onmouseover': visibility % 'visible',
'onmouseout': visibility % 'hidden',
}
map( lambda a: t.setAttribute( *a ), attributes.items() )
map(lambda a: t.setAttribute(*a), attributes.items())
def calculate_bottom_margin( self ):
def calculate_bottom_margin(self):
"""Override this (and call super) to change the margin to the bottom
of the plot area. Results in border_bottom being set."""
bb = 7
if self.key and self.key_position == 'bottom':
bb += len( self.data ) * ( self.font_size + 5 )
bb += len(self.data) * (self.font_size + 5)
bb += 10
if self.show_x_labels:
max_x_label_height_px = self.x_label_font_size
if self.rotate_x_labels:
label_lengths = map( len, self.get_x_labels() )
max_x_label_len = reduce( max, label_lengths )
label_lengths = map(len, self.get_x_labels())
max_x_label_len = reduce(max, label_lengths)
max_x_label_height_px *= 0.6 * max_x_label_len
bb += max_x_label_height_px
if self.stagger_x_labels: bb += max_x_label_height_px + 10
if self.show_x_title: bb += self.x_title_font_size + 5
self.border_bottom = bb
def draw_graph( self ):
transform = 'translate ( %s %s )' % ( self.border_left, self.border_top )
self.graph = self._create_element( 'g', { 'transform': transform } )
self.root.appendChild( self.graph )
def draw_graph(self):
transform = 'translate (%s %s)' % (self.border_left, self.border_top)
self.graph = self._create_element('g', {'transform': transform})
self.root.appendChild(self.graph)
self.graph.appendChild( self._create_element( 'rect', {
self.graph.appendChild(self._create_element('rect', {
'x': '0',
'y': '0',
'width': str( self.graph_width ),
'height': str( self.graph_height ),
'width': str(self.graph_width),
'height': str(self.graph_height),
'class': 'graphBackground'
} ) )
}))
#Axis
self.graph.appendChild( self._create_element( 'path', {
self.graph.appendChild(self._create_element('path', {
'd': 'M 0 0 v%s' % self.graph_height,
'class': 'axis',
'id': 'xAxis'
} ) )
self.graph.appendChild( self._create_element( 'path', {
'd': 'M 0 %s h%s' % ( self.graph_height, self.graph_width ),
}))
self.graph.appendChild(self._create_element('path', {
'd': 'M 0 %s h%s' % (self.graph_height, self.graph_width),
'class': 'axis',
'id': 'yAxis'
} ) )
}))
self.draw_x_labels()
self.draw_y_labels()
def x_label_offset( self, width ):
def x_label_offset(self, width):
"""Where in the X area the label is drawn
Centered in the field, should be width/2. Start, 0."""
return 0
def make_datapoint_text( self, x, y, value, style='' ):
def make_datapoint_text(self, x, y, value, style=''):
if self.show_data_values:
e = self._create_element( 'text', {
'x': str( x ),
'y': str( y ),
e = self._create_element('text', {
'x': str(x),
'y': str(y),
'class': 'dataPointLabel',
'style': '%(style)s stroke: #fff; stroke-width: 2;' % vars(),
} )
e.appendChild( self._doc.createTextNode( str( value ) ) )
self.foreground.appendChild( e )
e = self._create_element( 'text', {
'x': str( x ),
'y': str( y ),
'class': 'dataPointLabel' } )
e.appendChild( self._doc.createTextNode( str( value ) ) )
if style: e.setAttribute( 'style', style )
self.foreground.appendChild( e )
def draw_x_labels( self ):
})
e.appendChild(self._doc.createTextNode(str(value)))
self.foreground.appendChild(e)
e = self._create_element('text', {
'x': str(x),
'y': str(y),
'class': 'dataPointLabel'})
e.appendChild(self._doc.createTextNode(str(value)))
if style: e.setAttribute('style', style)
self.foreground.appendChild(e)
def draw_x_labels(self):
"Draw the X axis labels"
if self.show_x_labels:
labels = self.get_x_labels()
count = len( labels )
count = len(labels)
labels = enumerate( iter( labels ) )
start = int( not self.step_include_first_x_label )
labels = islice( labels, start, None, self.step_x_labels )
map( self.draw_x_label, labels )
self.draw_x_guidelines( self.field_width(), count )
labels = enumerate(iter(labels))
start = int(not self.step_include_first_x_label)
labels = islice(labels, start, None, self.step_x_labels)
map(self.draw_x_label, labels)
self.draw_x_guidelines(self.field_width(), count)
def draw_x_label( self, label ):
def draw_x_label(self, label):
label_width = self.field_width()
index, label = label
text = self._create_element( 'text', { 'class': 'xAxisLabels' } )
text.appendChild( self._doc.createTextNode( label ) )
self.graph.appendChild( text )
text = self._create_element('text', {'class': 'xAxisLabels'})
text.appendChild(self._doc.createTextNode(label))
self.graph.appendChild(text)
x = index * label_width + self.x_label_offset( label_width )
x = index * label_width + self.x_label_offset(label_width)
y = self.graph_height + self.x_label_font_size + 3
t = 0 - ( self.font_size / 2 )
t = 0 - (self.font_size / 2)
if self.stagger_x_labels and (index % 2 ):
if self.stagger_x_labels and (index % 2):
stagger = self.x_label_font_size + 5
y += stagger
graph_height = self.graph_height
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M%(x)f %(graph_height)f v%(stagger)d' % vars(),
'class': 'staggerGuideLine'
} )
self.graph.appendChild( path )
})
self.graph.appendChild(path)
text.setAttribute( 'x', str( x ) )
text.setAttribute( 'y', str( y ) )
text.setAttribute('x', str(x))
text.setAttribute('y', str(y))
if self.rotate_x_labels:
transform = 'rotate( 90 %d %d ) translate( 0 -%d )' % \
( x, y-self.x_label_font_size, x_label_font_size/4 )
text.setAttribute( 'transform', transform )
text.setAttribute( 'style', 'text-anchor: start' )
transform = 'rotate(90 %d %d) translate(0 -%d)' % \
(x, y-self.x_label_font_size, x_label_font_size/4)
text.setAttribute('transform', transform)
text.setAttribute('style', 'text-anchor: start')
else:
text.setAttribute( 'style', 'text-anchor: middle' )
text.setAttribute('style', 'text-anchor: middle')
def y_label_offset( self, height ):
def y_label_offset(self, height):
"""Where in the Y area the label is drawn
Centered in the field, should be width/2. Start, 0."""
return 0
def get_field_width( self ):
return float( self.graph_width - self.font_size*2*self.right_font ) / \
( len( self.get_x_labels() ) - self.right_align )
def get_field_width(self):
return float(self.graph_width - self.font_size*2*self.right_font) / \
(len(self.get_x_labels()) - self.right_align)
field_width = get_field_width
def get_field_height( self ):
return float( self.graph_height - self.font_size*2*self.top_font ) / \
( len( self.get_y_labels() ) - self.top_align )
def get_field_height(self):
return float(self.graph_height - self.font_size*2*self.top_font) / \
(len(self.get_y_labels()) - self.top_align)
field_height = get_field_height
def draw_y_labels( self ):
def draw_y_labels(self):
"Draw the Y axis labels"
if self.show_y_labels:
labels = self.get_y_labels()
count = len( labels )
count = len(labels)
labels = enumerate( iter( labels ) )
start = int( not self.step_include_first_y_label )
labels = islice( labels, start, None, self.step_y_labels )
map( self.draw_y_label, labels )
self.draw_y_guidelines( self.field_height(), count )
labels = enumerate(iter(labels))
start = int(not self.step_include_first_y_label)
labels = islice(labels, start, None, self.step_y_labels)
map(self.draw_y_label, labels)
self.draw_y_guidelines(self.field_height(), count)
def get_y_offset( self ):
#result = self.graph_height + self.y_label_offset( label_height )
result = self.graph_height + self.y_label_offset( self.field_height() )
def get_y_offset(self):
#result = self.graph_height + self.y_label_offset(label_height)
result = self.graph_height + self.y_label_offset(self.field_height())
if not self.rotate_y_labels: result += self.font_size/1.2
return result
y_offset = property( get_y_offset )
y_offset = property(get_y_offset)
def draw_y_label( self, label ):
def draw_y_label(self, label):
label_height = self.field_height()
index, label = label
text = self._create_element( 'text', { 'class': 'yAxisLabels' } )
text.appendChild( self._doc.createTextNode( label ) )
self.graph.appendChild( text )
text = self._create_element('text', {'class': 'yAxisLabels'})
text.appendChild(self._doc.createTextNode(label))
self.graph.appendChild(text)
y = self.y_offset - ( label_height * index )
y = self.y_offset - (label_height * index)
x = {True: 0, False:-3}[self.rotate_y_labels]
if self.stagger_y_labels and (index % 2 ):
if self.stagger_y_labels and (index % 2):
stagger = self.y_label_font_size + 5
x -= stagger
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M%(x)f %(y)f h%(stagger)d' % vars(),
'class': 'staggerGuideLine'
} )
self.graph.appendChild( path )
})
self.graph.appendChild(path)
text.setAttribute( 'x', str( x ) )
text.setAttribute( 'y', str( y ) )
text.setAttribute('x', str(x))
text.setAttribute('y', str(y))
if self.rotate_y_labels:
transform = 'translate( -%d 0 ) rotate ( 90 %d %d )' % \
( self.font_size, x, y )
text.setAttribute( 'transform', transform )
text.setAttribute( 'style', 'text-anchor: middle' )
transform = 'translate(-%d 0) rotate (90 %d %d)' % \
(self.font_size, x, y)
text.setAttribute('transform', transform)
text.setAttribute('style', 'text-anchor: middle')
else:
text.setAttribute( 'y', str( y - self.y_label_font_size/2 ) )
text.setAttribute( 'style', 'text-anchor: end' )
text.setAttribute('y', str(y - self.y_label_font_size/2))
text.setAttribute('style', 'text-anchor: end')
def draw_x_guidelines( self, label_height, count ):
def draw_x_guidelines(self, label_height, count):
"Draw the X-axis guidelines"
if not self.show_x_guidelines: return
# skip the first one
for count in range(1,count):
start = label_height*count
stop = self.graph_height
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M %(start)s 0 v%(stop)s' % vars(),
'class': 'guideLines' } )
self.graph.appendChild( path )
'class': 'guideLines'})
self.graph.appendChild(path)
def draw_y_guidelines( self, label_height, count ):
def draw_y_guidelines(self, label_height, count):
"Draw the Y-axis guidelines"
if not self.show_y_guidelines: return
for count in range( 1, count ):
for count in range(1, count):
start = self.graph_height - label_height*count
stop = self.graph_width
path = self._create_element( 'path', {
path = self._create_element('path', {
'd': 'M 0 %(start)s h%(stop)s' % vars(),
'class': 'guideLines' } )
self.graph.appendChild( path )
'class': 'guideLines'})
self.graph.appendChild(path)
def draw_titles( self ):
def draw_titles(self):
"Draws the graph title and subtitle"
if self.show_graph_title: self.draw_graph_title()
if self.show_graph_subtitle: self.draw_graph_subtitle()
if self.show_x_title: self.draw_x_title()
if self.show_y_title: self.draw_y_title()
def draw_graph_title( self ):
text = self._create_element( 'text', {
'x': str( self.width / 2 ),
'y': str( self.title_font_size ),
'class': 'mainTitle' } )
text.appendChild( self._doc.createTextNode( self.graph_title ) )
self.root.appendChild( text )
def draw_graph_title(self):
text = self._create_element('text', {
'x': str(self.width / 2),
'y': str(self.title_font_size),
'class': 'mainTitle'})
text.appendChild(self._doc.createTextNode(self.graph_title))
self.root.appendChild(text)
def draw_graph_subtitle( self ):
def draw_graph_subtitle(self):
raise NotImplementedError
def draw_x_title( self ):
def draw_x_title(self):
raise NotImplementedError
def draw_y_title( self ):
def draw_y_title(self):
x = self.y_title_font_size
if self.y_title_text_direction=='bt':
x += 3
@ -490,121 +490,121 @@ Copyright © 2008 Jason R. Coombs
x -= 3
rotate = 90
y = self.height / 2
text = self._create_element( 'text', {
'x': str( x ),
'y': str( y ),
text = self._create_element('text', {
'x': str(x),
'y': str(y),
'class': 'yAxisTitle',
} )
text.appendChild( self._doc.createTextNode( self.y_title ) )
text.setAttribute( 'transform', 'rotate( %(rotate)d, %(x)s, %(y)s )' % vars() )
self.root.appendChild( text )
})
text.appendChild(self._doc.createTextNode(self.y_title))
text.setAttribute('transform', 'rotate(%(rotate)d, %(x)s, %(y)s)' % vars())
self.root.appendChild(text)
def keys( self ):
return map( itemgetter( 'title' ), self.data )
def keys(self):
return map(itemgetter('title'), self.data)
def draw_legend( self ):
def draw_legend(self):
if self.key:
group = self._create_element( 'g' )
self.root.appendChild( group )
group = self._create_element('g')
self.root.appendChild(group)
for key_count, key_name in enumerate( self.keys() ):
y_offset = ( self.KEY_BOX_SIZE * key_count ) + (key_count * 5 )
rect = self._create_element( 'rect', {
for key_count, key_name in enumerate(self.keys()):
y_offset = (self.KEY_BOX_SIZE * key_count) + (key_count * 5)
rect = self._create_element('rect', {
'x': '0',
'y': str( y_offset ),
'width': str( self.KEY_BOX_SIZE ),
'height': str( self.KEY_BOX_SIZE ),
'y': str(y_offset),
'width': str(self.KEY_BOX_SIZE),
'height': str(self.KEY_BOX_SIZE),
'class': 'key%s' % (key_count + 1),
} )
group.appendChild( rect )
text = self._create_element( 'text', {
'x': str( self.KEY_BOX_SIZE + 5 ),
'y': str( y_offset + self.KEY_BOX_SIZE ),
'class': 'keyText' } )
text.appendChild( self._doc.createTextNode( key_name ) )
group.appendChild( text )
})
group.appendChild(rect)
text = self._create_element('text', {
'x': str(self.KEY_BOX_SIZE + 5),
'y': str(y_offset + self.KEY_BOX_SIZE),
'class': 'keyText'})
text.appendChild(self._doc.createTextNode(key_name))
group.appendChild(text)
if self.key_position == 'right':
x_offset = self.graph_width + self.border_left + 10
y_offset = self.border_top + 20
if self.key_position == 'bottom':
raise NotImplementedError
group.setAttribute( 'transform', 'translate(%(x_offset)d %(y_offset)d)' % vars() )
group.setAttribute('transform', 'translate(%(x_offset)d %(y_offset)d)' % vars())
def style( self ):
def style(self):
"hard code the styles into the xml if style sheets are not used."
if self.no_css:
styles = self.parse_css()
for node in xpath.Evaluate( '//*[@class]', self.root ):
cl = node.getAttribute( 'class' )
style = styles[ cl ]
if node.hasAttribute( 'style' ):
style += node.getAtrtibute( 'style' )
node.setAttribute( 'style', style )
def parse_css( self ):
for node in xpath.Evaluate('//*[@class]', self.root):
cl = node.getAttribute('class')
style = styles[cl]
if node.hasAttribute('style'):
style += node.getAtrtibute('style')
node.setAttribute('style', style)
def parse_css(self):
"""Take a .css file (classes only please) and parse it into a dictionary
of class/style pairs."""
css = self.get_style()
result = {}
for match in re.finditer( '^(?<names>\.(\w+)(?:\s*,\s*\.\w+)*)\s*\{(?<content>[^}]+)\}' ):
for match in re.finditer('^(?<names>\.(\w+)(?:\s*,\s*\.\w+)*)\s*\{(?<content>[^}]+)\}'):
names = match.group_dict()['names']
# apperantly, we're only interested in class names
names = filter( None, re.split( '\s*,?\s*\.' ) )
names = filter(None, re.split('\s*,?\s*\.'))
content = match.group_dict()['content']
# convert all whitespace to
content = re.sub( '\s+', ' ', content )
content = re.sub('\s+', ' ', content)
for name in names:
result[name] = ';'.join( result[name], content )
result[name] = ';'.join(result[name], content)
return result
def add_defs( self, defs ):
def add_defs(self, defs):
"Override and place code to add defs here"
pass
def start_svg( self ):
def start_svg(self):
"Base SVG Document Creation"
impl = dom.getDOMImplementation()
self._doc = impl.createDocument( None, 'svg', None )
self._doc = impl.createDocument(None, 'svg', None)
self.root = self._doc.documentElement
if hasattr( self, 'style_sheet' ):
pi = self._doc.createProcessingInstruction( 'xml-stylesheet',
'href="%s" type="text/css"' % self.style_sheet )
if hasattr(self, 'style_sheet'):
pi = self._doc.createProcessingInstruction('xml-stylesheet',
'href="%s" type="text/css"' % self.style_sheet)
attributes = {
'width': str( self.width ),
'height': str( self.height ),
'viewBox': '0 0 %s %s' % ( self.width, self.height ),
'width': str(self.width),
'height': str(self.height),
'viewBox': '0 0 %s %s' % (self.width, self.height),
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'xmlns:a3': 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
'a3:scriptImplementation': 'Adobe' }
map( lambda a: self.root.setAttribute( *a ), attributes.items() )
self.root.appendChild( self._doc.createComment( ' Created with SVG.Graph ' ) )
self.root.appendChild( self._doc.createComment( ' SVG.Graph by Jason R. Coombs ' ) )
self.root.appendChild( self._doc.createComment( ' Based on SVG::Graph by Sean E. Russel ' ) )
self.root.appendChild( self._doc.createComment( ' Based on Perl SVG:TT:Graph by Leo Lapworth & Stephan Morgan ' ) )
self.root.appendChild( self._doc.createComment( ' '+'/'*66 ) )
defs = self._create_element( 'defs' )
self.add_defs( defs )
self.root.appendChild( defs )
if not hasattr( self, 'style_sheet' ) and not self.no_css:
self.root.appendChild( self._doc.createComment( ' include default stylesheet if none specified ' ) )
style = self._create_element( 'style', { 'type': 'text/css' } )
defs.appendChild( style )
style_data = self._doc.createCDATASection( self.get_style() )
style.appendChild( style_data )
self.root.appendChild( self._doc.createComment( 'SVG Background' ) )
rect = self._create_element( 'rect', {
'width': str( self.width ),
'height': str( self.height ),
'a3:scriptImplementation': 'Adobe'}
map(lambda a: self.root.setAttribute(*a), attributes.items())
self.root.appendChild(self._doc.createComment(' Created with SVG.Graph '))
self.root.appendChild(self._doc.createComment(' SVG.Graph by Jason R. Coombs '))
self.root.appendChild(self._doc.createComment(' Based on SVG::Graph by Sean E. Russel '))
self.root.appendChild(self._doc.createComment(' Based on Perl SVG:TT:Graph by Leo Lapworth & Stephan Morgan '))
self.root.appendChild(self._doc.createComment(' '+'/'*66))
defs = self._create_element('defs')
self.add_defs(defs)
self.root.appendChild(defs)
if not hasattr(self, 'style_sheet') and not self.no_css:
self.root.appendChild(self._doc.createComment(' include default stylesheet if none specified '))
style = self._create_element('style', {'type': 'text/css'})
defs.appendChild(style)
style_data = self._doc.createCDATASection(self.get_style())
style.appendChild(style_data)
self.root.appendChild(self._doc.createComment('SVG Background'))
rect = self._create_element('rect', {
'width': str(self.width),
'height': str(self.height),
'x': '0',
'y': '0',
'class': 'svgBackground' } )
self.root.appendChild( rect )
'class': 'svgBackground'})
self.root.appendChild(rect)
def calculate_graph_dimensions( self ):
def calculate_graph_dimensions(self):
self.calculate_left_margin()
self.calculate_right_margin()
self.calculate_bottom_margin()
@ -612,7 +612,7 @@ Copyright © 2008 Jason R. Coombs
self.graph_width = self.width - self.border_left - self.border_right
self.graph_height = self.height - self.border_top - self.border_bottom
def get_style( self ):
def get_style(self):
result = """/* Copy from here for external style sheet */
.svgBackground{
fill:#ffffff;
@ -704,24 +704,24 @@ Copyright © 2008 Jason R. Coombs
font-weight: normal;
}
/* End copy for external style sheet */
""" % class_dict( self )
""" % class_dict(self)
result = result % self.get_css()
return result
def _create_element( self, nodeName, attributes={} ):
def _create_element(self, nodeName, attributes={}):
"Create an XML node and set the attributes from a dict"
node = self._doc.createElement( nodeName )
map( lambda a: node.setAttribute( *a ), attributes.items() )
node = self._doc.createElement(nodeName)
map(lambda a: node.setAttribute(*a), attributes.items())
return node
class class_dict( object ):
class class_dict(object):
"Emulates a dictionary, but retrieves class attributes"
def __init__( self, obj ):
def __init__(self, obj):
self.__obj__ = obj
def __getitem__( self, item ):
return getattr( self.__obj__, item )
def __getitem__(self, item):
return getattr(self.__obj__, item)
def keys( self ):
def keys(self):
# dir returns a good guess of what attributes might be available
return dir( self.__obj__ )
return dir(self.__obj__)

34
test/testing.py

@ -1,6 +1,6 @@
import sys, os
from SVG import Plot
g = Plot.Plot( {
g = Plot.Plot({
'min_x_value': 0,
'min_y_value': 0,
'area_fill': True,
@ -8,29 +8,29 @@ g = Plot.Plot( {
'stagger_y_labels': True,
'show_x_guidelines': True
})
g.add_data( { 'data': [ 1, 25, 2, 30, 3, 45 ], 'title': 'series 1' } )
g.add_data( { 'data': [ 1,30, 2, 31, 3, 40 ], 'title': 'series 2' } )
g.add_data( { 'data': [ .5,35, 1, 20, 3, 10.5 ], 'title': 'series 3' } )
g.add_data({'data': [1, 25, 2, 30, 3, 45], 'title': 'series 1'})
g.add_data({'data': [1,30, 2, 31, 3, 40], 'title': 'series 2'})
g.add_data({'data': [.5,35, 1, 20, 3, 10.5], 'title': 'series 3'})
res = g.burn()
f = open( r'Plot.py.svg', 'w' )
f.write( res )
f = open(r'Plot.py.svg', 'w')
f.write(res)
f.close()
from SVG import TimeSeries
g = TimeSeries.Plot( { } )
g = TimeSeries.Plot({})
g.timescale_divisions = '4 hours'
g.stagger_x_labels = True
g.x_label_format = '%d-%b %H:%M'
g.max_y_value = 200
g.add_data( { 'data': [ '2005-12-21T00:00:00', 20, '2005-12-22T00:00:00', 21 ], 'title': 'series 1' } )
g.add_data({'data': ['2005-12-21T00:00:00', 20, '2005-12-22T00:00:00', 21], 'title': 'series 1'})
res = g.burn()
f = open( r'TimeSeries.py.svg', 'w' )
f.write( res )
f = open(r'TimeSeries.py.svg', 'w')
f.write(res)
f.close()
from SVG import Bar
@ -45,8 +45,8 @@ g.width, g.height = 640,480
g.graph_title = 'Question 7'
g.show_graph_title = True
g.add_data( { 'data': [ -2, 3, 1, 3, 1 ], 'title': 'Female' } )
g.add_data( { 'data': [ 0, 2, 1, 5, 4 ], 'title': 'Male' } )
g.add_data({'data': [-2, 3, 1, 3, 1], 'title': 'Female'})
g.add_data({'data': [0, 2, 1, 5, 4], 'title': 'Male'})
open(r'VerticalBar.py.svg', 'w').write(g.burn())
@ -58,8 +58,8 @@ g.width, g.height = 640,480
g.graph_title = 'Question 7'
g.show_graph_title = True
g.add_data( { 'data': [ -2, 3, 1, 3, 1 ], 'title': 'Female' } )
g.add_data( { 'data': [ 0, 2, 1, 5, 4 ], 'title': 'Male' } )
g.add_data({'data': [-2, 3, 1, 3, 1], 'title': 'Female'})
g.add_data({'data': [0, 2, 1, 5, 4], 'title': 'Male'})
open(r'HorizontalBar.py.svg', 'w').write(g.burn())
@ -71,7 +71,7 @@ options = dict(
height=480,
graph_title='Question 8',
show_graph_title=True,
no_css=False, )
no_css=False,)
g.__dict__.update(options)
g.add_data(dict(data=[2,22,98,143,82], title='intermediate'))
@ -89,7 +89,7 @@ options = dict(
show_data_labels = True,
)
g.__dict__.update(options)
g.add_data( { 'data': [ -2, 3, 1, 3, 1 ], 'title': 'Female' } )
g.add_data( { 'data': [ 0, 2, 1, 5, 4 ], 'title': 'Male' } )
g.add_data({'data': [-2, 3, 1, 3, 1], 'title': 'Female'})
g.add_data({'data': [0, 2, 1, 5, 4], 'title': 'Male'})
open('Pie.py.svg', 'w').write(g.burn())
Loading…
Cancel
Save