#!/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 DNA_Viewer import BatchImport #===================================== CONSTANTS ===================================== # Tool info TOOL_NAME = "MetaFusion" TOOL_VERSION = "Beta v1.0.0" TOOL_AUTHOR = "CGNICO" TOOL_WSCL_NAME = "MetaFusionWorkSpaceControl" TOOL_HELP_URL = f"https://gitea.cgnico.com/CGNICO/MetaFusion/wiki" DEFAULT_WINDOW_SIZE = (500, 800) 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("\\", "/") 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("\\", "/") OUTPUT_PATH = os.path.join(DATA_PATH, "output").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}") print(f"MH4_PATH: {MH4_PATH}") print(f"MH4_DNA_PATH: {MH4_DNA_PATH}") print(f"OUTPUT_PATH: {OUTPUT_PATH}") print(f"SAVE_PATH: {SAVE_PATH}") print(f"MAYA_VERSION: {MAYA_VERSION}") print(f"PLUGIN_PATH: {PLUGIN_PATH}") #===================================== LANGUAGE SETTINGS ===================================== TOOL_LANG = 'en_US' SUPPORTED_LANGUAGES = ['en_US', 'zh_CN'] LANG = { "en_US": { "MetaFusion": "MetaFusion", "Prepare": "Prepare", "Body Prepare": "Body Prepare", "DNA Edit": "DNA Edit", "Open DNA Viewer": "Open DNA Viewer", "Import": "Import", "Batch Import": "Batch Import", "Help": "Help", "Switch Language": "Switch Language", "ZH": "ZH", "EN": "EN", "Language switched": "Language switched", }, "zh_CN": { "MetaFusion": "MetaFusion", "Prepare": "准备", "Body Prepare": "身体准备", "DNA Edit": "DNA编辑", "Open DNA Viewer": "打开DNA查看器", "Import": "导入", "Batch Import": "批量导入", "Help": "帮助", "Switch Language": "切换语言", "ZH": "中文", "EN": "英文", "Language switched": "语言已切换", } } #===================================== 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): 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") 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") 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") 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 = QtWidgets.QVBoxLayout() content_layout.setContentsMargins(5, 5, 5, 5) 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) 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) 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) main_layout.addLayout(content_layout) # Bottom layout main_layout.addStretch() 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.dna_viewer_btn.clicked.connect(self.run_dna_viewer) self.batch_import_btn.clicked.connect(self.run_batch_import) # Existing connections self.help_btn.clicked.connect(self.help) self.lang_btn.clicked.connect(self.switch_language) #===================================== FUNCTIONS ===================================== #===================================== MAIN FUNCTIONS ===================================== def run_body_prepare(self): BodyPrep.run() def run_dna_viewer(self): DNA_Viewer.show() def run_batch_import(self): BatchImport.run() #===================================== 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): self.body_prepare_btn.setText(LANG[TOOL_LANG]["Body Prepare"]) self.dna_viewer_btn.setText(LANG[TOOL_LANG]["Open DNA Viewer"]) self.batch_import_btn.setText(LANG[TOOL_LANG]["Batch Import"]) for button in [self.body_prepare_btn, self.batch_import_btn, self.dna_viewer_btn]: button.setFont(QtGui.QFont("Microsoft YaHei", 10)) 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)) def on_dna_selected(self, dna_path): self.dna_file_input.setText(dna_path) print(f"Selected DNA file: {dna_path}") def on_dna_file_changed(self): dna_path = self.dna_file_input.text() print(f"DNA file path updated: {dna_path}") #===================================== LAUNCH FUNCTIONS ===================================== def show(): return MainWindow.show_window()