Update
This commit is contained in:
@@ -13,37 +13,20 @@ Features:
|
||||
- Maya 2018+ compatible
|
||||
"""
|
||||
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.utils
|
||||
# Standard library imports
|
||||
import atexit
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
# Set MAYA_MODULE_PATH environment variable for external tools
|
||||
# Maya imports with error handling
|
||||
try:
|
||||
_current_file = os.path.abspath(__file__)
|
||||
_script_dir = os.path.dirname(_current_file)
|
||||
_project_root = os.path.dirname(os.path.dirname(_script_dir))
|
||||
_modules_dir = os.path.join(_project_root, 'modules')
|
||||
|
||||
if os.path.exists(_modules_dir):
|
||||
_modules_dir_normalized = os.path.normpath(_modules_dir)
|
||||
_current_module_path = os.environ.get('MAYA_MODULE_PATH', '')
|
||||
|
||||
# Check if already in path
|
||||
_paths = [p.strip() for p in _current_module_path.split(os.pathsep) if p.strip()]
|
||||
_normalized_paths = [os.path.normpath(p) for p in _paths]
|
||||
|
||||
if _modules_dir_normalized not in _normalized_paths:
|
||||
if _current_module_path:
|
||||
os.environ['MAYA_MODULE_PATH'] = f"{_modules_dir_normalized}{os.pathsep}{_current_module_path}"
|
||||
else:
|
||||
os.environ['MAYA_MODULE_PATH'] = _modules_dir_normalized
|
||||
print(f"[Tool] MAYA_MODULE_PATH set to: {_modules_dir_normalized}")
|
||||
except Exception as e:
|
||||
print(f"[Tool] Warning: Could not set MAYA_MODULE_PATH: {e}")
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.utils
|
||||
except ImportError as e:
|
||||
print(f"[Tool] ERROR: Failed to import Maya modules: {e}")
|
||||
raise
|
||||
|
||||
# Silently try to open default commandPort to avoid startup error if it's already in use
|
||||
try:
|
||||
@@ -51,6 +34,19 @@ try:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# =============================================================================
|
||||
# Constants
|
||||
# =============================================================================
|
||||
|
||||
# Command port name
|
||||
COMMAND_PORT_NAME = "commandportDefault"
|
||||
|
||||
# Log levels
|
||||
LOG_DEBUG = 0
|
||||
LOG_INFO = 1
|
||||
LOG_WARNING = 2
|
||||
LOG_ERROR = 3
|
||||
|
||||
# =============================================================================
|
||||
# Configuration
|
||||
# =============================================================================
|
||||
@@ -58,6 +54,9 @@ except Exception:
|
||||
# Shelves to load
|
||||
SHELF_NAMES = ["Nexus_Manage", "Nexus_Modeling", "Nexus_Rigging", "Nexus_Animation", "Nexus_DevTools"]
|
||||
|
||||
# User configuration file path (optional override)
|
||||
USER_CONFIG_FILE = os.path.join(os.path.expanduser("~"), ".nexus_maya_config.py")
|
||||
|
||||
# Tool packages configuration
|
||||
TOOL_CONFIG = {
|
||||
'scripts': [
|
||||
@@ -67,12 +66,10 @@ TOOL_CONFIG = {
|
||||
{'name': 'manage_tools', 'path': 'manage_tools'},
|
||||
],
|
||||
'plugins': [
|
||||
{'name': 'cv_manip.mll', 'path': 'modeling_tools/gs_curvetools/plugins/2023'},
|
||||
{'name': 'ngskintools2.mll', 'path': 'rigging_tools/ngskintools2/plug-ins/2023'},
|
||||
{'name': 'MGPicker_2023x64.mll', 'path': 'animation_tools/mgpicker/MGPicker_Program/Plug-ins'},
|
||||
],
|
||||
'icons': [
|
||||
{'name': 'gs_curvetools', 'path': 'modeling_tools/gs_curvetools/icons'},
|
||||
{'name': 'gs_toolbox', 'path': 'modeling_tools/gs_toolbox/icons'},
|
||||
{'name': 'modit', 'path': 'modeling_tools/ModIt/Icons/Theme_Classic'},
|
||||
{'name': 'creaseplus', 'path': 'modeling_tools/creaseplus/icons'},
|
||||
@@ -107,14 +104,41 @@ _ADDED_SCRIPT_PATHS = []
|
||||
# Utility Functions
|
||||
# =============================================================================
|
||||
|
||||
def _log(msg, level=LOG_INFO):
|
||||
"""
|
||||
Print log message with level control
|
||||
|
||||
Args:
|
||||
msg: Message to print
|
||||
level: LOG_DEBUG, LOG_INFO, LOG_WARNING, or LOG_ERROR
|
||||
"""
|
||||
if level == LOG_DEBUG and not TOOL_DEBUG:
|
||||
return
|
||||
|
||||
prefix_map = {
|
||||
LOG_DEBUG: "[Tool:DEBUG]",
|
||||
LOG_INFO: "[Tool]",
|
||||
LOG_WARNING: "[Tool:WARNING]",
|
||||
LOG_ERROR: "[Tool:ERROR]"
|
||||
}
|
||||
prefix = prefix_map.get(level, "[Tool]")
|
||||
print(f"{prefix} {msg}")
|
||||
|
||||
def _tool_log(msg):
|
||||
"""Print debug message if TOOL_DEBUG is enabled"""
|
||||
if TOOL_DEBUG:
|
||||
print(msg)
|
||||
_log(msg, LOG_DEBUG)
|
||||
|
||||
def _tool_print(msg):
|
||||
"""Print info message"""
|
||||
print(msg)
|
||||
_log(msg, LOG_INFO)
|
||||
|
||||
def _tool_warning(msg):
|
||||
"""Print warning message"""
|
||||
_log(msg, LOG_WARNING)
|
||||
|
||||
def _tool_error(msg):
|
||||
"""Print error message"""
|
||||
_log(msg, LOG_ERROR)
|
||||
|
||||
def _get_script_dir():
|
||||
"""Get the directory containing this script (with fallback)"""
|
||||
@@ -157,16 +181,25 @@ def _check_maya_version():
|
||||
try:
|
||||
maya_version = int(cmds.about(version=True).split()[0])
|
||||
if maya_version < MAYA_MIN_VERSION:
|
||||
_tool_print(f"[Tool] Warning: Maya {maya_version} detected. Minimum supported version is {MAYA_MIN_VERSION}")
|
||||
_tool_warning(f"Maya {maya_version} detected. Minimum supported version is {MAYA_MIN_VERSION}")
|
||||
return False
|
||||
_tool_log(f"[Tool] Maya version: {maya_version}")
|
||||
_tool_log(f"Maya version: {maya_version}")
|
||||
return True
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Warning: Could not determine Maya version: {e}")
|
||||
_tool_warning(f"Could not determine Maya version: {e}")
|
||||
return True # Continue anyway
|
||||
|
||||
def _find_file_in_paths(filename, search_paths):
|
||||
"""Find file in a list of search paths"""
|
||||
"""
|
||||
Find file in a list of search paths
|
||||
|
||||
Args:
|
||||
filename: Name of file to find
|
||||
search_paths: List of directory paths to search
|
||||
|
||||
Returns:
|
||||
Full path to file if found, None otherwise
|
||||
"""
|
||||
for p in search_paths:
|
||||
if not p:
|
||||
continue
|
||||
@@ -175,6 +208,45 @@ def _find_file_in_paths(filename, search_paths):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
def _validate_tool_config():
|
||||
"""
|
||||
Validate TOOL_CONFIG structure
|
||||
|
||||
Returns:
|
||||
True if valid, False otherwise
|
||||
"""
|
||||
try:
|
||||
required_keys = ['scripts', 'plugins', 'icons']
|
||||
for key in required_keys:
|
||||
if key not in TOOL_CONFIG:
|
||||
_tool_error(f"Missing required key in TOOL_CONFIG: {key}")
|
||||
return False
|
||||
|
||||
if not isinstance(TOOL_CONFIG[key], list):
|
||||
_tool_error(f"TOOL_CONFIG['{key}'] must be a list")
|
||||
return False
|
||||
|
||||
# Validate each item has required fields
|
||||
for idx, item in enumerate(TOOL_CONFIG[key]):
|
||||
if not isinstance(item, dict):
|
||||
_tool_error(f"TOOL_CONFIG['{key}'][{idx}] must be a dict")
|
||||
return False
|
||||
|
||||
if 'name' not in item:
|
||||
_tool_error(f"TOOL_CONFIG['{key}'][{idx}] missing 'name' field")
|
||||
return False
|
||||
|
||||
if 'path' not in item:
|
||||
_tool_error(f"TOOL_CONFIG['{key}'][{idx}] missing 'path' field")
|
||||
return False
|
||||
|
||||
_tool_log("TOOL_CONFIG validation passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
_tool_error(f"Error validating TOOL_CONFIG: {e}")
|
||||
return False
|
||||
|
||||
def _verify_shelf_icon_references(search_paths):
|
||||
"""Verify that shelf icon references can be resolved"""
|
||||
if not TOOL_DEBUG:
|
||||
@@ -198,9 +270,9 @@ def _verify_shelf_icon_references(search_paths):
|
||||
for img in imgs:
|
||||
found = any(os.path.exists(os.path.join(p, img).replace('\\', '/')) for p in search_paths)
|
||||
if not found:
|
||||
_tool_log(f"[Tool] Warning: Icon '{img}' not found for shelf '{shelf_name}'")
|
||||
_tool_warning(f"Icon '{img}' not found for shelf '{shelf_name}'")
|
||||
except Exception as e:
|
||||
_tool_log(f"[Tool] Icon verification error: {e}")
|
||||
_tool_log(f"Icon verification error: {e}")
|
||||
|
||||
def _remove_shelf_config(shelf_name):
|
||||
"""Remove shelf config from Maya prefs to force reload"""
|
||||
@@ -239,7 +311,7 @@ def setup_icon_paths():
|
||||
if os.path.exists(full_path):
|
||||
icon_paths[name] = full_path
|
||||
else:
|
||||
_tool_print(f"[Tool] Warning: Icon path not found: {full_path}")
|
||||
_tool_warning(f"Icon path not found: {full_path}")
|
||||
|
||||
# Check if already configured
|
||||
global _ICONS_PATHS
|
||||
@@ -273,7 +345,7 @@ def setup_icon_paths():
|
||||
_verify_shelf_icon_references(paths)
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error setting up icon paths: {e}")
|
||||
_tool_error(f"Error setting up icon paths: {e}")
|
||||
|
||||
def load_tool_shelves():
|
||||
"""Load Nexus shelves into Maya"""
|
||||
@@ -284,7 +356,7 @@ def load_tool_shelves():
|
||||
script_dir = _get_script_dir()
|
||||
shelf_paths = os.path.join(os.path.dirname(script_dir), "shelves")
|
||||
if not os.path.exists(shelf_paths):
|
||||
_tool_print("[Tool] Shelf directory not found, skipping")
|
||||
_tool_warning("Shelf directory not found, skipping")
|
||||
return
|
||||
|
||||
shelf_path_list = [p.strip() for p in shelf_paths.split(os.pathsep) if p.strip()]
|
||||
@@ -302,7 +374,7 @@ def load_tool_shelves():
|
||||
break
|
||||
|
||||
if not shelf_file_found:
|
||||
_tool_print(f"[Tool] Shelf not found: shelf_{shelf_name}.mel")
|
||||
_tool_warning(f"Shelf not found: shelf_{shelf_name}.mel")
|
||||
continue
|
||||
|
||||
# Remove existing shelf
|
||||
@@ -350,12 +422,12 @@ def load_tool_shelves():
|
||||
_remove_shelf_config(shelf_name)
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading shelf {shelf_name}: {e}")
|
||||
_tool_error(f"Error loading shelf {shelf_name}: {e}")
|
||||
|
||||
_tool_log(f"[Tool] ✓ Shelves: {loaded_count}/{len(SHELF_NAMES)} loaded")
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading shelves: {e}")
|
||||
_tool_error(f"Error loading shelves: {e}")
|
||||
|
||||
def load_tool_plugins():
|
||||
"""Load Nexus plugins"""
|
||||
@@ -379,13 +451,13 @@ def load_tool_plugins():
|
||||
# First check the configured path parameter
|
||||
plugin_path = None
|
||||
if plugin_rel_path:
|
||||
# Build the full path: script_dir/path/plugin_name
|
||||
# Build full path: script_dir/path/plugin_name
|
||||
full_path = _norm_path(os.path.join(script_dir, plugin_rel_path, plugin_name))
|
||||
if os.path.exists(full_path):
|
||||
plugin_path = full_path
|
||||
_tool_log(f"[Tool] Found plugin at configured path: {plugin_path}")
|
||||
|
||||
# If the configured path is not found, search the environment variable path.
|
||||
# If the configuration path is not found, search the environment variable path.
|
||||
if not plugin_path:
|
||||
plugin_path = _find_file_in_paths(plugin_name, os.environ.get(env_var, '').split(os.pathsep))
|
||||
|
||||
@@ -398,7 +470,7 @@ def load_tool_plugins():
|
||||
plugin_path = _find_file_in_paths(plugin_name, search_dirs)
|
||||
|
||||
if not plugin_path:
|
||||
_tool_print(f"[Tool] Plugin not found: {plugin_name}")
|
||||
_tool_warning(f"Plugin not found: {plugin_name}")
|
||||
continue
|
||||
|
||||
# Add plugin directory to environment
|
||||
@@ -435,12 +507,12 @@ def load_tool_plugins():
|
||||
loaded_count += 1
|
||||
_LOADED_PLUGINS[plugin_basename] = plugin_path
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading plugin {plugin_basename}: {e}")
|
||||
_tool_error(f"Error loading plugin {plugin_basename}: {e}")
|
||||
|
||||
_tool_log(f"[Tool] ✓ Plugins: {loaded_count}/{len(plugin_configs)} loaded")
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading plugins: {e}")
|
||||
_tool_error(f"Error loading plugins: {e}")
|
||||
|
||||
def load_tool_scripts():
|
||||
"""Add Nexus script paths to sys.path"""
|
||||
@@ -451,7 +523,6 @@ def load_tool_scripts():
|
||||
_tool_log("[Tool] No scripts configured")
|
||||
return
|
||||
|
||||
global _ADDED_SCRIPT_PATHS
|
||||
loaded = 0
|
||||
for cfg in script_configs:
|
||||
name = cfg.get('name', '')
|
||||
@@ -461,7 +532,7 @@ def load_tool_scripts():
|
||||
|
||||
full_path = os.path.normpath(os.path.join(script_dir, path))
|
||||
if not os.path.exists(full_path):
|
||||
_tool_print(f"[Tool] Script path not found: {full_path}")
|
||||
_tool_warning(f"Script path not found: {full_path}")
|
||||
continue
|
||||
|
||||
if full_path not in sys.path:
|
||||
@@ -473,10 +544,222 @@ def load_tool_scripts():
|
||||
_tool_log(f"[Tool] ✓ Script paths: {loaded}/{len(script_configs)} added")
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading scripts: {e}")
|
||||
_tool_error(f"Error loading scripts: {e}")
|
||||
|
||||
def _parse_mod_file_line(line, maya_version, modules_dir, module_name):
|
||||
"""
|
||||
Parse a single line from .mod file
|
||||
|
||||
Args:
|
||||
line: Line to parse
|
||||
maya_version: Current Maya version
|
||||
modules_dir: Modules directory path
|
||||
module_name: Name of the module
|
||||
|
||||
Returns:
|
||||
Tuple of (in_correct_version, current_module_root) or None
|
||||
"""
|
||||
parts = line.split()
|
||||
|
||||
if len(parts) < 4:
|
||||
return None
|
||||
|
||||
# Detect current platform
|
||||
current_platform = 'win64' if os.name == 'nt' else ('linux' if sys.platform.startswith('linux') else 'mac')
|
||||
|
||||
# Check for version and platform restrictions
|
||||
has_version_requirement = False
|
||||
version_matches = False
|
||||
platform_matches = True # Assume match if no platform specified
|
||||
|
||||
for part in parts:
|
||||
if part.startswith('MAYAVERSION:'):
|
||||
has_version_requirement = True
|
||||
required_version = int(part.split(':')[1])
|
||||
version_matches = (required_version == maya_version)
|
||||
|
||||
if part.startswith('PLATFORM:'):
|
||||
required_platform = part.split(':')[1].lower()
|
||||
platform_matches = (required_platform == current_platform)
|
||||
|
||||
# Both version and platform must match (or not be specified)
|
||||
if has_version_requirement:
|
||||
if version_matches and platform_matches:
|
||||
# Get the module root path (last parameter)
|
||||
current_module_root = parts[-1]
|
||||
if current_module_root.startswith('..'):
|
||||
current_module_root = _norm_path(os.path.join(modules_dir, current_module_root))
|
||||
_tool_print(f"{module_name}: Maya {maya_version} {current_platform} matched, root: {current_module_root}")
|
||||
return (True, current_module_root)
|
||||
else:
|
||||
return (False, None)
|
||||
|
||||
# If there are no version restrictions, it works for all versions (but still check platform)
|
||||
if platform_matches:
|
||||
current_module_root = parts[-1]
|
||||
if current_module_root.startswith('..'):
|
||||
current_module_root = _norm_path(os.path.join(modules_dir, current_module_root))
|
||||
_tool_print(f"{module_name}: No version restriction, root: {current_module_root}")
|
||||
return (True, current_module_root)
|
||||
|
||||
return (False, None)
|
||||
|
||||
def _load_plugins_from_directory(plugins_dir, mod_file):
|
||||
"""
|
||||
Load all plugins from a directory
|
||||
|
||||
Args:
|
||||
plugins_dir: Directory containing plugins
|
||||
mod_file: Name of .mod file for logging
|
||||
|
||||
Returns:
|
||||
Number of plugins loaded
|
||||
"""
|
||||
plugins_loaded = 0
|
||||
|
||||
if not os.path.exists(plugins_dir):
|
||||
_tool_log(f"Plugin dir not found: {plugins_dir}")
|
||||
return 0
|
||||
|
||||
_tool_log(f"Checking plugin dir: {plugins_dir}")
|
||||
|
||||
for plugin_file in os.listdir(plugins_dir):
|
||||
if not plugin_file.endswith(('.mll', '.so', '.bundle', '.py')):
|
||||
continue
|
||||
|
||||
plugin_name = os.path.splitext(plugin_file)[0]
|
||||
plugin_full_path = _norm_path(os.path.join(plugins_dir, plugin_file))
|
||||
|
||||
# Check if already tracked
|
||||
if plugin_name in _LOADED_PLUGINS:
|
||||
_tool_log(f"Plugin already tracked: {plugin_name}")
|
||||
continue
|
||||
|
||||
# Check if already loaded
|
||||
try:
|
||||
is_loaded = cmds.pluginInfo(plugin_name, query=True, loaded=True)
|
||||
except:
|
||||
is_loaded = False
|
||||
|
||||
if not is_loaded:
|
||||
try:
|
||||
cmds.loadPlugin(plugin_full_path, quiet=False)
|
||||
_tool_print(f"✓ Plugin loaded: {plugin_name} (from {mod_file})")
|
||||
plugins_loaded += 1
|
||||
_LOADED_PLUGINS[plugin_name] = plugin_full_path
|
||||
except Exception as e:
|
||||
_tool_error(f"Failed to load {plugin_name}: {e}")
|
||||
else:
|
||||
_tool_log(f"Plugin already loaded: {plugin_name}")
|
||||
_LOADED_PLUGINS[plugin_name] = plugin_full_path
|
||||
|
||||
return plugins_loaded
|
||||
|
||||
def _add_path_to_environment(env_var, path):
|
||||
"""
|
||||
Add a path to an environment variable
|
||||
|
||||
Args:
|
||||
env_var: Environment variable name (e.g., 'MAYA_SCRIPT_PATH')
|
||||
path: Path to add
|
||||
|
||||
Returns:
|
||||
True if added, False if already exists
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
_tool_warning(f"Path not found for {env_var}: {path}")
|
||||
return False
|
||||
|
||||
# Get current paths
|
||||
current_value = os.environ.get(env_var, '')
|
||||
current_paths = [p.strip() for p in current_value.split(os.pathsep) if p.strip()]
|
||||
|
||||
# Normalize the new path
|
||||
norm_path = _norm_path(path)
|
||||
|
||||
# Check if already in path
|
||||
if norm_path in [_norm_path(p) for p in current_paths]:
|
||||
_tool_log(f"{env_var} already contains: {norm_path}")
|
||||
return False
|
||||
|
||||
# Add to front of path list
|
||||
current_paths.insert(0, norm_path)
|
||||
new_value = os.pathsep.join(current_paths)
|
||||
os.environ[env_var] = new_value
|
||||
|
||||
# Update MEL environment if needed
|
||||
if env_var in ['XBMLANGPATH', 'MAYA_SCRIPT_PATH', 'MAYA_PLUG_IN_PATH']:
|
||||
try:
|
||||
safe_value = new_value.replace('\\', '/').replace('"', '\\"')
|
||||
mel.eval(f'putenv "{env_var}" "{safe_value}";')
|
||||
except Exception as e:
|
||||
_tool_warning(f"Could not update MEL environment for {env_var}: {e}")
|
||||
|
||||
# Also add PYTHONPATH to sys.path for Python imports
|
||||
if env_var == 'PYTHONPATH':
|
||||
if norm_path not in sys.path:
|
||||
sys.path.insert(0, norm_path)
|
||||
_tool_log(f"Added to sys.path: {norm_path}")
|
||||
|
||||
_tool_print(f"✓ Added to {env_var}: {os.path.basename(norm_path)}")
|
||||
return True
|
||||
|
||||
def _load_user_config():
|
||||
"""
|
||||
Load user configuration from optional config file
|
||||
Allows users to override default TOOL_CONFIG settings
|
||||
"""
|
||||
global TOOL_CONFIG, SHELF_NAMES
|
||||
|
||||
if not os.path.exists(USER_CONFIG_FILE):
|
||||
_tool_log(f"No user config file found at: {USER_CONFIG_FILE}")
|
||||
return
|
||||
|
||||
try:
|
||||
_tool_print(f"Loading user configuration from: {USER_CONFIG_FILE}")
|
||||
|
||||
# Create a namespace for user config
|
||||
user_namespace = {}
|
||||
|
||||
# Execute user config file
|
||||
with open(USER_CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||||
exec(f.read(), user_namespace)
|
||||
|
||||
# Override TOOL_CONFIG if provided
|
||||
if 'TOOL_CONFIG' in user_namespace:
|
||||
user_config = user_namespace['TOOL_CONFIG']
|
||||
|
||||
# Merge configurations
|
||||
for key in ['scripts', 'plugins', 'icons']:
|
||||
if key in user_config:
|
||||
if key in TOOL_CONFIG:
|
||||
# Extend existing config
|
||||
TOOL_CONFIG[key].extend(user_config[key])
|
||||
else:
|
||||
# Add new key
|
||||
TOOL_CONFIG[key] = user_config[key]
|
||||
|
||||
_tool_print("✓ User TOOL_CONFIG loaded and merged")
|
||||
|
||||
# Override SHELF_NAMES if provided
|
||||
if 'SHELF_NAMES' in user_namespace:
|
||||
user_shelves = user_namespace['SHELF_NAMES']
|
||||
if isinstance(user_shelves, list):
|
||||
# Extend shelf list
|
||||
for shelf in user_shelves:
|
||||
if shelf not in SHELF_NAMES:
|
||||
SHELF_NAMES.append(shelf)
|
||||
_tool_print(f"✓ User SHELF_NAMES loaded: {user_shelves}")
|
||||
|
||||
_tool_print("✓ User configuration applied successfully")
|
||||
|
||||
except Exception as e:
|
||||
_tool_error(f"Error loading user config: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def load_project_modules():
|
||||
"""从 modules 目录加载 .mod 文件定义的插件"""
|
||||
"""Load plugins defined in .mod files from the modules directory."""
|
||||
try:
|
||||
# Get current Maya version
|
||||
maya_version = int(cmds.about(version=True).split()[0])
|
||||
@@ -487,114 +770,155 @@ def load_project_modules():
|
||||
modules_dir = _norm_path(os.path.join(project_root, "modules"))
|
||||
|
||||
if not os.path.exists(modules_dir):
|
||||
_tool_log(f"[Tool] Modules directory not found: {modules_dir}")
|
||||
_tool_log(f"Modules directory not found: {modules_dir}")
|
||||
return
|
||||
|
||||
# Find all .mod files
|
||||
mod_files = [f for f in os.listdir(modules_dir) if f.endswith('.mod')]
|
||||
|
||||
if not mod_files:
|
||||
_tool_log(f"[Tool] No .mod files found in: {modules_dir}")
|
||||
_tool_log(f"No .mod files found in: {modules_dir}")
|
||||
return
|
||||
|
||||
_tool_print(f"[Tool] Found {len(mod_files)} module(s): {', '.join(mod_files)}")
|
||||
_tool_print(f"Found {len(mod_files)} module(s): {', '.join(mod_files)}")
|
||||
|
||||
# TWO-PASS APPROACH: First configure all environment variables, then load plugins
|
||||
# This ensures dependencies are available before plugins initialize
|
||||
|
||||
# PASS 1: Configure all environment variables (including MAYA_PLUG_IN_PATH paths)
|
||||
# Track processed modules to avoid duplicates
|
||||
processed_modules = set()
|
||||
|
||||
# Parse .mod files and load plugins.
|
||||
plugins_loaded = 0
|
||||
for mod_file in mod_files:
|
||||
mod_path = os.path.join(modules_dir, mod_file)
|
||||
_tool_log(f"[Tool] Processing: {mod_file}")
|
||||
module_name = os.path.splitext(mod_file)[0]
|
||||
|
||||
try:
|
||||
current_module_root = None
|
||||
in_correct_version = False
|
||||
module_key = None
|
||||
|
||||
with open(mod_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
|
||||
# Skip comments and empty lines
|
||||
if not line or line.startswith('#') or line.startswith('//'):
|
||||
continue
|
||||
|
||||
|
||||
# Parse module definition lines (starting with +)
|
||||
if line.startswith('+'):
|
||||
parts = line.split()
|
||||
in_correct_version = False
|
||||
current_module_root = None
|
||||
|
||||
if len(parts) >= 4:
|
||||
# Check for version restrictions.
|
||||
has_version_requirement = False
|
||||
for part in parts:
|
||||
if part.startswith('MAYAVERSION:'):
|
||||
has_version_requirement = True
|
||||
required_version = int(part.split(':')[1])
|
||||
if required_version == maya_version:
|
||||
in_correct_version = True
|
||||
# Get the module root path (last parameter)
|
||||
current_module_root = parts[-1]
|
||||
if current_module_root.startswith('..'):
|
||||
current_module_root = _norm_path(os.path.join(modules_dir, current_module_root))
|
||||
_tool_log(f"[Tool] Version matched for Maya {maya_version}: {current_module_root}")
|
||||
break
|
||||
|
||||
# If there are no version restrictions, it works for all versions.
|
||||
if not has_version_requirement:
|
||||
in_correct_version = True
|
||||
current_module_root = parts[-1]
|
||||
if current_module_root.startswith('..'):
|
||||
current_module_root = _norm_path(os.path.join(modules_dir, current_module_root))
|
||||
_tool_log(f"[Tool] Module without version restriction loaded: {current_module_root}")
|
||||
|
||||
# Only process MAYA_PLUG_IN_PATH that matches the version
|
||||
result = _parse_mod_file_line(line, maya_version, modules_dir, module_name)
|
||||
if result:
|
||||
in_correct_version, current_module_root = result
|
||||
# Create unique key for this module version
|
||||
module_key = f"{module_name}:{maya_version}:{current_module_root}"
|
||||
|
||||
# Skip if already processed
|
||||
if module_key in processed_modules:
|
||||
_tool_log(f"Skipping duplicate module definition: {module_key}")
|
||||
in_correct_version = False
|
||||
current_module_root = None
|
||||
continue
|
||||
|
||||
processed_modules.add(module_key)
|
||||
else:
|
||||
in_correct_version = False
|
||||
current_module_root = None
|
||||
module_key = None
|
||||
|
||||
# Process ALL environment variables in first pass (including MAYA_PLUG_IN_PATH to env, but don't load plugins)
|
||||
elif in_correct_version and current_module_root and ('+:=' in line or '+=' in line):
|
||||
if line.startswith('MAYA_PLUG_IN_PATH'):
|
||||
# Add plugin directory to MAYA_PLUG_IN_PATH environment, but don't load plugins yet
|
||||
plugin_path_relative = line.split('+:=')[-1].strip() if '+:=' in line else line.split('+=')[-1].strip()
|
||||
plugins_dir = _norm_path(os.path.join(current_module_root, plugin_path_relative))
|
||||
_add_path_to_environment('MAYA_PLUG_IN_PATH', plugins_dir)
|
||||
|
||||
elif line.startswith('MAYA_SCRIPT_PATH'):
|
||||
script_path_relative = line.split('+:=')[-1].strip() if '+:=' in line else line.split('+=')[-1].strip()
|
||||
script_path = _norm_path(os.path.join(current_module_root, script_path_relative))
|
||||
_add_path_to_environment('MAYA_SCRIPT_PATH', script_path)
|
||||
if script_path not in sys.path:
|
||||
sys.path.insert(0, script_path)
|
||||
|
||||
elif line.startswith('XBMLANGPATH'):
|
||||
icon_path_relative = line.split('+:=')[-1].strip() if '+:=' in line else line.split('+=')[-1].strip()
|
||||
icon_path = _norm_path(os.path.join(current_module_root, icon_path_relative))
|
||||
_add_path_to_environment('XBMLANGPATH', icon_path)
|
||||
|
||||
else:
|
||||
# Handle PATH, PYTHONPATH, and other environment variables
|
||||
try:
|
||||
env_var = line.split('+')[0].strip()
|
||||
rel_path = line.split('+:=')[-1].strip() if '+:=' in line else line.split('+=')[-1].strip()
|
||||
full_path = _norm_path(os.path.join(current_module_root, rel_path))
|
||||
_add_path_to_environment(env_var, full_path)
|
||||
except Exception as e:
|
||||
_tool_log(f"Could not process environment variable line: {line}, error: {e}")
|
||||
|
||||
except Exception as e:
|
||||
_tool_error(f"Error processing environment variables in {mod_file}: {e}")
|
||||
|
||||
# PASS 2: Now load plugins with all dependencies configured
|
||||
_tool_log("Environment variables configured, now loading plugins...")
|
||||
plugins_loaded = 0
|
||||
processed_modules.clear() # Reset for second pass
|
||||
|
||||
for mod_file in mod_files:
|
||||
mod_path = os.path.join(modules_dir, mod_file)
|
||||
module_name = os.path.splitext(mod_file)[0]
|
||||
|
||||
try:
|
||||
current_module_root = None
|
||||
in_correct_version = False
|
||||
module_key = None
|
||||
|
||||
with open(mod_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
if not line or line.startswith('#') or line.startswith('//'):
|
||||
continue
|
||||
|
||||
if line.startswith('+'):
|
||||
result = _parse_mod_file_line(line, maya_version, modules_dir, module_name)
|
||||
if result:
|
||||
in_correct_version, current_module_root = result
|
||||
module_key = f"{module_name}:{maya_version}:{current_module_root}"
|
||||
|
||||
# Skip if already processed
|
||||
if module_key in processed_modules:
|
||||
_tool_log(f"Skipping duplicate module definition for plugin loading: {module_key}")
|
||||
in_correct_version = False
|
||||
current_module_root = None
|
||||
continue
|
||||
|
||||
processed_modules.add(module_key)
|
||||
else:
|
||||
in_correct_version = False
|
||||
current_module_root = None
|
||||
module_key = None
|
||||
|
||||
# Now load plugins
|
||||
elif line.startswith('MAYA_PLUG_IN_PATH') and in_correct_version and current_module_root:
|
||||
if '+:=' in line or '+=' in line:
|
||||
plugin_path_relative = line.split('=')[-1].strip()
|
||||
|
||||
plugin_path_relative = line.split('+:=')[-1].strip() if '+:=' in line else line.split('+=')[-1].strip()
|
||||
plugins_dir = _norm_path(os.path.join(current_module_root, plugin_path_relative))
|
||||
|
||||
|
||||
if os.path.exists(plugins_dir):
|
||||
_tool_log(f"[Tool] Checking plugin dir: {plugins_dir}")
|
||||
|
||||
for plugin_file in os.listdir(plugins_dir):
|
||||
if plugin_file.endswith(('.mll', '.so', '.bundle', '.py')):
|
||||
plugin_name = os.path.splitext(plugin_file)[0]
|
||||
plugin_full_path = os.path.join(plugins_dir, plugin_file)
|
||||
|
||||
# Check if it has been loaded
|
||||
if plugin_name in _LOADED_PLUGINS:
|
||||
continue
|
||||
|
||||
try:
|
||||
is_loaded = cmds.pluginInfo(plugin_name, query=True, loaded=True)
|
||||
except:
|
||||
is_loaded = False
|
||||
|
||||
if not is_loaded:
|
||||
try:
|
||||
cmds.loadPlugin(plugin_full_path, quiet=True)
|
||||
_tool_print(f"[Tool] ✓ Plugin loaded: {plugin_name} (from {mod_file})")
|
||||
plugins_loaded += 1
|
||||
_LOADED_PLUGINS[plugin_name] = plugin_full_path
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Failed to load {plugin_name}: {e}")
|
||||
else:
|
||||
_tool_log(f"[Tool] Plugin dir not found: {plugins_dir}")
|
||||
plugins_loaded += _load_plugins_from_directory(plugins_dir, mod_file)
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error processing {mod_file}: {e}")
|
||||
_tool_error(f"Error processing {mod_file}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if plugins_loaded > 0:
|
||||
_tool_print(f"[Tool] ✓ Total {plugins_loaded} module plugin(s) loaded")
|
||||
_tool_print(f"✓ Total {plugins_loaded} module plugin(s) loaded")
|
||||
else:
|
||||
_tool_print(f"[Tool] No module plugins loaded")
|
||||
_tool_print("No module plugins loaded")
|
||||
|
||||
except Exception as e:
|
||||
_tool_print(f"[Tool] Error loading modules: {e}")
|
||||
_tool_error(f"Error loading modules: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -604,18 +928,18 @@ def setup_command_port():
|
||||
try:
|
||||
# Check if already open
|
||||
try:
|
||||
port_exists = bool(mel.eval('commandPort -q -name "commandportDefault"'))
|
||||
port_exists = bool(mel.eval(f'commandPort -q -name "{COMMAND_PORT_NAME}"'))
|
||||
except Exception:
|
||||
port_exists = False
|
||||
|
||||
if port_exists:
|
||||
_tool_log("[Tool] Command port already open")
|
||||
_tool_log("Command port already open")
|
||||
_COMMAND_PORT_OPENED = True
|
||||
return
|
||||
|
||||
# Open command port
|
||||
mel.eval('commandPort -securityWarning -name "commandportDefault";')
|
||||
_tool_log("[Tool] ✓ Command port opened")
|
||||
mel.eval(f'commandPort -securityWarning -name "{COMMAND_PORT_NAME}";')
|
||||
_tool_log("✓ Command port opened")
|
||||
_COMMAND_PORT_OPENED = True
|
||||
|
||||
except Exception as e:
|
||||
@@ -632,15 +956,19 @@ def initialize_tool():
|
||||
print("[Tool] Nexus Plugin System - Initializing...")
|
||||
print("=" * 80)
|
||||
|
||||
# Validate configuration
|
||||
if not _validate_tool_config():
|
||||
_tool_error("Configuration validation failed, continuing with caution...")
|
||||
|
||||
# Check Maya version compatibility
|
||||
if not _check_maya_version():
|
||||
print("[Tool] Warning: Running on unsupported Maya version")
|
||||
_tool_warning("Running on unsupported Maya version")
|
||||
|
||||
# Set icon paths first, so shelves can find icons
|
||||
# First, set the icon path so that the shelf can find the icon when it loads.
|
||||
setup_icon_paths()
|
||||
|
||||
# Load project modules
|
||||
load_project_modules() # Automatic loading using Maya standard module system
|
||||
load_project_modules() # Use Maya standard module systems for automatic loading.
|
||||
|
||||
# Setup command port
|
||||
setup_command_port()
|
||||
@@ -675,11 +1003,11 @@ def cleanup_on_exit():
|
||||
global _COMMAND_PORT_OPENED
|
||||
if _COMMAND_PORT_OPENED:
|
||||
try:
|
||||
if mel.eval('commandPort -q -name "commandportDefault"'):
|
||||
mel.eval('commandPort -cl "commandportDefault"')
|
||||
_tool_log("[Tool] ✓ Command port closed")
|
||||
if mel.eval(f'commandPort -q -name "{COMMAND_PORT_NAME}"'):
|
||||
mel.eval(f'commandPort -cl "{COMMAND_PORT_NAME}"')
|
||||
_tool_log("✓ Command port closed")
|
||||
except Exception as e:
|
||||
_tool_log(f"[Tool] Could not close command port: {e}")
|
||||
_tool_log(f"Could not close command port: {e}")
|
||||
|
||||
# Unload plugins
|
||||
if _LOADED_PLUGINS:
|
||||
@@ -727,4 +1055,4 @@ def cleanup_on_exit():
|
||||
_tool_log(f"[Tool] Cleanup error: {e}")
|
||||
|
||||
# Register cleanup function
|
||||
atexit.register(cleanup_on_exit)
|
||||
atexit.register(cleanup_on_exit)
|
||||
|
||||
Reference in New Issue
Block a user