# -*- 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