Browse Source

Removed extraneous space per PEP-8

pull/8/head
jaraco 17 years ago
parent
commit
ba31107e08
  1. 468
      ez_setup.py
  2. 82
      lib/SVG/Bar.py
  3. 6
      lib/SVG/Line.py
  4. 10
      lib/SVG/Pie.py
  5. 258
      lib/SVG/Plot.py
  6. 70
      lib/SVG/TimeSeries.py
  7. 484
      lib/SVG/__init__.py
  8. 36
      test/testing.py

468
ez_setup.py

@ -1,234 +1,234 @@
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c7"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict, e:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first.\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c7"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
pkg_resources.require("setuptools>="+version)
except pkg_resources.VersionConflict, e:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first.\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

82
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
@ -169,12 +169,12 @@ class VerticalBar( Bar ):
# :height => 500,
# :width => 300,
# :fields => fields
# )
# )
#
# graph.add_data(
# :data => data_sales_02,
# :title => 'Sales 2002'
# )
# )
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn
@ -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/Line.py

@ -23,17 +23,17 @@ class Line(SVG.Graph):
:height => 500,
:width => 300,
:fields => fields,
})
})
graph.add_data({
:data => data_sales_02,
:title => 'Sales 2002',
})
})
graph.add_data({
:data => data_sales_03,
:title => 'Sales 2003',
})
})
print "Content-type: image/svg+xml\r\n\r\n";
print graph.burn();

10
lib/SVG/Pie.py

@ -28,12 +28,12 @@ class Pie(SVG.Graph):
# :height => 500,
# :width => 300,
# :fields => fields,
# })
# })
#
# graph.add_data({
# :data => data_sales_02,
# :title => 'Sales 2002',
# })
# })
#
# print "Content-type: image/svg+xml\r\n\r\n"
# print graph.burn();
@ -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
@ -26,11 +26,11 @@ class Plot( SVG.Graph ):
projection = [
6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13,
7, 9
]
]
actual = [
0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
15, 6, 4, 17, 2, 12
]
]
graph = SVG::Graph::Plot.new({
:height => 500,
@ -38,17 +38,17 @@ class Plot( SVG.Graph ):
:key => true,
:scale_x_integers => true,
:scale_y_integerrs => true,
})
})
graph.add_data({
:data => projection
:title => 'Projected',
})
})
graph.add_data({
:data => actual,
:title => 'Actual',
})
})
print graph.burn()
@ -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 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_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_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 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 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 )
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 ):
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;

70
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,
@ -43,17 +43,17 @@ class Plot( SVG.Plot.Plot ):
:y_title_text_direction => :bt,
:stagger_x_labels => true,
:x_label_format => "%m/%d/%y",
})
})
graph.add_data({
:data => projection
:title => 'Projected',
})
})
graph.add_data({
:data => actual,
:title => 'Actual',
})
})
print graph.burn()
@ -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,12 +118,12 @@ 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(
graph.add_data(
:data => d1,
:title => 'One'
)
@ -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())

484
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 )
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() )
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())
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 )
'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 ),
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__)

36
test/testing.py

@ -1,36 +1,36 @@
import sys, os
from SVG import Plot
g = Plot.Plot( {
g = Plot.Plot({
'min_x_value': 0,
'min_y_value': 0,
'area_fill': True,
'stagger_x_labels': True,
'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