Maya Python - 将 zip 文件嵌入到 Maya 文件中?

Maya Python - Embed zip file into maya file?

这是另一个堆栈线程的建议,我终于回来了。这是关于如何将工具嵌入到 Maya 文件中的讨论的一部分。

You can write the whole thing as a python package, zip it, then stuff the binary contents of the zip file into a fileInfo. When you need to code, look for it in the user's $MAYA_APP_DIR; if there's no zip, write the contents of the fileInfo to disk as a zip and then insert the zip into sys.path

来源讨论是: python copy scripts into script

到目前为止编程还​​不错,但我想我遇到了障碍。当我尝试这样做时:

with open("..directory/myZip.zip","rb") as file:
    cmds.fileInfo("myZip", file.read())

..然后我..

print cmds.fileInfo("myZip",q=1)

我明白了

[u'PK[=15=]3[=15=]44']

当将 zip 文件作为文本文档阅读时,第一行乱码的错误翻译。

如何将我的 zip 文件作为二进制文件嵌入到我的 Maya 文件中?

====================

更新: Maya 不喜欢以直接读取 utf-8 编码的 zip 文件的方式写入文件。我发现了多种方法可以将其变成可以写入的可接受字符串,但解码回文件似乎没有用。我现在看到 Theodox 的建议是将其写入二进制文件并将其放入 fileInfo 节点中。

如何编码、存储然后解码以便稍后写入文件?

如果我要转换为二进制,例如:

' '.join(format(ord(x), 'b') for x in line)

我需要什么代码才能将其转换回原始的 utf-8 zip 信息?

找到答案了。堆栈溢出的好脚本。我必须编码为 'string_escape',这是我在试图找出整个字符情况时发现的。但无论如何,您打开 zip,编码为 'string_escape',将其写入 fileInfo,然后在获取以将其写回 zip 之前,将其解码回来。

Convert binary to ASCII and vice versa

import maya.cmds as cmds
import binascii

def text_to_bits(text, encoding='utf-8', errors='surrogatepass'):
    bits = bin(int(binascii.hexlify(text.encode(encoding, errors)), 16))[2:]
    return bits.zfill(8 * ((len(bits) + 7) // 8))

def text_from_bits(bits, encoding='utf-8', errors='surrogatepass'):
    n = int(bits, 2)
    return int2bytes(n).decode(encoding, errors)

def int2bytes(i):
    hex_string = '%x' % i
    n = len(hex_string)
    return binascii.unhexlify(hex_string.zfill(n + (n & 1)))

然后你可以

with open("..\maya\scripts/test.zip","rb") as thing:
    texty = text_to_bits(thing.read().encode('string_escape'))
    cmds.fileInfo("binaryZip",texty)

...稍后

with open("..\maya\scripts/test_2.zip","wb") as thing:
    texty = cmds.fileInfo("binaryZip",q=1)
    thing.write( text_from_bits( texty ).decode('string_escape') )

这似乎有效..到目前为止..

您可以在这里找到相关代码:

http://tech-artists.org/forum/showthread.php?4161-Maya-API-Singleton-Nodes&highlight=mayapersist

相关位是

  import base64
  encoded = base64.b64encode(value)
  decoded = base64.b64decode(encoded)

基本上是相同的想法,除了使用 base64 模块而不是 binascii。任何将任意字符流转换为 ascii-safe 表示的方法都可以正常工作,只要您使用可逆方法:您需要注意的潜在问题是数据块中看起来像 maya 的字符闭引号 - 开引号 int 文件信息在 MA 文件中会很混乱。

此示例使用 YAML 来执行任意 key-value 对,但该部分与存储二进制内容无关。我已经将这种技术用于相当大的数据(如果我记得的话高达 640k),但我不知道它是否有你可以在 Maya 中存储的上限

认为我会 post 为任何想要采用这种方法的人提供最终产品。我试着做一些损坏检查,这样一个坏的 zip 就不会在机器之间传递。这就是所有检查散列的目的。

def writeTimeFull(tl):
    import TimeFull
    #reload(TimeFull)
    with open(TimeFull.__file__.replace(".pyc",".py"),"r") as file:
        cmds.scriptNode( tl.scriptConnection[1][0], e=1, bs=file.read() )
    cmds.expression("spark_timeliner_activator",
                    e=1,s='if (Spark_Timeliner.ShowTimeliner == 1)\n'
                          '{\n'
                          '\tsetAttr Spark_Timeliner.ShowTimeliner 0;\n'
                          '\tpython \"Timeliner.InitTimeliner()\";\n'
                          '}',
                    o="Spark_Timeliner",ae=1,uc=all)

def checkHash(zipPath,hash1,hash2,hash3):
    check = False
    hashes = [hash1,hash2,hash3]
    for ii, hash in enumerate(hashes):
        if hash == hashes[(ii+1)%3]:
            hashes[(ii+2)%3] = hashes[ii]
            check = True
    if check:
        if md5(zipPath) == hashes[0]:
            return [zipPath,hashes[0],hashes[1],hashes[2]]
    else:
        cmds.warning("Hash checks and/or zip are corrupted. Attain toolbox_fix.zip, put it in scripts folder and restart.")
        return []

#this writes the zip file to the local users space
def saveOutZip(filename):
    if os.path.isfile(filename):
        if not os.path.isfile(filename.replace('_pkg','_'+__version__)):
            os.rename(filename,filename.replace('_pkg','_'+__version__))
    with open(filename,"w") as zipFile:
        zipInfo = cmds.fileInfo("zipInfo1",q=1)[0]
        zipHash_1 = cmds.fileInfo("zipHash1",q=1)[0]
        zipHash_2 = cmds.fileInfo("zipHash2",q=1)[0]
        zipHash_3 = cmds.fileInfo("zipHash3",q=1)[0]
        zipFile.write( base64.b64decode(zipInfo) )
        if checkHash(filename,zipHash_1,zipHash_2,zipHash_3):
            cmds.fileInfo("zipInfo2",zipInfo)
            return filename
    with open(filename,"w") as zipFile:
        zipInfo = cmds.fileInfo("zipInfo2",q=1)[0]
        zipHash_1 = cmds.fileInfo("zipHash1",q=1)[0]
        zipHash_2 = cmds.fileInfo("zipHash2",q=1)[0]
        zipHash_3 = cmds.fileInfo("zipHash3",q=1)[0]
        zipFile.write( base64.b64decode(zipInfo) )
        if checkHash(filename,zipHash_1,zipHash_2,zipHash_3):
            cmds.fileInfo("zipInfo1",zipInfo)
            return filename
    return False

#this writes the local zip to this file
def loadInZip(filename):
    zipResults = []
    for ii in range(0,10):
        with open(filename,"r") as theRead:
            zipResults.append([base64.b64encode(theRead.read())]+checkHash(filename,md5(filename),md5(filename),md5(filename)))
        if ii>0 and zipResults[ii]==zipResults[ii-1]:
            cmds.fileInfo("zipInfo1",zipResults[ii][0])
            cmds.fileInfo("zipInfo2",zipResults[ii-1][0])
            cmds.fileInfo("zipHash1",zipResults[ii][2])
            cmds.fileInfo("zipHash2",zipResults[ii][3])
            cmds.fileInfo("zipHash3",zipResults[ii][4])
            return True

#file check
#
def md5(fname):
    import hashlib
    hash = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash.update(chunk)
    return hash.hexdigest()

filename = path+'/toolbox_pkg.zip'
zipPaths = [path+'/toolbox_update.zip',
            path+'/toolbox_fix.zip',
            path+'/toolbox_'+__version__+'.zip',
            filename]
zipPaths_exist = [os.path.isfile(zipPath) for zipPath in zipPaths ]

if any(zipPaths_exist[:2]):
    if zipPaths_exist[0]:
        cmds.warning('Timeliner update present. Forcing file to update version')
        if zipPaths_exist[2]:
            os.remove(zipPaths[3])
        elif os.path.isfile(zipPaths[3]):
            os.rename(zipPaths[3], zipPaths[2])
        os.rename(zipPaths[0],zipPaths[3])
        if zipPaths_exist[1]:
            os.remove(zipPaths[1])
    else:
        cmds.warning('Timeliner fix present. Replacing file to the fix version.')
        if os.path.isfile(zipPaths[3]):
            os.remove(zipPaths[3])
        os.rename(zipPaths[1],zipPaths[3])
    loadInZip(filename)

if not cmds.fileInfo("zipInfo1",q=1) and not cmds.fileInfo("zipInfo2",q=1):
    loadInZip(filename)

if not os.path.isfile(filename):
    saveOutZip(filename)

sys.path.append(filename)
import Timeliner
Timeliner.InitTimeliner(theVers=__version__)

if not any(zipPaths[:2]):
    if __version__ > Timeliner.__version__:
        cmds.warning('Saving out newer version of timeliner to local machine. Restart Maya to access latest version.')
        saveOutZip(filename)
    elif __version__ < Timeliner.__version__:
        cmds.warning('Timeliner on machine is newer than file version. Saving machine version over timeliner in file.')
        loadInZip(filename)
        __version__ = Timeliner.__version__

if __name__ != "__main__":
    tl = getTimeliner()
    writeTimeFull(tl)