MetaFusion/Install.py

464 lines
17 KiB
Python
Raw Normal View History

2025-02-03 22:58:41 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===================================== 1. Module Imports =====================================
import maya.OpenMayaUI as omui
2025-02-06 04:46:41 +08:00
import maya.cmds as cmds
import maya.mel as mel
import webbrowser
import sys
import os
from scripts.config import data
2025-02-03 22:58:41 +08:00
2025-02-06 04:00:17 +08:00
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
2025-02-07 10:29:50 +08:00
ROOT_DIR = data.TOOL_PATH
MAYA_VERSION = data.MAYA_VERSION
PYDNA_PATH = data.PYDNA_PATH
print(f"ROOT_DIR: {ROOT_DIR}")
print(f"MAYA_VERSION: {MAYA_VERSION}")
print(f"PYDNA_PATH: {PYDNA_PATH}")
2025-02-03 22:58:41 +08:00
#===================================== 3. Utility Functions =====================================
def maya_main_window():
2025-02-06 04:00:17 +08:00
"""获取Maya主窗口"""
2025-02-03 22:58:41 +08:00
main_window_ptr = omui.MQtUtil.mainWindow()
2025-02-06 04:00:17 +08:00
if main_window_ptr:
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
return None
2025-02-03 22:58:41 +08:00
def ensure_directory(directory_path):
2025-02-06 04:00:17 +08:00
"""确保目录存在"""
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}")
2025-02-03 22:58:41 +08:00
return directory_path
def get_maya_modules_dir():
2025-02-06 04:00:17 +08:00
"""获取Maya模块目录"""
2025-02-03 22:58:41 +08:00
maya_app_dir = cmds.internalVar(userAppDir=True)
2025-02-06 04:00:17 +08:00
if maya_app_dir and isinstance(maya_app_dir, str):
return ensure_directory(os.path.join(maya_app_dir, "modules"))
return None
2025-02-03 22:58:41 +08:00
#===================================== 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):
2025-02-06 04:46:41 +08:00
with open(data.TOOL_STYLE_FILE, 'r', encoding='utf-8') as f:
2025-02-06 04:00:17 +08:00
style = f.read()
self.setStyleSheet(style)
2025-02-03 22:58:41 +08:00
def setup_ui(self):
"""Initialize and setup UI components"""
2025-02-06 04:46:41 +08:00
self.setWindowTitle(f"{data.TOOL_NAME} Installation")
2025-02-03 22:58:41 +08:00
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"""
2025-02-06 04:46:41 +08:00
if os.path.exists(data.TOOL_ICON):
self.setWindowIcon(QtGui.QIcon(data.TOOL_ICON))
2025-02-03 22:58:41 +08:00
else:
2025-02-06 04:46:41 +08:00
print(f"Warning: Icon file not found: {data.TOOL_ICON}")
2025-02-03 22:58:41 +08:00
#----------------- 5.1 UI Methods -----------------
def create_widgets(self):
2025-02-06 04:46:41 +08:00
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)
2025-02-03 22:58:41 +08:00
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)
2025-02-06 04:46:41 +08:00
welcome_label = QtWidgets.QLabel("Welcome to " + data.TOOL_NAME + "!")
2025-02-03 22:58:41 +08:00
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):
2025-02-06 04:46:41 +08:00
webbrowser.open(data.TOOL_HELP_URL)
2025-02-03 22:58:41 +08:00
QtWidgets.QApplication.restoreOverrideCursor()
2025-02-06 04:00:17 +08:00
def get_script_path():
2025-02-03 22:58:41 +08:00
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",
2025-02-06 04:46:41 +08:00
f"Are you sure you want to install {data.TOOL_NAME}?"
2025-02-03 22:58:41 +08:00
)
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",
2025-02-06 04:46:41 +08:00
f"Are you sure you want to uninstall {data.TOOL_NAME}?"
2025-02-03 22:58:41 +08:00
)
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()
2025-02-06 04:46:41 +08:00
mod_content = f"""+ {data.TOOL_NAME} {data.TOOL_VERSION} {data.TOOL_PATH}
2025-02-07 10:29:50 +08:00
scripts: {data.SCRIPTS_PATH}
plug-ins: plugins/{data.SYSTEM_OS}/{data.MAYA_VERSION}
XBMLANGPATH+:=resources/icons
PYTHONPATH+:=plugins/{data.SYSTEM_OS}/{data.MAYA_VERSION}
PYTHONPATH+:=plugins/{data.SYSTEM_OS}/{data.MAYA_VERSION}/pydna/{data.PYTHON_VERSION_DIR}
TOOL_PATH+:={data.TOOL_PATH}
"""
2025-02-06 04:46:41 +08:00
mod_file_path = os.path.join(modules_dir, data.TOOL_MOD_FILENAME)
2025-02-03 22:58:41 +08:00
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()
2025-02-06 04:46:41 +08:00
mod_file_path = os.path.join(modules_dir, data.TOOL_MOD_FILENAME)
2025-02-03 22:58:41 +08:00
if os.path.exists(mod_file_path):
try:
os.remove(mod_file_path)
2025-02-06 04:46:41 +08:00
print(f"{data.TOOL_NAME}.mod file deleted")
2025-02-03 22:58:41 +08:00
except Exception as e:
2025-02-06 04:46:41 +08:00
print(f"Error deleting {data.TOOL_NAME}.mod file: {e}")
2025-02-03 22:58:41 +08:00
def clean_existing_buttons(self):
2025-02-06 04:46:41 +08:00
if cmds.shelfLayout(data.TOOL_NAME, exists=True):
buttons = cmds.shelfLayout(data.TOOL_NAME, query=True, childArray=True) or []
2025-02-03 22:58:41 +08:00
for btn in buttons:
if cmds.shelfButton(btn, query=True, exists=True):
label = cmds.shelfButton(btn, query=True, label=True)
2025-02-06 04:46:41 +08:00
if label == data.TOOL_NAME:
2025-02-03 22:58:41 +08:00
cmds.deleteUI(btn)
2025-02-06 04:46:41 +08:00
print(f"Deleted existing {data.TOOL_NAME} button: {btn}")
2025-02-03 22:58:41 +08:00
def install_tool(self):
"""Install the tool to Maya"""
2025-02-06 04:46:41 +08:00
if not os.path.exists(data.SCRIPTS_PATH):
print(f"Error: Scripts path does not exist: {data.SCRIPTS_PATH}")
2025-02-03 22:58:41 +08:00
return
2025-02-06 04:46:41 +08:00
if not os.path.exists(data.TOOL_MAIN_SCRIPT):
print(f"Error: Main script file not found: {data.TOOL_MAIN_SCRIPT}")
2025-02-03 22:58:41 +08:00
return
# Add scripts path to Python path
2025-02-06 04:46:41 +08:00
if data.SCRIPTS_PATH not in sys.path:
sys.path.insert(0, data.SCRIPTS_PATH)
2025-02-03 22:58:41 +08:00
# Create shelf and button
self._create_shelf_button()
self.create_mod_file()
# Switch to the newly created shelf
try:
2025-02-06 04:46:41 +08:00
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=data.TOOL_NAME)
print(f"Switched to {data.TOOL_NAME} shelf")
2025-02-03 22:58:41 +08:00
except Exception as e:
2025-02-06 04:46:41 +08:00
print(f"Error switching to {data.TOOL_NAME} shelf: {e}")
2025-02-03 22:58:41 +08:00
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
2025-02-06 04:46:41 +08:00
if not cmds.shelfLayout(data.TOOL_NAME, exists=True):
cmds.shelfLayout(data.TOOL_NAME, parent=shelf_layout)
2025-02-03 22:58:41 +08:00
# Clean existing buttons
self.clean_existing_buttons()
# Create new button
2025-02-06 04:46:41 +08:00
icon_path = data.TOOL_ICON if os.path.exists(data.TOOL_ICON) else data.TOOL_COMMAND_ICON
2025-02-03 22:58:41 +08:00
command = self._get_shelf_button_command()
cmds.shelfButton(
2025-02-06 04:46:41 +08:00
parent=data.TOOL_NAME,
2025-02-03 22:58:41 +08:00
image1=icon_path,
2025-02-06 04:46:41 +08:00
label=data.TOOL_NAME,
2025-02-03 22:58:41 +08:00
command=command,
sourceType="python",
2025-02-06 04:46:41 +08:00
annotation=f"{data.TOOL_NAME} {data.TOOL_VERSION}",
2025-02-03 22:58:41 +08:00
noDefaultPopup=True,
style="iconOnly"
)
def _get_shelf_button_command(self):
"""Get the command string for shelf button"""
return f"""
import sys
import os
2025-02-06 04:46:41 +08:00
TOOL_PATH = r'{data.TOOL_PATH}'
2025-02-06 04:08:52 +08:00
if TOOL_PATH not in sys.path:
sys.path.insert(0, TOOL_PATH)
2025-02-06 04:46:41 +08:00
SCRIPTS_PATH = r'{data.SCRIPTS_PATH}'
2025-02-03 22:58:41 +08:00
if SCRIPTS_PATH not in sys.path:
sys.path.insert(0, SCRIPTS_PATH)
os.chdir(SCRIPTS_PATH)
try:
2025-02-06 04:46:41 +08:00
import MetaFusion
MetaFusion.show()
2025-02-03 22:58:41 +08:00
except ImportError as e:
2025-02-06 04:46:41 +08:00
print("Error importing MetaFusion:", str(e))
print(f"Scripts path: {data.SCRIPTS_PATH}")
2025-02-03 22:58:41 +08:00
print("sys.path:", sys.path)
2025-02-06 04:46:41 +08:00
print(f"Contents of Scripts folder: {os.listdir(data.SCRIPTS_PATH)}")
2025-02-03 22:58:41 +08:00
"""
2025-02-06 04:00:17 +08:00
2025-02-03 22:58:41 +08:00
def uninstall_tool(self):
"""Uninstall the tool from Maya"""
2025-02-06 04:46:41 +08:00
window_name = f"{data.TOOL_NAME}Window"
dock_name = f"{data.TOOL_NAME}WindowDock"
shelf_file = f"shelf_{data.TOOL_NAME}.mel"
2025-02-03 22:58:41 +08:00
if cmds.window(window_name, exists=True):
try:
cmds.deleteUI(window_name)
except Exception as e:
2025-02-06 04:46:41 +08:00
print(f"Error closing {data.TOOL_NAME} window: {e}")
2025-02-03 22:58:41 +08:00
if cmds.dockControl(dock_name, exists=True):
try:
cmds.deleteUI(dock_name)
except Exception as e:
2025-02-06 04:46:41 +08:00
print(f"Error closing docked {data.TOOL_NAME} window: {e}")
2025-02-03 22:58:41 +08:00
self.uninstall_mod_file()
# Get the current shelf before removing it
current_shelf = cmds.shelfTabLayout("ShelfLayout", query=True, selectTab=True)
# Delete Shelves and Buttons
2025-02-06 04:46:41 +08:00
if cmds.shelfLayout(data.TOOL_NAME, exists=True):
2025-02-03 22:58:41 +08:00
try:
2025-02-06 04:46:41 +08:00
cmds.deleteUI(data.TOOL_NAME, layout=True)
2025-02-03 22:58:41 +08:00
except Exception as e:
2025-02-06 04:46:41 +08:00
print(f"Error deleting {data.TOOL_NAME} shelf: {e}")
2025-02-03 22:58:41 +08:00
self._clean_all_shelf_buttons()
# Remove from Python path
2025-02-06 04:46:41 +08:00
if data.SCRIPTS_PATH in sys.path:
sys.path.remove(data.SCRIPTS_PATH)
2025-02-03 22:58:41 +08:00
# Deleting Shelf Files
shelf_path = os.path.join(
cmds.internalVar(userAppDir=True),
cmds.about(version=True),
"prefs",
"shelves",
2025-02-06 04:46:41 +08:00
f"shelf_{data.TOOL_NAME}.mel"
2025-02-03 22:58:41 +08:00
)
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
2025-02-06 04:46:41 +08:00
if current_shelf == data.TOOL_NAME:
2025-02-03 22:58:41 +08:00
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):
2025-02-06 04:46:41 +08:00
if cmds.shelfButton(btn, query=True, label=True) == data.TOOL_NAME:
2025-02-03 22:58:41 +08:00
cmds.deleteUI(btn)
def _show_uninstall_success_message(self):
"""Show uninstallation success message"""
2025-02-06 05:45:42 +08:00
msg_box = QtWidgets.QMessageBox(self)
2025-02-03 22:58:41 +08:00
msg_box.setWindowTitle("Uninstallation Successful")
2025-02-06 04:46:41 +08:00
msg_box.setText(f"{data.TOOL_NAME} has been successfully uninstalled!")
2025-02-03 22:58:41 +08:00
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
2025-02-06 04:46:41 +08:00
msg_box.setWindowIcon(QtGui.QIcon(data.TOOL_ICON))
2025-02-03 22:58:41 +08:00
msg_box.setStyleSheet(self.styleSheet())
msg_box.exec_()
def _show_install_success_message(self):
2025-02-06 05:45:42 +08:00
msg_box = QtWidgets.QMessageBox(self)
2025-02-03 22:58:41 +08:00
msg_box.setWindowTitle("Installation Successful")
2025-02-06 04:46:41 +08:00
msg_box.setText(f"{data.TOOL_NAME} has been successfully installed!")
2025-02-03 22:58:41 +08:00
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
2025-02-06 04:46:41 +08:00
msg_box.setWindowIcon(QtGui.QIcon(data.TOOL_ICON))
2025-02-03 22:58:41 +08:00
msg_box.setStyleSheet(self.styleSheet())
msg_box.exec_()
def _validate_paths(self):
"""Validate all required paths exist"""
paths = {
2025-02-06 04:46:41 +08:00
"Root": data.TOOL_PATH,
"Scripts": data.SCRIPTS_PATH,
"Icons": data.ICONS_PATH
2025-02-03 22:58:41 +08:00
}
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:
2025-02-06 04:46:41 +08:00
mel.eval(f'loadNewShelf "shelf_{data.TOOL_NAME}.mel"')
2025-02-03 22:58:41 +08:00
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()