This screencast will give you a peek at where I am headed with these blog posts: the use of IronPython and Visio to create (nice-looking) Infographics
The previous posts (1, 2) were about getting started with the Python Tools for Visual Studio. Now we will do something productive: draw something useful.
There are three pieces of code involved.
ironvisio.py – to hold the code I always need when talking to visio
import clr import System clr.AddReference("Microsoft.Office.Interop.Visio") import Microsoft.Office.Interop.Visio IVisio = Microsoft.Office.Interop.Visio
charting.py – this will draw some very basic charts. At a later point I am going to move the charting code to a C# project. At that point it will be a good demonstration of how you can reuse an existing .NET assembly.
import math class DataPoint : def __init__( self, value, label = None ) : self.Value = value if (label==None) : self.Label = str(value) else: self.Label = label class Size: def __init__( self, w, h) : self.Width = w self.Height = h class Point: def __init__( self, x, y) : self.X = x self.Y = y class VerticalBarChart : def __init__( self) : self.DataPoints = [] self.Categories = [] self.MaxHeight = 3.0 self.BarWidth=1.0 self.BarDistance=0.5 self.CategoryHeight = 0.5 self.CategoryDistance=0.0 self.Origin = Point(0,0) def Draw(self, page) : startx = self.Origin.X starty = self.Origin.Y numpoints = len(self.DataPoints) indices = xrange(numpoints ) skip = self.BarWidth+self.BarDistance lefts = [ startx + i*skip for i in indices] heights = normalize_to( (p.Value for p in self.DataPoints), self.MaxHeight) bottom = starty + self.CategoryHeight + self.CategoryDistance barrects = [ (left,bottom,left+self.BarWidth,bottom+height) for (left,height) in zip(lefts,heights) ] catrects = [ (left,starty,left+self.BarWidth,starty+self.CategoryHeight) for left in lefts ] # draw bars barshapes = [ page.DrawRectangle(*r) for r in barrects ] # set bar text for (shape,p) in zip(barshapes,self.DataPoints) : shape.Text = p.Label # draw category textboxes catshapes = [ page.DrawRectangle(*r) for r in catrects ] # set category text for (shape,cattext) in zip(catshapes,self.Categories) : shape.Text = cattext class CircleChart : def __init__( self) : self.DataPoints = [] self.Categories = [] self.MaxRadius= 0.5 self.CircleDistance=0.5 self.CategoryHeight = 0.5 self.CategoryDistance=0.0 self.Origin = Point(0,0) def Draw(self, page) : startx = self.Origin.X starty = self.Origin.Y numpoints = len(self.DataPoints) indices = xrange(numpoints ) maxv = max( ( p.Value for p in self.DataPoints) ) skip = 2*self.MaxRadius + self.CircleDistance centerxs= [ startx + i*skip for i in indices] normalized_values = normalize( (p.Value for p in self.DataPoints) ) radii = [ math.sqrt(v/math.pi) for v in normalized_values] radii = normalize_to( radii, self.MaxRadius ) bottom = starty centery = bottom + self.MaxRadius + self.CategoryHeight + self.CategoryDistance circlerects = [ (centerx-r,centery-r,centerx+r,centery+r) for (centerx,r) in zip(centerxs,radii) ] catrects = [ (centerx-self.MaxRadius,bottom,centerx+self.MaxRadius,bottom + self.CategoryHeight) for centerx in centerxs ] # draw circle circleshapes = [ page.DrawOval(*r) for r in circlerects] # set circle text for (shape,p) in zip(circleshapes,self.DataPoints) : shape.Text = p.Label # draw category textboxes catshapes = [ page.DrawRectangle(*r) for r in catrects ] # set category text for (shape,cattext) in zip(catshapes,self.Categories) : shape.Text = cattext def normalize( seq ) : items = [v for v in seq] m = max( items ) return [ float(v)/m for v in items ] def normalize_to( seq , s) : items = [v for v in seq] m = max( items ) return [ float(v)/m*s for v in items ]
demo.py – the “main” code that drives the demo
from ironvisio import * import charting app = IVisio.ApplicationClass() docs = app.Documents doc = docs.Add("") page = app.ActivePage values = [5,2,3,7,4] category_labels = ["A", "B", "C", "D", "E"] chart1= charting.VerticalBarChart() chart1.DataPoints = [ charting.DataPoint(v) for v in values ] chart1.Categories = category_labels chart1.Origin = charting.Point(0.5,0) chart2= charting.CircleChart() chart2.DataPoints = [ charting.DataPoint(v) for v in values ] chart2.Categories = category_labels chart2.Origin = charting.Point(0.5,4) chart1.Draw(page) chart2.Draw(page)
As you can see in the screencast, all the demo does is draw two rather plain charts:
With a minute of formatting in Visio – as demonstrated in the screencast - we end up with this:
In the last blog post, I used the interactive shell, in this one I went back to making a project with three source code files. If you haven’t used the Python Tools for VS before, you should at least know how to tell VS which file to run when the project is executed.
Right-click on the project and select Properties
And in the General tab, set the Startup file to demo.py (in my example) and make sure that Interpeter is set to IronPython
You can download the project here