#!/usr/bin/env python # -*- coding: utf-8 -*- """ Definition UI Module for Plugin 定义系统UI模块 - 负责显示DNA定义编辑界面和基础操作 基本功能: - LOD, Meshes, Joints, Blendshape, AnimatedMap 加载和刷新 - 写入: 写入关节默认位置,写入几何体,写入蒙皮,写入混合变形目标 - 创建:创建混合变形,绑定蒙皮,取消蒙皮 - 工具:重新定位头部关节,重新定位身体关节,重新定位全身关节,快速创建预设 """ #===================================== IMPORT MODULES ===================================== from Qt import QtWidgets, QtCore, QtGui from Qt.QtCompat import wrapInstance from maya import OpenMayaUI as omui import sys import os #===================================== IMPORT FUNCTIONS =================================== from scripts.utils import utils_definition as utils_definition from scripts.ui import ui_utils import config # 导入图标路径 ICONS_PATH = config.ICONS_PATH #========================================== WIDGETS ========================================== # 全局变量存储UI控件 definition_tabs = None dna_elements = {} definition_buttons = {} def set_button_icon(button, icon_name): """ 设置按钮图标,使用Maya默认图标 Args: button: 要设置图标的按钮 icon_name: 图标文件名 """ # 使用Maya内置图标 from maya import cmds # 设置按钮图标使用Maya的图标 button.setIcon(QtGui.QIcon(":/{}".format(icon_name))) button.setIconSize(QtCore.QSize(24, 24)) # 图标尺寸 print(f"成功设置按钮图标: {icon_name}") # 注意:如果图标加载失败,我们会看到一个空图标,但不会引起错误 def widgets(): """ 创建定义系统UI控件 """ global definition_tabs, dna_elements, definition_buttons # 创建子标签页 definition_tabs = QtWidgets.QTabWidget() # 创建各类元素列表 dna_elements["lod_list"] = QtWidgets.QListWidget() dna_elements["lod_list"].setObjectName("LodList") dna_elements["mesh_list"] = QtWidgets.QListWidget() dna_elements["mesh_list"].setObjectName("MeshList") dna_elements["joint_list"] = QtWidgets.QListWidget() dna_elements["joint_list"].setObjectName("JointList") dna_elements["blendshape_list"] = QtWidgets.QListWidget() dna_elements["blendshape_list"].setObjectName("BlendshapeList") dna_elements["animmap_list"] = QtWidgets.QListWidget() dna_elements["animmap_list"].setObjectName("AnimatedMapList") # 创建元素信息面板 dna_elements["element_info"] = QtWidgets.QTextEdit() dna_elements["element_info"].setReadOnly(True) dna_elements["element_info"].setObjectName("ElementInfo") # 创建列表标题标签 dna_elements["lod_label"] = QtWidgets.QLabel("LODs [000]") dna_elements["mesh_label"] = QtWidgets.QLabel("Meshes [000]") dna_elements["joint_label"] = QtWidgets.QLabel("Joints [000]") dna_elements["blendshape_label"] = QtWidgets.QLabel("BlendShapes [000]") dna_elements["animmap_label"] = QtWidgets.QLabel("AnimatedMap [000]") # 创建刷新按钮 dna_elements["refresh_lod"] = QtWidgets.QPushButton("刷新") dna_elements["refresh_lod"].setObjectName("RefreshButton") dna_elements["refresh_lod"].setFixedWidth(60) dna_elements["refresh_mesh"] = QtWidgets.QPushButton("刷新") dna_elements["refresh_mesh"].setObjectName("RefreshButton") dna_elements["refresh_mesh"].setFixedWidth(60) dna_elements["refresh_joint"] = QtWidgets.QPushButton("刷新") dna_elements["refresh_joint"].setObjectName("RefreshButton") dna_elements["refresh_joint"].setFixedWidth(60) dna_elements["refresh_blendshape"] = QtWidgets.QPushButton("刷新") dna_elements["refresh_blendshape"].setObjectName("RefreshButton") dna_elements["refresh_blendshape"].setFixedWidth(60) dna_elements["refresh_animmap"] = QtWidgets.QPushButton("刷新") dna_elements["refresh_animmap"].setObjectName("RefreshButton") dna_elements["refresh_animmap"].setFixedWidth(60) # 创建LOD和Meshes特殊按钮 dna_elements["define_lod_joint"] = QtWidgets.QPushButton("定义LOD关节") set_button_icon(dna_elements["define_lod_joint"], "layerEditor.png") dna_elements["define_lod_joint"].setMinimumHeight(28) dna_elements["define_lod_joint"].setStyleSheet("text-align: left; padding-left: 30px;") dna_elements["define_lod_joint"].setToolTip("定义LOD关节") dna_elements["create_geometry"] = QtWidgets.QPushButton("创建几何体") set_button_icon(dna_elements["create_geometry"], "polyCube.png") dna_elements["create_geometry"].setMinimumHeight(28) dna_elements["create_geometry"].setStyleSheet("text-align: left; padding-left: 30px;") dna_elements["create_geometry"].setToolTip("创建几何体") # 功能按钮 # 写入组按钮 definition_buttons["write_joint_defaults"] = QtWidgets.QPushButton("写入关节默认位置") set_button_icon(definition_buttons["write_joint_defaults"], "HIKCharacterToolBodyPart.png") definition_buttons["write_geometry"] = QtWidgets.QPushButton("写入几何体") set_button_icon(definition_buttons["write_geometry"], "polyCube.png") definition_buttons["write_skinning"] = QtWidgets.QPushButton("写入蒙皮权重") set_button_icon(definition_buttons["write_skinning"], "paintSkinWeights.png") definition_buttons["write_blendshapes"] = QtWidgets.QPushButton("写入混合变形目标") set_button_icon(definition_buttons["write_blendshapes"], "blendShape.png") # 创建组按钮 definition_buttons["create_blendshapes"] = QtWidgets.QPushButton("创建混合变形") set_button_icon(definition_buttons["create_blendshapes"], "blendShapeEditor.png") definition_buttons["bind_skin"] = QtWidgets.QPushButton("绑定蒙皮") set_button_icon(definition_buttons["bind_skin"], "smoothSkin.png") definition_buttons["unbind_skin"] = QtWidgets.QPushButton("取消蒙皮") set_button_icon(definition_buttons["unbind_skin"], "detachSkin.png") # 工具组按钮 definition_buttons["reposition_head"] = QtWidgets.QPushButton("重新定位头部关节") set_button_icon(definition_buttons["reposition_head"], "HIKCharacterToolSkeleton.png") definition_buttons["reposition_body"] = QtWidgets.QPushButton("重新定位身体关节") set_button_icon(definition_buttons["reposition_body"], "HIKCharacterToolSkeleton.png") definition_buttons["reposition_all"] = QtWidgets.QPushButton("重新定位全身关节") set_button_icon(definition_buttons["reposition_all"], "HIKCharacterToolSkeleton.png") definition_buttons["quick_preset"] = QtWidgets.QPushButton("快速创建预设") set_button_icon(definition_buttons["quick_preset"], "QR_QuickRigTool.png") # 设置按钮样式 for button in definition_buttons.values(): button.setMinimumHeight(28) # 设置按钮文本对齐方式,使图标在左侧,文本在右侧 button.setStyleSheet("text-align: left; padding-left: 30px;") # 设置工具提示与按钮文本相同 button.setToolTip(button.text()) #========================================== LAYOUTS ========================================== def layouts(parent_tab=None): """ 创建定义系统UI布局 Args: parent_tab: 父容器控件,由Main.py传入 """ # 获取父容器(在Main.py中创建的definition_tab) if not parent_tab: parent_tab = ui_utils.get_parent_widget("definition_tab") if not parent_tab: print("无法获取父容器,布局创建失败") return # 创建主布局 main_layout = parent_tab.layout() if not main_layout: print("父容器没有布局,布局创建失败") return # 创建主分割控件 - 水平分割 main_splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal) # 创建左侧区域 - 垂直分割 left_splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) # 创建LOD区域 lod_widget = QtWidgets.QWidget() lod_layout = QtWidgets.QVBoxLayout(lod_widget) lod_layout.setContentsMargins(4, 4, 4, 4) lod_layout.setSpacing(4) # LOD标题栏 lod_header = QtWidgets.QWidget() lod_header_layout = QtWidgets.QHBoxLayout(lod_header) lod_header_layout.setContentsMargins(0, 0, 0, 0) lod_header_layout.addWidget(dna_elements["lod_label"]) lod_header_layout.addStretch() lod_header_layout.addWidget(dna_elements["refresh_lod"]) lod_layout.addWidget(lod_header) lod_layout.addWidget(dna_elements["lod_list"]) lod_layout.addWidget(dna_elements["define_lod_joint"]) # 创建Meshes区域 mesh_widget = QtWidgets.QWidget() mesh_layout = QtWidgets.QVBoxLayout(mesh_widget) mesh_layout.setContentsMargins(4, 4, 4, 4) mesh_layout.setSpacing(4) # Meshes标题栏 mesh_header = QtWidgets.QWidget() mesh_header_layout = QtWidgets.QHBoxLayout(mesh_header) mesh_header_layout.setContentsMargins(0, 0, 0, 0) mesh_header_layout.addWidget(dna_elements["mesh_label"]) mesh_header_layout.addStretch() mesh_header_layout.addWidget(dna_elements["refresh_mesh"]) mesh_layout.addWidget(mesh_header) mesh_layout.addWidget(dna_elements["mesh_list"]) mesh_layout.addWidget(dna_elements["create_geometry"]) # 添加到左侧垂直分割控件 left_splitter.addWidget(lod_widget) left_splitter.addWidget(mesh_widget) # 创建右侧区域 - 垂直分割 right_splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) # 创建右上区域 - Joints right_top_widget = QtWidgets.QWidget() right_top_layout = QtWidgets.QVBoxLayout(right_top_widget) right_top_layout.setContentsMargins(4, 4, 4, 4) right_top_layout.setSpacing(4) # Joints标题栏 joint_header = QtWidgets.QWidget() joint_header_layout = QtWidgets.QHBoxLayout(joint_header) joint_header_layout.setContentsMargins(0, 0, 0, 0) joint_header_layout.addWidget(dna_elements["joint_label"]) joint_header_layout.addStretch() joint_header_layout.addWidget(dna_elements["refresh_joint"]) right_top_layout.addWidget(joint_header) right_top_layout.addWidget(dna_elements["joint_list"]) # 创建右中区域 - BlendShapes right_middle_widget = QtWidgets.QWidget() right_middle_layout = QtWidgets.QVBoxLayout(right_middle_widget) right_middle_layout.setContentsMargins(4, 4, 4, 4) right_middle_layout.setSpacing(4) # BlendShapes标题栏 blendshape_header = QtWidgets.QWidget() blendshape_header_layout = QtWidgets.QHBoxLayout(blendshape_header) blendshape_header_layout.setContentsMargins(0, 0, 0, 0) blendshape_header_layout.addWidget(dna_elements["blendshape_label"]) blendshape_header_layout.addStretch() blendshape_header_layout.addWidget(dna_elements["refresh_blendshape"]) right_middle_layout.addWidget(blendshape_header) right_middle_layout.addWidget(dna_elements["blendshape_list"]) # 创建右下区域 - AnimatedMap right_bottom_widget = QtWidgets.QWidget() right_bottom_layout = QtWidgets.QVBoxLayout(right_bottom_widget) right_bottom_layout.setContentsMargins(4, 4, 4, 4) right_bottom_layout.setSpacing(4) # AnimatedMap标题栏 animmap_header = QtWidgets.QWidget() animmap_header_layout = QtWidgets.QHBoxLayout(animmap_header) animmap_header_layout.setContentsMargins(0, 0, 0, 0) animmap_header_layout.addWidget(dna_elements["animmap_label"]) animmap_header_layout.addStretch() animmap_header_layout.addWidget(dna_elements["refresh_animmap"]) right_bottom_layout.addWidget(animmap_header) right_bottom_layout.addWidget(dna_elements["animmap_list"]) # 添加到右侧分割控件 right_splitter.addWidget(right_top_widget) right_splitter.addWidget(right_middle_widget) right_splitter.addWidget(right_bottom_widget) # 设置右侧分割控件比例 right_splitter.setSizes([200, 200, 200]) # 设置左侧分割控件比例 - 使其均等 left_splitter.setSizes([300, 300]) # 设置左侧分割控件的伸缩因子,使两个模块均等分配空间 left_splitter.setStretchFactor(0, 1) # LOD区域伸缩因子为1 left_splitter.setStretchFactor(1, 1) # Meshes区域伸缩因子为1 # 添加到主分割控件 main_splitter.addWidget(left_splitter) main_splitter.addWidget(right_splitter) # 设置主分割控件比例 - 左右宽度相等 main_splitter.setSizes([500, 500]) # 设置分割控件的伸缩因子,使其根据窗口大小均等分配空间 main_splitter.setStretchFactor(0, 1) # 左侧伸缩因子为1 main_splitter.setStretchFactor(1, 1) # 右侧伸缩因子为1 # 创建底部功能区域 bottom_widget = QtWidgets.QWidget() bottom_layout = QtWidgets.QHBoxLayout(bottom_widget) bottom_layout.setContentsMargins(4, 4, 4, 4) # 创建功能分组 write_group = QtWidgets.QGroupBox("写入") write_layout = QtWidgets.QVBoxLayout(write_group) write_layout.setAlignment(QtCore.Qt.AlignTop) # 设置向顶部对齐 write_layout.setContentsMargins(4, 8, 4, 4) # 减小内边距 write_layout.setSpacing(4) # 减小间距 write_layout.addWidget(definition_buttons["write_joint_defaults"]) write_layout.addWidget(definition_buttons["write_geometry"]) write_layout.addWidget(definition_buttons["write_skinning"]) write_layout.addWidget(definition_buttons["write_blendshapes"]) write_layout.addStretch() # 添加弹性空间,使按钮向顶部对齐 create_group = QtWidgets.QGroupBox("创建") create_layout = QtWidgets.QVBoxLayout(create_group) create_layout.setAlignment(QtCore.Qt.AlignTop) # 设置向顶部对齐 create_layout.setContentsMargins(4, 8, 4, 4) # 减小内边距 create_layout.setSpacing(4) # 减小间距 create_layout.addWidget(definition_buttons["create_blendshapes"]) create_layout.addWidget(definition_buttons["bind_skin"]) create_layout.addWidget(definition_buttons["unbind_skin"]) create_layout.addStretch() # 添加弹性空间,使按钮向顶部对齐 tools_group = QtWidgets.QGroupBox("工具") tools_layout = QtWidgets.QVBoxLayout(tools_group) tools_layout.setAlignment(QtCore.Qt.AlignTop) # 设置向顶部对齐 tools_layout.setContentsMargins(4, 8, 4, 4) # 减小内边距 tools_layout.setSpacing(4) # 减小间距 tools_layout.addWidget(definition_buttons["reposition_head"]) tools_layout.addWidget(definition_buttons["reposition_body"]) tools_layout.addWidget(definition_buttons["reposition_all"]) tools_layout.addWidget(definition_buttons["quick_preset"]) tools_layout.addStretch() # 添加弹性空间,使按钮向顶部对齐 # 添加到底部布局 bottom_layout.addWidget(write_group) bottom_layout.addWidget(create_group) bottom_layout.addWidget(tools_group) # 创建主容器 main_container = QtWidgets.QWidget() container_layout = QtWidgets.QVBoxLayout(main_container) container_layout.setContentsMargins(0, 0, 0, 0) container_layout.setSpacing(4) container_layout.addWidget(main_splitter, 1) container_layout.addWidget(bottom_widget, 0) # 添加到主布局 main_layout.addWidget(main_container) #========================================== CONNECTIONS ========================================== def connections(): """ 连接定义系统UI信号和槽 """ # 连接功能按钮点击事件 definition_buttons["write_joint_defaults"].clicked.connect(lambda: print("写入关节默认位置功能待实现")) definition_buttons["write_geometry"].clicked.connect(lambda: print("写入几何体功能待实现")) definition_buttons["write_skinning"].clicked.connect(lambda: print("写入蒙皮功能待实现")) definition_buttons["write_blendshapes"].clicked.connect(lambda: print("写入混合变形目标功能待实现")) definition_buttons["create_blendshapes"].clicked.connect(lambda: print("创建混合变形功能待实现")) definition_buttons["bind_skin"].clicked.connect(lambda: print("绑定蒙皮功能待实现")) definition_buttons["unbind_skin"].clicked.connect(lambda: print("取消蒙皮功能待实现")) definition_buttons["reposition_head"].clicked.connect(lambda: print("重新定位头部关节功能待实现")) definition_buttons["reposition_body"].clicked.connect(lambda: print("重新定位身体关节功能待实现")) definition_buttons["reposition_all"].clicked.connect(lambda: print("重新定位全身关节功能待实现")) definition_buttons["quick_preset"].clicked.connect(lambda: print("快速创建预设功能待实现")) # 连接列表选择事件 for key, widget in dna_elements.items(): if isinstance(widget, QtWidgets.QListWidget): widget.itemSelectionChanged.connect(lambda k=key: on_list_selection_changed(k)) widget.itemDoubleClicked.connect(lambda item, k=key: on_list_item_double_clicked(item, k)) # 连接刷新按钮点击事件 dna_elements["refresh_lod"].clicked.connect(lambda: refresh_list("lod_list")) dna_elements["refresh_mesh"].clicked.connect(lambda: refresh_list("mesh_list")) dna_elements["refresh_joint"].clicked.connect(lambda: refresh_list("joint_list")) dna_elements["refresh_blendshape"].clicked.connect(lambda: refresh_list("blendshape_list")) dna_elements["refresh_animmap"].clicked.connect(lambda: refresh_list("animmap_list")) # 连接LOD和Meshes特殊按钮点击事件 dna_elements["define_lod_joint"].clicked.connect(lambda: print("定义LOD关节功能待实现")) dna_elements["create_geometry"].clicked.connect(lambda: print("创建几何体功能待实现")) # 列表选择变化处理函数 def on_list_selection_changed(list_key): """ 处理列表选择变化事件 Args: list_key: 列表键名 """ selected_items = dna_elements[list_key].selectedItems() if selected_items: item = selected_items[0] info_text = f"选中的{list_key.split('_')[0]}: {item.text()}\n" info_text += f"\n属性信息将在此显示" dna_elements["element_info"].setText(info_text) print(f"{list_key} 选中项: {item.text()}") # 列表项双击处理函数 def on_list_item_double_clicked(item, list_key): """ 处理列表项双击事件 Args: item: 被双击的列表项 list_key: 列表键名 """ print(f"{list_key} 双击项: {item.text()}") # 刷新列表函数 def refresh_list(list_key): """ 刷新指定列表 Args: list_key: 要刷新的列表键名 """ list_widget = dna_elements[list_key] list_widget.clear() # 模拟数据加载 test_data = { "lod_list": ["LOD_0", "LOD_1", "LOD_2"], "mesh_list": ["Head", "Body", "Eyes", "Teeth", "Tongue"], "joint_list": ["Head_Joint", "Neck_Joint", "Spine_Joint", "Arm_L_Joint", "Arm_R_Joint"], "blendshape_list": ["Smile", "Frown", "Blink", "Jaw_Open", "Brow_Raise"], "animmap_list": ["FaceMap_01", "BodyMap_01", "ExpressionMap_01"] } # 添加测试数据到列表 if list_key in test_data: for item in test_data[list_key]: list_widget.addItem(item) # 更新对应标签的计数 label_key = list_key.replace("list", "label") if label_key in dna_elements: count = list_widget.count() label_text = f"{label_key.split('_')[0].capitalize()}s [{count:03d}]" dna_elements[label_key].setText(label_text) print(f"刷新{list_key}完成") #===================================== PLACEHOLDER FUNCTION =================================== def definition_temp_function(): utils_definition.definition_temp_utils_function