#!/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 from scripts.config import data try: from PySide2 import QtCore, QtGui, QtWidgets from shiboken2 import wrapInstance print("从PySide2加载Qt和shiboken2") except ImportError: try: from PySide6 import QtCore, QtGui, QtWidgets from shiboken6 import wrapInstance print("从PySide6加载Qt和shiboken6") except ImportError: try: from PySide import QtCore, QtGui, QtWidgets from shiboken import wrapInstance print("从PySide加载Qt和shiboken") except ImportError as e: print(f"Qt加载失败: {str(e)}") QtCore = QtGui = QtWidgets = None wrapInstance = None TOOL_PATH = data.TOOL_PATH #===================================== 3. Utility Functions ===================================== def maya_main_window(): """获取Maya主窗口""" 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): """确保目录存在""" 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(): """获取Maya模块目录""" 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): with open(data.TOOL_STYLE_FILE, 'r', encoding='utf-8') as f: style = f.read() self.setStyleSheet(style) print(f"已加载样式文件: {data.TOOL_STYLE_FILE}") def setup_ui(self): """Initialize and setup UI components""" self.setWindowTitle(f"{data.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(data.TOOL_ICON): self.setWindowIcon(QtGui.QIcon(data.TOOL_ICON)) else: print(f"Warning: Icon file not found: {data.TOOL_ICON}") #----------------- 5.1 UI Methods ----------------- def create_widgets(self): self.new_shelf_toggle = QtWidgets.QCheckBox(f"{data.TOOL_NAME} Installation") self.install_button = SetButton("Install " + data.TOOL_NAME) self.uninstall_button = SetButton("Uninstall " + data.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 " + data.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) msg_box.setStyleSheet(self.styleSheet()) 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 e: print(f"Error closing window: {e}") event.accept() def helpEvent(self, event): self.open_help_url() event.accept() #----------------- 5.3 Utility Methods ----------------- def open_help_url(self): webbrowser.open(data.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() #----------------- 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 {data.TOOL_NAME}?" ) if msg_box.exec_() == QtWidgets.QMessageBox.Yes: try: self.install_tool() self.close() except Exception as e: error_msg = f"Error during installation: {e}" 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 {data.TOOL_NAME}?" ) if msg_box.exec_() == QtWidgets.QMessageBox.Yes: try: self.uninstall_tool() self.close() except Exception as e: error_msg = f"Error during uninstallation: {e}" print(error_msg) QtWidgets.QMessageBox.critical(self, "Error", error_msg) else: print("Uninstallation cancelled") def create_mod_file(self): """Create or update the .mod file for Maya""" modules_dir = get_maya_modules_dir() mod_content = f"""+ {data.TOOL_NAME} {data.TOOL_VERSION} {data.TOOL_PATH} scripts: {data.SCRIPTS_PATH} """ mod_file_path = os.path.join(modules_dir, data.TOOL_MOD_FILENAME) self._write_mod_file(mod_file_path, mod_content) def _write_mod_file(self, file_path, content): """Helper method to write .mod file""" try: with open(file_path, "w") as f: f.write(content) print(f"Successfully created/updated: {file_path}") except Exception as e: error_msg = f"Error writing .mod file: {e}" print(error_msg) QtWidgets.QMessageBox.critical(self, "Error", error_msg) def uninstall_mod_file(self): modules_dir = get_maya_modules_dir() mod_file_path = os.path.join(modules_dir, data.TOOL_MOD_FILENAME) if os.path.exists(mod_file_path): try: os.remove(mod_file_path) print(f"{data.TOOL_NAME}.mod file deleted") except Exception as e: print(f"Error deleting {data.TOOL_NAME}.mod file: {e}") def clean_existing_buttons(self): if cmds.shelfLayout(data.TOOL_NAME, exists=True): buttons = cmds.shelfLayout(data.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 == data.TOOL_NAME: cmds.deleteUI(btn) print(f"Deleted existing {data.TOOL_NAME} button: {btn}") def install_tool(self): """Install the tool to Maya""" if not os.path.exists(data.SCRIPTS_PATH): print(f"Error: Scripts path does not exist: {data.SCRIPTS_PATH}") return if not os.path.exists(data.TOOL_MAIN_SCRIPT): print(f"Error: Main script file not found: {data.TOOL_MAIN_SCRIPT}") return # Add scripts path to Python path if data.SCRIPTS_PATH not in sys.path: sys.path.insert(0, data.SCRIPTS_PATH) # Create shelf and button self._create_shelf_button() self.create_mod_file() # Switch to the newly created shelf try: cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=data.TOOL_NAME) print(f"Switched to {data.TOOL_NAME} shelf") except Exception as e: print(f"Error switching to {data.TOOL_NAME} shelf: {e}") 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(data.TOOL_NAME, exists=True): cmds.shelfLayout(data.TOOL_NAME, parent=shelf_layout) # Clean existing buttons self.clean_existing_buttons() # Create new button icon_path = data.TOOL_ICON if os.path.exists(data.TOOL_ICON) else data.TOOL_COMMAND_ICON command = self._get_shelf_button_command() cmds.shelfButton( parent=data.TOOL_NAME, image1=icon_path, label=data.TOOL_NAME, command=command, sourceType="python", annotation=f"{data.TOOL_NAME} {data.TOOL_VERSION}", noDefaultPopup=True, style="iconOnly" ) def _get_shelf_button_command(self): """Get the command string for shelf button""" return f""" import sys import os TOOL_PATH = r'{data.TOOL_PATH}' if TOOL_PATH not in sys.path: sys.path.insert(0, TOOL_PATH) SCRIPTS_PATH = r'{data.SCRIPTS_PATH}' if SCRIPTS_PATH not in sys.path: sys.path.insert(0, SCRIPTS_PATH) os.chdir(SCRIPTS_PATH) try: import MetaFusion MetaFusion.show() except ImportError as e: print("Error importing MetaFusion:", str(e)) print(f"Scripts path: {data.SCRIPTS_PATH}") print("sys.path:", sys.path) print(f"Contents of Scripts folder: {os.listdir(data.SCRIPTS_PATH)}") """ def uninstall_tool(self): """Uninstall the tool from Maya""" window_name = f"{data.TOOL_NAME}Window" dock_name = f"{data.TOOL_NAME}WindowDock" shelf_file = f"shelf_{data.TOOL_NAME}.mel" if cmds.window(window_name, exists=True): try: cmds.deleteUI(window_name) except Exception as e: print(f"Error closing {data.TOOL_NAME} window: {e}") if cmds.dockControl(dock_name, exists=True): try: cmds.deleteUI(dock_name) except Exception as e: print(f"Error closing docked {data.TOOL_NAME} window: {e}") 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(data.TOOL_NAME, exists=True): try: cmds.deleteUI(data.TOOL_NAME, layout=True) except Exception as e: print(f"Error deleting {data.TOOL_NAME} shelf: {e}") self._clean_all_shelf_buttons() # Remove from Python path if data.SCRIPTS_PATH in sys.path: sys.path.remove(data.SCRIPTS_PATH) # Deleting Shelf Files shelf_path = os.path.join( cmds.internalVar(userAppDir=True), cmds.about(version=True), "prefs", "shelves", f"shelf_{data.TOOL_NAME}.mel" ) if os.path.exists(shelf_path): try: os.remove(shelf_path) except Exception as e: print(f"Error deleting shelf file: {e}") # If the current tool shelf is a deleted tool shelf, switch to another tool shelf if current_shelf == data.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) == data.TOOL_NAME: cmds.deleteUI(btn) def _show_uninstall_success_message(self): """Show uninstallation success message""" msg_box = QtWidgets.QMessageBox() msg_box.setWindowTitle("Uninstallation Successful") msg_box.setText(f"{data.TOOL_NAME} has been successfully uninstalled!") msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) msg_box.setWindowIcon(QtGui.QIcon(data.TOOL_ICON)) msg_box.setStyleSheet(self.styleSheet()) msg_box.exec_() def _show_install_success_message(self): msg_box = QtWidgets.QMessageBox() msg_box.setWindowTitle("Installation Successful") msg_box.setText(f"{data.TOOL_NAME} has been successfully installed!") msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok) msg_box.setWindowIcon(QtGui.QIcon(data.TOOL_ICON)) msg_box.setStyleSheet(self.styleSheet()) msg_box.exec_() def _validate_paths(self): """Validate all required paths exist""" paths = { "Root": data.TOOL_PATH, "Scripts": data.SCRIPTS_PATH, "Icons": data.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_{data.TOOL_NAME}.mel"') except Exception as e: self._log(f"Error loading shelf file: {e}", error=True) #===================================== 6. Main Function ===================================== def main(): """Main entry point for the installer""" try: dialog = InstallDialog() dialog.show() except Exception as e: print(f"Error launching installer: {e}") return -1 return dialog if __name__ == "__main__": main()