Visio Diagrams with Python: Resize to fit

Resize Page to Fit Contents, Set Zoom to Fit Page

When the drawing is ready, the size of the page will be too large or too small – „fit to contents“ might be the solution.

Additionally, I prefer the Zoom of the active Window to be set that it shows the whole drawing.

page.ResizeToFitContents()      #https://docs.microsoft.com/de-de/office/vba/api/visio.page.resizetofitcontents
window = visio.ActiveWindow     #https://docs.microsoft.com/de-de/office/vba/api/visio.application.activewindow
window.ViewFit=1                #https://docs.microsoft.com/de-de/office/vba/api/visio.window.viewfit

Result

Visio Diagrams with Python: Colors

24 Colors to choose from.

„Cells“ contain all „Shape-Format“-Properties

Details: https://docs.microsoft.com/en-us/office/client-developer/visio/cells-visio-shapesheet-reference

# Line-Color
# https://docs.microsoft.com/de-de/office/client-developer/visio/linecolor-cell-line-format-section
oval2.Cells("LineColor").FormulaForce = 3

# Fill-Color
# https://docs.microsoft.com/de-de/office/client-developer/visio/fillforegnd-cell-fill-format-section
rect1.Cells("FillForegnd").FormulaForce=3

# Fill-Pattern "0" - no fill
# https://docs.microsoft.com/de-de/office/client-developer/visio/fillpattern-cell-fill-format-section
rect2.Cells("FillPattern").FormulaForce=0

# Fill-Pattern "3"

# yellow background
# https://docs.microsoft.com/de-de/office/client-developer/visio/fillbkgnd-cell-fill-format-section
#
# light-blue foreground
#
oval1.Cells("FillPattern").FormulaForce = 3
oval1.Cells("FillBkgnd").FormulaForce = 5
oval1.Cells("FillForegnd").FormulaForce = 7

Result

Visio Diagrams with Python: Connectors

Connect two existing Shapes

The API is somehow weird, since it creates an (Shape-)Object, but it doesn’t return it.

  • you’ll have to fetch the last object in the shapes-List
#https://docs.microsoft.com/en-us/office/vba/api/visio.shape.autoconnect
rect1.AutoConnect(rect2,0)
#
#https://docs.microsoft.com/en-us/office/vba/api/visio.shapes.itemu
connector1=shapes.ItemU(len(shapes))
#
connector1.Text = "Connector1"
# 
#https://docs.microsoft.com/de-de/office/client-developer/visio/linecolor-cell-line-format-section
# * color "3" is "light green"
connector1.Cells("LineColor").FormulaForce = 3
#
#https://docs.microsoft.com/de-de/office/client-developer/visio/conlinerouteext-cell-shape-layout-section
# * ConLineRouteExt "2" is "Curved"
connector1.Cells("ConLineRouteExt").FormulaForce = 2

Result

>>> len(shapes)
6

Visio Diagrams with Python: Stencils

Stencils and Stencil-Files

Stencils are stored in „VSSX“-Files

server_stencils_filename = "C:\Program Files (x86)\Microsoft Office\root\Office16\Visio Content\1031\SERVER_M.VSSX"

#64 means “open hidden”
server_stencils = visio.Documents.OpenEx(server_stencils_filename,64)

ftp_stencil=server_stencils.Masters("FTP-Server")

Page-Object

Add a FTP-Server to the page

ftp1 = page.Drop(ftp_stencil,3,3)
ftp1.Text = "FTP-Server#1"

Result

>>> len(shapes)
5

Visio Diagrams with Python using COM

Visualize Data from Python in Visio

I needed to illustrate some data discovered by a python script in Microsoft Visio.

As a starting-point i found two sources of infomation in the Internet:

(1) Python => COM => MS Excel

How to access a Windows COM-Application from Python: https://pbpython.com/windows-com.html

  • MS Excel not Visio
  • but 50% of the information needed

(2) PowerShell => COM => MS Visio

How to access Microsoft Visio using the COM-Interface https://www.powershellstation.com/2016/01/20/powershell-and-visio-1

  • PowerShell
  • the other 50% needed.

Result: „Python => COM => MS Visio“

Putting both pieces together, will allow a Python-Script to control MS Visio.

(3) MS Visio „Object Model“

Without the original documentation provided by Microsoft: https://docs.microsoft.com/en-us/office/vba/api/overview/visio there is _no chance_ to get anything to work.

  • the next 50% 😉

The Basics

(1) Python „PyWin32“

Install „PyWin32“-Library

C:\RH>pip install pywin32
Collecting pywin32
  Downloading pywin32-300-cp38-cp38-win_amd64.whl (9.3 MB)
     |████████████████████████████████| 9.3 MB 2.2 MB/s
Installing collected packages: pywin32
Successfully installed pywin32-300

Access Windows/COM from Python

  • open the Visio-Application
    • „visio“-object: stores a pointer to access application-Level functions
  • add a document
    • „document“-object
  • fetch the active Visio-Page
    • „page“-object
  • fetch the list of all Shapes at this page
    • „shapes“-object
import win32com.client as win32
#
visio = win32.gencache.EnsureDispatch('Visio.Application')
document = visio.Documents.Add("")
#
page = visio.ActivePage
#
shapes = page.Shapes

(2) Explore the Visio-Object-Model

Methods

Object-Methods could get discovered using the Python „dir“, notice for example

  • DrawOval
  • DrawRectangle
>>> dir(page)
['AddDataVisualization', 'AddGuide', 'AutoConnectMany', 'AutoSizeDrawing', 'AvoidPageBreaks', 'BoundingBox', 'CLSID', 'CenterDrawing', 'CreateDataVisualizerDiagram', 'CreateSelection', 'Delete', 'DrawArcByThreePoints', 'DrawBezier', 'DrawCircularArc', 'DrawLine', 'DrawNURBS', 'DrawOval', 'DrawPolyline', 'DrawQuarterArc', 'DrawRectangle', 'DrawSpline', 'Drop', 'DropCallout', 'DropConnected', 'DropContainer', 'DropIntoList', 'DropLegend', 'DropLinked', 'DropMany', 'DropManyLinkedU', 'DropManyU', 'Duplicate', 'Export', 'GetCallouts', 'GetContainers', 'GetFormulas', 'GetFormulasU', 'GetResults', 'GetShapesLinkedToData', 'GetShapesLinkedToDataRow', 'GetTheme', 'GetThemeVariant', 'Import', 'InsertFromFile', 'InsertObject', 'Layout', 'LayoutChangeDirection', 'LayoutIncremental', 'LinkShapesToDataRows', 'OpenDrawWindow', 'Paste', 'PasteSpecial', 'PasteToLocation', 'Print', 'PrintTile', 'ResizeToFitContents', 'SetFormulas', 'SetResults', 'SetTheme', 'SetThemeVariant', 'ShapeIDsToUniqueIDs', 'SpatialSearch', 'SplitConnector', 'UniqueIDsToShapeIDs', 'VisualBoundingBox', '_ApplyTypes_', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_get_good_object_', '_get_good_single_object_', '_oleobj_', '_prop_map_get_', '_prop_map_put_', 'coclass_clsid', 'old_Paste', 'old_PasteSpecial']

Properties

But Object-Parameters won’t and if you don’t want to guess required parameters of Object-Methods, it’s time to bookmark the original documentation provided by Microsoft: https://docs.microsoft.com/en-us/office/vba/api/overview/visio

Start drawing

Page-Object

A good starting point might be the „Page“-Object: https://docs.microsoft.com/en-us/office/vba/api/visio.page

Well add „Shapes“ to the „Page“(-Object) using Methods of the Page-Object:

https://docs.microsoft.com/en-us/office/vba/api/visio.page.drawrectangle or https://docs.microsoft.com/en-us/office/vba/api/visio.page.drawoval – both Methods will return an „Shape“-Object.

#https://docs.microsoft.com/en-us/office/vba/api/visio.page.drawrectangle
rect1 = page.DrawRectangle(1,1,2,2)
rect2 = page.DrawRectangle(4,4,5,5)
#
#https://docs.microsoft.com/en-us/office/vba/api/visio.page.drawoval
oval1 = page.DrawOval(1,4,2,5)
oval2 = page.DrawOval(4,1,5,2)

Shape Object

Look at https://docs.microsoft.com/en-us/office/vba/api/visio.shape – there’s a „Text“-Property:

#https://docs.microsoft.com/en-us/office/vba/api/visio.shape
#https://docs.microsoft.com/en-us/office/vba/api/visio.shape.text
rect1.Text="Rect1"
rect2.Text="Rect2"
oval1.Text="Oval1"
oval2.Text="Oval2"

Result

>>> len(shapes)
4