1 写在前面

最近需要给老师们做家族树,向visio模板中套excel数据,但是在手动套模板的过程中发现间距的控制是一个较为繁琐的事情,于是萌生了用python完成这一过程的想法。
在探索的过程中经验性地总结了一些东西,这里记录分享给大家。
后续文章分为三个部分:首先是现有使用python操作visio文件的方法简介和资源汇总;其次是个人在使用win32com操作visio过程中总结整理得到的一些方法和经验;最后是一个家族树示例的创建案例。
案例相关的程序和文件可以从https://github.com/M73ACat/python-visio-familyTree获取。

2 方法调研

大概了解了一下,使用python控制visio有这么两类方案:
第一类为通过python的win32com操作visio程序;
第二类则通过xml操作并创建visio文件。
相较之下,个人感觉通过win32com操作visio程序的方法在网络上有更多的相关资料且方案较成熟,而第二类方法资料十分少而且需要对xml有一定的了解。在简单尝试两类方案后,还是选择了使用win32com操作visio程序的方案。
这里简单记录一下两类方案的相关介绍、资料等,以供参考。

2.1 win32com操作visio

2.1.1 简单介绍和示例

从使用体验上来讲,使用win32com来操作visio像是通过python打开visio程序和相应文件,通过特定的语句控制visio程序对特定文件进行更改,以满足文件操作需求。
通过使用下列语句,可以打开visio程序并打开“source.vsdx”文件,并显示对应窗口,其效果见图1(窗口可以隐藏)。

import win32com.client as win32
visio = win32.gencache.EnsureDispatch("Visio.Application")
vdoc = visio.Documents.Open("source.vsdx")

图1

2.1.2 一些资料

pywin32/win32com的官方文档应该是这个https://mhammond.github.io/pywin32/html/com/win32com/HTML/docindex.html,但可能是个人水平不足,难以从该文档中获取到什么有帮助性的指引,所以还是转向了零散的资料。

  1. 首先是知乎博主小雨的专栏https://www.zhihu.com/column/c_1362546109030723584,专栏中的几篇文章提供了一些基础语法和简单的示例;
  2. 其次是csdn文章https://blog.csdn.net/q774318039a/article/details/128082172,提供了一个较为完整的案例,当然相关的介绍还是不太丰富;
  3. github资源https://github.com/john-bessire/PythonVisioFlowchart,提供了一个更加丰富和完整的案例。这里还是要挂一个csdn资源(链接:https://download.csdn.net/download/weixin_42157188/18540786),题为“PythonVisioFlowchart:使用Python创建Visio流程图”,该资源照搬了该github资源并设置为收费。不过在一定程度上要感谢这个csdn,让我能够在github上找到该案例;
  4. https://www.coder.work/article/1260877,给出了一些属性设置的示例。

总的来说,网络上的这些资源都较为零散且不太全面。

2.2 visiopy(通过操作xml操作visio)

该方案来自于github https://github.com/thiezn/visiopy,其大概操作过程和原理为:

  1. 更改vsdx文件拓展名为rar等压缩文件格式拓展名;
  2. 解压缩后可以得到一些文件,其中包含了许多xml文件、rels文件以及visio中包含的媒体文件。解压缩后文件夹结构和部分document.xml示例如图2所示(visio文件见图1),个人推测visio通过这样的xml文件控制着每个元素的属性;
  3. 将相应的文件压缩为zip格式(此过程中可以通过更改xml文件实现visio操作);
  4. 将文件拓展名改回vsdx,文件可以正常打开,并实现visio操作。该过程的流程图见图3。(注:在压缩时rar格式更改后缀名为vsdx后无法打开,zip格式可以)

图2

图3

就个人感觉而言,该方案是一种较为巧妙的方案,但是相关的资料仅有作者提供的源码和简单示例。而且简单翻阅其源代码发现目前提供的可修改参数较少(见pages文件PageCollection.to_xml)。所以如果采用该方案可能还需要在源程序的基础上进行拓展,这又需要对visio解压后得到的xml文件有一定的了解,加之没有什么参考资料,于是该方案在简单尝试后被暂时放弃了。

3 一些基础操作

上一部分简单介绍并分析了两个方案,最终选择了基于win32com的visio文件操作方案,这里介绍一些探索到的操作和总结得到的经验。

3.1 visio中的两个有用工具

首先需要介绍一下两个很有用的visio中的工具:绘图资源管理器和ShapeSheet。

3.1.1 绘图资源管理器

绘图资源管理器的打开方式见图4,打开之后的界面如图5所示。

图4

图5

如图5的绘图资源管理器所示,前景页-> 页名->形状 栏目下列出了当前页面下所有的元素名称,点击相应形状,visio页面中对应的元素会呈现被选中的状态。 该工具可以帮助定位到目标对象/元素/形状,从而方便在程序中调用。

3.1.2 ShapeSheet

ShapeSheet的打开方式如图6所示,在某个对象/元素/形状上点击鼠标右键,在弹出的列表中选择“显示ShapeSheet”,即可打开该形状的ShapeSheet。打开后的该元素的ShapeSheet如图7所示。

图6

图7

在图7所示的ShapeSheet中,可以观察到包含了该矩形框的形状(Shape Transform)、线条格式(Line Format)、填充格式(Fill Format)、字体格式(Character)、段落(Paragraph)等等所有属性。 我们通过python和win32com在程序中修改某一形状的属性,最终还是会反映到visio中该形状的ShapeSheet中,当然在visio中直接修改ShapeSheet也是可以的。 该工具一方面可以告诉我们一个形状在程序中有哪些可以修改、调用的属性,另一方面也可以供我们参考这些属性对应的值。当然不同属性的修改、调用方法有所不同。

3.2 文件操作

3.2.1 基础文件操作

打开visio程序

import win32com.client as win32
visio = win32.gencache.EnsureDispatch("Visio.Application")

设置窗口是否可见,0不可见,1可见,不设置则默认为1
【注意】设置窗口不可见的话,需要通过程序关闭文件和visio程序,或在任务管理器找到后台的visio程序结束进程

visio.Visible = 0

打开文件

vdoc = visio.Documents.Open("source.vsdx")

选择页面

page = vdoc.Pages.Item(1)

文件另存为(当然也可直接在visio窗口中保存当前文件)

vdoc.SaveAs("familyTree.vsdx")

关闭打开的文件

vdoc.Close()

关闭visio程序

visio.Quit()

3.2.2 模板选择与打开

Visio的模板/模具保存在C:\Program Files\Microsoft Office\root\Office16\Visio Content\2052目录下(2019专业版),通过以下命令,我们可以打开连接符相关的模板:

con = visio.Documents.Open("CONNEC_M.VSSX")

在打开相应模板后,可以通过Drop指令向页面内拖入指定形状,相关程序如下:

con_shp = page.Drop(con.Masters.ItemU('Dynamic connector'),0,0)

其中’Dynamic connector’为指定形状的名称,后续的两个参数为页面内的坐标,其单位默认为英尺,支持数字和字符串,但貌似无法更改单位。该行程序会返回所拖入的图形,后续可以更改con_shp参数以调整其位置、格式等(见3.3)。
可能会遇到的问题是:不知道想拖入形状的名称是什么。这里有一种略微麻烦一些的方法,我们在visio中打开一个形状模板/模具时会显示相应的名称,如图8所示。通过con.Masters.GetNamesU()可以获取con即连接线模板中所有形状的英文名称列表,如果害怕理解的不对的话可以通过con.Masters.GetNames()获取con中形状的中文名称列表,三者相对应着就可以挑到想要的形状了。
当然也可以通过con.Masters(1)来得到该模板中的第一个shape。

图8

3.3 内容操作

这部分具体介绍一下如何对每个形状进行操作,包括形状信息的查看、特定形状的选中、复制、粘贴、线型、字体、添加连接线、连接线各拐点的修改等
以下程序需要前置程序如下:

import win32com.client as win32
visio = win32.gencache.EnsureDispatch("Visio.Application")
vdoc = visio.Documents.Open("source.vsdx")
page = vdoc.Pages.Item(1)

3.3.1 形状的查看与选择

查看页面元素数量:

page.Shapes.Count
# 或
page.Shapes.Count16

page.Shapes()中存储着page页面中的每个形状,可以通过以下程序查看每个shape的信息:

for num, shp in enumerate(page.Shapes):print('num: %s, name: %s, nameID: %s'%(num, shp.Name, shp.NameID))

其中,shp.NameU/Name均为查看形状名称的操作,所得到的名称与3.1.1节中介绍的绘图资源管理器中的名称相对应
shp.NameU = ‘1’ 可直接更改名称(Name和NameU修改一个即可),有些时候因为在visio中创建并删除了很多图形,所以最终图形名称的编号较为混乱,可以自行修改名称,例:

for num, shp in enumerate(page.Shapes):shp.Name = num + 1print('num: %s, name: %s, nameID: %s'%(num, shp.Name, shp.NameID))

NameID为shape的唯一ID,只能查看不能修改
借由绘图资源管理器中相应的名称(当然也可以辅以其他手段比方说shape.Text查看该shape中的文字来确定的shape),针对特定的shape,可以通过该shape在page.Shapes中的顺序或NameID来进行选择:

header_shape = page.Shapes(1) # 选择page.Shapes中的第一个shape 【注意,Shapes中的第一个shape从1开始】
header_shape = page.Shapes.ItemU('Sheet.10') # 选择page.Shapes中NameID为'Sheet.10'的shape

3.3.2 复制与粘贴

我们首先获取一个shape:shp = page.Shapes(1),shp的复制与粘贴有两种方法:
方法一,通过调用系统的剪贴板实现粘贴复制:

shp.Copy()
page.Paste() # 参数只支持0、1、2,貌似为粘贴得到图形的初始位置,1为左下角

这种方法无法直接返回粘贴得到的新shape,可以通过shp_copy = page.Shapes(-1)来得到粘贴后的新shape
2. 方法二,不通过剪贴板,可以直接获得粘贴后的新shape,有些类似于在visio中直接进行ctrl+D:

shp_copy = shp.Duplicate()

3.3.3 参数的查看和修改

我们首先获取一个新的shape,该shape如图9所示:

time_shp = page.Shapes(2)

图9

一个较为基本的操作是,获取该shape上的文字,对其进行修改:

time_shp.Text
time_shp.Text = '2020级'

除此之外,对time_shp参数的大部分操作都需要使用到time_shp.CellsU()来实现,我们以该shape的位置参数(x方向中心点PinX和y方向中心点PinY)为例,展示如何进行参数的查看和修改:

pinx = time_shp.CellsU('pinx').ResultIU # 返回pinx值,数字;“pinx“为x方向中心点的参数名称
piny = time_shp.CellsU('piny').ResultIU # 返回piny值,数字;“piny“为y方向中心点的参数名称
time_shp.CellsU('pinx').FormulaU = '10 mm' # 修改time_shp的中心点x坐标为10 mm
time_shp.CellsU('piny').FormulaU = '250 mm' # 修改time_shp的中心点y坐标为250 mm,从而实现对time_shp的移动

一些注意事项:

  1. 返回值的默认单位为英尺;
  2. CellsU()括号中的参数名称对大小写不敏感;
  3. 在赋值时可以指定单位。

上述程序以PinX和PinY两个参数为例介绍了shape的获取和修改操作,但一个显而易见的问题是,我们不知道一个shape有多少个参数,我们想修改的参数对应的名称是什么。
这里需要引入3.1.2节介绍的工具ShapeSheet。该工具中列出了某一shape的所有参数和名称。其中大部分常用参数的名称都是对应的,可以直接复制粘贴到CellsU()中进行操作,如:

CellsU('width') # 宽度
CellsU('height') # 高度
CellsU('rounding') # 圆角
CellsU('lineweight') # 线宽
CellsU('linecolor') # 线颜色

除了借鉴参数的名称之外,ShapeSheet中所列出的参数值也是可以借鉴的。比方说我们可以参照visio参数的样例以确定参数的赋值方法,或者通过在visio中修改相应的参数值来定位ShapeSheet中的变化值,从而确定相应的参数意义及相应参数值的意义,例:

  1. 在修改填充颜色时,参考ShapeSheet中相应区域的取值(见图10),我们可以直接搬进程序中:

图10
time_shp.CellsU('FillForegnd').FormulaU = 'RGB(0,0,0)' # 调整shape填充颜色为黑色
  1. 在修改线型时,参考相应区域(见图11),我们可以尝试修改其取值,或在 开始中修改其线型,便可以知道特定参数值的意义。

图11

然而并非所有参数名称和ShapeSheet中列出的名称是一一对应的。笔者在使用过程中发现“Character”(图12)和连接线的“Geometry”(图13)这两部分参数的操作略有不同。这里将连接线的“Geometry”放在3.3.4进行介绍,本节只介绍“Character”的操作。

图12

图13

一部分修改字体的程序示例如下:

time_shp.CellsU('char.size').FormulaU = '10 pt' # 修改文字大小
time_shp.CellsU('char.font').FormulaU = '20' # 修改字体

需要注意的是,通过char.size/font等参数会统一修改shape中的所有文字,不支持部分修改。目前尚不知道如何直接在程序中进行部分修改。当然,还是有一些间接一些的方法:

  1. 方法一:https://www.coder.work/article/126087中给出了获取shape.Characters并部分调整文字格式的方法和相应程序,但貌似有些复杂;
  2. 方法二:笔者在测试中,发现char支持x1……xn、y1……yn、z等等调用方法,其中x1-n为不同部分的字体,从而可以实现分部分的字体调控【注意1,尚无法实现通过程序将而文字分为不同部分,只能先在visio中完成划分后进行调整;注意2,仅发现x对应font,其他字母间的对应关系尚未发现】。例:
time_shp.CellsU('char.x1').FormulaU = '15'
time_shp.CellsU('char.x2').FormulaU = '30'

图14

另外,个人推测可能这类以表格形式存储的属性(类似的还有段落属性)具有类似的操作方法。
经过上述介绍,相信大家也能发现shape参数的调整是一个比较繁琐的工作,我个人更倾向于在文件中建立并设置好相应的所需图形,在程序中使用一些复制粘贴操作,只需调整位置从而减少查找和调整参数的时间和难度。

3.3.4 连接线操作

3.3.4.1 连接线创建

创建连接线并连接两个形状有许多种方式,笔者在此将连接线连接的方法简单分为三类(以上文中得到的header_shp和time_shp为例):

  1. 使用shape.AutoConnect自动将两个shape通过连接线相连。例:
time_shp.AutoConnect(header_shp, constants.visAutoConnectDirRight, con.Masters.ItemU("Dynamic connector"))

其中,constants.visAutoConnectDirRight控制了连接线的方向,已知的四个方向分别为Right、Left、Up和Down,但该方法会自动调整两个相连形状的位置,其结果如图15所示。更多的资料可以访问博主小雨的专栏(https://zhuanlan.zhihu.com/c_1362546109030723584)。

图15
  1. 通过GlueTo设置连接线的起始点。例:
conn_shp = page.Drop(visio.Application.ConnectorToolDataObject, 0, 0)
conn_shp.CellsU("BeginX").GlueTo(header_shp.CellsU("PinX"))
conn_shp.CellsU("EndY").GlueTo(time_shp.CellsU("PinY"))

该写法参考自https://github.com/john-bessire/PythonVisioFlowchart,其中conn_shp同样可以采用从模板中选择并Drop的方式(所得到的结果见图16):

conn_shp = page.Drop(con.Masters(1), 0, 0)

图16
  1. 通过手动设置起始点等属性调整其在页面上的样式。例:
conn_shp = page.Shapes(5).Duplicate() # 复制现有连接线
conn_shp.CellsU("BeginX").FormulaU = header_shp.CellsU("PinX").ResultIU # 设置连接线起点x为header_shp的PinX
conn_shp.CellsU("EndX").FormulaU = time_shp.CellsU("PinX").ResultIU # 设置连接线终点x为time_shp的PinX
conn_shp.CellsU("BeginY").FormulaU = header_shp.CellsU("PinY").ResultIU - header_shp.CellsU("Height").ResultIU / 2 # 设置连接线起点y为header_shp底边的中点
conn_shp.CellsU("EndY").FormulaU = time_shp.CellsU("PinY").ResultIU + time_shp.CellsU("Height").ResultIU / 2 # 设置连接线终点y为time_shp顶边的中点

上述程序复制了现有的连接线并手动将其起始点定位到相应的位置。同时,复制还保证了连接线之间具备相同的样式,其中最主要的一条就是控制了连接线的走向,在图17所示的结果图中可以看到,复制之后的连接线依然保持着两次转折的样式。在所使用的家族树案例中,这可以保证绘图整体的样式统一。

图17

连接线的一般创建方式与其他shape的创建相通,Drop或者复制均可。当然上面三个例子中也给出了此外的两类连接线创建方法,可供参考。

3.3.4.2 连接线调整

通过设定起始点/连接对象,连接线的位置便不需要再手动调整了。此外的颜色、线型、线宽等常规属性与其他shape的属性设置相通,见3.3.3节。然而连接线还有一个较为重要且较不常规的属性“Geometry”,3.3.4.1节例3(图17)新得到的连接线的ShapeSheet如图18所示。其中“Geometry 1”一栏包含着连接线每个点的相对位置。我们可以通过调整每个点的相对位置来进一步修正连接线的样式。

图18

一个简单的例子是(接3.3.4.1节例3):

conn_shp.CellsU("Geometry1.Y2").FormulaU = '1 mm'
conn_shp.CellsU("Geometry1.Y3").FormulaU = '1 mm'

图19

图19为调整第2、3行y值前后的对比图,通过这种方式,我们就可以统一所有连接线的样式。需要说明的是,暂未明晰如何在程序中新增或删去连接线上的点,仍然需要手动在visio中建立特定样式的连接线。

4 案例

到此为止,关于一些基本操作的介绍就完成了。下面就回到正题,完成家族树自动创建的程序。完整案例(程序和文件)可以从github获取(https://github.com/M73ACat/python-visio-familyTree),这里简单分享一下笔者的实现思路和最终的效果。

  1. 首先在“source.vsdx”中创建并手动调整各基础形状的样式,其结果见图1;
  2. 查看并调整各形状的名称,对照绘图资源管理器定位各基础形状;
  3. 根据所确定的间距,通过复制粘贴的方式保证形状的样式,通过PinX和PinY调整形状的位置,通过BeginX、BeginY、EndX、EndY、Geometry调整连接线的起始点和中间点的位置;
  4. 从“信息.xlsx”中获取信息并执行第3步;
  5. 另存为新文件。
    其过程效果图如图20所示。

图20

5 总结

受限于笔者水平,文章可能存在不合理、疏漏乃至错误之处,文章中一些名词的使用也较为混乱,大家可以共同探讨、改进。
另外,需要强调的一点是,本文为在粗略的试错上建立起来的经验之谈,相关的理论、技术基础等几乎为零,请各位看官酌情参考。

基于python win32com的visio文件基础操作语句简介及案例展示(家族树自动创建)相关推荐

  1. python读取遥感 dat_基于python批量处理dat文件及科学计算方法详解

    摘要:主要介绍一些python的文件读取功能,文件内容修改,文件名后缀更改等操作. 批处理文件功能 import os path1 = 'C:\\Users\\awake_ljw\\Documents ...

  2. python中的doc_基于Python获取docx/doc文件内容代码解析

    这篇文章主要介绍了基于Python获取docx/doc文件内容代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 整体思路: 下载文件并修改后缀 ...

  3. Tableau基础操作——界面简介及功能介绍

    Tableau基础操作--界面简介及功能介绍 Tableau基础操作--界面简介及功能介绍 一.界面简介 菜单栏:设置工作表或仪表板的格式,保存文件等操作 工具栏:展示常用命令,撤销.保存.添加数据源 ...

  4. 删除所有学生记录mysql_【MySQL】MySQL基础操作语句

    mysql基础操作语句,包括数据库的增.删.切换,以及表的增.删.改.查.复制. 创建数据库 mysql> create database tem; 使用数据库 mysql> use te ...

  5. mysql修改学生信息语句,【MySQL】MySQL基础操作语句

    mysql基础操作语句,包括数据库的增.删.切换,以及表的增.删.改.查.复制. 创建数据库 mysql> create database tem; 使用数据库 mysql> use te ...

  6. Python - 文件基础操作

    目录 文件的读取 open()打开函数 read类型 read()方法 readlines()方法 readline()方法 for循环读取文件行 close() 关闭文件对象 with open 语 ...

  7. python自动化测试开发_基于python的selenium2自动化测试从基础到实战(Python3、selenium2、自动化测试、web测试)...

    Selenium2是目前比较流行的一款针对web页面测试的自动化测试工具,他的前身是Selenium .Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE.Mozi ...

  8. Python入门教程之文件读写操作知识

    Python是随着人工智能时代的来临而火爆起来的编程语言,入门简单.功能强大,吸引了人们的广泛学习加入.想要学好Python,一定要从基础学起,然后进阶深入学习,今天千锋小编就给大家分享Python培 ...

  9. [Python学习] 专题四.文件基础知识

            前面讲述了函数.语句和字符串的基础知识,该篇文章主要讲述文件的基础知识(与其他语言非常类似). 一. 文件的基本操作         文件是指存储在外部介质(如磁盘)上数据的集合.文件 ...

最新文章

  1. ASP.NET Razor – C# 逻辑条件简介
  2. HTML5 大战移动应用(一)
  3. poj 3077Rounders(模拟)
  4. 怎么给web 服务器 传文件,web文件传到服务器
  5. 贝叶斯 定理_贝叶斯定理实际上是一个直观的分数
  6. docker update
  7. thinkpad 使用技巧
  8. Using mysqldump for Backups(备份还原数据库实例及参数详细说明)
  9. 新手如何备考GRE考试作文
  10. 查找Linux系统中某个软件是否安装/存在
  11. C语言读取文件内容创建二叉树
  12. ORA-01653: 解决方案
  13. 美国北亚利桑那大学计算机专业排名,美国北亚利桑那大学排名学费
  14. java实现订单轨迹_B端零售业:订单轨迹日志功能设计思考
  15. 苏宁数据中台架构实践
  16. sql文件反向生成物理概念模型
  17. 英语单词Caement水泥
  18. 如何裁剪linux内核
  19. 工行银企互联(NC模式)中间件开发模式
  20. linux 设备驱动(一)——字符设备驱动

热门文章

  1. 【计算机网络】第四章:数据链路层(Part2.广播信道的数据链路)
  2. html 请假条(恶搞)
  3. 最好的电子书下载网站
  4. 15kw充电桩模块设计,源代码
  5. QT Creator 代码自动补全---快捷键设定
  6. 迁移学习:经典算法解析
  7. 2014年新版健康助手 “马上安装”享受健康生活
  8. 管理信息系统相关计算机技术,分析管理信息系统中计算机通信技术要点
  9. ImageX 语法详解
  10. Win7添加快速启动栏