在使用 python 和 win32com 自动化 Adobe 软件时,经常会遇到传递二维坐标数组的问题。如果查看 Adobe 提供的 Visual Basic (VB) 代码,你会发现其实很简单。以下是一个在 Illustrator 中绘制直线的VB代码示例:
Set appObj = CreateObject("Illustrator.Application")
Set docObj = appObj.Documents.Add
Set pathItem = docObj.PathItems.Add
pathItem.SetEntirePath Array(Array(0.0, 0.0), Array(20.0, 20.0))
自然而然会以为可以直接将 VB 代码转换为 python 代码,只需将代码转换为以下形式:
from win32com.client import Dispatch
appObj = Dispatch("Illustrator.Application")
docObj = appObj.Documents.Add()
pathItem = docObj.PathItems.Add()
pathItem.SetEntirePath( [ [0.0,0.0], [20.0,20.0] ] )
但实际上没有那么简单,python 会抛出一个错误,提示“Only arrays with dimension 1 are supported”。我明白数组和二维数组之间存在差异,所以问题是,我该如何强制 python 创建正确类型的数组?
我尝试过自己创建 VARIANT 类型,但失败了。我还考虑过使用 ctypes 来解决这个问题。有没有人遇到过同样的问题并能提供一些建议?
- 解决方案
以下是两种解决此问题的方案:
方案一:使用 win32com 模块
事实证明,Illustrator 和 Photoshop 的数组类型都是单一数组的变量类型,变量类型也是一个数组。Solidworks 等其他应用程序也使用同样的策略。你可以使用以下代码强制 win32com 创建变量类型:
from win32com.client import VARIANT
from pythoncom import VT_VARIANT
def variant(data):
return VARIANT(VT_VARIANT, data)
这样就更方便了,你不需要在每个地方都键入 variant,只需使用以下代码将 Python 数组的每个子元素都转换为变量:
import collections
def vararr(*data):
if ( len(data) == 1 and
isinstance(data, collections.Iterable) ):
data = data[0]
return map(variant, data)
最终代码如下:
from win32com.client import Dispatch, VARIANT
from pythoncom import VT_VARIANT
import collections
appObj = Dispatch("Illustrator.Application")
docObj = appObj.Documents.Add()
def variant(data):
return VARIANT(VT_VARIANT, data)
def vararr(*data):
if ( len(data) == 1 and
isinstance(data, collections.Iterable) ):
data = data[0]
return map(variant, data)
pathItem = docObj.PathItems.Add()
pathItem.SetEntirePath( vararr( [0.0,0.0], [20.0,20.0] ) )
#或者你也可以使用可迭代项的列表
pathItem = docObj.PathItems.Add()
pathItem.SetEntirePath( vararr( [[30.0,10.0], [60.0,60.0]] ) )
方案二:使用 comtypes 模块
当尝试使用 win32com 确定选择区域时,我遇到了同样的问题。我发现使用 comtypes 而不是 win32com 来访问 Photoshop 直接解决了多维数组问题。我猜想一维数组问题是 win32com 的局限,但也有可能是我错了。你可以从此处下载 comtypes:
你可以参阅 Tech Artists Org 上关于此问题的一篇文章。以下是该帖中提供的使用 comtypes 传递数组的示例。路径点的实现应该类似。
import comtypes.client
ps_app = comtypes.client.CreateObject('Photoshop.Application')
# 创建一张 128x128 像素的图片
ps_app.Documents.Add(128, 128, 72)
# 在左上角选择 10x10 像素
sel_area = ((0, 0), (10, 0), (10, 10), (0, 10), (0, 0))
ps_app.ActiveDocument.Selection.Select(sel_area)