Update
This commit is contained in:
parent
2134bba30a
commit
069f7929ef
18
Install.mel
Normal file
18
Install.mel
Normal file
@ -0,0 +1,18 @@
|
||||
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 += "import Install\n";
|
||||
$pythonCmd += "Install.main()\n";
|
||||
|
||||
python($pythonCmd);
|
||||
}
|
||||
|
||||
install();
|
500
Install.py
Normal file
500
Install.py
Normal file
@ -0,0 +1,500 @@
|
||||
#!/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 =====================================
|
||||
try:
|
||||
ROOT_PATH = os.path.dirname(INSTALL_PATH).replace("\\", "/")
|
||||
except NameError:
|
||||
ROOT_PATH = os.path.dirname(os.path.abspath(__file__)).replace("\\", "/")
|
||||
TOOL_NAME = "MetaFusion"
|
||||
TOOL_VERSION = "Beta v1.0.0"
|
||||
TOOL_AUTHOR = "Virtuos"
|
||||
TOOL_LANG = 'en_US'
|
||||
SCRIPTS_PATH = os.path.join(ROOT_PATH, "scripts").replace("\\", "/")
|
||||
ICONS_PATH = os.path.join(ROOT_PATH, "icons").replace("\\", "/")
|
||||
TOOL_ICON = os.path.join(ICONS_PATH, "logo.png").replace("\\", "/")
|
||||
DEFAULT_ICON = "commandButton.png"
|
||||
TOOL_HELP_URL = f"http://10.72.61.59:3000/ArtGroup/{TOOL_NAME}/wiki"
|
||||
TOOL_WSCL_NAME = "MetaFusionWorkSpaceControl"
|
||||
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} {TOOL_VERSION} {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()
|
||||
|
||||
# Switch to the newly created shelf
|
||||
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()
|
||||
|
||||
# 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 e:
|
||||
print(f"Error deleting {TOOL_NAME} shelf: {e}")
|
||||
|
||||
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 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 == 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"""
|
||||
try:
|
||||
dialog = InstallDialog()
|
||||
dialog.show()
|
||||
except Exception as e:
|
||||
print(f"Error launching installer: {e}")
|
||||
return -1
|
||||
return dialog
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
48
MetaFusion.mod
Normal file
48
MetaFusion.mod
Normal file
@ -0,0 +1,48 @@
|
||||
+ MAYAVERSION:2018 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2018
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2018
|
||||
|
||||
+ MAYAVERSION:2019 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2019
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2019
|
||||
|
||||
+ MAYAVERSION:2020 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2020
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2020
|
||||
|
||||
+ MAYAVERSION:2021 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2021
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2021
|
||||
|
||||
+ MAYAVERSION:2022 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2022
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2022
|
||||
|
||||
+ MAYAVERSION:2023 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2023
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2023
|
||||
|
||||
+ MAYAVERSION:2024 PLATFORM:win64 MetaFusion any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= scripts
|
||||
PYTHONPATH +:= plugins/2024
|
||||
MAYA_PLUG_IN_PATH +:= plugins/2024
|
16
README.md
16
README.md
@ -1 +1,15 @@
|
||||
# MetaHuman Custome Tool For Maya
|
||||
# Metahuman Customized Tool
|
||||
|
||||
Metahuman Customized Toolbag for Maya
|
||||
|
||||
# Tool Name
|
||||
|
||||
MetaFusion
|
||||
|
||||
## VERSION
|
||||
|
||||
beta 1.0.0
|
||||
|
||||
## MAYA VERSION
|
||||
|
||||
2023
|
||||
|
BIN
data/img/Preview.png
Normal file
BIN
data/img/Preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -1,35 +0,0 @@
|
||||
+ MAYAVERSION:2022 PLATFORM:win64 MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2022/windows
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2022/windows
|
||||
|
||||
+ MAYAVERSION:2022 PLATFORM:linux MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2022/linux
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2022/linux
|
||||
|
||||
+ MAYAVERSION:2023 PLATFORM:win64 MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2023/windows
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2023/windows
|
||||
|
||||
+ MAYAVERSION:2023 PLATFORM:linux MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2023/linux
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2023/linux
|
||||
|
||||
+ MAYAVERSION:2024 PLATFORM:win64 MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2024/windows
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2024/windows
|
||||
|
||||
+ MAYAVERSION:2024 PLATFORM:linux MetaHuman-DNA-Calibration any .
|
||||
PYTHONPATH +:=
|
||||
PYTHONPATH +:= data
|
||||
PYTHONPATH +:= lib/Maya2024/linux
|
||||
MAYA_PLUG_IN_PATH +:= lib/Maya2024/linux
|
304
metafusion.py
304
metafusion.py
@ -1,304 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
from shiboken2 import wrapInstance
|
||||
from maya import OpenMayaUI as omui
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
import maya.utils as utils
|
||||
import importlib
|
||||
import traceback
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import locale
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 全局变量声明
|
||||
TOOL_PATH = os.path.dirname(os.path.abspath(__file__)).replace('\\', '/')
|
||||
TOOL_ICON = os.path.join(TOOL_PATH, "icons", "logo.png")
|
||||
if not os.path.exists(TOOL_ICON):
|
||||
print(f"Warning: Icon file not found at {TOOL_ICON}")
|
||||
TOOL_ICON = "" # 设置为空字符串而不是 None
|
||||
MAYA_VERSION = cmds.about(version=True)
|
||||
TOOL_NAME = "MetaFusion"
|
||||
TOOL_VERSION = "v1.0.0"
|
||||
TOOL_AUTHOR = "CGNICO"
|
||||
CURRENT_LANG = "en_US"
|
||||
WKSP_CTRL = TOOL_NAME
|
||||
DNA_PATH = ""
|
||||
WKSP_PATH = "MetaFusion"
|
||||
HelpURL = f"https://gitea.cgnico.com/CGNICO/{TOOL_NAME}/wiki"
|
||||
|
||||
#==================================================== GLOBAL FUNCTIONS ====================================================
|
||||
class SetButton(QtWidgets.QPushButton):
|
||||
"""
|
||||
Custom rounded button class
|
||||
|
||||
Features:
|
||||
- Rounded design
|
||||
- Custom color and hover effect
|
||||
- Bold text
|
||||
"""
|
||||
def __init__(self, text="", icon=None, color="#D0D0D0", hover_color="#E0E0E0", pressed_color="#C0C0C0"):
|
||||
super(SetButton, self).__init__(text)
|
||||
if icon:
|
||||
self.setIcon(icon)
|
||||
self.setIconSize(QtCore.QSize(24, 24))
|
||||
self.setMinimumHeight(30) # Set minimum height
|
||||
self.setStyleSheet(
|
||||
f"""
|
||||
QPushButton {{
|
||||
background-color: {color};
|
||||
color: #303030;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
background-color: {hover_color};
|
||||
}}
|
||||
QPushButton:pressed {{
|
||||
background-color: {pressed_color};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
def get_system_encoding():
|
||||
encoding = sys.getdefaultencoding()
|
||||
if encoding.lower() == 'ascii':
|
||||
encoding = locale.getpreferredencoding()
|
||||
return encoding
|
||||
|
||||
def maya_main_window():
|
||||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||||
|
||||
LANG = {
|
||||
'en_US': {
|
||||
"Group 01": "Group 01",
|
||||
"Group 02": "Group 02",
|
||||
"Button 01": "Button 01",
|
||||
"Button 02": "Button 02",
|
||||
"DOCUMENT": "DOCUMENT",
|
||||
"Help": "Help",
|
||||
"Switch Language": "Switch Language"
|
||||
},
|
||||
'zh_CN': {
|
||||
"Group 01": "组 01",
|
||||
"Group 02": "组 02",
|
||||
"Button 01": "按钮 01",
|
||||
"Button 02": "按钮 02",
|
||||
"DOCUMENT": "文档",
|
||||
"Help": "帮助",
|
||||
"Switch Language": "切换语言"
|
||||
}
|
||||
}
|
||||
|
||||
#==================================================== MAIN WINDOW CLASS ====================================================
|
||||
class MainWindow(QtWidgets.QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
self.setWindowTitle(TOOL_NAME)
|
||||
self.setObjectName(TOOL_NAME)
|
||||
self.setWindowFlags(QtCore.Qt.Window)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||
self.setMinimumSize(300, 800)
|
||||
self.create_widgets()
|
||||
self.create_layouts()
|
||||
self.create_connections()
|
||||
|
||||
if os.path.exists(TOOL_ICON):
|
||||
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
else:
|
||||
print(f"WARNING: Icon file not found: {TOOL_ICON}")
|
||||
|
||||
#==================================================== UI COMPONENTS ====================================================
|
||||
def create_widgets(self):
|
||||
|
||||
self.group_01 = QtWidgets.QGroupBox(LANG[CURRENT_LANG]["Group 01"])
|
||||
self.group_01_button = SetButton(LANG[CURRENT_LANG]["Button 01"], color="#A7C6ED", hover_color="#B2D3F0", pressed_color="#8BB8E0")
|
||||
self.group_02 = QtWidgets.QGroupBox(LANG[CURRENT_LANG]["Group 02"])
|
||||
self.group_02_button = SetButton(LANG[CURRENT_LANG]["Button 02"], color="#FFCCBC", hover_color="#FFAB91", pressed_color="#FF8A65")
|
||||
|
||||
self.help_btn = QtWidgets.QPushButton(LANG[CURRENT_LANG]["DOCUMENT"])
|
||||
self.help_btn.setToolTip(LANG[CURRENT_LANG]["Help"])
|
||||
self.help_btn.setFixedSize(100, 20)
|
||||
self.help_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: gray;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
color: black;
|
||||
}
|
||||
""")
|
||||
self.lang_btn = QtWidgets.QPushButton("EN" if CURRENT_LANG == 'zh_CN' else "ZH")
|
||||
self.lang_btn.setToolTip(LANG[CURRENT_LANG]["Switch Language"])
|
||||
self.lang_btn.setFixedSize(30, 20)
|
||||
self.lang_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: gray;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
color: black;
|
||||
}
|
||||
""")
|
||||
|
||||
def create_layouts(self):
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(2, 2, 2, 2)
|
||||
main_layout.addStretch()
|
||||
main_layout.addWidget(self.group_01)
|
||||
main_layout.addWidget(self.group_01_button)
|
||||
main_layout.addWidget(self.group_02)
|
||||
main_layout.addWidget(self.group_02_button)
|
||||
main_layout.addStretch()
|
||||
|
||||
# Bottom layout
|
||||
bottom_layout = QtWidgets.QHBoxLayout()
|
||||
icon_label = QtWidgets.QLabel()
|
||||
if TOOL_ICON and os.path.exists(TOOL_ICON):
|
||||
icon = QtGui.QPixmap(TOOL_ICON).scaled(24, 24, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
icon_label.setPixmap(icon)
|
||||
|
||||
bottom_layout.addWidget(icon_label)
|
||||
version_label = QtWidgets.QLabel(f"{TOOL_VERSION}")
|
||||
version_label.setStyleSheet("color: gray; font-size: 12px;")
|
||||
bottom_layout.addWidget(version_label)
|
||||
bottom_layout.addStretch()
|
||||
bottom_layout.addWidget(self.help_btn)
|
||||
bottom_layout.addWidget(self.lang_btn)
|
||||
main_layout.addLayout(bottom_layout)
|
||||
|
||||
def create_connections(self):
|
||||
self.group_01_button.clicked.connect(self.run_group_01)
|
||||
self.group_02_button.clicked.connect(self.run_group_02)
|
||||
self.help_btn.clicked.connect(self.show_help)
|
||||
self.lang_btn.clicked.connect(self.switch_language)
|
||||
|
||||
def run_group_01(self):
|
||||
print("Button 01")
|
||||
|
||||
def run_group_02(self):
|
||||
print("Button 02")
|
||||
|
||||
def dock_to_maya(self):
|
||||
if not WKSP_CTRL:
|
||||
print("Error: WKSP_CTRL is not defined")
|
||||
return
|
||||
|
||||
if cmds.workspaceControl(WKSP_CTRL, exists=True):
|
||||
cmds.deleteUI(WKSP_CTRL)
|
||||
|
||||
def create_control():
|
||||
try:
|
||||
if not self.objectName():
|
||||
self.setObjectName(TOOL_NAME)
|
||||
|
||||
workspace_control = cmds.workspaceControl(
|
||||
WKSP_CTRL,
|
||||
label=TOOL_NAME,
|
||||
floating=True,
|
||||
retain=False,
|
||||
resizeWidth=True,
|
||||
initialWidth=300,
|
||||
minimumWidth=300
|
||||
)
|
||||
|
||||
if not workspace_control:
|
||||
raise RuntimeError("Failed to create workspace control")
|
||||
|
||||
cmds.workspaceControl(WKSP_CTRL, e=True, resizeWidth=True)
|
||||
|
||||
try:
|
||||
cmds.control(self.objectName(), e=True, p=workspace_control)
|
||||
except Exception as e:
|
||||
print(f"Error parenting control: {e}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating workspace control: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
cmds.evalDeferred(create_control)
|
||||
|
||||
def show_help(self):
|
||||
# Specify the URL of the website you want to open
|
||||
webbrowser.open(HelpURL)
|
||||
|
||||
def switch_language(self):
|
||||
global CURRENT_LANG
|
||||
CURRENT_LANG = 'en_US' if CURRENT_LANG == 'zh_CN' else 'zh_CN'
|
||||
self.lang_btn.setText("EN" if CURRENT_LANG == 'zh_CN' else "CN")
|
||||
self.retranslate_ui()
|
||||
|
||||
QtWidgets.QToolTip.showText(
|
||||
self.lang_btn.mapToGlobal(QtCore.QPoint(0, -30)),
|
||||
"Language switched" if CURRENT_LANG == 'en_US' else "语言已切换",
|
||||
self.lang_btn
|
||||
)
|
||||
|
||||
def show():
|
||||
global main_window
|
||||
|
||||
try:
|
||||
current_width = 300
|
||||
if cmds.workspaceControl(WKSP_CTRL, exists=True):
|
||||
try:
|
||||
current_width = cmds.workspaceControl(WKSP_CTRL, q=True, width=True)
|
||||
except:
|
||||
pass
|
||||
cmds.deleteUI(WKSP_CTRL, control=True)
|
||||
|
||||
if 'main_window' in globals() and main_window:
|
||||
try:
|
||||
main_window.close()
|
||||
main_window.deleteLater()
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error cleaning up previous window: {e}")
|
||||
|
||||
def create_ui(retry_count=0, width=300):
|
||||
global main_window
|
||||
try:
|
||||
if not WKSP_CTRL:
|
||||
raise ValueError("WKSP_CTRL is not defined")
|
||||
|
||||
main_window = MainWindow()
|
||||
if not main_window:
|
||||
raise RuntimeError("Failed to create main window")
|
||||
|
||||
# 使用 try-except 包装每个 evalDeferred 调用
|
||||
def deferred_actions():
|
||||
try:
|
||||
main_window.dock_to_maya()
|
||||
cmds.workspaceControl(WKSP_CTRL, e=True, width=width)
|
||||
cmds.workspaceControl(WKSP_CTRL, e=True, resizeWidth=True)
|
||||
except Exception as e:
|
||||
print(f"Error in deferred actions: {e}")
|
||||
|
||||
cmds.evalDeferred(deferred_actions)
|
||||
|
||||
except Exception as e:
|
||||
if retry_count < 3:
|
||||
print(f"{TOOL_NAME} creation failed, retrying... (Attempt {retry_count + 1})")
|
||||
print(f"Error: {str(e)}")
|
||||
utils.executeDeferred(lambda: create_ui(retry_count + 1, width))
|
||||
else:
|
||||
print(f"Failed to create {TOOL_NAME} after 3 attempts:")
|
||||
traceback.print_exc()
|
||||
|
||||
utils.executeDeferred(lambda: create_ui(width=current_width))
|
||||
|
||||
if __name__ == "__main__":
|
||||
show()
|
148
scripts/BatchImport.py
Normal file
148
scripts/BatchImport.py
Normal file
@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import maya.cmds as cmds
|
||||
import sys
|
||||
|
||||
def onMayaDroppedPythonFile(*args):
|
||||
run()
|
||||
|
||||
#Let's you batch import:
|
||||
def run(*args):
|
||||
if CheckForBonusTools:
|
||||
multipleFilters = 'All native importable files (*.3ds *.abc *.ass *.at *.catpart *.dae *.fbx *.igs *.iges *.jt *.ma *.mb *.obj *.prt *.sat *.step *.stp *.wire);; Maya binary (*.mb);; Maya Ascii (*.ma);; WIRE_ATF (*.wire);; Obj (*.obj);; FBX (*.fbx);; DAE_FBX (*.dae);; Alembic Cache (*.abc);; Atom (*.atom);; Step (*.stp *.step);; IGES_ATF (*.igs *.iges);; ASS (*.ass);; 3DS Max (*.3ds);; CATIAV5_ATF (*.catpart);; JT_ATF (*.jt);; SAT_ATF (*.sat);; NX_ATF (*.prt)'
|
||||
else:
|
||||
multipleFilters = 'All native importable files (*.abc *.ass *.at *.catpart *.dae *.fbx *.igs *.iges *.jt *.ma *.mb *.obj *.prt *.sat *.step *.stp *.wire);; Maya binary (*.mb);; Maya Ascii (*.ma);; WIRE_ATF (*.wire);; Obj (*.obj);; FBX (*.fbx);; DAE_FBX (*.dae);; Alembic Cache (*.abc);; Atom (*.atom);; Step (*.stp *.step);; IGES_ATF (*.igs *.iges);; ASS (*.ass);; 3DS Max (*.3ds);; CATIAV5_ATF (*.catpart);; JT_ATF (*.jt);; SAT_ATF (*.sat);; NX_ATF (*.prt)'
|
||||
files = cmds.fileDialog2(caption = 'Choose files to import', ds = 2, fileMode = 4, okCaption = 'Import', fileFilter = multipleFilters, hideNameEdit = False)
|
||||
|
||||
# Add checks to ensure files are valid
|
||||
if not files or not isinstance(files, list) or len(files) == 0:
|
||||
cmds.warning('No file was selected and the import operation was canceled.')
|
||||
return # Return directly to avoid subsequent code execution
|
||||
|
||||
for x in files:
|
||||
if any(y in x for y in ['.ma', '.MA']):
|
||||
fileType = 'mayaAscii'
|
||||
options = ''
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.mb', '.MB']):
|
||||
fileType = 'mayaBinary'
|
||||
options = ''
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.wire', '.WIRE']):
|
||||
fileType = 'WIRE_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.obj', '.OBJ']):
|
||||
fileType = 'OBJ'
|
||||
options = 'mo=0'
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.fbx', '.FBX']):
|
||||
fileType = 'FBX'
|
||||
options = ''
|
||||
LoadPlugin('fbxmaya')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.dae', '.DAE']):
|
||||
fileType = 'DAE_FBX'
|
||||
options = ''
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.abc', '.ABC']):
|
||||
fileType = 'Alembic'
|
||||
options = ''
|
||||
LoadPlugin('AbcImport')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.atom', '.ATOM', '.at', '.AT']):
|
||||
fileType = 'atomImport'
|
||||
options = ''
|
||||
LoadPlugin('atomImportExport')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in ['.step', '.STEP', '.stp', '.STP']):
|
||||
fileType = 'STEP_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['igs.', '.IGS', '.iges', '.IGES']):
|
||||
fileType = 'IGES_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.ass', '.ASS']):
|
||||
fileType = 'ASS'
|
||||
options = ''
|
||||
LoadPlugin('mtoa')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.3ds', '.3DS']):
|
||||
fileType = '3ds'
|
||||
options = ''
|
||||
if CheckForBonusTools:
|
||||
LoadPlugin('3ds')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.catpart', '.CATPART']):
|
||||
fileType = 'CATIAV5_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.jt', '.JT']):
|
||||
fileType = 'JT_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.sat', '.SAT']):
|
||||
fileType = 'SAT_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
if any(y in x for y in['.prt', '.PRT']):
|
||||
fileType = 'NX_ATF'
|
||||
options = ''
|
||||
LoadPlugin('ATFPlugin')
|
||||
ImportFiles(fileType, x, options)
|
||||
|
||||
def CheckForBonusTools(*args):
|
||||
for x in sys.path:
|
||||
if 'MayaBonusTools' in x:
|
||||
return True
|
||||
return False
|
||||
|
||||
def LoadPlugin(plugin, *args):
|
||||
if not cmds.pluginInfo(plugin, query = True, loaded = True):
|
||||
try:
|
||||
cmds.loadPlugin(plugin)
|
||||
sys.stdout.write('Plugin "' + plugin + '" loaded.\n')
|
||||
except(RuntimeError):
|
||||
cmds.warning('Could not find "' + plugin + '" plugin or could not load it. Open the Plugin Manager and make sure Maya recognized the plugin and try again.\n')
|
||||
|
||||
def ImportFiles(fileType, file, options, *args):
|
||||
namespace = file.split('/')
|
||||
namespace = namespace[-1].split('.')
|
||||
namespace = namespace[0]
|
||||
try:
|
||||
cmds.file(str(file), i = True, type = fileType, ignoreVersion = True, mergeNamespacesOnClash = False, namespace = namespace, options = options)
|
||||
sys.stdout.write('Imported "' + str(file) + '".\n')
|
||||
except(UnicodeEncodeError):
|
||||
sys.stdout.write('Either the directory path or the file name have some special characters.\n')
|
||||
sys.stdout.write('The names will be changed.\n')
|
||||
cmds.file(file, i = True, type = fileType, ignoreVersion = True, mergeNamespacesOnClash = False, namespace = namespace, options = options)
|
||||
sys.stdout.write('Imported "' + file + '".\n')
|
||||
except(ImportError):
|
||||
cmds.warning('Could not import ' + file + '. Maybe you dont have the requiered permissions to the folder.\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not cmds.about(batch = True):
|
||||
run()
|
37
scripts/BodyPrep.py
Normal file
37
scripts/BodyPrep.py
Normal file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
用于准备身体文件,去除了body_grp的锁定,清理不需要的节点,并重命名。并存储为body_drv.mb文件用于后续加载。
|
||||
"""
|
||||
|
||||
import maya.cmds as cmds
|
||||
|
||||
def run():
|
||||
cmds.upAxis(axis='y')
|
||||
cmds.delete("DHIhead:spine_04", "Lights", "export_geo_GRP", "head_grp")
|
||||
cmds.rename("rig", "body_rig")
|
||||
cmds.rename("geometry_grp", "body_geometry_grp")
|
||||
body_grp_lock = cmds.listRelatives('body_grp', allDescendents=True, type='transform')
|
||||
for obj in body_grp_lock:
|
||||
cmds.setAttr(obj + '.translateX', lock=False)
|
||||
cmds.setAttr(obj + '.translateY', lock=False)
|
||||
cmds.setAttr(obj + '.translateZ', lock=False)
|
||||
cmds.setAttr(obj + '.rotateX', lock=False)
|
||||
cmds.setAttr(obj + '.rotateY', lock=False)
|
||||
cmds.setAttr(obj + '.rotateZ', lock=False)
|
||||
cmds.setAttr(obj + '.scaleX', lock=False)
|
||||
cmds.setAttr(obj + '.scaleY', lock=False)
|
||||
cmds.setAttr(obj + '.scaleZ', lock=False)
|
||||
correctiveCube = cmds.polyCube()[0]
|
||||
cmds.parent('root_drv', correctiveCube)
|
||||
cmds.rotate(-90,0,0,correctiveCube,relative=True)
|
||||
cmds.parent('root_drv',world=True)
|
||||
cmds.delete(correctiveCube)
|
||||
save_path = cmds.fileDialog2(fileMode=0, caption="Save Maya Scene", fileFilter="Maya Binary (*.mb)")[0]
|
||||
cmds.file(rename=save_path)
|
||||
cmds.file(save=True, type='mayaBinary')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not cmds.about(batch = True):
|
||||
run()
|
237
scripts/DNA_Browser.py
Normal file
237
scripts/DNA_Browser.py
Normal file
@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
import maya.cmds as cmds
|
||||
import os
|
||||
|
||||
class DNABrowserWidget(QtWidgets.QWidget):
|
||||
dna_selected = QtCore.Signal(str) # 信号:当DNA被选中时发出
|
||||
|
||||
def __init__(self, dna_path, img_path, parent=None):
|
||||
super().__init__(parent)
|
||||
self.dna_path = dna_path
|
||||
self.img_path = img_path
|
||||
self.setup_ui()
|
||||
self.scan_dna_files()
|
||||
self.update_grid()
|
||||
|
||||
def setup_ui(self):
|
||||
# 创建主布局
|
||||
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# 创建流式布局容器
|
||||
self.flow_widget = QtWidgets.QWidget()
|
||||
self.flow_layout = FlowLayout(self.flow_widget)
|
||||
self.flow_layout.setSpacing(5)
|
||||
|
||||
# 创建滚动区域
|
||||
self.scroll_area = QtWidgets.QScrollArea()
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
self.scroll_area.setWidget(self.flow_widget)
|
||||
self.scroll_area.setStyleSheet("""
|
||||
QScrollArea {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
QScrollBar:vertical {
|
||||
border: none;
|
||||
background: #F0F0F0;
|
||||
width: 8px;
|
||||
margin: 0px;
|
||||
}
|
||||
QScrollBar::handle:vertical {
|
||||
background: #CCCCCC;
|
||||
border-radius: 4px;
|
||||
min-height: 20px;
|
||||
}
|
||||
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
|
||||
height: 0px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.main_layout.addWidget(self.scroll_area)
|
||||
|
||||
def scan_dna_files(self):
|
||||
"""扫描DNA文件夹并建立索引"""
|
||||
self.dna_files = {}
|
||||
if not os.path.exists(self.dna_path):
|
||||
cmds.warning(f"DNA path not found: {self.dna_path}")
|
||||
return
|
||||
|
||||
if not os.path.exists(self.img_path):
|
||||
cmds.warning(f"Image path not found: {self.img_path}")
|
||||
return
|
||||
|
||||
for file in os.listdir(self.dna_path):
|
||||
if file.endswith('.dna'):
|
||||
name = os.path.splitext(file)[0]
|
||||
dna_file = os.path.join(self.dna_path, file).replace("\\", "/")
|
||||
|
||||
# 直接在img目录下查找图片
|
||||
img_file = None
|
||||
for ext in ['.jpg', '.png', '.jpeg']:
|
||||
img_path = os.path.join(self.img_path, f"{name}{ext}").replace("\\", "/")
|
||||
if os.path.exists(img_path):
|
||||
img_file = img_path
|
||||
break
|
||||
|
||||
self.dna_files[name] = {
|
||||
'dna_path': dna_file,
|
||||
'img_path': img_file
|
||||
}
|
||||
|
||||
# 打印调试信息
|
||||
print(f"DNA file: {name}")
|
||||
print(f" DNA path: {dna_file}")
|
||||
print(f" Image path: {img_file}")
|
||||
print(f" Image exists: {bool(img_file and os.path.exists(img_file))}")
|
||||
|
||||
def update_grid(self):
|
||||
"""更新DNA网格"""
|
||||
# 清除现有按钮
|
||||
for i in reversed(range(self.flow_layout.count())):
|
||||
self.flow_layout.itemAt(i).widget().deleteLater()
|
||||
|
||||
# 计算按钮大小 - 减小到原来的1/4左右
|
||||
container_width = self.flow_widget.width() or 300
|
||||
button_width = (container_width - 60) // 6 # 每行6个按钮
|
||||
button_height = int(button_width * 1.2) # 保持宽高比
|
||||
|
||||
# 创建DNA样本按钮
|
||||
for name, info in self.dna_files.items():
|
||||
dna_btn = self.create_dna_button(name, info, button_width, button_height)
|
||||
self.flow_layout.addWidget(dna_btn)
|
||||
|
||||
def create_dna_button(self, name, info, width, height):
|
||||
"""创建DNA按钮"""
|
||||
btn = QtWidgets.QPushButton()
|
||||
btn.setFixedSize(width, height)
|
||||
|
||||
# 创建按钮布局
|
||||
layout = QtWidgets.QVBoxLayout(btn)
|
||||
layout.setContentsMargins(2, 2, 2, 2)
|
||||
layout.setSpacing(1)
|
||||
|
||||
# 创建图标标签
|
||||
icon_label = QtWidgets.QLabel()
|
||||
icon_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
|
||||
if info['img_path']:
|
||||
pixmap = QtGui.QPixmap(info['img_path'])
|
||||
scaled_pixmap = pixmap.scaled(
|
||||
width - 4,
|
||||
height - 16,
|
||||
QtCore.Qt.KeepAspectRatio,
|
||||
QtCore.Qt.SmoothTransformation
|
||||
)
|
||||
icon_label.setPixmap(scaled_pixmap)
|
||||
else:
|
||||
icon_label.setText("No Image")
|
||||
icon_label.setStyleSheet("color: #FFFFFF; font-size: 8px;") # 改为白色
|
||||
|
||||
# 创建文本标签
|
||||
text_label = QtWidgets.QLabel(name)
|
||||
text_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
text_label.setStyleSheet("color: #FFFFFF; font-size: 8px;") # 改为白色
|
||||
|
||||
layout.addWidget(icon_label)
|
||||
layout.addWidget(text_label)
|
||||
|
||||
# 设置样式 - 保持黑色背景,文字改为白色
|
||||
btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #303030;
|
||||
border: 1px solid #202020;
|
||||
border-radius: 5px;
|
||||
padding: 2px;
|
||||
color: #FFFFFF; /* 按钮文字颜色改为白色 */
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #404040;
|
||||
border: 1px solid #303030;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #202020;
|
||||
}
|
||||
""")
|
||||
|
||||
btn.setProperty('dna_path', info['dna_path'])
|
||||
btn.clicked.connect(lambda: self.on_dna_selected(info['dna_path']))
|
||||
|
||||
return btn
|
||||
|
||||
def on_dna_selected(self, dna_path):
|
||||
"""当DNA被选中时发出信号"""
|
||||
self.dna_selected.emit(dna_path)
|
||||
|
||||
class FlowLayout(QtWidgets.QLayout):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.itemList = []
|
||||
self.spacing_x = 5
|
||||
self.spacing_y = 5
|
||||
|
||||
def addItem(self, item):
|
||||
self.itemList.append(item)
|
||||
|
||||
def count(self):
|
||||
return len(self.itemList)
|
||||
|
||||
def itemAt(self, index):
|
||||
if 0 <= index < len(self.itemList):
|
||||
return self.itemList[index]
|
||||
return None
|
||||
|
||||
def takeAt(self, index):
|
||||
if 0 <= index < len(self.itemList):
|
||||
return self.itemList.pop(index)
|
||||
return None
|
||||
|
||||
def expandingDirections(self):
|
||||
return QtCore.Qt.Orientations(QtCore.Qt.Orientation(0))
|
||||
|
||||
def hasHeightForWidth(self):
|
||||
return True
|
||||
|
||||
def heightForWidth(self, width):
|
||||
height = self.doLayout(QtCore.QRect(0, 0, width, 0), True)
|
||||
return height
|
||||
|
||||
def setGeometry(self, rect):
|
||||
super().setGeometry(rect)
|
||||
self.doLayout(rect, False)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
||||
def minimumSize(self):
|
||||
size = QtCore.QSize()
|
||||
for item in self.itemList:
|
||||
size = size.expandedTo(item.minimumSize())
|
||||
return size
|
||||
|
||||
def doLayout(self, rect, testOnly):
|
||||
x = rect.x()
|
||||
y = rect.y()
|
||||
lineHeight = 0
|
||||
for item in self.itemList:
|
||||
widget = item.widget()
|
||||
spaceX = self.spacing_x
|
||||
spaceY = self.spacing_y
|
||||
nextX = x + item.sizeHint().width() + spaceX
|
||||
if nextX - spaceX > rect.right() and lineHeight > 0:
|
||||
x = rect.x()
|
||||
y = y + lineHeight + spaceY
|
||||
nextX = x + item.sizeHint().width() + spaceX
|
||||
lineHeight = 0
|
||||
if not testOnly:
|
||||
item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
|
||||
x = nextX
|
||||
lineHeight = max(lineHeight, item.sizeHint().height())
|
||||
return y + lineHeight - rect.y()
|
||||
|
||||
def create_browser(dna_path, img_path, parent=None):
|
||||
"""创建并返回DNA浏览器实例"""
|
||||
return DNABrowserWidget(dna_path, img_path, parent)
|
474
scripts/MetaFusion.py
Normal file
474
scripts/MetaFusion.py
Normal file
@ -0,0 +1,474 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#===================================== IMPORTS =====================================
|
||||
# Standard library imports
|
||||
import os
|
||||
import sys
|
||||
import webbrowser
|
||||
import locale
|
||||
|
||||
# Qt imports
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
from shiboken2 import wrapInstance
|
||||
|
||||
# Maya imports
|
||||
from maya import OpenMayaUI as omui
|
||||
import maya.cmds as cmds
|
||||
import maya.mel as mel
|
||||
|
||||
#===================================== IMPORT MODULES =====================================
|
||||
# Standard library imports
|
||||
import BodyPrep
|
||||
import BatchImport
|
||||
import DNA_Viewer
|
||||
import DNA_Browser
|
||||
from DNA_Browser import FlowLayout
|
||||
|
||||
#===================================== CONSTANTS =====================================
|
||||
# Tool info
|
||||
TOOL_NAME = "MetaFusion"
|
||||
TOOL_VERSION = "Beta v1.0.0"
|
||||
TOOL_AUTHOR = "Virtuos"
|
||||
# UI Constants
|
||||
TOOL_WSCL_NAME = "MetaFusionWorkSpaceControl"
|
||||
TOOL_HELP_URL = f"http://10.72.61.59:3000/ArtGroup/{TOOL_NAME}/wiki"
|
||||
DEFAULT_WINDOW_SIZE = (500, 800)
|
||||
|
||||
# Paths
|
||||
TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace("\\", "/")
|
||||
SCRIPTS_PATH = os.path.join(TOOL_PATH, "scripts").replace("\\", "/")
|
||||
ICONS_PATH = os.path.join(TOOL_PATH, "icons").replace("\\", "/")
|
||||
TOOL_ICON = os.path.join(ICONS_PATH, "logo.png").replace("\\", "/")
|
||||
|
||||
# Metahuman paths
|
||||
DATA_PATH = os.path.join(TOOL_PATH, "data").replace("\\", "/")
|
||||
DNA_PATH = os.path.join(DATA_PATH, "dna").replace("\\", "/")
|
||||
BODY_PATH = os.path.join(DATA_PATH, "body").replace("\\", "/")
|
||||
IMG_PATH = os.path.join(DATA_PATH, "img").replace("\\", "/")
|
||||
MAP_PATH = os.path.join(DATA_PATH, "map").replace("\\", "/")
|
||||
MASKS_PATH = os.path.join(DATA_PATH, "masks").replace("\\", "/")
|
||||
SHADERS_PATH = os.path.join(DATA_PATH, "shaders").replace("\\", "/")
|
||||
MH4_PATH = os.path.join(DATA_PATH, "mh4").replace("\\", "/")
|
||||
MH4_DNA_PATH = os.path.join(MH4_PATH, "dna").replace("\\", "/")
|
||||
OUT_PATH = os.path.join(DATA_PATH, "out").replace("\\", "/")
|
||||
SAVE_PATH = os.path.join(DATA_PATH, "save").replace("\\", "/")
|
||||
MAYA_VERSION = cmds.about(version=True)
|
||||
PLUGIN_PATH = os.path.join(TOOL_PATH, "plugins", f"{MAYA_VERSION}").replace("\\", "/")
|
||||
if not os.path.exists(PLUGIN_PATH):
|
||||
cmds.warning(f"Plugin path not found: {PLUGIN_PATH}")
|
||||
|
||||
# 打印上面的所有变量
|
||||
print(f"TOOL_PATH: {TOOL_PATH}")
|
||||
print(f"SCRIPTS_PATH: {SCRIPTS_PATH}")
|
||||
print(f"ICONS_PATH: {ICONS_PATH}")
|
||||
print(f"TOOL_ICON: {TOOL_ICON}")
|
||||
print(f"DATA_PATH: {DATA_PATH}")
|
||||
print(f"DNA_PATH: {DNA_PATH}")
|
||||
print(f"BODY_PATH: {BODY_PATH}")
|
||||
print(f"IMG_PATH: {IMG_PATH}")
|
||||
print(f"MAP_PATH: {MAP_PATH}")
|
||||
print(f"MASKS_PATH: {MASKS_PATH}")
|
||||
print(f"SHADERS_PATH: {SHADERS_PATH}")
|
||||
|
||||
#===================================== LANGUAGE SETTINGS =====================================
|
||||
TOOL_LANG = 'en_US'
|
||||
SUPPORTED_LANGUAGES = ['en_US', 'zh_CN']
|
||||
|
||||
LANG = {
|
||||
"en_US": {
|
||||
"MetaFusion": "MetaFusion",
|
||||
"Load DNA": "Load DNA",
|
||||
"Prepare Body": "Prepare Body",
|
||||
"Help": "Help",
|
||||
"Switch Language": "Switch Language",
|
||||
"EN": "EN",
|
||||
"ZH": "ZH",
|
||||
"English": "English",
|
||||
"Chinese": "Chinese",
|
||||
"Language switched": "Language switched",
|
||||
"DNA Samples": "DNA Samples",
|
||||
"Prepare": "Prepare",
|
||||
"Import": "Import",
|
||||
"Body Prepare": "Body Prepare",
|
||||
"Batch Import": "Batch Import",
|
||||
"DNA Edit": "DNA Edit",
|
||||
"Open DNA Viewer": "Open DNA Viewer",
|
||||
"Load DNA": "Load DNA",
|
||||
"DNA File:": "DNA File:"
|
||||
},
|
||||
"zh_CN": {
|
||||
"MetaFusion": "MetaFusion",
|
||||
"DNA Samples": "DNA 样本",
|
||||
"Load DNA": "加载 DNA",
|
||||
"Prepare Body": "准备身体",
|
||||
"Help": "帮助",
|
||||
"Switch Language": "切换语言",
|
||||
"EN": "英文",
|
||||
"ZH": "中文",
|
||||
"English": "英语",
|
||||
"Chinese": "中文",
|
||||
"Language switched": "语言已切换",
|
||||
"DNA Samples": "DNA 样本",
|
||||
"Prepare": "准备",
|
||||
"Import": "导入",
|
||||
"Body Prepare": "身体准备",
|
||||
"Batch Import": "批量导入",
|
||||
"DNA Edit": "DNA 编辑",
|
||||
"Open DNA Viewer": "打开 DNA 查看器",
|
||||
"Load DNA": "加载 DNA",
|
||||
"DNA File:": "DNA 文件:"
|
||||
}
|
||||
}
|
||||
|
||||
#===================================== UTILITY FUNCTIONS =====================================
|
||||
def get_system_encoding():
|
||||
encoding = sys.getdefaultencoding()
|
||||
if encoding.lower() == 'ascii':
|
||||
encoding = locale.getpreferredencoding()
|
||||
return encoding
|
||||
|
||||
def maya_main_window():
|
||||
main_window_ptr = omui.MQtUtil.mainWindow()
|
||||
return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
|
||||
|
||||
#===================================== UI COMPONENTS =====================================
|
||||
class MainButton(QtWidgets.QPushButton):
|
||||
DEFAULT_COLORS = {
|
||||
"normal": "#D0D0D0",
|
||||
"hover": "#E0E0E0",
|
||||
"pressed": "#C0C0C0"
|
||||
}
|
||||
|
||||
def __init__(self, text="", icon=None, color=None, hover_color=None, pressed_color=None):
|
||||
super().__init__(text)
|
||||
if icon:
|
||||
self.setIcon(icon)
|
||||
self.setIconSize(QtCore.QSize(24, 24))
|
||||
self.setMinimumHeight(30)
|
||||
colors = {
|
||||
"normal": color or self.DEFAULT_COLORS["normal"],
|
||||
"hover": hover_color or self.DEFAULT_COLORS["hover"],
|
||||
"pressed": pressed_color or self.DEFAULT_COLORS["pressed"]
|
||||
}
|
||||
self.setStyleSheet(self._generate_style_sheet(**colors))
|
||||
|
||||
@staticmethod
|
||||
def _generate_style_sheet(normal, hover, pressed):
|
||||
return f"""
|
||||
QPushButton {{
|
||||
background-color: {normal};
|
||||
color: #303030;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
background-color: {hover};
|
||||
}}
|
||||
QPushButton:pressed {{
|
||||
background-color: {pressed};
|
||||
}}
|
||||
"""
|
||||
|
||||
class BottomButton(QtWidgets.QPushButton):
|
||||
def __init__(self, text="", icon=None):
|
||||
super().__init__(text)
|
||||
self.setMinimumHeight(20)
|
||||
self.setStyleSheet(self._generate_style_sheet())
|
||||
self.setFont(QtGui.QFont("Microsoft YaHei", 10))
|
||||
|
||||
@staticmethod
|
||||
def _generate_style_sheet():
|
||||
return """
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: gray;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
color: black;
|
||||
}
|
||||
"""
|
||||
|
||||
#===================================== MAIN WINDOW =====================================
|
||||
class MainWindow(QtWidgets.QWidget):
|
||||
|
||||
instance = None
|
||||
|
||||
def __init__(self, parent=maya_main_window()):
|
||||
super(MainWindow, self).__init__(parent)
|
||||
self.setWindowTitle(f"{TOOL_NAME} - {TOOL_VERSION}")
|
||||
self.setObjectName(TOOL_PATH)
|
||||
self.setWindowFlags(QtCore.Qt.Window)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
|
||||
self.setMinimumSize(300, 800)
|
||||
|
||||
self.create_widgets()
|
||||
self.create_layouts()
|
||||
self.create_connections()
|
||||
|
||||
if os.path.exists(TOOL_ICON):
|
||||
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
|
||||
else:
|
||||
print(f"WARNING: Icon file not found: {TOOL_ICON}")
|
||||
|
||||
@classmethod
|
||||
def show_window(cls):
|
||||
try:
|
||||
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
|
||||
cmds.deleteUI(TOOL_WSCL_NAME, control=True)
|
||||
|
||||
if cls.instance is not None:
|
||||
try:
|
||||
cls.instance.close()
|
||||
cls.instance.deleteLater()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cls.instance = cls()
|
||||
cls.instance.dock_to_maya()
|
||||
|
||||
return cls.instance
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error showing {TOOL_NAME} window: {e}")
|
||||
return None
|
||||
|
||||
def dock_to_maya(self):
|
||||
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
|
||||
cmds.deleteUI(TOOL_WSCL_NAME)
|
||||
try:
|
||||
workspace_control = cmds.workspaceControl(
|
||||
TOOL_WSCL_NAME,
|
||||
label=TOOL_NAME,
|
||||
floating=True,
|
||||
retain=True,
|
||||
resizeWidth=True,
|
||||
initialWidth=500,
|
||||
minimumWidth=500
|
||||
)
|
||||
cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True)
|
||||
cmds.control(self.objectName(), e=True, p=workspace_control)
|
||||
cmds.evalDeferred(lambda: cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True))
|
||||
except Exception as e:
|
||||
print(f"Error creating workspace control: {e}")
|
||||
|
||||
#===================================== UI COMPONENTS =====================================
|
||||
def create_widgets(self):
|
||||
# DNA Samples group
|
||||
self.dna_browser = DNA_Browser.create_browser(DNA_PATH, IMG_PATH)
|
||||
self.dna_browser.dna_selected.connect(self.on_dna_selected)
|
||||
|
||||
# DNA File input
|
||||
self.dna_file_layout = QtWidgets.QHBoxLayout()
|
||||
self.dna_file_label = QtWidgets.QLabel("DNA File:")
|
||||
self.dna_file_input = QtWidgets.QLineEdit()
|
||||
self.dna_file_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
background-color: #303030;
|
||||
color: #CCCCCC;
|
||||
border: 1px solid #202020;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-size: 10px;
|
||||
}
|
||||
QLineEdit:hover {
|
||||
border: 1px solid #404040;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #505050;
|
||||
background-color: #353535;
|
||||
}
|
||||
""")
|
||||
self.dna_file_input.textChanged.connect(self.on_dna_file_changed)
|
||||
|
||||
self.dna_file_layout.addWidget(self.dna_file_label)
|
||||
self.dna_file_layout.addWidget(self.dna_file_input)
|
||||
|
||||
self.load_dna_btn = MainButton(LANG[TOOL_LANG]["Load DNA"],
|
||||
color="#E6B3B3", hover_color="#F2BFBF",
|
||||
pressed_color="#D99E9E")
|
||||
|
||||
# Create function buttons
|
||||
# Prepare group
|
||||
self.prepare_btn = MainButton(LANG[TOOL_LANG]["Prepare"])
|
||||
self.body_prepare_btn = MainButton(LANG[TOOL_LANG]["Body Prepare"], color="#FFEBA1", hover_color="#FFF5B3", pressed_color="#FFE68A")
|
||||
|
||||
# Import group
|
||||
self.import_btn = MainButton(LANG[TOOL_LANG]["Import"])
|
||||
self.batch_import_btn = MainButton(LANG[TOOL_LANG]["Batch Import"], color="#A7C6ED", hover_color="#B2D3F0", pressed_color="#8BB8E0")
|
||||
|
||||
# DNA Edit group
|
||||
self.dna_edit_btn = MainButton(LANG[TOOL_LANG]["DNA Edit"])
|
||||
self.dna_viewer_btn = MainButton(LANG[TOOL_LANG]["Open DNA Viewer"], color="#B8E6B3", hover_color="#C4F2BF", pressed_color="#A3D99E")
|
||||
|
||||
# Bottom buttons (existing code)
|
||||
self.help_btn = BottomButton(LANG[TOOL_LANG]["Help"])
|
||||
self.help_btn.setToolTip(LANG[TOOL_LANG]["Help"])
|
||||
self.help_btn.setFixedSize(100, 20)
|
||||
|
||||
self.lang_btn = BottomButton(LANG[TOOL_LANG]["ZH" if TOOL_LANG == 'en_US' else "EN"])
|
||||
self.lang_btn.setToolTip(LANG[TOOL_LANG]["Switch Language"])
|
||||
self.lang_btn.setFixedSize(30, 20)
|
||||
|
||||
for button in [self.help_btn, self.lang_btn]:
|
||||
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
|
||||
|
||||
def create_layouts(self):
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(2, 2, 2, 2)
|
||||
|
||||
# Content layout
|
||||
content_layout = QtWidgets.QVBoxLayout()
|
||||
content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# DNA Samples group
|
||||
dna_samples_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Samples"])
|
||||
dna_samples_layout = QtWidgets.QVBoxLayout(dna_samples_group)
|
||||
dna_samples_layout.addWidget(self.dna_browser)
|
||||
dna_samples_layout.addLayout(self.dna_file_layout)
|
||||
dna_samples_layout.addWidget(self.load_dna_btn)
|
||||
content_layout.addWidget(dna_samples_group)
|
||||
|
||||
# Prepare group
|
||||
prepare_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["Prepare"])
|
||||
prepare_layout = QtWidgets.QVBoxLayout(prepare_group)
|
||||
prepare_layout.addWidget(self.body_prepare_btn)
|
||||
content_layout.addWidget(prepare_group)
|
||||
|
||||
# Import group
|
||||
import_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["Import"])
|
||||
import_layout = QtWidgets.QVBoxLayout(import_group)
|
||||
import_layout.addWidget(self.batch_import_btn)
|
||||
content_layout.addWidget(import_group)
|
||||
|
||||
# DNA Edit group
|
||||
dna_edit_group = QtWidgets.QGroupBox(LANG[TOOL_LANG]["DNA Edit"])
|
||||
dna_edit_layout = QtWidgets.QVBoxLayout(dna_edit_group)
|
||||
dna_edit_layout.addWidget(self.dna_viewer_btn)
|
||||
content_layout.addWidget(dna_edit_group)
|
||||
|
||||
main_layout.addLayout(content_layout)
|
||||
main_layout.addStretch()
|
||||
|
||||
# Bottom layout (existing code)
|
||||
bottom_layout = QtWidgets.QHBoxLayout()
|
||||
bottom_layout.setContentsMargins(5, 0, 5, 5)
|
||||
|
||||
icon_label = QtWidgets.QLabel()
|
||||
if os.path.exists(TOOL_ICON):
|
||||
icon = QtGui.QPixmap(TOOL_ICON).scaled(24, 24, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
icon_label.setPixmap(icon)
|
||||
|
||||
version_label = QtWidgets.QLabel(f"{TOOL_VERSION}")
|
||||
version_label.setStyleSheet("color: gray; font-size: 12px;")
|
||||
|
||||
bottom_layout.addWidget(icon_label)
|
||||
bottom_layout.addWidget(version_label)
|
||||
bottom_layout.addStretch()
|
||||
bottom_layout.addWidget(self.help_btn)
|
||||
bottom_layout.addWidget(self.lang_btn)
|
||||
|
||||
main_layout.addLayout(bottom_layout)
|
||||
|
||||
def create_connections(self):
|
||||
# Connect function buttons
|
||||
self.body_prepare_btn.clicked.connect(self.run_body_prepare)
|
||||
self.batch_import_btn.clicked.connect(self.run_batch_import)
|
||||
self.dna_viewer_btn.clicked.connect(self.run_dna_viewer)
|
||||
|
||||
# Connect DNA Samples buttons
|
||||
self.load_dna_btn.clicked.connect(self.run_load_dna)
|
||||
|
||||
# Existing connections
|
||||
self.help_btn.clicked.connect(self.help)
|
||||
self.lang_btn.clicked.connect(self.switch_language)
|
||||
|
||||
#===================================== FUNCTIONS =====================================
|
||||
#===================================== MAIN FUNCTIONS =====================================
|
||||
# Prepare group
|
||||
def run_body_prepare(self):
|
||||
import BodyPrep
|
||||
BodyPrep.run()
|
||||
|
||||
# Import group
|
||||
def run_batch_import(self):
|
||||
import BatchImport
|
||||
BatchImport.run()
|
||||
|
||||
# DNA Edit group
|
||||
def run_dna_viewer(self):
|
||||
import DNA_Viewer
|
||||
DNA_Viewer.show()
|
||||
|
||||
# DNA Samples group
|
||||
def run_load_dna(self):
|
||||
"""加载选中的DNA文件"""
|
||||
if hasattr(self, 'dna_list') and self.dna_list.currentItem():
|
||||
import DNA_Viewer
|
||||
DNA_Viewer.load_dna(DNA_File)
|
||||
else:
|
||||
cmds.warning("Please select a DNA file first")
|
||||
|
||||
#===================================== BOTTOM LAYOUT =====================================
|
||||
def help(self):
|
||||
webbrowser.open(TOOL_HELP_URL)
|
||||
|
||||
def switch_language(self):
|
||||
global TOOL_LANG
|
||||
TOOL_LANG = 'en_US' if TOOL_LANG == 'zh_CN' else 'zh_CN'
|
||||
self.lang_btn.setText("ZH" if TOOL_LANG == 'en_US' else "EN")
|
||||
self.retranslate_ui()
|
||||
|
||||
QtWidgets.QToolTip.showText(
|
||||
self.lang_btn.mapToGlobal(QtCore.QPoint(0, -30)),
|
||||
"Language switched" if TOOL_LANG == 'en_US' else "语言已切换",
|
||||
self.lang_btn
|
||||
)
|
||||
|
||||
def retranslate_ui(self):
|
||||
|
||||
# Update function button translations
|
||||
self.load_dna_btn.setText(LANG[TOOL_LANG]["Load DNA"])
|
||||
self.body_prepare_btn.setText(LANG[TOOL_LANG]["Body Prepare"])
|
||||
self.batch_import_btn.setText(LANG[TOOL_LANG]["Batch Import"])
|
||||
self.dna_viewer_btn.setText(LANG[TOOL_LANG]["Open DNA Viewer"])
|
||||
|
||||
for button in [
|
||||
self.body_prepare_btn,
|
||||
self.batch_import_btn,
|
||||
self.dna_viewer_btn
|
||||
]:
|
||||
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
|
||||
|
||||
# Update bottom button translations
|
||||
self.help_btn.setText(LANG[TOOL_LANG]["Help"])
|
||||
self.lang_btn.setText("ZH" if TOOL_LANG == 'en_US' else "EN")
|
||||
for button in [
|
||||
self.help_btn,
|
||||
self.lang_btn
|
||||
]:
|
||||
button.setFont(QtGui.QFont("Microsoft YaHei", 10))
|
||||
|
||||
self.dna_file_label.setText(LANG[TOOL_LANG]["DNA File:"])
|
||||
|
||||
def on_dna_selected(self, dna_path):
|
||||
"""当DNA被选中时"""
|
||||
global DNA_File
|
||||
DNA_File = dna_path
|
||||
self.dna_file_input.setText(DNA_File)
|
||||
print(f"Selected DNA file: {DNA_File}")
|
||||
|
||||
def on_dna_file_changed(self):
|
||||
"""当DNA文件输入框内容改变时"""
|
||||
global DNA_File
|
||||
DNA_File = self.dna_file_input.text()
|
||||
print(f"DNA file path updated: {DNA_File}")
|
||||
|
||||
#===================================== LAUNCH FUNCTIONS =====================================
|
||||
def show():
|
||||
return MainWindow.show_window()
|
Loading…
Reference in New Issue
Block a user