246 lines
9.0 KiB
Python
246 lines
9.0 KiB
Python
#!/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()
|