This commit is contained in:
2025-05-05 18:15:02 +08:00
parent 01c0ec760f
commit 46d93efb3c
11 changed files with 1728 additions and 1943 deletions

View File

@@ -5,7 +5,7 @@
Main module for Metahuman customize plugin
主模块 - 负责初始化UI和功能集成
功能: 从ui模块加载子模块显示
作者: Virtuos Games
作者: CGNICO
版本: Alpha v1.0.0
"""
#===================================== IMPORT MODULES =====================================
@@ -23,261 +23,194 @@ import locale
import sys
import os
#===================================== IMPORT UI MODULES ===================================
from scripts.ui import ui_utils
from scripts.ui import toolbar
from scripts.ui import geometry
from scripts.ui import rigging
from scripts.ui import behaviour
from scripts.ui import definition
#====================================== LOCALIZATION ==================================
#========================================= LOCALIZATION =====================================
from scripts.ui import localization
LANG = localization.LANG
#========================================== CONFIG ========================================
# 从 scripts 模块导入配置变量
from scripts import (
TOOL_NAME, TOOL_VERSION, TOOL_AUTHOR, TOOL_YEAR, TOOL_MOD_FILENAME,
TOOL_LANG, TOOL_WSCL_NAME, TOOL_HELP_URL, TOOL_PATH, SCRIPTS_PATH,
TOOL_MAIN_SCRIPT, UI_PATH, STYLE_FILE, ICONS_PATH, TOOL_ICON,
ASSETS_PATH, DNA_FILE_PATH, DNA_IMG_PATH, TOOL_COMMAND_ICON
)
#========================================= INIT =======================================
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)
#===================================== MAIN FUNCTION ===================================
#=========================================== CONFIG =========================================
import config
TOOL_NAME = config.TOOL_NAME
TOOL_VERSION = config.TOOL_VERSION
TOOL_AUTHOR = config.TOOL_AUTHOR
TOOL_YEAR = config.TOOL_YEAR
TOOL_MOD_FILENAME = config.TOOL_MOD_FILENAME
TOOL_LANG = config.TOOL_LANG
TOOL_WSCL_NAME = config.TOOL_WSCL_NAME
TOOL_HELP_URL = config.TOOL_HELP_URL
TOOL_PATH = config.TOOL_PATH
SCRIPTS_PATH = config.SCRIPTS_PATH
TOOL_MAIN_SCRIPT = config.TOOL_MAIN_SCRIPT
UI_PATH = config.UI_PATH
STYLE_FILE = config.STYLE_FILE
ICONS_PATH = config.ICONS_PATH
TOOL_ICON = config.TOOL_ICON
ASSETS_PATH = config.ASSETS_PATH
DNA_FILE_PATH = config.DNA_FILE_PATH
DNA_IMG_PATH = config.DNA_IMG_PATH
TOOL_COMMAND_ICON = config.TOOL_COMMAND_ICON
#======================================= MAIN FUNCTION =====================================
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=maya_main_window()):
def __init__(self, parent=ui_utils.get_maya_main_window()):
super(MainWindow, self).__init__(parent)
self.setWindowTitle(TOOL_NAME)
self.setObjectName(f"{TOOL_NAME}MainWindow")
self.setWindowFlags(QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setMinimumSize(1200, 800)
# 设置自适应大小策略
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.setMinimumSize(300, 600) # 减小最小高度,让窗口更灵活
# 设置窗口图标
if os.path.exists(TOOL_ICON):
self.setWindowIcon(QtGui.QIcon(TOOL_ICON))
# 加载样式表
self.load_stylesheet()
# 初始化UI模块
self.toolbar_ui = toolbar.ToolbarUI()
self.definition_ui = definition.DefinitionUI()
self.geometry_ui = geometry.GeometryUI()
self.rigging_ui = rigging.RiggingUI()
self.behaviour_ui = behaviour.BehaviourUI()
# 初始化UI组件
self.create_widgets()
self.create_layouts()
self.create_connections()
# 不设置窗口图标
def dock_to_maya(self):
if cmds.workspaceControl(TOOL_WSCL_NAME, exists=True):
cmds.deleteUI(TOOL_WSCL_NAME)
def create_control():
try:
# 直接使用TOOL_ICON__init__.py中已经检查了图标文件是否存在
workspace_control = cmds.workspaceControl(
TOOL_WSCL_NAME,
label=TOOL_NAME,
floating=True,
retain=True,
resizeWidth=True,
initialWidth=600,
minimumWidth=600
)
cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True)
cmds.control(self.objectName(), e=True, p=workspace_control)
# 尝试设置工作区控件的尺寸和属性
try:
# 使用Maya命令设置工作区控件属性
cmds.workspaceControl(TOOL_WSCL_NAME, e=True, widthProperty="preferred", heightProperty="preferred")
# 使用原始标题,不添加额外的图标字符
cmds.workspaceControl(TOOL_WSCL_NAME, e=True, label=TOOL_NAME)
except Exception as e:
print(f"设置工作区控件属性失败: {e}")
cmds.evalDeferred(lambda: cmds.workspaceControl(TOOL_WSCL_NAME, e=True, resizeWidth=True))
except Exception as e:
print("Error creating workspace control: {}".format(e))
cmds.evalDeferred(create_control)
#===================================== UI COMPONENTS =====================================
def create_widgets(self):
# 创建滚动区域
self.scroll_area = QtWidgets.QScrollArea()
self.scroll_area.setObjectName("main_scroll_area")
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) # 改回按需显示
self.scroll_area.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 创建滚动区域内容控件
self.scroll_content = QtWidgets.QWidget()
self.scroll_content.setObjectName("scroll_content")
self.scroll_content.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 创建主标签页
self.main_tab = QtWidgets.QTabWidget()
self.main_tab.setObjectName("main_tab")
self.main_tab.setTabPosition(QtWidgets.QTabWidget.North)
self.main_tab.setTabShape(QtWidgets.QTabWidget.Rounded)
self.main_tab.setDocumentMode(True)
self.main_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.main_tab.setMinimumHeight(400)
# 创建各功能模块标签页
self.geometry_tab = QtWidgets.QWidget()
self.geometry_tab.setObjectName("geometry_tab")
self.geometry_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.rigging_tab = QtWidgets.QWidget()
self.rigging_tab.setObjectName("rigging_tab")
self.rigging_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.behaviour_tab = QtWidgets.QWidget()
self.behaviour_tab.setObjectName("behaviour_tab")
self.behaviour_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.definition_tab = QtWidgets.QWidget()
self.definition_tab.setObjectName("definition_tab")
self.definition_tab.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# 创建工具栏
self.toolbar_frame = QtWidgets.QFrame()
self.toolbar_frame.setObjectName("toolbar_frame")
self.toolbar_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.toolbar_frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.toolbar_frame.setMaximumHeight(40)
self.toolbar_frame.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# 创建状态栏
self.status_bar = QtWidgets.QStatusBar()
self.status_bar.setObjectName("status_bar")
self.status_bar.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
self.status_bar.showMessage(f"{TOOL_NAME} {TOOL_VERSION}")
# 初始化各模块UI组件
toolbar.widgets()
geometry.widgets()
rigging.widgets()
behaviour.widgets()
definition.widgets()
def create_layouts(self):
# 主布局
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setContentsMargins(2, 2, 2, 2)
self.main_layout.setSpacing(2)
# 滚动区域内容布局
self.scroll_content_layout = QtWidgets.QVBoxLayout(self.scroll_content)
self.scroll_content_layout.setContentsMargins(2, 2, 2, 2)
self.scroll_content_layout.setSpacing(2)
# 创建工具栏布局
self.toolbar_layout = QtWidgets.QVBoxLayout(self.toolbar_frame)
self.toolbar_layout.setContentsMargins(0, 0, 0, 0)
# 使用toolbar模块创建工具栏元素
toolbar.layouts(parent_frame=self.toolbar_frame)
# 设置各标签页布局
self.geometry_layout = QtWidgets.QVBoxLayout(self.geometry_tab)
self.geometry_layout.setContentsMargins(4, 4, 4, 4)
geometry.layouts(parent_tab=self.geometry_tab)
self.rigging_layout = QtWidgets.QVBoxLayout(self.rigging_tab)
self.rigging_layout.setContentsMargins(4, 4, 4, 4)
rigging.layouts(parent_tab=self.rigging_tab)
self.behaviour_layout = QtWidgets.QVBoxLayout(self.behaviour_tab)
self.behaviour_layout.setContentsMargins(4, 4, 4, 4)
behaviour.layouts(parent_tab=self.behaviour_tab)
self.definition_layout = QtWidgets.QVBoxLayout(self.definition_tab)
self.definition_layout.setContentsMargins(4, 4, 4, 4)
definition.layouts(parent_tab=self.definition_tab)
# 添加标签页到主标签控件
self.main_tab.addTab(self.geometry_tab, "几何模型")
self.main_tab.addTab(self.rigging_tab, "绑定系统")
self.main_tab.addTab(self.behaviour_tab, "行为系统")
self.main_tab.addTab(self.definition_tab, "定义系统")
# 将组件添加到滚动区域内容布局
self.scroll_content_layout.addWidget(self.toolbar_frame)
self.scroll_content_layout.addWidget(self.main_tab)
# 设置滚动区域的内容控件
self.scroll_area.setWidget(self.scroll_content)
# 将滚动区域和状态栏添加到主布局
self.main_layout.addWidget(self.scroll_area)
self.main_layout.addWidget(self.status_bar)
# 加载样式表
def load_stylesheet(self):
"""加载样式表"""
if os.path.exists(STYLE_FILE):
try:
with open(STYLE_FILE, "r", encoding="utf-8") as f:
with open(STYLE_FILE, 'r', encoding='utf-8') as f:
style = f.read()
self.setStyleSheet(style)
except UnicodeDecodeError:
# 尝试使用系统默认编码
encoding = get_system_encoding()
try:
with open(STYLE_FILE, "r", encoding=encoding) as f:
style = f.read()
self.setStyleSheet(style)
except Exception as e:
print(f"警告: 无法加载样式表文件: {e}")
print(f"样式表已加载: {STYLE_FILE}")
except Exception as e:
print(f"加载样式表失败: {e}")
else:
print(f"警告: 样式表文件不存在: {STYLE_FILE}")
def create_connections(self):
# 连接各模块的信号和槽
toolbar.connections()
geometry.connections()
rigging.connections()
behaviour.connections()
definition.connections()
print(f"样式表文件不存在: {STYLE_FILE}")
#========================================= WIDGET =======================================
def create_widgets(self):
"""创建UI控件"""
# 主标签页控件
self.main_tab_widget = QtWidgets.QTabWidget()
self.main_tab_widget.setObjectName("mainTabWidget")
# 创建各个标签页
self.toolbar_tab = QtWidgets.QWidget()
self.definition_tab = QtWidgets.QWidget()
self.geometry_tab = QtWidgets.QWidget()
self.rigging_tab = QtWidgets.QWidget()
self.behaviour_tab = QtWidgets.QWidget()
# 设置标签页名称
self.main_tab_widget.addTab(self.toolbar_tab, LANG.get("toolbar", "工具栏"))
self.main_tab_widget.addTab(self.definition_tab, LANG.get("definition", "定义"))
self.main_tab_widget.addTab(self.geometry_tab, LANG.get("geometry", "几何体"))
self.main_tab_widget.addTab(self.rigging_tab, LANG.get("rigging", "绑定"))
self.main_tab_widget.addTab(self.behaviour_tab, LANG.get("behaviour", "行为"))
#========================================= LAYOUT =======================================
def create_layouts(self):
"""创建布局"""
# 主布局
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.setContentsMargins(5, 5, 5, 5)
main_layout.setSpacing(5)
main_layout.addWidget(self.main_tab_widget)
# 设置各标签页的布局
toolbar_layout = QtWidgets.QVBoxLayout(self.toolbar_tab)
definition_layout = QtWidgets.QVBoxLayout(self.definition_tab)
geometry_layout = QtWidgets.QVBoxLayout(self.geometry_tab)
rigging_layout = QtWidgets.QVBoxLayout(self.rigging_tab)
behaviour_layout = QtWidgets.QVBoxLayout(self.behaviour_tab)
# 将各UI模块添加到对应的标签页布局中
# 工具栏UI
if hasattr(self.toolbar_ui, 'main_widget'):
toolbar_layout.addWidget(self.toolbar_ui.main_widget)
# 定义UI
if hasattr(self.definition_ui, 'main_widget'):
definition_layout.addWidget(self.definition_ui.main_widget)
# 几何体UI
if hasattr(self.geometry_ui, 'main_widget'):
geometry_layout.addWidget(self.geometry_ui.main_widget)
# 绑定UI
if hasattr(self.rigging_ui, 'main_widget'):
rigging_layout.addWidget(self.rigging_ui.main_widget)
# 行为UI
if hasattr(self.behaviour_ui, 'main_widget'):
behaviour_layout.addWidget(self.behaviour_ui.main_widget)
#======================================= CONNECTION =====================================
def create_connections(self):
"""连接信号和槽"""
# 标签页切换信号
self.main_tab.currentChanged.connect(self.on_tab_changed)
self.main_tab_widget.currentChanged.connect(self.tab_changed)
def on_tab_changed(self, index):
tab_name = self.main_tab.tabText(index)
self.status_bar.showMessage(f"当前模块: {tab_name}")
print(f"切换到模块: {tab_name}")
def tab_changed(self, index):
"""标签页切换事件处理"""
tab_name = self.main_tab_widget.tabText(index)
print(f"切换到标签页: {tab_name}")
# 调整分割器宽度
self.reset_splitters()
def reset_splitters(self):
"""重置所有分割器的宽度,确保左右栏宽度均等"""
# 延迟执行确保UI完全加载
QtCore.QTimer.singleShot(50, self._do_reset_splitters)
QtCore.QTimer.singleShot(200, self._do_reset_splitters)
def _do_reset_splitters(self):
"""实际执行分割器宽度重置的函数"""
current_tab = self.main_tab_widget.currentWidget()
if not current_tab:
return
# 查找当前标签页中的所有水平分割器
splitters = current_tab.findChildren(QtWidgets.QSplitter)
for splitter in splitters:
if splitter.orientation() == QtCore.Qt.Horizontal:
# 获取分割器宽度
width = splitter.width()
# 计算每个部分应该的宽度
count = splitter.count()
if count > 0:
part_width = width // count
sizes = [part_width] * count
# 设置分割器各部分的宽度
splitter.setSizes(sizes)
# 为每个子项设置相同的伸缩因子
for i in range(count):
splitter.setStretchFactor(i, 1)
def main():
"""
Main function to initialize and show the Plugin UI
"""
"""主函数,创建并显示主窗口"""
try:
# Initialize UI modules with placeholder functions
# Each module only contains UI framework without actual functionality
toolbar.toolbar_temp_function()
geometry.geometry_temp_function()
rigging.rigging_temp_function()
behaviour.behaviour_temp_function()
definition.definition_temp_function()
# 如果已存在窗口,则关闭
for widget in QtWidgets.QApplication.allWidgets():
if widget.objectName() == f"{TOOL_NAME}MainWindow" and isinstance(widget, QtWidgets.QWidget):
widget.close()
widget.deleteLater()
# Create and show main window
global tool_window
tool_window = MainWindow()
tool_window.dock_to_maya()
print(f"{TOOL_NAME} plugin initialized successfully!")
return tool_window
# 创建新窗口
window = MainWindow()
window.show()
return window
except Exception as e:
error_msg = f"Error initializing {TOOL_NAME} plugin: {str(e)}"
print(error_msg)
traceback.print_exc()
cmds.warning(error_msg)
error_msg = traceback.format_exc()
print(f"启动失败: {e}\n{error_msg}")
return None
# Auto-run when imported
if __name__ == "__main__":
main()