Python & Visio - can create Rectangle and Circle just fine, but...

Started by approx, December 10, 2023, 05:17:32 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

approx

...not anything else!

Here's the python I am playing around with, a conglomeration of code from the deep reaches of the web.

import win32com.client

app = win32com.client.Dispatch("Visio.Application")
app.Visible = True
#open Visio document and assign it to variable doc
doc = app.Documents.Open(r"c:\python\MyDrawing.vsdx")
page = app.ActivePage

# drop shape to the page from doc stencil by name "Master.4" at x,y coord.
added_new_shape1 = page.Drop(doc.Masters("Rectangle"), 6.5, 6.5)
added_new_shape2 = page.Drop(doc.Masters("Circle"), 3.25, 3.75)
added_new_shape3 = page.Drop(doc.Masters.ItemU("Diamond"), 5.25, 5.75)

# create connection point for added_new_shape1
# by adding row to 7 section (stores an object's connection points), after last exists row, unnamed rows
conPt1 = added_new_shape1.AddRow(7, -2, 153) # .AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
conRow1 = added_new_shape1.Section(7).Row(conPt1) #get the created row
# set coordinates of the connection point (0) - x, (1) - y
conRow1.Cell(0).FormulaU = "Width*1"
conRow1.Cell(1).FormulaU = "Height*0.5"

# create connection point for added_new_shape2
conPt2 = added_new_shape2.AddRow(7, -2, 153)
conRow2 = added_new_shape2.Section(7).Row(conPt2)
conRow2.Cell(0).FormulaU = "Width*0.5"
conRow2.Cell(1).FormulaU = "Height*1"

# create connection point for added_new_shape2
conPt3 = added_new_shape3.AddRow(7, -2, 153)
conRow3 = added_new_shape3.Section(7).Row(conPt3)
conRow3.Cell(0).FormulaU = "Width*0.5"
conRow3.Cell(1).FormulaU = "Height*1"

# drop the connector onto page
myConnector = page.Drop(app.ConnectorToolDataObject, 4, 10)
myConnectorBegin = myConnector.Cells("BeginX") #get start point of the connector
myConnectorEnd = myConnector.Cells("EndX") #get end point of the connector

vsoCellGlueToObject = added_new_shape1.Cells("Connections.X1") # get early created connection point of the first shape
vsoCellGlueToObject2 = added_new_shape2.Cells("Connections.X5") # get early created connection point of the second shape
#X1 = bottom (but top for shape2)
#X2 = right side? (but bottom for shape2)
#X3 = top? (but left side for shape2)
#X4 = left side? (but bottom for shape2)
#X5 = middle? (but right side for shape2)
#X6 = right side?

myConnectorBegin.GlueTo(vsoCellGlueToObject) # connect start point of the connector to shape's connection point
myConnectorEnd.GlueTo(vsoCellGlueToObject2)


# second connector onto page
myConnector2 = page.Drop(app.ConnectorToolDataObject, 4, 10)
myConnectorBegin2 = myConnector2.Cells("BeginX") #get start point of the connector
myConnectorEnd2 = myConnector2.Cells("EndX") #get end point of the connector

vsoCellGlueToObject = added_new_shape1.Cells("Connections.X1") # get early created connection point of the first shape
vsoCellGlueToObject2 = added_new_shape3.Cells("Connections.X1") # get early created connection point of the second shape

myConnectorBegin2.GlueTo(vsoCellGlueToObject) # connect start point of the connector to shape's connection point
myConnectorEnd2.GlueTo(vsoCellGlueToObject2)

#Change text of shape
added_new_shape1.Text = "testing"
added_new_shape2.Text = "EMMA"

#Change color of shape
added_new_shape2.Cells("FillForegnd").FormulaForceU = "RGB(255,153,51)"


Now here's the error I'm getting:

C:\python>py createvisio2.py
Traceback (most recent call last):
  File "C:\python\createvisio2.py", line 12, in <module>
    added_new_shape3 = page.Drop(doc.Masters.ItemU("Diamond"), 5.25, 5.75)
  File "<COMObject <unknown>>", line 2, in ItemU
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, 'MyDrawing.vsdx - Visio Professional', '\n\nObject name not found.', None, 0, -2032465660), None)


Why can I not create a Diamond by name?  Oh, and I've tried it without ItemU as well, it doesn't work either way!  I haven't found any shapes besides Rectangle and Circle that work to create via python... but when I record a macro, they all are referred to by shape name.

I appreciate any help!

Surrogate

Quote from: approx on December 10, 2023, 05:17:32 AMWhy can I not create a Diamond by name?  O
Your document contains masters named Rectangle, Circle and Diamond?
# drop shape to the page from doc stencil by name "Master.4" at x,y coord.
added_new_shape1 = page.Drop(doc.Masters("Rectangle"), 6.5, 6.5)

Paul Herber

The deep reaches of the web may contain all sorts of incorrect code. Stay here, you know it makes sense!  8)
Unless you have already used a diamond shape in your document there won't be such a shape in the document masters. You need to load the shape from a suitable stencil.
Electronic and Electrical engineering, business and software stencils for Visio -

https://www.paulherber.co.uk/


Yacine

Doing the homework for you ...

You somehow need to make sure that the master you want to drop is available. Either start from a template or load the right stencil. Relying on the drawing might be a worse idea.
Find out which stencil contains the desired "diamond" master.
Open the stencil as you did for the document itself.
stencil = app.Documents.Open(r"... full path of stencil ...")
Then provided the masters are all in that one stencil, you could either define the different masters directly:
rectangle = stencil.Masters.Item("Rectangle")
...
diamond = stencil.Masters.Item("Diamond")
added_new_shape3 = page.Drop(diamond, 5.25, 5.75)



or build up some structure to hold them:
eg a lazy way to collect them all.
masters = {}
for mstr in stencil.Masters:
  masters[mstr.Name] = mstr

added_new_shape3 = page.Drop(masters['Diamond'], 5.25, 5.75)


PS: You might ask, why use variables for accessing the masters? That is actually not really necessary - you still can write the full original object path, app.Documents("stencil name").Masters.Item("Diamond") - but the intermediate definition of variables gives you the opportunity to catch errors (availability of the objects), to count and list the masters, ... and whatever you may discover to be helpful.
Yacine

wapperdude

Quote... but when I record a macro, they all are referred to by shape name.

??? 
You used macro recorder to verify the steps, albeit in VBA.  You were successful using the macro recorder to get code for dropping the shapes and placing them on drawing page?  All three shapes?
Visio 2019 Pro

approx

Thank you ALL for the responses, much appreciated!  I really didn't know what "master" was, so it's very helpful to learn from your comments that master only contains the shapes that have been previously used in the document.

I've seen some examples of loading stencil files, so I'm confident I can figure out how to do that in python-ese.  I'm struggling to find WHERE those stencil files are found.  And once I find them, how do I know which shape is loaded from which file?

Based on this article (https://support.microsoft.com/en-au/office/change-the-visio-default-working-folder-757efbd8-67ac-4e82-a626-d52c04ef2b50), I checked the following folders: 

Users\<user name>\Documents
No .vss or .vssx files here

Users\<user name>\Documents\My Shapes
Favorites.vssx is here, but 0 KB (probably because I haven't favorited any shapes)

Program Files\Microsoft Office\Office<version>\language
I can find Office16, but language is not a folder within it.  Just 5 fairly irrelevant files from 2002 exist here.  Also checked Program Files (x86), but no Microsoft Office folder there

Users\<user name>\AppData\Microsoft\Addins
This folder is empty

I feel like I'm definitely in the "asking stupid questions" phase of learning all of this, so I appreciate your patience and advice.

approx

Quote from: wapperdude on December 10, 2023, 04:04:22 PM
Quote... but when I record a macro, they all are referred to by shape name.

??? 
You used macro recorder to verify the steps, albeit in VBA.  You were successful using the macro recorder to get code for dropping the shapes and placing them on drawing page?  All three shapes?
All three shapes and more.  But I didn't save the document that way, so now it makes sense why I could not add a diamond.  I must have saved it along the way after adding and then deleting a rectangle and circle, but not a diamond.

approx

Ok a bit more progress.  Searched my C:\ for *.vss* and found stencils... 659 of them to be precise!  Without loading up each one, how can I know which stencil a particular shape comes from?  Just talking about the default shapes included with Visio.


approx

Quote from: Yacine on December 10, 2023, 09:22:58 AM
Doing the homework for you ...

You somehow need to make sure that the master you want to drop is available. Either start from a template or load the right stencil. Relying on the drawing might be a worse idea.
Find out which stencil contains the desired "diamond" master.
Open the stencil as you did for the document itself.
stencil = app.Documents.Open(r"... full path of stencil ...")
Then provided the masters are all in that one stencil, you could either define the different masters directly:
rectangle = stencil.Masters.Item("Rectangle")
...
diamond = stencil.Masters.Item("Diamond")
added_new_shape3 = page.Drop(diamond, 5.25, 5.75)



or build up some structure to hold them:
eg a lazy way to collect them all.
masters = {}
for mstr in stencil.Masters:
  masters[mstr.Name] = mstr

added_new_shape3 = page.Drop(masters['Diamond'], 5.25, 5.75)


PS: You might ask, why use variables for accessing the masters? That is actually not really necessary - you still can write the full original object path, app.Documents("stencil name").Masters.Item("Diamond") - but the intermediate definition of variables gives you the opportunity to catch errors (availability of the objects), to count and list the masters, ... and whatever you may discover to be helpful.

Sorry for the many posts - documenting my progress as I am discovering things that work.  I've done it!  Figured out that BASIC_U.VSSX is the stencil I was looking for (it seems to work anyway), so I added code to load it up prior to loading the doc to edit, and was able to insert all three shapes.  Thanks Yacine for "doing my homework" - much appreciated!

My current code (in case anyone happens to be looking here later on):
import win32com.client

app = win32com.client.Dispatch("Visio.Application")
app.Visible = True
#open Visio document and assign it to variable doc
stencil = app.Documents.Open(r"C:\Program Files\Microsoft Office\root\Office16\Visio Content\1033\BASIC_U.VSSX")
doc = app.Documents.Open(r"c:\python\MyDrawing.vsdx")
page = app.ActivePage

# drop shape to the page from doc stencil by name "Master.4" at x,y coord.
added_new_shape1 = page.Drop(stencil.Masters("Rectangle"), 6.5, 6.5)
added_new_shape2 = page.Drop(stencil.Masters("Circle"), 3.25, 3.75)
added_new_shape3 = page.Drop(stencil.Masters.ItemU("Diamond"), 5.25, 5.75)

# create connection point for added_new_shape1
# by adding row to 7 section (stores an object's connection points), after last exists row, unnamed rows
conPt1 = added_new_shape1.AddRow(7, -2, 153) # .AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
conRow1 = added_new_shape1.Section(7).Row(conPt1) #get the created row
# set coordinates of the connection point (0) - x, (1) - y
conRow1.Cell(0).FormulaU = "Width*1"
conRow1.Cell(1).FormulaU = "Height*0.5"

# create connection point for added_new_shape2
conPt2 = added_new_shape2.AddRow(7, -2, 153)
conRow2 = added_new_shape2.Section(7).Row(conPt2)
conRow2.Cell(0).FormulaU = "Width*0.5"
conRow2.Cell(1).FormulaU = "Height*1"

# create connection point for added_new_shape2
conPt3 = added_new_shape3.AddRow(7, -2, 153)
conRow3 = added_new_shape3.Section(7).Row(conPt3)
conRow3.Cell(0).FormulaU = "Width*0.5"
conRow3.Cell(1).FormulaU = "Height*1"

# drop the connector onto page
myConnector = page.Drop(app.ConnectorToolDataObject, 4, 10)
myConnectorBegin = myConnector.Cells("BeginX") #get start point of the connector
myConnectorEnd = myConnector.Cells("EndX") #get end point of the connector

vsoCellGlueToObject = added_new_shape1.Cells("Connections.X1") # get early created connection point of the first shape
vsoCellGlueToObject2 = added_new_shape2.Cells("Connections.X5") # get early created connection point of the second shape
#X1 = bottom (but top for shape2)
#X2 = right side? (but bottom for shape2)
#X3 = top? (but left side for shape2)
#X4 = left side? (but bottom for shape2)
#X5 = middle? (but right side for shape2)
#X6 = right side?

myConnectorBegin.GlueTo(vsoCellGlueToObject) # connect start point of the connector to shape's connection point
myConnectorEnd.GlueTo(vsoCellGlueToObject2)


# second connector onto page
myConnector2 = page.Drop(app.ConnectorToolDataObject, 4, 10)
myConnectorBegin2 = myConnector2.Cells("BeginX") #get start point of the connector
myConnectorEnd2 = myConnector2.Cells("EndX") #get end point of the connector

vsoCellGlueToObject = added_new_shape1.Cells("Connections.X1") # get early created connection point of the first shape
vsoCellGlueToObject2 = added_new_shape3.Cells("Connections.X1") # get early created connection point of the second shape

myConnectorBegin2.GlueTo(vsoCellGlueToObject) # connect start point of the connector to shape's connection point
myConnectorEnd2.GlueTo(vsoCellGlueToObject2)

#Change text of shape
added_new_shape1.Text = "testing"
added_new_shape2.Text = "EMMA"

#Change color of shape
added_new_shape2.Cells("FillForegnd").FormulaForceU = "RGB(255,153,51)"