#!/usr/bin/env python # -*- coding: utf-8 -*- """ Maya 插件启动模块 负责检测 Maya 版本并设置正确的插件路径 """ import os import re import subprocess from typing import Dict, Optional, Tuple, List # 尝试导入 Qt,使用 Qt.py 模块实现跨版本兼容 try: from .Qt import QtWidgets QMessageBox = QtWidgets.QMessageBox HAS_QT = True except ImportError: HAS_QT = False QMessageBox = None class MayaLauncher: """Maya 启动器""" def __init__(self, maya_exe_path: str, plugin_base_path: str): """ 初始化 Maya 启动器 Args: maya_exe_path: Maya 可执行文件路径 plugin_base_path: 插件基础路径(不含版本号) """ self.maya_exe_path = maya_exe_path self.plugin_base_path = plugin_base_path self.maya_version = self._detect_maya_version() def _detect_maya_version(self) -> Optional[str]: """从 Maya 路径中检测版本号 支持的格式: - Maya2023 -> 2023 - Maya2023.2 -> 2023 - Maya2025.0.1 -> 2025 Returns: 主版本号字符串,如 "2023", "2025" 等,如果检测失败返回 None """ # 从路径中提取版本号(支持 Maya2023, Maya2023.2, Maya2025.0.1 等格式) match = re.search(r'Maya(\d{4})(?:\.\d+)*', self.maya_exe_path, re.IGNORECASE) if match: version = match.group(1) # 只取主版本号 print(f"[INFO] Detected Maya major version: {version}") return version print(f"[WARNING] Could not detect Maya version from path: {self.maya_exe_path}") return None def _get_available_plugin_versions(self) -> List[str]: """获取所有可用的插件版本 Returns: 可用版本列表,如 ['2023', '2024', '2025'] """ if not os.path.exists(self.plugin_base_path): return [] versions = [] try: for item in os.listdir(self.plugin_base_path): item_path = os.path.join(self.plugin_base_path, item) # 检查是否是目录且名称是4位数字(版本号) if os.path.isdir(item_path) and re.match(r'^\d{4}$', item): versions.append(item) except Exception as e: print(f"[WARNING] Error scanning plugin versions: {e}") return sorted(versions) def _get_versioned_plugin_path(self) -> Tuple[Optional[str], Optional[str]]: """获取带版本号的插件路径 Returns: (插件路径, 错误消息) 元组 - 如果成功: (路径, None) - 如果失败: (None, 错误消息) """ if not self.maya_version: return None, "无法检测 Maya 版本号" # 拼接版本号路径 versioned_path = os.path.join(self.plugin_base_path, self.maya_version) # 检查路径是否存在 if not os.path.exists(versioned_path): # 获取所有可用版本 available_versions = self._get_available_plugin_versions() if not available_versions: error_msg = ( f"插件目录不存在:\n{self.plugin_base_path}\n\n" f"未找到任何可用的插件版本。" ) else: error_msg = ( f"Maya 版本: {self.maya_version}\n" f"插件版本不匹配!\n\n" f"当前 Maya 版本: {self.maya_version}\n" f"可用插件版本: {', '.join(available_versions)}\n\n" f"插件将不会被加载。\n" f"请检查 Maya 软件版本或安装对应版本的插件。" ) return None, error_msg print(f"[INFO] Using plugin path: {versioned_path}") return versioned_path, None def _setup_environment(self) -> Tuple[Dict[str, str], Optional[str]]: """设置 Maya 环境变量 Returns: (环境变量字典, 错误消息) 元组 """ env = os.environ.copy() # 获取带版本号的插件路径 plugin_path, error_msg = self._get_versioned_plugin_path() if not plugin_path: return env, error_msg # 设置 Maya 环境变量 shelves_path = os.path.join(plugin_path, "shelves") scripts_path = os.path.join(plugin_path, "scripts") plugins_path = os.path.join(plugin_path, "plug-ins") icons_path = os.path.join(plugin_path, "icons") # MAYA_SHELF_PATH - 工具架路径(追加到现有路径) if os.path.exists(shelves_path): existing_shelf_path = env.get("MAYA_SHELF_PATH", "") if existing_shelf_path: env["MAYA_SHELF_PATH"] = f"{shelves_path};{existing_shelf_path}" else: env["MAYA_SHELF_PATH"] = shelves_path print(f"[OK] Set MAYA_SHELF_PATH: {shelves_path}") # MAYA_SCRIPT_PATH - MEL/Python 脚本路径(追加到现有路径) if os.path.exists(scripts_path): existing_script_path = env.get("MAYA_SCRIPT_PATH", "") if existing_script_path: env["MAYA_SCRIPT_PATH"] = f"{scripts_path};{existing_script_path}" else: env["MAYA_SCRIPT_PATH"] = scripts_path print(f"[OK] Set MAYA_SCRIPT_PATH: {scripts_path}") # PYTHONPATH - Python 模块路径 if os.path.exists(scripts_path): existing_pythonpath = env.get("PYTHONPATH", "") if existing_pythonpath: env["PYTHONPATH"] = f"{scripts_path};{existing_pythonpath}" else: env["PYTHONPATH"] = scripts_path print(f"[OK] Set PYTHONPATH: {scripts_path}") # MAYA_PLUG_IN_PATH - 插件路径(追加到现有路径) if os.path.exists(plugins_path): existing_plugin_path = env.get("MAYA_PLUG_IN_PATH", "") if existing_plugin_path: env["MAYA_PLUG_IN_PATH"] = f"{plugins_path};{existing_plugin_path}" else: env["MAYA_PLUG_IN_PATH"] = plugins_path print(f"[OK] Set MAYA_PLUG_IN_PATH: {plugins_path}") # XBMLANGPATH - 图标路径(追加到现有路径) if os.path.exists(icons_path): existing_icon_path = env.get("XBMLANGPATH", "") if existing_icon_path: env["XBMLANGPATH"] = f"{icons_path};{existing_icon_path}" else: env["XBMLANGPATH"] = icons_path print(f"[OK] Set XBMLANGPATH: {icons_path}") return env, None def launch(self, show_error_dialog: bool = True) -> bool: """启动 Maya Args: show_error_dialog: 是否显示错误对话框 Returns: 是否成功启动 """ try: # 检查 Maya 可执行文件是否存在 if not os.path.exists(self.maya_exe_path): error_msg = f"Maya 可执行文件不存在:\n{self.maya_exe_path}" print(f"[ERROR] {error_msg}") if show_error_dialog and HAS_QT: QMessageBox.critical(None, "Maya 启动失败", error_msg) return False # 设置环境变量 env, error_msg = self._setup_environment() # 如果有错误消息,显示警告但继续启动(不加载插件) if error_msg: print(f"[WARNING] {error_msg}") if show_error_dialog and HAS_QT: QMessageBox.warning( None, "插件版本不匹配", error_msg + "\n\nMaya 将在不加载插件的情况下启动。" ) # 启动 Maya print(f"[INFO] Launching Maya: {self.maya_exe_path}") subprocess.Popen([self.maya_exe_path], env=env) if error_msg: print(f"[OK] Maya launched without plugins") else: print(f"[OK] Maya launched successfully with plugins") return True except Exception as e: error_msg = f"启动 Maya 时发生错误:\n{str(e)}" print(f"[ERROR] {error_msg}") if show_error_dialog and HAS_QT: QMessageBox.critical(None, "Maya 启动失败", error_msg) return False def launch_maya(maya_exe_path: str, plugin_base_path: str) -> bool: """启动 Maya 的便捷函数 Args: maya_exe_path: Maya 可执行文件路径 plugin_base_path: 插件基础路径(不含版本号) Returns: 是否成功启动 """ launcher = MayaLauncher(maya_exe_path, plugin_base_path) return launcher.launch()