preload
Controlling Visio Windows with Ruby Python Connectivity Check Script
Feb 01

I found it a bit odd that there doesn’t seem to be any tutorials on how to automate Microsoft Visio using Ruby. Most of the other Office apps have a bunch of pages on automation, but none on Visio….that or my Google Fu is leaving me ;)

So…. Here’s a brief tutorial on how to automate Visio using Ruby. First thing you need to do is bring in the WIN32OLE module and create a new instance of the Visio application

require 'win32ole'
visio = WIN32OLE.new('Visio.Application')

If you already have Visio running, you can connect to it with this line instead

visio = WIN32OLE.connect('Visio.Application')

Once you have your instance, its usually best to load in all the constants from the application. Visio has tons and tons, so if you are going to try and convert any VBA or C++ code over, you will need these.

class VisioConst
   # Empty class to hold constants
end
...
WIN32OLE.const_load(visio, VisioConst)

Be sure to put the class declaration below the ‘require’ statements so your code will read better. Now we will load in a Visio template just to make things easier on us. We could load in the stencil and open a new document with individual commands, but loading in the template does that all for us

docs = visio.Documents
mydoc = docs.Add("Basic Diagram.vst")

A lot of times applications can start up with a reduced window size, and with apps like Visio, I really would rather it use the entire screen. This code here sets Visio to maximize, and the “Zoom” level to 100%

ShowWindow(visio.WindowHandle32, 3)
visio.ActiveWindow.Zoom = 1

The ShowWindow function was covered in my last post. Ok! Now its time to start dropping some shapes onto the document! First you grab a reference to the page

page = visio.ActiveDocument.Pages.Item(1)

Next we’ll grab a rectangle off the stencil

stencil = visio.Documents("Basic Shapes.vss")
rectangle1 = stencil.Masters("Rectangle")

Once you have your reference to a ‘Master’ shape, you can keep using it to place multiple shapes onto your document. Placing the rectangle is rather simple

shape1 = page.Drop(rectangle1, 4.25, 5.5)
shape1.Text = "This is a test Rectangle"

If you don’t specify the unit of measurement, Visio seems to default to inches. The 4.25, 5.5 units are inches from the lower left corner of the document page. These numbers place the rectangle just about in the middle of the page. Ok, time for a circle

circle1 = stencil.Masters("Circle")
shape2 = page.Drop(circle1,  4.25, 7.5)
shape2.Text = "Test Circle"

Just for added fun, lets increase the size

shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormPinX).FormulaU = "101.475 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormPinY).FormulaU = "184.025 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormWidth).FormulaU = "52.95 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormHeight).FormulaU = "52.95 mm"

Whoa! What’s all that? Well, I must confess that I cheated here and just recorded a macro while I was resizing the circle and used the resultant VBA code as the basis for this Ruby code. Two things get moved when you resize: The center of the circle denoted by PinX and PinY, and size of the circle (Width and Height). This is where loading in all the constants earlier pays off. Interesting to note that the Visio macros seem to default to millimeters for its units of measurement. Now that we have resized it, we need to center it. Again, I cheated and recorded a macro to find out what the VBA code to do this was

visio.ActiveWindow.Select(shape2, VisioConst::VisSelect)
visio.ActiveWindow.Selection.Move(0.19685, -0.019685)

You would think that you should be able to just manipulate your shape2 object, but I haven’t found a good way to do that yet, so when all else fails….record a macro and convert that code. In this case, you select the shape, and then move the selection. Finally, its time to save our masterpiece

filename = getAbsolutePathName("#{Dir.pwd}/Demo1.vsd")
visio.ActiveDocument.SaveAsEx(
         filename,
         VisioConst::VisSaveAsWS + VisioConst::VisSaveAsListInMRU)

Visio doesn’t like the Ruby default seperator of ‘/’ so I use a scripting function to format the directory path to something Visio does like. I covered this function in a previous post. This file save is an ‘Extended’ form that saves the workspace (VisSaveAsWS) and also places the document on the Most Recently Used (VisSaveAsListInMRU) list.

There you have it! If you have any questions, please feel free to ask!
Here’s the complete Demo program:

require 'win32ole'
require 'exo/showwindow'

class VisioConst
   #Empty class to hold Visio Constants
end

def getAbsolutePathName(file)
   fso = WIN32OLE.new('Scripting.FileSystemObject')  ## VBA File System commands
   return fso.GetAbsolutePathName(file)
 end

# Open Visio
visio = WIN32OLE.new('Visio.Application')
# Load in Constants
WIN32OLE.const_load(visio, VisioConst)
# Load in a basic template that has a couple of defaul stencils
docs = visio.Documents
mydoc = docs.Add("Basic Diagram.vst")
# Max window & Zoom to 100% View
ShowWindow(visio.WindowHandle32, 3)
visio.ActiveWindow.Zoom = 1
# Get a reference to the new Page-1
page = visio.ActiveDocument.Pages.Item(1)
# Grab a Rectangle
stencil = visio.Documents("Basic Shapes.vss")
rectangle1 = stencil.Masters("Rectangle")
# Place it on the page
shape1 = page.Drop(rectangle1, 4.25, 5.5)
shape1.Text = "This is a test Rectangle"
# Grab a Circle
circle1 = stencil.Masters("Circle")
# Place it on the page
shape2 = page.Drop(circle1,  4.25, 7.5)
shape2.Text = "Test Circle"
# Resize to make bigger
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormPinX).FormulaU = "101.475 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormPinY).FormulaU = "184.025 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormWidth).FormulaU = "52.95 mm"
shape2.CellsSRC(VisioConst::VisSectionObject,
         VisioConst::VisRowXFormOut,
         VisioConst::VisXFormHeight).FormulaU = "52.95 mm"
# Move over a bit
visio.ActiveWindow.Select(shape2, VisioConst::VisSelect)
visio.ActiveWindow.Selection.Move(0.19685, -0.019685)
# Save the File
filename = getAbsolutePathName("#{Dir.pwd}/Demo1.vsd")
visio.ActiveDocument.SaveAsEx(
         filename,
         VisioConst::VisSaveAsWS + VisioConst::VisSaveAsListInMRU)
#Exit Visio
visio.Quit
SociBook del.icio.us Digg Facebook Google Yahoo Buzz StumbleUpon

Leave a Reply