"""
File: file.py
Description: Maya-specific file handling operations for openPypeline Studio.
This module is dynamically loaded by opsEngine when Maya is detected.
"""
import maya.cmds as cmds
import os
import logging
logger = logging.getLogger("openPypeline.maya.file")
def _ensure_plugins_loaded(filepath=None, file_type=None):
"""Helper to ensure required plugins are loaded based on file extension or type."""
ext = os.path.splitext(filepath)[-1].lower() if filepath else ""
if ext in ['.usd', '.usda', '.usdc'] or file_type == "USD Export":
if not cmds.pluginInfo("mayaUsdPlugin", query=True, loaded=True):
try: cmds.loadPlugin("mayaUsdPlugin")
except Exception as e: logger.error(f"Failed to load Maya USD Plugin: {e}")
if ext == '.abc' or file_type == "Alembic":
for plugin in ["AbcImport", "AbcExport"]:
if not cmds.pluginInfo(plugin, query=True, loaded=True):
try: cmds.loadPlugin(plugin)
except Exception as e: logger.error(f"Failed to load {plugin}: {e}")
[docs]
def open(filepath):
"""
Opens a Maya scene file (.ma, .mb) or supported container given the filepath.
"""
if not os.path.exists(filepath):
cmds.warning(f"openPypeline Studio: File does not exist: {filepath}")
return False
_ensure_plugins_loaded(filepath=filepath)
try:
cmds.file(filepath, force=True, open=True)
logger.info(f"Successfully opened {filepath}")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to open {filepath}. Error: {e}")
return False
[docs]
def save():
"""Saves the current Maya scene."""
try:
cmds.file(save=True)
logger.info("Successfully saved scene.")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to save. Error: {e}")
return False
[docs]
def import_file(filepath):
"""Imports a file into the current Maya scene."""
_ensure_plugins_loaded(filepath=filepath)
try:
cmds.file(filepath, i=True)
logger.info(f"Successfully imported {filepath}")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to import {filepath}. Error: {e}")
return False
[docs]
def reference_file(filepath):
"""References a file into the current Maya scene."""
_ensure_plugins_loaded(filepath=filepath)
try:
cmds.file(filepath, reference=True)
logger.info(f"Successfully referenced {filepath}")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to reference {filepath}. Error: {e}")
return False
[docs]
def new_file():
"""Creates a new empty scene."""
try:
cmds.file(force=True, newFile=True)
logger.info("Created new scene.")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to create new scene. Error: {e}")
return False
[docs]
def save_as(filepath, file_type=None):
"""Renames and saves the current scene."""
try:
cmds.file(rename=filepath)
kwargs = {"save": True}
if file_type:
kwargs["type"] = file_type
cmds.file(**kwargs)
logger.info(f"Successfully saved as {filepath}")
return True
except Exception as e:
cmds.warning(f"openPypeline Studio: Failed to save as {filepath}. Error: {e}")
return False
[docs]
def export_file(filepath, file_type=None, selected=False):
"""Exports the current scene or selection to a file."""
_ensure_plugins_loaded(filepath=filepath, file_type=file_type)
try:
kwargs = {"force": True}
if file_type:
kwargs["type"] = file_type
if selected:
kwargs["exportSelected"] = True
else:
kwargs["exportAll"] = True
# Only pass Maya-specific export flags if using a native Maya format
if file_type in ["mayaAscii", "mayaBinary", None]:
kwargs["preserveReferences"] = True
kwargs["constructionHistory"] = True
kwargs["channels"] = True
kwargs["constraints"] = True
kwargs["expressions"] = True
kwargs["shader"] = True
cmds.file(filepath, **kwargs)
logger.info(f"Successfully exported to {filepath}")
return True
except Exception as e:
logger.error(f"Failed to export {filepath}. Error: {e}")
return False
[docs]
def flatten_references(master_name, workshop_name):
"""Flattens the scene by importing all referenced files."""
referenced_files = cmds.file(query=True, reference=True) or []
if not referenced_files:
logger.warning("openPypeline Studio: no references to import")
return
for ref in referenced_files:
if cmds.file(ref, query=True, deferReference=True):
msg = (f"Referenced file '{ref}' is currently unloaded and cannot be imported.\n"
f"Would you like to keep or remove this reference in the {master_name} file (it will remain in the {workshop_name} file)?")
result = cmds.confirmDialog(title="openPypeline Studio", message=msg, button=["Keep", "Remove"], defaultButton="Keep")
if result == "Remove": cmds.file(ref, removeReference=True)
else:
cmds.file(ref, importReference=True)
logger.info(f"{ref} imported into current file")
[docs]
def delete_display_layers():
"""Deletes all display layers except defaultLayer."""
layers = cmds.ls(type="displayLayer") or []
for layer in layers:
if layer != "defaultLayer":
cmds.delete(layer)
[docs]
def set_workspace(proj_path, rules):
"""Sets the Maya workspace and file rules."""
try:
for rule_name, rule_path in rules.items():
if rule_path:
cmds.workspace(fileRule=[rule_name, rule_path])
cmds.workspace(proj_path, openWorkspace=True)
logger.info(f"Set workspace to {proj_path}")
return True
except Exception as e:
logger.error(f"Failed to set workspace: {e}")
return False
[docs]
def is_modified():
"""Returns True if the current scene has unsaved changes."""
return cmds.file(query=True, modified=True)
[docs]
def record_playblast(filepath):
"""Records a playblast to the given filepath."""
try:
cmds.playblast(filename=filepath, forceOverwrite=True, format="movie", viewer=False, showOrnaments=False)
logger.info(f"Playblast recorded to {filepath}")
return True
except Exception as e:
logger.error(f"Failed to record playblast: {e}")
return False
[docs]
def create_thumbnail(filepath):
"""Takes a snapshot of the current scene and saves it to filepath."""
try:
curr_frame = cmds.currentTime(query=True)
format_val = cmds.getAttr("defaultRenderGlobals.imageFormat")
cmds.setAttr("defaultRenderGlobals.imageFormat", 8) # JPEG
cmds.playblast(frame=curr_frame, format="image", completeFilename=filepath, showOrnaments=False, viewer=False, widthHeight=(164, 105), percent=100)
cmds.setAttr("defaultRenderGlobals.imageFormat", format_val)
logger.info(f"Thumbnail created at {filepath}")
return True
except Exception as e:
logger.error(f"Failed to create thumbnail: {e}")
return False