first commit
This commit is contained in:
commit
f5aec154d2
5
CleanPycache.bat
Normal file
5
CleanPycache.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
REM Delete all __pycache__ folders
|
||||
for /d /r %%d in (__pycache__) do @if exist "%%d" rd /s /q "%%d"
|
||||
|
||||
echo Cleaned up!
|
17
Install.mel
Normal file
17
Install.mel
Normal file
@ -0,0 +1,17 @@
|
||||
global proc install()
|
||||
{
|
||||
string $scriptPath = `whatIs install`;
|
||||
string $dirPath = `substring $scriptPath 25 (size($scriptPath))`;
|
||||
$dirPath = `dirname $dirPath`;
|
||||
string $pythonPath = $dirPath + "/Install.py";
|
||||
$pythonPath = substituteAllString($pythonPath, "\\", "/");
|
||||
|
||||
string $pythonCmd = "import os, sys\n";
|
||||
$pythonCmd += "INSTALL_PATH = r'" + $pythonPath + "'\n";
|
||||
$pythonCmd += "sys.path.append(os.path.dirname(INSTALL_PATH))\n";
|
||||
$pythonCmd += "exec(open(INSTALL_PATH, encoding='utf-8').read())\n";
|
||||
|
||||
python($pythonCmd);
|
||||
}
|
||||
|
||||
install();
|
499
Install.py
Normal file
499
Install.py
Normal file
@ -0,0 +1,499 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#===================================== 1. Module Imports =====================================
|
||||
# Standard library imports
|
||||
import os
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
# Maya imports
|
||||
import maya.mel as mel
|
||||
import maya.cmds as cmds
|
||||
import maya.OpenMayaUI as omui
|
||||
|
||||
# Qt imports
|
||||
from PySide2 import QtWidgets, QtGui, QtCore
|
||||
from shiboken2 import wrapInstance
|
||||
|
||||
#===================================== 2. Global Variables =====================================
|
||||
# Path configurations
|
||||
try:
|
||||
ROOT_PATH = os.path.dirname(INSTALL_PATH)
|
||||
except NameError:
|
||||
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
# Tool information
|
||||
TOOL_NAME = "MetaBox"
|
||||
TOOL_VERSION = "Beta v1.0.0"
|
||||
TOOL_AUTHOR = "Virtuos"
|
||||
TOOL_LANG = 'en_US'
|
||||
SCRIPTS_PATH = os.path.join(ROOT_PATH, "Scripts")
|
||||
ICONS_PATH = os.path.join(ROOT_PATH, "icons")
|
||||
TOOL_ICON = os.path.join(ICONS_PATH, "logo.png")
|
||||
DEFAULT_ICON = "commandButton.png"
|
||||
TOOL_HELP_URL = f"http://10.72.61.59:3000/ArtGroup/{TOOL_NAME}/wiki"
|
||||
TOOL_WSCL_NAME = "ToolBoxWorkSpaceControl"
|
||||
MOD_FILE_NAME = f"{TOOL_NAME}.mod"
|
||||
MAIN_SCRIPT_NAME = f"{TOOL_NAME}.py"
|
||||
|
||||
# UI Style configurations
|
||||
BUTTON_STYLE = """
|
||||
QPushButton {
|
||||
background-color: #D0D0D0;
|
||||
color: #303030;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #E0E0E0;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #C0C0C0;
|
||||
}
|
||||
"""
|
||||
|
||||
MESSAGE_BUTTON_STYLE = """
|
||||
QPushButton {
|
||||
background-color: #B0B0B0;
|
||||
color: #303030;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #C0C0C0;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #A0A0A0;
|
||||
}
|
||||
"""
|
||||
|
||||
#===================================== 3. Utility Functions =====================================
|
||||
def maya_main_window():
|
||||
"""Get Maya main window as QWidget"""
|
||||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||||
|
||||
def ensure_directory(directory_path):
|
||||
"""Ensure directory exists, create if not"""
|
||||
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 path"""
|
||||
maya_app_dir = cmds.internalVar(userAppDir=True)
|
||||
return ensure_directory(os.path.join(maya_app_dir, "modules"))
|
||||
|
||||
#===================================== 4. UI Component Classes =====================================
|
||||
class SetButton(QtWidgets.QPushButton):
|
||||
"""Custom styled button for installation interface"""
|
||||
def __init__(self, text):
|
||||
super(SetButton, self).__init__(text)
|
||||
self.setStyleSheet(BUTTON_STYLE)
|
||||
|
||||
#===================================== 5. Main Window Class =====================================
|
||||
class InstallDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent=maya_main_window()):
|
||||
super(InstallDialog, self).__init__(parent)
|
||||
self.setup_ui()
|
||||
|
||||
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)
|
||||
|
||||
button_style = """
|
||||
QPushButton {
|
||||
background-color: #B0B0B0;
|
||||
color: #303030;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #C0C0C0;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #A0A0A0;
|
||||
}
|
||||
"""
|
||||
|
||||
for button in msg_box.buttons():
|
||||
button.setStyleSheet(button_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 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(TOOL_HELP_URL)
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def get_script_path(self):
|
||||
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 {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 {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"""+ {TOOL_NAME} 1.0 {ROOT_PATH}
|
||||
scripts: {SCRIPTS_PATH}
|
||||
"""
|
||||
mod_file_path = os.path.join(modules_dir, MOD_FILE_NAME)
|
||||
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, MOD_FILE_NAME)
|
||||
if os.path.exists(mod_file_path):
|
||||
try:
|
||||
os.remove(mod_file_path)
|
||||
print(f"{TOOL_NAME}.mod file deleted")
|
||||
except Exception as e:
|
||||
print(f"Error deleting {TOOL_NAME}.mod file: {e}")
|
||||
|
||||
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
|
||||
|
||||
main_script = os.path.join(SCRIPTS_PATH, MAIN_SCRIPT_NAME)
|
||||
if not os.path.exists(main_script):
|
||||
print(f"Error: Main script file not found: {main_script}")
|
||||
return
|
||||
|
||||
# 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()
|
||||
|
||||
# 切换到新创建的工具架
|
||||
try:
|
||||
cmds.shelfTabLayout("ShelfLayout", edit=True, selectTab=TOOL_NAME)
|
||||
print(f"Switched to {TOOL_NAME} shelf")
|
||||
except Exception as e:
|
||||
print(f"Error switching to {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(TOOL_NAME, exists=True):
|
||||
cmds.shelfLayout(TOOL_NAME, parent=shelf_layout)
|
||||
|
||||
# Clean existing buttons
|
||||
self.clean_existing_buttons()
|
||||
|
||||
# Create new button
|
||||
icon_path = TOOL_ICON if os.path.exists(TOOL_ICON) else DEFAULT_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"
|
||||
)
|
||||
|
||||
def _get_shelf_button_command(self):
|
||||
"""Get the command string for shelf button"""
|
||||
return f"""
|
||||
import sys
|
||||
import os
|
||||
SCRIPTS_PATH = r'{SCRIPTS_PATH}'
|
||||
if SCRIPTS_PATH not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_PATH)
|
||||
os.chdir(SCRIPTS_PATH)
|
||||
try:
|
||||
import {TOOL_NAME}
|
||||
{TOOL_NAME}.show()
|
||||
except ImportError as e:
|
||||
print("Error importing {TOOL_NAME}:", str(e))
|
||||
print("Scripts path:", SCRIPTS_PATH)
|
||||
print("sys.path:", sys.path)
|
||||
print("Contents of Scripts folder:", os.listdir(SCRIPTS_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"
|
||||
|
||||
if cmds.window(window_name, exists=True):
|
||||
try:
|
||||
cmds.deleteUI(window_name)
|
||||
except Exception as e:
|
||||
print(f"Error closing {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 {TOOL_NAME} window: {e}")
|
||||
|
||||
self.uninstall_mod_file()
|
||||
|
||||
# 移除工具架之前先获取当前工具架
|
||||
current_shelf = cmds.shelfTabLayout("ShelfLayout", query=True, selectTab=True)
|
||||
|
||||
# 删除工具架和按钮
|
||||
if cmds.shelfLayout(TOOL_NAME, exists=True):
|
||||
try:
|
||||
cmds.deleteUI(TOOL_NAME, layout=True)
|
||||
except Exception as e:
|
||||
print(f"Error deleting {TOOL_NAME} shelf: {e}")
|
||||
|
||||
self._clean_all_shelf_buttons()
|
||||
|
||||
# 从Python路径中移除
|
||||
if SCRIPTS_PATH in sys.path:
|
||||
sys.path.remove(SCRIPTS_PATH)
|
||||
|
||||
# 删除工具架文件
|
||||
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 e:
|
||||
print(f"Error deleting shelf file: {e}")
|
||||
|
||||
# 如果当前工具架是被删除的工具架,切换到其他工具架
|
||||
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()
|
||||
msg_box.setWindowTitle("Uninstallation Successful")
|
||||
msg_box.setText(f"{TOOL_NAME} has been successfully uninstalled!")
|
||||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
|
||||
for button in msg_box.buttons():
|
||||
button.setStyleSheet(MESSAGE_BUTTON_STYLE)
|
||||
|
||||
msg_box.exec_()
|
||||
|
||||
def _show_install_success_message(self):
|
||||
msg_box = QtWidgets.QMessageBox()
|
||||
msg_box.setWindowTitle("Installation Successful")
|
||||
msg_box.setText(f"{TOOL_NAME} has been successfully installed!")
|
||||
msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
for button in msg_box.buttons():
|
||||
button.setStyleSheet(MESSAGE_BUTTON_STYLE)
|
||||
msg_box.exec_()
|
||||
|
||||
def _validate_paths(self):
|
||||
"""Validate all required paths exist"""
|
||||
paths = {
|
||||
"Root": ROOT_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 e:
|
||||
self._log(f"Error loading shelf file: {e}", error=True)
|
||||
|
||||
#===================================== 6. Main Function =====================================
|
||||
def main():
|
||||
"""Main entry point for the installer"""
|
||||
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
|
||||
dialog = InstallDialog()
|
||||
dialog.show()
|
||||
return app.exec_()
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
Loading…
Reference in New Issue
Block a user