MzTranslator/plug-ins/mz_translator.py

370 lines
10 KiB
Python

# -*- coding: utf-8 -*-
"""
Mz Translator - Maya File Translator Plugin
Maya Python API implementation for .mz file format translator
.mz format is ZIP compressed, contains data.ma file
Usage:
Load plugin in Maya:
cmds.loadPlugin('mz_translator.py')
"""
import os
import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaUI as OpenMayaUI
import maya.cmds as cmds
from importlib import reload
# Import core modules
from mz_core import reader, writer
reload(reader)
reload(writer)
# Plugin name
kPluginName = "MzTranslator"
kPluginVersion = "1.0.0"
class MzFileTranslator(OpenMayaMPx.MPxFileTranslator):
"""
Maya .mz file format translator
Supports reading and writing .mz files (ZIP compressed, contains data.ma)
"""
_translator_name = "MayaZip"
_extension = "mz"
# Class variable
_temp_file_path = None
def __init__(self):
"""Initialize translator"""
super(MzFileTranslator, self).__init__()
self._options = {}
@staticmethod
def creator():
"""Factory method to create translator instance"""
return MzFileTranslator()
# =========================================================================
# MPxFileTranslator virtual functions
# =========================================================================
def canBeOpened(self):
"""
Return whether translator can be opened
Returns:
bool: Always returns True (can be opened)
"""
return True
def translatorName(self):
"""
Return translator name
Returns:
str: Translator name
"""
return self._translator_name
def haveWriteMethod(self):
"""
Return whether write is supported
Returns:
bool: Always returns True
"""
return True
def haveReferenceMethod(self):
"""
Return whether referencing is supported
Returns:
bool: Always returns False (not supported)
"""
return True
def haveReadMethod(self):
"""
Return whether read is supported
Returns:
bool: Always returns True
"""
return True
def fileType(self):
"""
Return file type name
Returns:
str: File type name
"""
return self._translator_name
def defaultExtension(self):
"""
Return default file extension
Returns:
str: Extension without dot
"""
return self._extension
def identifyFile(self, fileObject, buffer, size):
"""
Identify if file is .mz format
Args:
fileObject: MFileObject
buffer: File buffer
buffer_size: Buffer size
Returns:
int: kIsMyFileType if identified, kUnknown if failed
"""
import zipfile
file_name = fileObject.fullName()
# Check extension
if not file_name.lower().endswith('.mz'):
return OpenMayaMPx.MPxFileTranslator.kNotMyFileType
# Try to identify as ZIP file
try:
# Check if file is valid ZIP format
with open(file_name, 'rb') as f:
# Check ZIP file magic (PK\x03\x04)
magic = f.read(4)
if magic == b'PK\x03\x04' or magic == b'PK\x05\x06':
# Further check if contains data.ma
with zipfile.ZipFile(file_name, 'r') as zf:
if 'data.ma' in zf.namelist():
return OpenMayaMPx.MPxFileTranslator.kIsMyFileType
except Exception:
pass
return OpenMayaMPx.MPxFileTranslator.kNotMyFileType
def writer(self, fileObject, options, mode):
"""
Write Maya scene to .mz file
Args:
fileObject: MFileObject
options: Options dictionary
mode: File mode
Returns:
bool: True if successful
"""
try:
file_path = fileObject.fullName()
# Ensure extension is correct
if not file_path.lower().endswith('.mz'):
file_path += '.mz'
fileObject.setRawFullName(file_path)
# Parse options
writer_options = self._parse_options(options)
# Write .mz file
writer.write_scene_to_mz(file_path, writer_options)
return True
except Exception as e:
raise RuntimeError(f"Failed to write .mz file: {str(e)}")
def reader(self, fileObject, options, mode):
"""
Read Maya scene from .mz file
Args:
fileObject: MFileObject
options: Options dictionary
mode: File mode
Returns:
bool: True if successful
"""
try:
file_path = fileObject.fullName()
# Read .mz file to temp file
temp_ma_path = reader.read_to_temp_file(file_path)
# Save temp file path for cleanup
MzFileTranslator._temp_file_path = temp_ma_path
print(temp_ma_path)
print(options)
if mode == OpenMayaMPx.MPxFileTranslator.kOpenAccessMode:
OpenMaya.MFileIO.open(temp_ma_path, None, True)
if mode == OpenMayaMPx.MPxFileTranslator.kImportAccessMode:
OpenMaya.MFileIO.importFile(temp_ma_path, None, True)
# Use Maya's file command to read
# cmds.file(temp_ma_path, force=True, open=True, type='mayaAscii')
return True
except Exception as e:
raise RuntimeError(f"Failed to read .mz file: {str(e)}")
def optionsScript(self):
"""
Return Export Options UI script
Returns:
str: Python script string
"""
script = """
import maya.cmds as cmds
def MzTranslatorOptions():
# Create options dialog
if cmds.window('MzTranslatorOptions', exists=True):
cmds.deleteUI('MzTranslatorOptions')
cmds.window('MzTranslatorOptions', title='Mz Translator Options')
cmds.columnLayout(adjustableColumn=True)
# Compression level
cmds.text(label='Compression Level (0-9):')
cmds.intSliderGrp('compressionLevel',
label='Compression',
field=True,
minValue=0,
maxValue=9,
value=9,
step=1)
# Version info
cmds.text(label='Version: 1.0.0')
cmds.button(label='OK', command='cmds.layoutDialog(dismiss=\"ok\")')
cmds.button(label='Cancel', command='cmds.layoutDialog(dismiss=\"cancel\")')
cmds.showWindow()
return 'ok'
# Call options dialog
result = cmds.layoutDialog(ui=MzTranslatorOptions)
"""
return script
# =========================================================================
# Helper methods
# =========================================================================
def _parse_options(self, options):
"""
Parse options string to dictionary
Args:
options: Options string (format: key=value;key=value)
Returns:
dict: Options dictionary
"""
result = {}
if not options:
return result
# Parse options string
for pair in options.split(';'):
if '=' in pair:
key, value = pair.split('=', 1)
result[key.strip()] = value.strip()
return result
# =========================================================================
# Maya plugin registration functions
# =========================================================================
# creator
def translatorCreator():
return OpenMayaMPx.asMPxPtr( MzFileTranslator() )
def initializePlugin(mobject):
"""
Initialize plugin
Args:
mobject: MObject
"""
# Create plugin
pluginFn = OpenMayaMPx.MFnPlugin(mobject, "MzTranslator", "1.0.0", "Any")
# Register file translator
try:
pluginFn.registerFileTranslator(
MzFileTranslator._translator_name, # Translator name
"", # Options script (optional)
translatorCreator, # Factory function
"", # Default options
MzFileTranslator._extension # File extension
)
print(f"[{kPluginName}] Plugin initialized successfully (v{kPluginVersion})")
except Exception as e:
sys.stderr.write(f"Failed to register file translator: {str(e)}\n")
raise
def uninitializePlugin(mobject):
"""
Uninitialize plugin
Args:
mobject: MObject
"""
# Get plugin function
pluginFn = OpenMayaMPx.MFnPlugin(mobject)
# Deregister file translator
try:
pluginFn.deregisterFileTranslator(MzFileTranslator._translator_name)
print(f"[{kPluginName}] Plugin uninitialized successfully")
except Exception as e:
sys.stderr.write(f"Failed to deregister file translator: {str(e)}\n")
# =========================================================================
# Convenience functions
# =========================================================================
def cleanup_temp_file():
"""Cleanup temp file"""
if MzFileTranslator._temp_file_path:
import os
import shutil
try:
temp_dir = os.path.dirname(MzFileTranslator._temp_file_path)
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
except Exception:
pass
MzFileTranslator._temp_file_path = None
# =========================================================================
# Auto cleanup when plugin loads
# =========================================================================
# Register cleanup callback when Maya scene closes
try:
# This will cleanup temp files when Maya scene closes
cmds.scriptJob(event=['SceneClosed', cleanup_temp_file], permanent=True)
except Exception:
pass