Browse Source

Completed initial port of HorizontalBar

pull/8/head
jaraco 17 years ago
parent
commit
a913b7d374
  1. 136
      lib/SVG/Bar.py
  2. 9
      readme.txt
  3. 13
      test/testing.py
  4. 20
      test/testing.rb

136
lib/SVG/Bar.py

@ -21,6 +21,36 @@ class Bar( Graph ):
self.fields = fields
super( Bar, self ).__init__( *args, **kargs )
# adapted from Plot
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 ) )
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( )
range = max_value - min_value
data_pad = range / 20.0 or 10
scale_range = ( max_value + data_pad ) - min_value
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 ):
return self.fields
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 ) ) )
# above is same as
@ -32,6 +62,15 @@ class Bar( Graph ):
min_value = min( min_value, 0 )
return min_value
def get_bar_gap(self, field_size):
bar_gap = 10 # default gap
if field_size < 10:
# adjust for narrow fields
bar_gap = field_size / 2
# the following zero's out the gap if bar_gap is False
bar_gap = int(self.bar_gap) * bar_gap
return bar_gap
def get_css( self ):
return """\
/* default fill styles for multiple datasets (probably only use a single dataset on this graph though) */
@ -172,36 +211,10 @@ class VerticalBar( Bar ):
top_align = top_font = 1
def get_x_labels( self ):
return self.fields
return self.get_field_labels()
# adapted from plot (very much like calling data_range('y'))
def data_range( self ):
min_value = self.data_min( )
max_value = self.data_max( )
range = max_value - min_value
top_pad = range / 20.0 or 10
scale_range = ( max_value + top_pad ) - min_value
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
# adapted from Plot
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 ) )
if self.scale_integers:
result = map(int, result)
return result
# adapted from Plot
def get_y_labels( self ):
return map( str, self.get_data_values() )
return self.get_data_labels()
def x_label_offset( self, width ):
return width / 2.0
@ -210,12 +223,8 @@ class VerticalBar( Bar ):
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()) )
bar_gap = 0
if self.bar_gap:
bar_gap = 10
if self.get_field_width() < 10:
bar_gap = self.get_field_width() / 2
bar_gap = self.get_bar_gap(self.get_field_width())
bar_width = self.get_field_width() - bar_gap
if self.stack == 'side':
@ -254,3 +263,62 @@ class VerticalBar( Bar ):
self.graph.appendChild( rect )
self.make_datapoint_text( left + bar_width/2.0, top-6, value )
class HorizontalBar(Bar):
rotate_y_labels = True
show_x_guidelines = True
show_y_guidelines = False
right_align = right_font = True
def get_x_labels(self):
return self.get_data_labels()
def get_y_labels(self):
return self.get_field_labels()
def y_label_offset(self, height):
return height / -2.0
def draw_data(self):
min_value = self.data_min()
unit_size = float(self.graph_width)
unit_size -= self.font_size*2*self.right_font
unit_size /= max(self.get_data_values()) - min(self.get_data_values())
bar_gap = self.get_bar_gap(self.get_field_height())
bar_height = self.get_field_height() - bar_gap
if self.stack == 'side':
bar_height /= len(self.data)
y_mod = (bar_height / 2) + (self.font_size / 2)
for field_count, field in enumerate(self.fields):
for dataset_count, dataset in enumerate(self.data):
value = dataset['data'][field_count]
top = self.graph_height - (self.get_field_height() * (field_count+1))
if self.stack == 'side':
top += (bar_height * dataset_count)
# cases (assume 0 = +ve):
# value min length left
# +ve +ve value.abs - min minvalue.abs
# +ve -ve value.abs - 0 minvalue.abs
# -ve -ve value.abs - 0 minvalue.abs + value
length = (abs(value) - max(min_value, 0)) * unit_size
# left is 0 if value is negative
left = (abs(min_value) + min(value, 0)) * unit_size
rect = self._create_element('rect', {
'x': str(left),
'y': str(top),
'width': str(length),
'height': str(bar_height),
'class': 'fill%s' % (dataset_count+1),
})
self.graph.appendChild(rect)
self.make_datapoint_text(left+length+5, top+y_mod, value,
"text-anchor: start; ")

9
readme.txt

@ -1,6 +1,7 @@
--- Still to do ---
1) Finish porting all of the chart objects from the Ruby libraries.
2) Factor out default stylesheets so that individual properties may be overridden without copying the entire stylesheet into another app.
3) Implement javascript-based animation (See JellyGraph for a Silverlight example of what simple animation can do for a charting library).
4) Implement namespace-based packages (see http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages)... so that the package is svg.chart.Graph, etc.
- Finish porting all of the chart objects from the Ruby libraries.
- Factor out default stylesheets so that individual properties may be overridden without copying the entire stylesheet into another app.
- Implement javascript-based animation (See JellyGraph for a Silverlight example of what simple animation can do for a charting library).
- Implement namespace-based packages (see http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages)... so that the package is svg.chart.Graph, etc.
- Convert to using element-tree instead of whatever I'm using

13
test/testing.py

@ -50,6 +50,19 @@ g.add_data( { 'data': [ 0, 2, 1, 5, 4 ], 'title': 'Male' } )
open(r'VerticalBar.py.svg', 'w').write(g.burn())
g = Bar.HorizontalBar(fields)
g.stack = 'side'
g.scale_integers = True
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' } )
open(r'HorizontalBar.py.svg', 'w').write(g.burn())
g = Bar.VerticalBar(fields)
options = dict(
scale_integers=True,

20
test/testing.rb

@ -58,6 +58,26 @@ f = File.new('VerticalBar.rb.svg', 'w')
f.write(g.burn())
f.close()
require 'SVG/Graph/BarHorizontal'
g = SVG::Graph::BarHorizontal.new( {
:scale_integers=>true,
:stack=>:side,
:width=>640,
:height=>480,
:fields=>fields,
:graph_title=>'Question 7',
:show_graph_title=>true,
:no_css=>false
})
g.add_data({:data=>[-2,3,1,3,1], :title=>'Female'})
g.add_data({:data=>[0,2,1,5,4], :title=>'Male'})
f = File.new('HorizontalBar.rb.svg', 'w')
f.write(g.burn())
f.close()
g = SVG::Graph::Bar.new({
:scale_integers=>true,
:stack=>:side,

Loading…
Cancel
Save