775 lines
27 KiB
Python
775 lines
27 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
#===================================== 1. Module Imports =====================================
|
||
import maya.OpenMayaUI as omui
|
||
import maya.cmds as cmds
|
||
import maya.mel as mel
|
||
import webbrowser
|
||
import sys
|
||
import os
|
||
|
||
# Import configuration module
|
||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||
import config
|
||
|
||
try:
|
||
from PySide2 import QtCore, QtGui, QtWidgets
|
||
from shiboken2 import wrapInstance
|
||
print("Loading Qt and shiboken2 from PySide2")
|
||
except ImportError:
|
||
try:
|
||
from PySide6 import QtCore, QtGui, QtWidgets
|
||
from shiboken6 import wrapInstance
|
||
print("Loading Qt and shiboken6 from PySide6")
|
||
except ImportError:
|
||
try:
|
||
from PySide import QtCore, QtGui, QtWidgets
|
||
from shiboken import wrapInstance
|
||
print("Loading Qt and shiboken from PySide")
|
||
except ImportError as error:
|
||
print(f"Qt loading failed: {str(error)}")
|
||
QtCore = QtGui = QtWidgets = None
|
||
wrapInstance = None
|
||
|
||
# Use config directly instead of duplicating variables
|
||
# This ensures we always use the latest values from config
|
||
# and reduces maintenance overhead
|
||
TOOL_NAME = config.TOOL_NAME
|
||
TOOL_VERSION = config.TOOL_VERSION
|
||
TOOL_AUTHOR = config.TOOL_AUTHOR
|
||
TOOL_PATH = config.TOOL_PATH
|
||
UI_PATH = config.UI_PATH
|
||
SCRIPTS_PATH = config.SCRIPTS_PATH
|
||
ASSETS_PATH = config.ASSETS_PATH
|
||
ICONS_PATH = config.ICONS_PATH
|
||
PLUGINS_PATH = config.PLUGINS_PATH
|
||
PYDNA_PATH = config.PYDNA_PATH
|
||
SYSTEM_OS = config.SYSTEM_OS
|
||
MAYA_VERSION = config.MAYA_VERSION
|
||
PYVERSION_DIR = config.PYVERSION_DIR
|
||
DNA_FILE_PATH = config.DNA_FILE_PATH
|
||
DNA_IMG_PATH = config.DNA_IMG_PATH
|
||
STYLE_FILE = config.STYLE_FILE
|
||
TOOL_ICON = config.TOOL_ICON
|
||
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
|
||
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
|
||
TOOL_YEAR = config.TOOL_YEAR
|
||
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
|
||
TOOL_LANG = config.TOOL_LANG
|
||
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
|
||
TOOL_HELP_URL = config.TOOL_HELP_URL
|
||
|
||
print("=" * 50)
|
||
print(f"TOOL_NAME: {TOOL_NAME}")
|
||
print(f"TOOL_VERSION: {TOOL_VERSION}")
|
||
print(f"TOOL_YEAR: {TOOL_YEAR}")
|
||
print(f"SYSTEM_OS: {SYSTEM_OS}")
|
||
print(f"MAYA_VERSION: {MAYA_VERSION}")
|
||
print(f"TOOL_AUTHOR: {TOOL_AUTHOR}")
|
||
print(f"TOOL_LANG: {TOOL_LANG}")
|
||
print(f"PYVERSION_DIR: {PYVERSION_DIR}")
|
||
print(f"TOOL_WSCL_NAME: {TOOL_WSCL_NAME}")
|
||
print(f"TOOL_MOD_FILENAME: {TOOL_MOD_FILENAME}")
|
||
print(f"TOOL_HELP_URL: {TOOL_HELP_URL}")
|
||
print(f"TOOL_PATH: {TOOL_PATH}")
|
||
print(f"PLUGINS_PATH: {PLUGINS_PATH}")
|
||
print(f"PYDNA_PATH: {PYDNA_PATH}")
|
||
print(f"SCRIPTS_PATH: {SCRIPTS_PATH}")
|
||
print(f"TOOL_MAIN_SCRIPT: {TOOL_MAIN_SCRIPT}")
|
||
print(f"UI_PATH: {UI_PATH}")
|
||
print(f"STYLE_FILE: {STYLE_FILE}")
|
||
print(f"ICONS_PATH: {ICONS_PATH}")
|
||
print(f"TOOL_ICON: {TOOL_ICON}")
|
||
print(f"TOOL_COMMAND_ICON: {TOOL_COMMAND_ICON}")
|
||
print(f"ASSETS_PATH: {ASSETS_PATH}")
|
||
print(f"DNA_FILE_PATH: {DNA_FILE_PATH}")
|
||
print(f"DNA_IMG_PATH: {DNA_IMG_PATH}")
|
||
print("=" * 50)
|
||
|
||
#===================================== 3. Utility Functions =====================================
|
||
def maya_main_window():
|
||
"""Get Maya main window"""
|
||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||
if main_window_ptr:
|
||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||
return None
|
||
|
||
def ensure_directory(directory_path):
|
||
"""Ensure directory exists"""
|
||
if directory_path and isinstance(directory_path, str):
|
||
if not os.path.exists(directory_path):
|
||
os.makedirs(directory_path)
|
||
print(f"Created directory: {directory_path}")
|
||
return directory_path
|
||
|
||
def get_maya_modules_dir():
|
||
"""Get Maya modules directory"""
|
||
maya_app_dir = cmds.internalVar(userAppDir=True)
|
||
if maya_app_dir and isinstance(maya_app_dir, str):
|
||
return ensure_directory(os.path.join(maya_app_dir, "modules"))
|
||
return None
|
||
|
||
#===================================== 4. UI Component Classes =====================================
|
||
class SetButton(QtWidgets.QPushButton):
|
||
"""Custom styled button for installation interface"""
|
||
def __init__(self, text):
|
||
super(SetButton, self).__init__(text)
|
||
|
||
#===================================== 5. Main Window Class =====================================
|
||
class InstallDialog(QtWidgets.QDialog):
|
||
def __init__(self, parent=maya_main_window()):
|
||
super(InstallDialog, self).__init__(parent)
|
||
self.load_stylesheet()
|
||
self.setup_ui()
|
||
|
||
def load_stylesheet(self):
|
||
try:
|
||
with open(STYLE_FILE, 'r', encoding='utf-8') as f:
|
||
style = f.read()
|
||
self.setStyleSheet(style)
|
||
except Exception as error:
|
||
print(f"Error loading stylesheet: {error}")
|
||
# Use a default style if the file can't be loaded
|
||
self.setStyleSheet("QDialog { background-color: #333333; color: #CCCCCC; }")
|
||
|
||
def setup_ui(self):
|
||
"""Initialize and setup UI components"""
|
||
self.setWindowTitle(f"{TOOL_NAME} Installation")
|
||
self.setFixedSize(220, 120)
|
||
self.setup_window_icon()
|
||
self.create_widgets()
|
||
self.create_layouts()
|
||
self.create_connections()
|
||
|
||
def setup_window_icon(self):
|
||
"""Setup window icon if available"""
|
||
if os.path.exists(TOOL_ICON):
|
||
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||
else:
|
||
print(f"Warning: Icon file not found: {TOOL_ICON}")
|
||
|
||
#----------------- 5.1 UI Methods -----------------
|
||
def create_widgets(self):
|
||
self.new_shelf_toggle = QtWidgets.QCheckBox(f"{TOOL_NAME} Installation")
|
||
self.install_button = SetButton("Install " + TOOL_NAME)
|
||
self.uninstall_button = SetButton("Uninstall " + TOOL_NAME)
|
||
|
||
def create_layouts(self):
|
||
main_layout = QtWidgets.QVBoxLayout(self)
|
||
main_layout.setContentsMargins(10, 2, 10, 5)
|
||
main_layout.setSpacing(5)
|
||
|
||
header_layout = QtWidgets.QHBoxLayout()
|
||
header_layout.setSpacing(5)
|
||
|
||
welcome_label = QtWidgets.QLabel("Welcome to " + TOOL_NAME + "!")
|
||
welcome_label.setStyleSheet("font-size: 11px; padding: 0px; margin: 0px;")
|
||
header_layout.addWidget(welcome_label)
|
||
header_layout.addStretch()
|
||
|
||
main_layout.addLayout(header_layout)
|
||
main_layout.addWidget(self.install_button)
|
||
main_layout.addWidget(self.uninstall_button)
|
||
|
||
self.install_button.setFixedHeight(30)
|
||
self.uninstall_button.setFixedHeight(30)
|
||
|
||
def create_connections(self):
|
||
self.install_button.clicked.connect(self.install)
|
||
self.uninstall_button.clicked.connect(self.uninstall)
|
||
|
||
def create_styled_message_box(self, title, text):
|
||
msg_box = QtWidgets.QMessageBox(self)
|
||
msg_box.setWindowTitle(title)
|
||
msg_box.setText(text)
|
||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||
|
||
# 自定义样式,去掉提示文字的边框
|
||
custom_style = self.styleSheet() + """
|
||
QMessageBox { background-color: #333333; color: #CCCCCC; }
|
||
QLabel { border: none; background-color: transparent; color: #CCCCCC; }
|
||
QPushButton { min-width: 80px; min-height: 24px; }
|
||
"""
|
||
msg_box.setStyleSheet(custom_style)
|
||
|
||
return msg_box
|
||
|
||
#----------------- 5.2 Event Handler Methods -----------------
|
||
def event(self, event):
|
||
if event.type() == QtCore.QEvent.EnterWhatsThisMode:
|
||
QtWidgets.QWhatsThis.leaveWhatsThisMode()
|
||
self.open_help_url()
|
||
return True
|
||
return QtWidgets.QDialog.event(self, event)
|
||
|
||
def closeEvent(self, event):
|
||
"""Handle window close event"""
|
||
try:
|
||
super(InstallDialog, self).closeEvent(event)
|
||
except Exception as error:
|
||
print(f"Error closing window: {error}")
|
||
event.accept()
|
||
|
||
def helpEvent(self, event):
|
||
self.open_help_url()
|
||
event.accept()
|
||
|
||
#----------------- 5.3 Utility Methods -----------------
|
||
def open_help_url(self):
|
||
webbrowser.open(TOOL_HELP_URL)
|
||
QtWidgets.QApplication.restoreOverrideCursor()
|
||
|
||
def get_script_path():
|
||
maya_script = mel.eval('getenv("MAYA_SCRIPT_NAME")')
|
||
if maya_script and os.path.exists(maya_script):
|
||
return os.path.dirname(maya_script)
|
||
|
||
for sys_path in sys.path:
|
||
install_path = os.path.join(sys_path, "install.py")
|
||
if os.path.exists(install_path):
|
||
return os.path.dirname(install_path)
|
||
|
||
return os.getcwd()
|
||
|
||
def get_maya_modules_dir(self):
|
||
"""Get Maya modules directory"""
|
||
# Try to get module directory from Maya environment variable
|
||
try:
|
||
maya_app_dir = mel.eval('getenv("MAYA_APP_DIR")')
|
||
if maya_app_dir:
|
||
modules_dir = os.path.join(maya_app_dir, "modules")
|
||
if not os.path.exists(modules_dir):
|
||
os.makedirs(modules_dir)
|
||
return modules_dir
|
||
except Exception as error:
|
||
print(f"Error getting Maya modules directory from environment: {error}")
|
||
|
||
# If environment variable is not available, use default path
|
||
user_home = os.path.expanduser("~")
|
||
maya_version = MAYA_VERSION
|
||
|
||
# Windows path
|
||
if SYSTEM_OS == "Windows":
|
||
modules_dir = os.path.join(user_home, "Documents", "maya", maya_version, "modules")
|
||
# Linux path
|
||
else:
|
||
modules_dir = os.path.join(user_home, "maya", maya_version, "modules")
|
||
|
||
# Make sure the path exists
|
||
if not os.path.exists(modules_dir):
|
||
try:
|
||
os.makedirs(modules_dir)
|
||
except Exception as error:
|
||
print(f"Error creating modules directory: {error}")
|
||
|
||
return modules_dir
|
||
|
||
#----------------- 5.4 Installation Methods -----------------
|
||
def install(self):
|
||
"""Handle install request with error handling"""
|
||
if not self._validate_paths():
|
||
return
|
||
|
||
msg_box = self.create_styled_message_box(
|
||
"Confirm Installation",
|
||
f"Are you sure you want to install {TOOL_NAME}?"
|
||
)
|
||
if msg_box.exec_() == QtWidgets.QMessageBox.Yes:
|
||
try:
|
||
self.install_tool()
|
||
self.close()
|
||
except Exception as error:
|
||
error_msg = f"Error during installation: {error}"
|
||
print(error_msg)
|
||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||
|
||
def uninstall(self, *args):
|
||
"""Handle uninstall request"""
|
||
msg_box = self.create_styled_message_box(
|
||
"Confirm Uninstallation",
|
||
f"Are you sure you want to uninstall {TOOL_NAME}?"
|
||
)
|
||
|
||
if msg_box.exec_() == QtWidgets.QMessageBox.Yes:
|
||
try:
|
||
self.uninstall_tool()
|
||
self.close()
|
||
except Exception as error:
|
||
error_msg = f"Error during uninstallation: {error}"
|
||
print(error_msg)
|
||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||
else:
|
||
print("Uninstallation cancelled")
|
||
|
||
def clean_pycache(self):
|
||
"""Clean __pycache__ directories and .pyc files"""
|
||
count = 0
|
||
try:
|
||
for root, dirs, files in os.walk(TOOL_PATH):
|
||
# Remove __pycache__ directories
|
||
if "__pycache__" in dirs:
|
||
pycache_path = os.path.join(root, "__pycache__")
|
||
try:
|
||
import shutil
|
||
shutil.rmtree(pycache_path)
|
||
print(f"Removed __pycache__ directory: {pycache_path}")
|
||
count += 1
|
||
except Exception as error:
|
||
print(f"Failed to remove {pycache_path}: {error}")
|
||
|
||
# Remove .pyc files
|
||
for file in files:
|
||
if file.endswith(".pyc"):
|
||
try:
|
||
os.remove(os.path.join(root, file))
|
||
print(f"Removed .pyc file: {os.path.join(root, file)}")
|
||
count += 1
|
||
except Exception as error:
|
||
print(f"Failed to remove {os.path.join(root, file)}: {error}")
|
||
except Exception as error:
|
||
print(f"Error cleaning __pycache__ directories: {error}")
|
||
|
||
return count
|
||
|
||
def create_mod_file(self):
|
||
"""Create or update Maya's .mod file"""
|
||
modules_dir = self.get_maya_modules_dir()
|
||
|
||
# Get all Maya versions and their corresponding Python versions
|
||
version_map = {
|
||
"2022": "python3",
|
||
"2023": "python397",
|
||
"2024": "python3108",
|
||
"2025": "python311"
|
||
}
|
||
|
||
# System mapping
|
||
os_paths = {
|
||
"win64": "Windows",
|
||
"linux": "Linux"
|
||
}
|
||
|
||
# Create mod file content
|
||
mod_content = f"""+ {TOOL_NAME} {TOOL_VERSION} {TOOL_PATH}
|
||
"""
|
||
|
||
# Add each Maya version's configuration
|
||
for maya_version, python_version in version_map.items():
|
||
# Add each operating system's configuration
|
||
for os_name, os_path in os_paths.items():
|
||
mod_content += f"""
|
||
if MAYA_VERSION == {maya_version} && PLATFORM == {os_name}
|
||
scripts: {SCRIPTS_PATH}
|
||
plug-ins: {os.path.join(PLUGINS_PATH, os_path)}
|
||
XBMLANGPATH+:={ICONS_PATH}
|
||
PATH+:={os.path.join(PLUGINS_PATH, os_path)}
|
||
PATH+:={os.path.join(PLUGINS_PATH, os_path, "pydna", python_version)}
|
||
PYTHONPATH+:={SCRIPTS_PATH}
|
||
PYTHONPATH+:={os.path.join(PLUGINS_PATH, os_path, "pydna", python_version)}
|
||
endif
|
||
"""
|
||
|
||
# Write mod file
|
||
mod_file_path = os.path.join(modules_dir, TOOL_MOD_FILENAME)
|
||
try:
|
||
with open(mod_file_path, "w", encoding="utf-8") as f:
|
||
f.write(mod_content)
|
||
print(f"Created mod file: {mod_file_path}")
|
||
except Exception as error:
|
||
print(f"Error creating mod file {mod_file_path}: {error}")
|
||
|
||
def uninstall_mod_file(self):
|
||
modules_dir = self.get_maya_modules_dir()
|
||
mod_file_path = os.path.join(modules_dir, TOOL_MOD_FILENAME)
|
||
if os.path.exists(mod_file_path):
|
||
try:
|
||
os.remove(mod_file_path)
|
||
print(f"{TOOL_NAME}.mod file deleted")
|
||
except Exception as error:
|
||
print(f"Error deleting {TOOL_NAME}.mod file: {error}")
|
||
|
||
def clean_existing_buttons(self):
|
||
if cmds.shelfLayout(TOOL_NAME, exists=True):
|
||
buttons = cmds.shelfLayout(TOOL_NAME, query=True, childArray=True) or []
|
||
for btn in buttons:
|
||
if cmds.shelfButton(btn, query=True, exists=True):
|
||
label = cmds.shelfButton(btn, query=True, label=True)
|
||
if label == TOOL_NAME:
|
||
cmds.deleteUI(btn)
|
||
print(f"Deleted existing {TOOL_NAME} button: {btn}")
|
||
|
||
def install_tool(self):
|
||
"""Install the tool to Maya"""
|
||
if not os.path.exists(SCRIPTS_PATH):
|
||
print(f"Error: Scripts path does not exist: {SCRIPTS_PATH}")
|
||
return
|
||
|
||
if not os.path.exists(TOOL_MAIN_SCRIPT):
|
||
print(f"Error: Main script file not found: {TOOL_MAIN_SCRIPT}")
|
||
return
|
||
|
||
# Clean __pycache__ directories
|
||
print("Cleaning __pycache__ directories...")
|
||
pycache_count = self.clean_pycache()
|
||
print(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||
|
||
# Add scripts path to Python path
|
||
if SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, SCRIPTS_PATH)
|
||
|
||
# Create shelf and button
|
||
self._create_shelf_button()
|
||
self.create_mod_file()
|
||
|
||
# 安装完成后不需要在这里切换到对应shelf,因为在_create_shelf_button函数中已经处理了
|
||
print(f"Successfully created {TOOL_NAME} shelf and buttons")
|
||
|
||
self._show_install_success_message()
|
||
|
||
def _create_shelf_button(self):
|
||
"""Create shelf button for the tool"""
|
||
shelf_layout = mel.eval('$tmpVar=$gShelfTopLevel')
|
||
|
||
# Create shelf if not exists
|
||
if not cmds.shelfLayout(TOOL_NAME, exists=True):
|
||
cmds.shelfLayout(TOOL_NAME, parent=shelf_layout)
|
||
|
||
# Clean existing buttons
|
||
self.clean_existing_buttons()
|
||
|
||
# Create main button
|
||
icon_path = TOOL_ICON if os.path.exists(TOOL_ICON) else TOOL_COMMAND_ICON
|
||
command = self._get_shelf_button_command()
|
||
|
||
cmds.shelfButton(
|
||
parent=TOOL_NAME,
|
||
image1=icon_path,
|
||
label=TOOL_NAME,
|
||
command=command,
|
||
sourceType="python",
|
||
annotation=f"{TOOL_NAME} {TOOL_VERSION}",
|
||
noDefaultPopup=True,
|
||
style="iconOnly"
|
||
)
|
||
|
||
# Create reload modules button
|
||
reload_icon_path = os.path.join(ICONS_PATH, "reload.png")
|
||
if not os.path.exists(reload_icon_path):
|
||
reload_icon_path = icon_path # 如果没有专用图标,使用主图标
|
||
|
||
reload_command = self._get_reload_modules_command()
|
||
|
||
cmds.shelfButton(
|
||
parent=TOOL_NAME,
|
||
image1=reload_icon_path,
|
||
label="ReloadModules",
|
||
command=reload_command,
|
||
sourceType="python",
|
||
annotation=f"重新加载 {TOOL_NAME} 模块",
|
||
noDefaultPopup=True,
|
||
style="iconOnly"
|
||
)
|
||
|
||
# 自动切换到工具架
|
||
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=TOOL_NAME)
|
||
|
||
def _get_reload_modules_command(self):
|
||
"""Get the command string for the reload modules button"""
|
||
return f"""
|
||
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import sys
|
||
import os
|
||
import importlib
|
||
|
||
# 首先导入主配置文件
|
||
TOOL_PATH = r'{TOOL_PATH}'
|
||
if TOOL_PATH not in sys.path:
|
||
sys.path.insert(0, TOOL_PATH)
|
||
|
||
try:
|
||
# 导入主配置文件
|
||
import config
|
||
importlib.reload(config)
|
||
|
||
# 使用主配置文件中的路径
|
||
if config.SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, config.SCRIPTS_PATH)
|
||
|
||
# 导入并运行重载模块脚本
|
||
import scripts.ReloadModules
|
||
importlib.reload(scripts.ReloadModules)
|
||
scripts.ReloadModules.main()
|
||
except ImportError:
|
||
# 备用方案:如果无法导入主配置,使用硬编码路径
|
||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||
if SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, SCRIPTS_PATH)
|
||
|
||
try:
|
||
import scripts.ReloadModules
|
||
importlib.reload(scripts.ReloadModules)
|
||
scripts.ReloadModules.main()
|
||
except Exception as error:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error reloading modules: {{str(error)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
except Exception as error:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error reloading modules: {{str(error)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
"""
|
||
|
||
def _get_shelf_button_command(self):
|
||
"""Get the command string for the shelf button"""
|
||
return f"""
|
||
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import sys
|
||
import os
|
||
import importlib
|
||
|
||
# 首先导入主配置文件
|
||
TOOL_PATH = r'{TOOL_PATH}'
|
||
if TOOL_PATH not in sys.path:
|
||
sys.path.insert(0, TOOL_PATH)
|
||
|
||
try:
|
||
# 导入主配置文件
|
||
import config
|
||
importlib.reload(config)
|
||
|
||
# 使用主配置文件中的路径
|
||
if config.SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, config.SCRIPTS_PATH)
|
||
|
||
# 尝试导入并运行主模块
|
||
try:
|
||
# 从scripts包导入Main
|
||
from scripts import Main
|
||
importlib.reload(Main)
|
||
Main.main()
|
||
except ImportError:
|
||
# 尝试直接导入Main
|
||
import Main
|
||
importlib.reload(Main)
|
||
Main.main()
|
||
except Exception as e:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
print(f"Scripts path: {{config.SCRIPTS_PATH}}")
|
||
print("sys.path:", sys.path)
|
||
print(f"Contents of Scripts folder: {{os.listdir(config.SCRIPTS_PATH)}}")
|
||
except ImportError:
|
||
# 备用方案:如果无法导入主配置,使用硬编码路径
|
||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||
if SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, SCRIPTS_PATH)
|
||
|
||
try:
|
||
# 尝试从scripts包导入Main
|
||
from scripts import Main
|
||
importlib.reload(Main)
|
||
Main.main()
|
||
except ImportError:
|
||
# 尝试直接导入Main
|
||
try:
|
||
import Main
|
||
importlib.reload(Main)
|
||
Main.main()
|
||
except Exception as e:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
print(f"Scripts path: {{SCRIPTS_PATH}}")
|
||
print("sys.path:", sys.path)
|
||
print(f"Contents of Scripts folder: {{os.listdir(SCRIPTS_PATH)}}")
|
||
except Exception as e:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error loading {TOOL_NAME}: {{str(e)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
"""
|
||
def _get_reload_modules_command(self):
|
||
"""Get the command string for the reload modules button"""
|
||
return f"""
|
||
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import sys
|
||
import os
|
||
import importlib
|
||
|
||
# Set tool path
|
||
TOOL_PATH = r'{TOOL_PATH}'
|
||
if TOOL_PATH not in sys.path:
|
||
sys.path.insert(0, TOOL_PATH)
|
||
|
||
# Set scripts path
|
||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||
if SCRIPTS_PATH not in sys.path:
|
||
sys.path.insert(0, SCRIPTS_PATH)
|
||
|
||
# Try to import and reload modules
|
||
try:
|
||
# Import ReloadModules directly
|
||
from scripts import ReloadModules
|
||
importlib.reload(ReloadModules)
|
||
ReloadModules.show_reload_ui()
|
||
except Exception as e:
|
||
import maya.cmds as cmds
|
||
error_msg = f"Error loading ReloadModules: {{str(e)}}"
|
||
print(error_msg)
|
||
cmds.warning(error_msg)
|
||
print(f"Scripts path: {{SCRIPTS_PATH}}")
|
||
print("sys.path:", sys.path)
|
||
"""
|
||
|
||
def uninstall_tool(self):
|
||
"""Uninstall the tool from Maya"""
|
||
window_name = f"{TOOL_NAME}Window"
|
||
dock_name = f"{TOOL_NAME}WindowDock"
|
||
shelf_file = f"shelf_{TOOL_NAME}.mel"
|
||
|
||
# Clean __pycache__ directories
|
||
print("Cleaning __pycache__ directories...")
|
||
pycache_count = self.clean_pycache()
|
||
print(f"Removed {pycache_count} __pycache__ directories and .pyc files")
|
||
|
||
if cmds.window(window_name, exists=True):
|
||
try:
|
||
cmds.deleteUI(window_name)
|
||
except Exception as error:
|
||
print(f"Error closing {TOOL_NAME} window: {error}")
|
||
|
||
if cmds.dockControl(dock_name, exists=True):
|
||
try:
|
||
cmds.deleteUI(dock_name)
|
||
except Exception as error:
|
||
print(f"Error closing docked {TOOL_NAME} window: {error}")
|
||
|
||
self.uninstall_mod_file()
|
||
|
||
# Get the current shelf before removing it
|
||
current_shelf = cmds.shelfTabLayout("ShelfLayout", query=True, selectTab=True)
|
||
|
||
# Delete Shelves and Buttons
|
||
if cmds.shelfLayout(TOOL_NAME, exists=True):
|
||
try:
|
||
cmds.deleteUI(TOOL_NAME, layout=True)
|
||
except Exception as error:
|
||
print(f"Error deleting {TOOL_NAME} shelf: {error}")
|
||
|
||
self._clean_all_shelf_buttons()
|
||
|
||
# Remove from Python path
|
||
if SCRIPTS_PATH in sys.path:
|
||
sys.path.remove(SCRIPTS_PATH)
|
||
|
||
# Deleting Shelf Files
|
||
shelf_path = os.path.join(
|
||
cmds.internalVar(userAppDir=True),
|
||
cmds.about(version=True),
|
||
"prefs",
|
||
"shelves",
|
||
f"shelf_{TOOL_NAME}.mel"
|
||
)
|
||
|
||
if os.path.exists(shelf_path):
|
||
try:
|
||
os.remove(shelf_path)
|
||
except Exception as error:
|
||
print(f"Error deleting shelf file: {error}")
|
||
|
||
# If the current tool shelf is a deleted tool shelf, switch to another tool shelf
|
||
if current_shelf == TOOL_NAME:
|
||
shelves = cmds.shelfTabLayout("ShelfLayout", query=True, childArray=True)
|
||
if shelves and len(shelves) > 0:
|
||
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=shelves[0])
|
||
|
||
self._show_uninstall_success_message()
|
||
|
||
def _clean_all_shelf_buttons(self):
|
||
"""Clean up all shelf buttons related to the tool"""
|
||
all_shelves = cmds.shelfTabLayout("ShelfLayout", query=True, childArray=True) or []
|
||
for shelf in all_shelves:
|
||
shelf_buttons = cmds.shelfLayout(shelf, query=True, childArray=True) or []
|
||
for btn in shelf_buttons:
|
||
if cmds.shelfButton(btn, query=True, exists=True):
|
||
if cmds.shelfButton(btn, query=True, label=True) == TOOL_NAME:
|
||
cmds.deleteUI(btn)
|
||
|
||
def _show_uninstall_success_message(self):
|
||
"""Show uninstallation success message"""
|
||
msg_box = QtWidgets.QMessageBox(self)
|
||
msg_box.setWindowTitle("Uninstallation Successful")
|
||
msg_box.setText(f"{TOOL_NAME} has been successfully uninstalled!")
|
||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||
msg_box.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||
msg_box.setStyleSheet(self.styleSheet())
|
||
msg_box.exec_()
|
||
|
||
def _show_install_success_message(self):
|
||
msg_box = QtWidgets.QMessageBox(self)
|
||
msg_box.setWindowTitle("Installation Successful")
|
||
msg_box.setText(f"{TOOL_NAME} has been successfully installed!")
|
||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||
msg_box.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||
msg_box.setStyleSheet(self.styleSheet())
|
||
msg_box.exec_()
|
||
|
||
def _validate_paths(self):
|
||
"""Validate all required paths exist"""
|
||
paths = {
|
||
"Root": TOOL_PATH,
|
||
"Scripts": SCRIPTS_PATH,
|
||
"Icons": ICONS_PATH
|
||
}
|
||
|
||
for name, path in paths.items():
|
||
if not os.path.exists(path):
|
||
error_msg = f"Error: {name} path does not exist: {path}"
|
||
print(error_msg)
|
||
QtWidgets.QMessageBox.critical(self, "Error", error_msg)
|
||
return False
|
||
return True
|
||
|
||
def _log(self, message, error=False):
|
||
"""Log messages with timestamp"""
|
||
from datetime import datetime
|
||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
log_message = f"[{timestamp}] {message}"
|
||
print(log_message)
|
||
if error:
|
||
QtWidgets.QMessageBox.critical(self, "Error", message)
|
||
|
||
def _load_mel_shelf(self):
|
||
"""Load mel shelf file with error handling"""
|
||
try:
|
||
mel.eval(f'loadNewShelf "shelf_{TOOL_NAME}.mel"')
|
||
except Exception as error:
|
||
self._log(f"Error loading shelf file: {error}", error=True)
|
||
|
||
#===================================== 6. Main Function =====================================
|
||
def main():
|
||
"""Main entry point for the installer"""
|
||
try:
|
||
dialog = InstallDialog()
|
||
dialog.show()
|
||
except Exception as error:
|
||
print(f"Error launching installer: {error}")
|
||
return -1
|
||
return dialog
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
|