Python 查询 maya 文件的引用节点及引用文件的路径
环境
- Maya 2017 & 2018
- Python 3.6
- VS Code
实现
需求:给定一个 maya 文件,查询这个 maya 文件引用了哪些文件,返回引用的文件路径。
首先,需要使用 maya 软件自带的 mayapy.exe 解释器来执行 maya 包的 Python 命令,如:cmds 的相关命令,maya 中的 Python 为 Python 2,但是开发环境使用的是 Python 3.6,所以需要变通一下。通过 subprocess 模块的 Popen 方法,调用 mayapy.exe 执行一个用 Python 2.7 写的脚本。主文件 main.py
如下:
import os
import subprocess
def main():
mayaPath = "D:\\Maya2018\\maya2018\\bin\\mayapy.exe" # 修改成你的 mayapy.exe 路径
scriptPath = "C:\\test\\Coding\\test3.py" # 要执行查询引用的文件
file = "C:\\test\\maya\\文档\\w2.ma" # 要查询的文件
try:
referenceFilePath = subprocess.Popen(mayaPath+' '+scriptPath+' '+upload_file_path,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
out, err = referenceFilePath.communicate()
except Exception as e:
pass
else:
try:
ref_filepaths = eval(out.decode('utf-8')) # 正确的结果为字符串 list,此命令可以转为 list。
except Exception as e:
print(err.decode('utf-8'))
print('文件版本高于 maya 软件版本')
else:
print('引用文件列表 -------:', ref_filepaths)
# return ref_filepaths
if __name__ == '__main__':
main()
以上代码中,得到的结果 out、err 可能存在编码问题,需要处理。如果要在执行的脚本中再传入参数,如上面代码中的 file 参数,在脚本中需要再获取参数。
mayapy.exe 解释器使用的是 Python 2.7 的版本。查阅 maya 技术文档,get_ref.py
具体实现入下:
# -*- coding:utf-8 -*-
import os
import sys
import maya.cmds as cmds
reload(sys)
sys.setdefaultencoding('utf-8')
os.environ["MAYA_SKIP_USERSETUP_PY"] = ""
os.environ["setupScriptPaths"] = "D:\\我的文档\\maya\\scripts\\userSetup.py" # 注意修改路径
try:
import maya.standalone
maya.standalone.initialize(name='python')
except Exception as e:
pass
def get_ref_file_path(upload_file_path):
rfnNodes = {}
try:
cmds.file(upload_file_path, reference=True, namespace='ns')
rfnNode = cmds.referenceQuery(upload_file_path, referenceNode=True, child=True) # list
except Exception, e:
raise e
else:
rfnNodes[upload_file_path] = rfnNode
ref_file_paths = []
if rfnNodes[upload_file_path]:
for node in rfnNodes[upload_file_path]:
try:
cmds.file(upload_file_path, reference=True, namespace='ns')
ref_filepath = cmds.referenceQuery(node, filename=True)
except Exception, e:
raise e
else:
ref_file_paths.append(ref_filepath)
print ref_file_paths # 结果返回到 main.py 中
upload_file_path = sys.argv[1] # 获取传入的 file 参数,即:file = "C:\\test\\maya\\文档\\w2.ma",此参数在拿到之前无法直接使用 file 参数。
get_ref_file_path(upload_file_path)
maya.standalone.uninitialize()
以上查询存在一个问题,如果 文件是由高版本 maya 软件(如 2018)创建,此时用低版本 maya(如 2017)打开,maya app 会报意外关闭错误。
因此,需要先获取文件和软件版本信息,比较后再处理查询。修改 get_ref.py
文件如下:
import os
...
def get_ref_file_path(upload_file_path):
# ...
def get_version(upload_file_path):
cmds.file(upload_file_path, open=True)
file_version = cmds.fileInfo('version', query=True) # ['2018']
maya_version = cmds.about(version=True)
cmds.file(upload_file_path, open=True)
if file_version <= maya_version:
print get_ref_file_path(upload_file_path)
upload_file_path = sys.argv[1] # 获取传入的 file 参数,即:file = "C:\\test\\maya\\文档\\w2.ma",此参数在拿到之前无法直接使用 file 参数。
get_version(upload_file_path)
maya.standalone.uninitialize()
经测试,以上修改并不能解决 maya 软件报意外关闭错误,无论是在外部判断版本信息,还是内部,均不能幸免。甚至引入新的错误,由于此代码 cmds.file(upload_file_path, open=True)
导致查询引用文件时报错:错误: 请指定一个引用节点、一个被引用文件或一个来自被引用文件的节点
。
经思考,是否可以给个标志,执行两次 subprocess.Popen(mayaPath+' '+scriptPath+' '+upload_file_path+' '+flag,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
,第一次执行函数 get_version,获取版本信息,在 main.py
中比较后再决定是否查询引用,依然不行,最后发现无论是查文件版本信息,还是文件的引用信息,都需要先打开文件,只要用低版本软件打开高版本文件,就会出错。所以得出结论,用 Python 命令获取文件版本信息是错误的方法。
经研究,maya 文件是一个可以打开的文本文件,存储着创建文件的 maya 版本信息,格式如:fileInfo "version" 2017;
, 因此可以以二进制格式打开文件,获取文件中存储的创建文件时的 maya 软件版本。修改 get_ref.py
如下:
import os
...
def get_ref_file_path(upload_file_path):
# ...
def get_version(upload_file_path):
maya_version = cmds.about(version=True) # unicode
with open(upload_file_path, 'rb') as f:
for i in f.readlines():
if i.startswith(b'fileInfo "version"'):
file_version = ((i.decode()).split('"')[-2])
break
return maya_version, file_version
if __name__ == '__main__':
upload_file_path = sys.argv[1]
maya_version, file_version = get_version(upload_file_path)
if file_version <= maya_version:
print get_ref_file_path(upload_file_path)
maya.standalone.uninitialize()
如果仔细一点,还存在一个明显的版本号问题,即:查到的版本信息格式可能不一致,导致两个版本号无法比较。maya 有多个版本,目前了解到的版本号有如下格式:“2018”、“6.0”、“6.0.1”、“2011 x64”,如果格式一致,如 “2018” 和 “2017”,版本很容易比较大小。现在如果 1 个 “2018”,1 个 “6.5”,可以通过 float 转换成浮点数比较大小。再其次,如果,1 个 “2018”,1 个 “6.0.1”,该如何比较呢?又,如果 1 个 “6.0”,1 个 “6.0.1” 该如何比较?6.0 版本的软件与 6.0.1 版本的软件同属一个大版本,所以 “6.0.1” 版本的软件能否打开 “6.0” 版本的文件,而不出错呢?我没有这两个版本的软件,无法测试。这不是一个问题,而是一系列问题。可能还有许多其他的版本号,没有发现,所以需要调查,以及尽可能全面的解决这个问题。而如果能够确定 maya 以及文件的版本均是年份命名,那么这个问题也就不是问题了。
至此,相关的问题大概解决完毕,最后的问题可能是编码问题,由于 get_ref 文件执行 maya.cmds 的是 Python 2,而 main 使用的是 Python 3,编码是个问题。
在 main 文件中,得到正确的结果,是一个字符串列表,使用命令 eval(out.decode('utf-8'))
可以转为列表格式,而如果查询 err 错误信息,使用 err.decode('utf-8')
命令可能就不太行了,将 utf-8 改为 gbk 似乎可以,如果还是不行,可以把 \x
开头的字符单独复制出来,使用 utf-8 解码,得到中文字符,查看报错信息。
最后,似乎问题全部解决了。