This commit is contained in:
2025-05-09 01:21:24 +08:00
parent 1ea05737c0
commit 7b88f844ff
2 changed files with 173 additions and 814 deletions

View File

@@ -490,34 +490,20 @@ class RiggingUI(ui_utils.BaseUI):
#======================================= FUNCTIONS ======================================
def create_connections(self):
"""
创建信号连接设置UI控件的交互行为风格参考definition.py
创建信号连接设置UI控件的交互行为
"""
# 主要功能按钮直接连接
if "add_joint_btn" in self.buttons:
self.buttons["add_joint_btn"].clicked.connect(utils_rigging.add_joint)
if "remove_joint_btn" in self.buttons:
self.buttons["remove_joint_btn"].clicked.connect(utils_rigging.remove_joint)
if "duplicate_joint_btn" in self.buttons:
self.buttons["duplicate_joint_btn"].clicked.connect(utils_rigging.duplicate_joint)
if "add_controller_btn" in self.buttons:
self.buttons["add_controller_btn"].clicked.connect(utils_rigging.add_controller)
if "remove_controller_btn" in self.buttons:
self.buttons["remove_controller_btn"].clicked.connect(utils_rigging.remove_controller)
if "duplicate_controller_btn" in self.buttons:
self.buttons["duplicate_controller_btn"].clicked.connect(utils_rigging.duplicate_controller)
if "import_dna_btn" in self.buttons:
self.buttons["import_dna_btn"].clicked.connect(utils_rigging.import_dna)
if "export_dna_btn" in self.buttons:
self.buttons["export_dna_btn"].clicked.connect(utils_rigging.export_dna)
if "calibrate_dna_btn" in self.buttons:
self.buttons["calibrate_dna_btn"].clicked.connect(utils_rigging.calibrate_dna)
# 导入绑定工具函数
from scripts.utils import utils_rigging
# 预设导入和导出按钮
self.buttons["export_presets"].clicked.connect(utils_rigging.export_dna)
self.buttons["import_presets"].clicked.connect(utils_rigging.import_dna)
# 其它已创建按钮
if "browse_path" in self.buttons:
self.buttons["browse_path"].clicked.connect(lambda: utils_rigging.browse_file(self, "项目路径", self.controls["project_path_input"]))
if "browse_dna" in self.buttons:
self.buttons["browse_dna"].clicked.connect(lambda: utils_rigging.browse_file(self, "DNA文件", self.controls["presets_dna_input"], "dna"))
if "export_presets" in self.buttons:
self.buttons["export_presets"].clicked.connect(utils_rigging.export_presets)
if "import_presets" in self.buttons:
self.buttons["import_presets"].clicked.connect(utils_rigging.import_presets)
# 浏览文件按钮连接
self.buttons["browse_path"].clicked.connect(lambda: utils_rigging.browse_file(self, "项目路径", self.controls["project_path_input"]))
self.buttons["browse_dna"].clicked.connect(lambda: utils_rigging.browse_file(self, "DNA文件", self.controls["presets_dna_input"], "dna"))
# 底部按钮连接
self.buttons["remove_all"].clicked.connect(utils_rigging.remove_all)
self.buttons["import_skeleton"].clicked.connect(utils_rigging.import_skeleton)
self.buttons["build_rigging"].clicked.connect(utils_rigging.build_rigging)

View File

@@ -58,312 +58,40 @@ original_controller_properties = {}
#========================================== FUNCTIONS ========================================
#------------------------------------ UI UTILITIES ------------------------------------
def handle_ui_event(event_type, event_data):
def browse_file(parent_widget, title, line_edit, file_type=None):
"""
处理UI事件
浏览文件对话框
Args:
event_type: 事件类型
event_data: 事件数据
parent_widget: 父窗口
title: 对话框标题
line_edit: 要更新的文本框
file_type: 文件类型筛选
"""
if event_type == "button_click":
# 处理按钮点击事件
print(f"Button clicked: {event_data}")
elif event_type == "slider_change":
# 处理滑条改变事件
print(f"Slider changed: {event_data}")
else:
print(f"Unknown event type: {event_type}")
def connect_ui_signals(ui_widget, event_type, event_handler):
"""
连接UI信号
Args:
ui_widget: UI控件
event_type: 事件类型
event_handler: 事件处理函数
"""
if event_type == "button_click":
# 连接按钮点击信号
ui_widget.clicked.connect(event_handler)
elif event_type == "slider_change":
# 连接滑条改变信号
ui_widget.valueChanged.connect(event_handler)
else:
print(f"Unknown event type: {event_type}")
#------------------------------------ JOINT OPERATIONS ------------------------------------
def add_joint():
"""
添加关节
在场景中创建新的关节并添加到骨骼层级结构中
"""
# 在场景中创建新的关节
try:
# 创建新关节
new_joint = cmds.joint(name="joint1", position=[0, 0, 0])
print(f"成功创建关节: {new_joint}")
return new_joint
except Exception as e:
print(f"创建关节失败: {str(e)}")
# 设置文件过滤器
if file_type == "dna":
file_filter = "DNA文件 (*.dna);;所有文件 (*.*)"
elif file_type == "fbx":
file_filter = "FBX文件 (*.fbx);;所有文件 (*.*)"
else:
file_filter = "所有文件 (*.*)"
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter=file_filter,
dialogStyle=2,
caption=TEXT(title, title)
)
if file_path:
# 更新文本框
line_edit.setText(file_path[0])
return file_path[0]
return None
def remove_joint():
"""
移除选中的关节
从场景中删除选中的关节
"""
# 获取当前选中的关节
selected = cmds.ls(selection=True, type="joint")
if not selected:
print("没有选中关节")
return False
try:
# 删除选中的关节
for joint in selected:
cmds.delete(joint)
print(f"成功删除关节: {selected}")
return True
except Exception as e:
print(f"删除关节失败: {str(e)}")
return False
def duplicate_joint():
"""
复制选中的关节
复制场景中选中的关节及其属性
"""
# 获取当前选中的关节
selected = cmds.ls(selection=True, type="joint")
if not selected:
print("没有选中关节")
print(f"浏览文件失败: {str(e)}")
return None
try:
# 复制选中的关节
duplicated = cmds.duplicate(selected, returnRootsOnly=True)
print(f"成功复制关节: {duplicated}")
return duplicated
except Exception as e:
print(f"复制关节失败: {str(e)}")
return None
def update_joint_properties(joint_name, position=None, rotation=None, scale=None):
"""
更新关节属性
Args:
joint_name: 关节名称
position: 位置坐标 [x, y, z]
rotation: 旋转角度 [rx, ry, rz]
scale: 缩放比例 [sx, sy, sz]
"""
if not cmds.objExists(joint_name):
print(f"关节不存在: {joint_name}")
return False
try:
# 更新位置
if position:
cmds.setAttr(f"{joint_name}.translateX", position[0])
cmds.setAttr(f"{joint_name}.translateY", position[1])
cmds.setAttr(f"{joint_name}.translateZ", position[2])
# 更新旋转
if rotation:
cmds.setAttr(f"{joint_name}.rotateX", rotation[0])
cmds.setAttr(f"{joint_name}.rotateY", rotation[1])
cmds.setAttr(f"{joint_name}.rotateZ", rotation[2])
# 更新缩放
if scale:
cmds.setAttr(f"{joint_name}.scaleX", scale[0])
cmds.setAttr(f"{joint_name}.scaleY", scale[1])
cmds.setAttr(f"{joint_name}.scaleZ", scale[2])
print(f"成功更新关节属性: {joint_name}")
return True
except Exception as e:
print(f"更新关节属性失败: {str(e)}")
return False
def reset_joint_properties(joint_name):
"""
重置关节属性
Args:
joint_name: 关节名称
"""
if not cmds.objExists(joint_name):
print(f"关节不存在: {joint_name}")
return False
try:
# 重置位置
cmds.setAttr(f"{joint_name}.translateX", 0)
cmds.setAttr(f"{joint_name}.translateY", 0)
cmds.setAttr(f"{joint_name}.translateZ", 0)
# 重置旋转
cmds.setAttr(f"{joint_name}.rotateX", 0)
cmds.setAttr(f"{joint_name}.rotateY", 0)
cmds.setAttr(f"{joint_name}.rotateZ", 0)
# 重置缩放
cmds.setAttr(f"{joint_name}.scaleX", 1)
cmds.setAttr(f"{joint_name}.scaleY", 1)
cmds.setAttr(f"{joint_name}.scaleZ", 1)
print(f"成功重置关节属性: {joint_name}")
return True
except Exception as e:
print(f"重置关节属性失败: {str(e)}")
return False
#------------------------------------ CONTROLLER OPERATIONS ------------------------------------
def add_controller():
"""
添加控制器
在场景中创建新的控制器
"""
try:
# 创建控制器曲线
circle = cmds.circle(name="controller1", normal=[0, 1, 0], radius=1)[0]
print(f"成功创建控制器: {circle}")
return circle
except Exception as e:
print(f"创建控制器失败: {str(e)}")
return None
def remove_controller():
"""
移除选中的控制器
从场景中删除选中的控制器
"""
# 获取当前选中的控制器
selected = cmds.ls(selection=True, type="transform")
if not selected:
print("没有选中控制器")
return False
try:
# 删除选中的控制器
for ctrl in selected:
cmds.delete(ctrl)
print(f"成功删除控制器: {selected}")
return True
except Exception as e:
print(f"删除控制器失败: {str(e)}")
return False
def duplicate_controller():
"""
复制选中的控制器
复制场景中选中的控制器及其属性
"""
# 获取当前选中的控制器
selected = cmds.ls(selection=True, type="transform")
if not selected:
print("没有选中控制器")
return None
try:
# 复制选中的控制器
duplicated = cmds.duplicate(selected, returnRootsOnly=True)
print(f"成功复制控制器: {duplicated}")
return duplicated
except Exception as e:
print(f"复制控制器失败: {str(e)}")
return None
def update_controller_properties(controller_name, position=None, rotation=None, scale=None, color=None):
"""
更新控制器属性
Args:
controller_name: 控制器名称
position: 位置坐标 [x, y, z]
rotation: 旋转角度 [rx, ry, rz]
scale: 缩放比例 [sx, sy, sz]
color: 控制器颜色索引
"""
if not cmds.objExists(controller_name):
print(f"控制器不存在: {controller_name}")
return False
try:
# 更新位置
if position:
cmds.setAttr(f"{controller_name}.translateX", position[0])
cmds.setAttr(f"{controller_name}.translateY", position[1])
cmds.setAttr(f"{controller_name}.translateZ", position[2])
# 更新旋转
if rotation:
cmds.setAttr(f"{controller_name}.rotateX", rotation[0])
cmds.setAttr(f"{controller_name}.rotateY", rotation[1])
cmds.setAttr(f"{controller_name}.rotateZ", rotation[2])
# 更新缩放
if scale:
cmds.setAttr(f"{controller_name}.scaleX", scale[0])
cmds.setAttr(f"{controller_name}.scaleY", scale[1])
cmds.setAttr(f"{controller_name}.scaleZ", scale[2])
# 更新颜色
if color is not None:
shapes = cmds.listRelatives(controller_name, shapes=True)
if shapes:
for shape in shapes:
cmds.setAttr(f"{shape}.overrideEnabled", 1)
cmds.setAttr(f"{shape}.overrideColor", color)
print(f"成功更新控制器属性: {controller_name}")
return True
except Exception as e:
print(f"更新控制器属性失败: {str(e)}")
return False
def reset_controller_properties(controller_name):
"""
重置控制器属性
Args:
controller_name: 控制器名称
"""
if not cmds.objExists(controller_name):
print(f"控制器不存在: {controller_name}")
return False
try:
# 重置位置
cmds.setAttr(f"{controller_name}.translateX", 0)
cmds.setAttr(f"{controller_name}.translateY", 0)
cmds.setAttr(f"{controller_name}.translateZ", 0)
# 重置旋转
cmds.setAttr(f"{controller_name}.rotateX", 0)
cmds.setAttr(f"{controller_name}.rotateY", 0)
cmds.setAttr(f"{controller_name}.rotateZ", 0)
# 重置缩放
cmds.setAttr(f"{controller_name}.scaleX", 1)
cmds.setAttr(f"{controller_name}.scaleY", 1)
cmds.setAttr(f"{controller_name}.scaleZ", 1)
print(f"成功重置控制器属性: {controller_name}")
return True
except Exception as e:
print(f"重置控制器属性失败: {str(e)}")
return False
#------------------------------------ DNA OPERATIONS ------------------------------------
def import_dna():
@@ -373,20 +101,23 @@ def import_dna():
从文件中导入DNA数据
"""
try:
# 打开文件对话框选择DNA文件
file_path = cmds.fileDialog2(fileFilter="DNA Files (*.dna);;All Files (*.*)", dialogStyle=2, fileMode=1)
if not file_path:
return None
file_path = file_path[0] # 获取选中的文件路径
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter="DNA文件 (*.dna);;所有文件 (*.*)",
dialogStyle=2,
caption="选择DNA文件"
)
# 这里应该调用DNA导入API
# 暂时使用打印信息代替
print(f"导入DNA文件: {file_path}")
return file_path
if not file_path:
print("未选择文件")
return False
print(f"导入DNA文件: {file_path[0]}")
# 实现DNA导入逻辑
return True
except Exception as e:
print(f"导入DNA文件失败: {str(e)}")
return None
return False
def export_dna():
"""
@@ -395,501 +126,143 @@ def export_dna():
将当前绑定数据导出为DNA文件
"""
try:
# 打开文件对话框选择保存路径
file_path = cmds.fileDialog2(fileFilter="DNA Files (*.dna);;All Files (*.*)", dialogStyle=2, fileMode=0)
# 打开文件保存对话框
file_path = cmds.fileDialog2(
fileFilter="DNA文件 (*.dna);;所有文件 (*.*)",
dialogStyle=2,
caption="保存DNA文件",
fileMode=0
)
if not file_path:
return None
file_path = file_path[0] # 获取选中的文件路径
print("未选择保存位置")
return False
# 确保文件扩展名为.dna
if not file_path.lower().endswith(".dna"):
file_path += ".dna"
if not file_path[0].lower().endswith('.dna'):
file_path[0] += '.dna'
# 这里应该调用DNA导出API
# 暂时使用打印信息代替
print(f"导出DNA文件: {file_path}")
return file_path
except Exception as e:
print(f"导出DNA文件失败: {str(e)}")
return None
def calibrate_dna():
"""
校准DNA数据
校准当前绑定数据与DNA标准
"""
try:
# 这里应该调用DNA校准API
# 暂时使用打印信息代替
print("校准DNA数据")
print(f"导出DNA文件: {file_path[0]}")
# 实现DNA导出逻辑
return True
except Exception as e:
print(f"校准DNA数据失败: {str(e)}")
print(f"导出DNA文件失败: {str(e)}")
return False
#------------------------------------ UTILITY FUNCTIONS ------------------------------------
def update_ui_list(list_widget, items):
"""
更新UI列表控件
#------------------------------------ Rigging ------------------------------------
def remove_all(*args):
"""移除所有绑定组件
Args:
list_widget: 列表控件
items: 要添加的项目列表
从场景中删除所有绑定相关的组件,包括关节、控制器等
"""
if not list_widget:
return
# 清空列表
list_widget.clear()
# 添加项目
for item in items:
list_widget.addItem(item)
def update_group_count(list_widget, group_name):
"""
更新组标题中的计数
Args:
list_widget: 列表控件
group_name: 组名称
"""
if not list_widget:
return
# 获取列表项目数量
count = list_widget.count()
# 更新组标题
group_box = list_widget.parent()
if isinstance(group_box, QtWidgets.QGroupBox):
group_box.setTitle(f"{group_name} ({count})")
#------------------------------------ UI EVENT HANDLERS ------------------------------------
def handle_joint_name_changed(ui_widget):
"""
处理关节名称变化事件
Args:
ui_widget: UI控件应该是一个文本输入框
"""
global selected_joint
# 获取当前选中的关节和新名称
if selected_joint and ui_widget:
new_name = ui_widget.text()
if new_name and new_name != selected_joint:
try:
# 重命名关节
cmds.rename(selected_joint, new_name)
selected_joint = new_name
return True
except Exception as e:
print(f"重命名关节失败: {str(e)}")
return False
return False
def handle_joint_position_changed(axis, value=None, ui_widgets=None):
"""
处理关节位置变化事件
Args:
axis: 轴向 (0=X, 1=Y, 2=Z)
value: 新的位置值如果为None则从ui_widgets中获取
ui_widgets: UI控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not selected_joint:
return False
try:
# 获取所有关节
all_joints = cmds.ls(type="joint")
# 获取当前位置
pos = [
cmds.getAttr(f"{selected_joint}.translateX"),
cmds.getAttr(f"{selected_joint}.translateY"),
cmds.getAttr(f"{selected_joint}.translateZ")
]
# 更新指定轴向的位置
if value is not None:
pos[axis] = value
elif ui_widgets and len(ui_widgets) >= 3:
if axis == 0 and ui_widgets[0]:
pos[0] = ui_widgets[0].value()
elif axis == 1 and ui_widgets[1]:
pos[1] = ui_widgets[1].value()
elif axis == 2 and ui_widgets[2]:
pos[2] = ui_widgets[2].value()
else:
return False
# 更新关节属性
return update_joint_properties(selected_joint, position=pos)
def handle_joint_rotation_changed(axis, value=None, ui_widgets=None):
"""
处理关节旋转变化事件
Args:
axis: 轴向 (0=X, 1=Y, 2=Z)
value: 新的旋转值如果为None则从ui_widgets中获取
ui_widgets: UI控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not selected_joint:
return False
# 获取所有可能的控制器(这里简化为所有曲线)
all_controllers = cmds.ls(type="nurbsCurve")
all_controller_transforms = []
# 获取当前旋转
rot = [
cmds.getAttr(f"{selected_joint}.rotateX"),
cmds.getAttr(f"{selected_joint}.rotateY"),
cmds.getAttr(f"{selected_joint}.rotateZ")
]
# 更新指定轴向的旋转
if value is not None:
rot[axis] = value
elif ui_widgets and len(ui_widgets) >= 3:
if axis == 0 and ui_widgets[0]:
rot[0] = ui_widgets[0].value()
elif axis == 1 and ui_widgets[1]:
rot[1] = ui_widgets[1].value()
elif axis == 2 and ui_widgets[2]:
rot[2] = ui_widgets[2].value()
else:
return False
# 更新关节属性
return update_joint_properties(selected_joint, rotation=rot)
def handle_joint_scale_changed(axis, value=None, ui_widgets=None):
"""
处理关节缩放变化事件
Args:
axis: 轴向 (0=X, 1=Y, 2=Z)
value: 新的缩放值如果为None则从ui_widgets中获取
ui_widgets: UI控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not selected_joint:
return False
# 获取控制器的变换节点
for ctrl in all_controllers:
parent = cmds.listRelatives(ctrl, parent=True)
if parent:
all_controller_transforms.extend(parent)
# 获取当前缩放
scale = [
cmds.getAttr(f"{selected_joint}.scaleX"),
cmds.getAttr(f"{selected_joint}.scaleY"),
cmds.getAttr(f"{selected_joint}.scaleZ")
]
# 更新指定轴向的缩放
if value is not None:
scale[axis] = value
elif ui_widgets and len(ui_widgets) >= 3:
if axis == 0 and ui_widgets[0]:
scale[0] = ui_widgets[0].value()
elif axis == 1 and ui_widgets[1]:
scale[1] = ui_widgets[1].value()
elif axis == 2 and ui_widgets[2]:
scale[2] = ui_widgets[2].value()
else:
return False
# 更新关节属性
return update_joint_properties(selected_joint, scale=scale)
def apply_joint_properties_from_ui(pos_widgets=None, rot_widgets=None, scale_widgets=None):
"""
从UI控件中应用关节属性
Args:
pos_widgets: 位置控件列表 [x_spin, y_spin, z_spin]
rot_widgets: 旋转控件列表 [x_spin, y_spin, z_spin]
scale_widgets: 缩放控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not selected_joint:
return False
# 确认删除
result = cmds.confirmDialog(
title=TEXT("confirm_delete", "确认删除"),
message=TEXT("delete_all_confirm", "确定要删除所有绑定组件吗?"),
button=[TEXT("yes", ""), TEXT("no", "")],
defaultButton=TEXT("no", ""),
cancelButton=TEXT("no", ""),
dismissString=TEXT("no", "")
)
# 获取UI中的属性值
pos = None
rot = None
scale = None
if pos_widgets and len(pos_widgets) >= 3:
pos = [
pos_widgets[0].value() if pos_widgets[0] else 0,
pos_widgets[1].value() if pos_widgets[1] else 0,
pos_widgets[2].value() if pos_widgets[2] else 0
]
if rot_widgets and len(rot_widgets) >= 3:
rot = [
rot_widgets[0].value() if rot_widgets[0] else 0,
rot_widgets[1].value() if rot_widgets[1] else 0,
rot_widgets[2].value() if rot_widgets[2] else 0
]
if scale_widgets and len(scale_widgets) >= 3:
scale = [
scale_widgets[0].value() if scale_widgets[0] else 1,
scale_widgets[1].value() if scale_widgets[1] else 1,
scale_widgets[2].value() if scale_widgets[2] else 1
]
# 更新关节属性
return update_joint_properties(selected_joint, position=pos, rotation=rot, scale=scale)
def reset_joint_properties_ui(pos_widgets=None, rot_widgets=None, scale_widgets=None):
"""
重置关节属性UI
Args:
pos_widgets: 位置控件列表 [x_spin, y_spin, z_spin]
rot_widgets: 旋转控件列表 [x_spin, y_spin, z_spin]
scale_widgets: 缩放控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not selected_joint:
return False
# 重置关节属性
reset_joint_properties(selected_joint)
# 更新UI显示
if pos_widgets and len(pos_widgets) >= 3:
if pos_widgets[0]:
pos_widgets[0].setValue(0)
if pos_widgets[1]:
pos_widgets[1].setValue(0)
if pos_widgets[2]:
pos_widgets[2].setValue(0)
if rot_widgets and len(rot_widgets) >= 3:
if rot_widgets[0]:
rot_widgets[0].setValue(0)
if rot_widgets[1]:
rot_widgets[1].setValue(0)
if rot_widgets[2]:
rot_widgets[2].setValue(0)
if scale_widgets and len(scale_widgets) >= 3:
if scale_widgets[0]:
scale_widgets[0].setValue(1)
if scale_widgets[1]:
scale_widgets[1].setValue(1)
if scale_widgets[2]:
scale_widgets[2].setValue(1)
return True
def update_joint_ui(joint_name, name_widget=None, pos_widgets=None, rot_widgets=None, scale_widgets=None):
"""
更新关节UI
Args:
joint_name: 关节名称
name_widget: 名称控件
pos_widgets: 位置控件列表 [x_spin, y_spin, z_spin]
rot_widgets: 旋转控件列表 [x_spin, y_spin, z_spin]
scale_widgets: 缩放控件列表 [x_spin, y_spin, z_spin]
"""
global selected_joint
if not cmds.objExists(joint_name):
return False
# 更新选中的关节
selected_joint = joint_name
# 更新名称控件
if name_widget:
name_widget.setText(joint_name)
# 获取关节属性
pos = [
cmds.getAttr(f"{joint_name}.translateX"),
cmds.getAttr(f"{joint_name}.translateY"),
cmds.getAttr(f"{joint_name}.translateZ")
]
rot = [
cmds.getAttr(f"{joint_name}.rotateX"),
cmds.getAttr(f"{joint_name}.rotateY"),
cmds.getAttr(f"{joint_name}.rotateZ")
]
scale = [
cmds.getAttr(f"{joint_name}.scaleX"),
cmds.getAttr(f"{joint_name}.scaleY"),
cmds.getAttr(f"{joint_name}.scaleZ")
]
# 更新UI控件
if pos_widgets and len(pos_widgets) >= 3:
if pos_widgets[0]:
pos_widgets[0].setValue(pos[0])
if pos_widgets[1]:
pos_widgets[1].setValue(pos[1])
if pos_widgets[2]:
pos_widgets[2].setValue(pos[2])
if result == TEXT("yes", ""):
# 删除所有控制器
if all_controller_transforms:
cmds.delete(all_controller_transforms)
if rot_widgets and len(rot_widgets) >= 3:
if rot_widgets[0]:
rot_widgets[0].setValue(rot[0])
if rot_widgets[1]:
rot_widgets[1].setValue(rot[1])
if rot_widgets[2]:
rot_widgets[2].setValue(rot[2])
# 删除所有关节
if all_joints:
cmds.delete(all_joints)
if scale_widgets and len(scale_widgets) >= 3:
if scale_widgets[0]:
scale_widgets[0].setValue(scale[0])
if scale_widgets[1]:
scale_widgets[1].setValue(scale[1])
if scale_widgets[2]:
scale_widgets[2].setValue(scale[2])
return True
def on_selection_changed():
"""
选择变化事件处理
当Maya中的选择变化时更新UI
"""
global selected_joint, selected_controller
# 获取当前选中的对象
selected = cmds.ls(selection=True)
if not selected:
# 清除选中状态
selected_joint = None
selected_controller = None
return
# 检查选中的对象类型
for obj in selected:
# 检查是否是关节
if cmds.objectType(obj) == "joint":
selected_joint = obj
# 可以在这里添加代码来更新UI
print(f"选中关节: {selected_joint}")
break
# 检查是否是控制器通常是transform节点
elif cmds.objectType(obj) == "transform":
# 这里可以添加额外的检查来确定是否是控制器
# 例如检查是否有特定的属性或命名规则
selected_controller = obj
# 可以在这里添加代码来更新UI
print(f"选中控制器: {selected_controller}")
break
def browse_file(ui_instance, title, input_widget, file_filter=None):
"""浏览文件或目录
Args:
ui_instance: UI实例用于获取主窗口
title (str): 对话框标题
input_widget (QLineEdit): 用于显示路径的输入框控件
file_filter (str, optional): 文件过滤器如果为None则浏览目录否则浏览文件
"""
from Qt import QtWidgets
import os
current_path = input_widget.text() or os.path.expanduser("~")
if file_filter:
# 浏览文件
if file_filter == "dna":
file_filter = "DNA文件 (*.dna);;所有文件 (*.*)"
elif file_filter == "json":
file_filter = "JSON文件 (*.json);;所有文件 (*.*)"
print("成功删除所有绑定组件")
return True
else:
file_filter = "所有文件 (*.*)"
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(
ui_instance.main_widget, title, current_path, file_filter
)
if file_path:
input_widget.setText(file_path)
else:
# 浏览目录
dir_path = QtWidgets.QFileDialog.getExistingDirectory(
ui_instance.main_widget, title, current_path
)
if dir_path:
input_widget.setText(dir_path)
print("取消删除操作")
return False
except Exception as e:
print(f"删除绑定组件失败: {str(e)}")
return False
def on_selection_changed(ui_instance=None):
"""处理Maya选择变化事件
def import_skeleton(*args):
"""导入骨骼
Args:
ui_instance: UI实例可选
从文件中导入骨骼结构
"""
import maya.cmds as cmds
# 获取当前选择
selection = cmds.ls(selection=True)
if not selection:
return
try:
# 打开文件选择对话框
file_path = cmds.fileDialog2(
fileFilter="Maya文件 (*.ma *.mb);;FBX文件 (*.fbx);;所有文件 (*.*)",
dialogStyle=2,
caption="选择骨骼文件"
)
# 更新UI显示
print(f"当前选择: {selection}")
# 这里可以添加更多的选择处理逻辑
if not file_path:
print("未选择文件")
return False
file_path = file_path[0] # fileDialog2返回的是列表
# 根据文件类型选择导入方法
if file_path.lower().endswith(('.ma', '.mb')):
# 导入Maya文件
cmds.file(file_path, i=True, type="mayaAscii" if file_path.lower().endswith('.ma') else "mayaBinary",
ignoreVersion=True, mergeNamespacesOnClash=False, namespace="skeleton")
elif file_path.lower().endswith('.fbx'):
# 导入FBX文件
cmds.file(file_path, i=True, type="FBX", ignoreVersion=True)
else:
print(f"不支持的文件类型: {file_path}")
return False
print(f"成功导入骨骼: {file_path}")
return True
except Exception as e:
print(f"导入骨骼失败: {str(e)}")
return False
def build_rigging(*args):
"""构建绑定系统
# 如果提供了UI实例可以更新UI
if ui_instance:
# 更新UI显示
pass
def export_presets(*args):
"""导出预设"""
print("导出预设")
# 实现导出预设的功能
def import_presets(*args):
"""导入预设"""
print("导入预设")
# 实现导入预设的功能
# ======================================= 占位函数 =======================================
# 以下是未实现功能的占位函数,用于连接未创建的按钮
def add_joint(*args):
"""添加关节(占位函数)"""
print("添加关节功能尚未实现")
def remove_joint(*args):
"""删除关节(占位函数)"""
print("删除关节功能尚未实现")
def duplicate_joint(*args):
"""复制关节(占位函数)"""
print("复制关节功能尚未实现")
def add_controller(*args):
"""添加控制器(占位函数)"""
print("添加控制器功能尚未实现")
def remove_controller(*args):
"""删除控制器(占位函数)"""
print("删除控制器功能尚未实现")
def duplicate_controller(*args):
"""复制控制器(占位函数)"""
print("复制控制器功能尚未实现")
def import_dna(*args):
"""导入DNA占位函数"""
print("导入DNA功能尚未实现")
def export_dna(*args):
"""导出DNA占位函数"""
print("导出DNA功能尚未实现")
def calibrate_dna(*args):
"""校准DNA占位函数"""
print("校准DNA功能尚未实现")
基于当前场景中的骨骼构建完整的绑定系统
"""
try:
# 获取场景中的所有关节
all_joints = cmds.ls(type="joint")
if not all_joints:
print("场景中没有骨骼,无法构建绑定")
return False
# 为每个关节创建控制器
for joint in all_joints:
# 获取关节位置
pos = cmds.xform(joint, query=True, translation=True, worldSpace=True)
# 创建控制器这里简化为创建一个NURBS圆环
ctrl = cmds.circle(name=f"{joint}_ctrl", normal=[1, 0, 0], radius=1)[0]
# 移动控制器到关节位置
cmds.move(pos[0], pos[1], pos[2], ctrl)
# 创建约束
cmds.parentConstraint(ctrl, joint, maintainOffset=True)
print("成功构建绑定系统")
return True
except Exception as e:
print(f"构建绑定系统失败: {str(e)}")
return False