Files
MetaFusion/scripts/ui/definition.py
2025-05-03 23:17:52 +08:00

468 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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