752 lines
28 KiB
Python
752 lines
28 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Tool - BlendShape UI
|
|
Provides the UI for editing and managing BlendShapes
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import maya.cmds as cmds
|
|
|
|
try:
|
|
from PySide2 import QtCore, QtGui, QtWidgets
|
|
except ImportError:
|
|
try:
|
|
from PySide6 import QtCore, QtGui, QtWidgets
|
|
except ImportError:
|
|
from PySide import QtCore, QtGui, QtWidgets
|
|
|
|
# Import configuration
|
|
import config
|
|
|
|
class BlendShapeWidget(QtWidgets.QWidget):
|
|
"""Widget for editing and managing BlendShapes"""
|
|
|
|
def __init__(self, parent=None):
|
|
"""Initialize the BlendShape widget"""
|
|
super(BlendShapeWidget, self).__init__(parent)
|
|
|
|
# Initialize UI
|
|
self._create_widgets()
|
|
self._create_layouts()
|
|
self._create_connections()
|
|
|
|
def _create_widgets(self):
|
|
"""Create widgets for the BlendShape interface"""
|
|
# BlendShape Selection section
|
|
self.blendshape_group = QtWidgets.QGroupBox("BlendShape Selection")
|
|
|
|
self.blendshape_label = QtWidgets.QLabel("BlendShape Node:")
|
|
self.blendshape_combo = QtWidgets.QComboBox()
|
|
self.blendshape_combo.setEditable(True)
|
|
self.blendshape_combo.setInsertPolicy(QtWidgets.QComboBox.NoInsert)
|
|
|
|
self.refresh_blendshape_button = QtWidgets.QPushButton()
|
|
self.refresh_blendshape_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "refresh.png")))
|
|
self.refresh_blendshape_button.setToolTip("Refresh BlendShape nodes in the scene")
|
|
self.refresh_blendshape_button.setFixedSize(24, 24)
|
|
|
|
self.create_blendshape_button = QtWidgets.QPushButton("Create BlendShape")
|
|
self.create_blendshape_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "create.png")))
|
|
|
|
# Target Mesh section
|
|
self.target_group = QtWidgets.QGroupBox("Target Mesh")
|
|
|
|
self.target_label = QtWidgets.QLabel("Target Mesh:")
|
|
self.target_combo = QtWidgets.QComboBox()
|
|
self.target_combo.setEditable(True)
|
|
self.target_combo.setInsertPolicy(QtWidgets.QComboBox.NoInsert)
|
|
|
|
self.refresh_target_button = QtWidgets.QPushButton()
|
|
self.refresh_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "refresh.png")))
|
|
self.refresh_target_button.setToolTip("Refresh meshes in the scene")
|
|
self.refresh_target_button.setFixedSize(24, 24)
|
|
|
|
self.select_target_button = QtWidgets.QPushButton("Select Target")
|
|
self.select_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "select.png")))
|
|
|
|
# BlendShape Targets section
|
|
self.targets_group = QtWidgets.QGroupBox("BlendShape Targets")
|
|
|
|
self.targets_tree = QtWidgets.QTreeWidget()
|
|
self.targets_tree.setHeaderLabels(["Target Name", "Weight", "Status"])
|
|
self.targets_tree.setColumnWidth(0, 200)
|
|
self.targets_tree.setColumnWidth(1, 100)
|
|
self.targets_tree.setAlternatingRowColors(True)
|
|
|
|
# Target Controls section
|
|
self.controls_group = QtWidgets.QGroupBox("Target Controls")
|
|
|
|
self.add_target_button = QtWidgets.QPushButton("Add Target")
|
|
self.add_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "add.png")))
|
|
|
|
self.remove_target_button = QtWidgets.QPushButton("Remove Target")
|
|
self.remove_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "remove.png")))
|
|
|
|
self.edit_target_button = QtWidgets.QPushButton("Edit Target")
|
|
self.edit_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "edit.png")))
|
|
|
|
self.duplicate_target_button = QtWidgets.QPushButton("Duplicate Target")
|
|
self.duplicate_target_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "duplicate.png")))
|
|
|
|
# Batch Operations section
|
|
self.batch_group = QtWidgets.QGroupBox("Batch Operations")
|
|
|
|
self.export_all_button = QtWidgets.QPushButton("Export All Targets")
|
|
self.export_all_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "export.png")))
|
|
|
|
self.import_targets_button = QtWidgets.QPushButton("导入目标")
|
|
self.import_targets_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "import.png")))
|
|
|
|
self.connect_to_rig_button = QtWidgets.QPushButton("连接到控制器")
|
|
self.connect_to_rig_button.setIcon(QtGui.QIcon(os.path.join(config.ICONS_PATH, "connect.png")))
|
|
|
|
def _create_layouts(self):
|
|
"""Create layouts for the BlendShape interface"""
|
|
# BlendShape Selection layout
|
|
blendshape_layout = QtWidgets.QGridLayout()
|
|
blendshape_layout.addWidget(self.blendshape_label, 0, 0)
|
|
blendshape_layout.addWidget(self.blendshape_combo, 0, 1)
|
|
blendshape_layout.addWidget(self.refresh_blendshape_button, 0, 2)
|
|
blendshape_layout.addWidget(self.create_blendshape_button, 1, 1)
|
|
self.blendshape_group.setLayout(blendshape_layout)
|
|
|
|
# Target Mesh layout
|
|
target_layout = QtWidgets.QGridLayout()
|
|
target_layout.addWidget(self.target_label, 0, 0)
|
|
target_layout.addWidget(self.target_combo, 0, 1)
|
|
target_layout.addWidget(self.refresh_target_button, 0, 2)
|
|
target_layout.addWidget(self.select_target_button, 1, 1)
|
|
self.target_group.setLayout(target_layout)
|
|
|
|
# BlendShape Targets layout
|
|
targets_layout = QtWidgets.QVBoxLayout()
|
|
targets_layout.addWidget(self.targets_tree)
|
|
self.targets_group.setLayout(targets_layout)
|
|
|
|
# Target Controls layout
|
|
controls_layout = QtWidgets.QGridLayout()
|
|
controls_layout.addWidget(self.add_target_button, 0, 0)
|
|
controls_layout.addWidget(self.remove_target_button, 0, 1)
|
|
controls_layout.addWidget(self.edit_target_button, 1, 0)
|
|
controls_layout.addWidget(self.duplicate_target_button, 1, 1)
|
|
self.controls_group.setLayout(controls_layout)
|
|
|
|
# Batch Operations layout
|
|
batch_layout = QtWidgets.QHBoxLayout()
|
|
batch_layout.addWidget(self.export_all_button)
|
|
batch_layout.addWidget(self.import_targets_button)
|
|
batch_layout.addWidget(self.connect_to_rig_button)
|
|
self.batch_group.setLayout(batch_layout)
|
|
|
|
# Main layout
|
|
main_layout = QtWidgets.QVBoxLayout()
|
|
main_layout.addWidget(self.blendshape_group)
|
|
main_layout.addWidget(self.target_group)
|
|
main_layout.addWidget(self.targets_group)
|
|
main_layout.addWidget(self.controls_group)
|
|
main_layout.addWidget(self.batch_group)
|
|
|
|
self.setLayout(main_layout)
|
|
|
|
def _create_connections(self):
|
|
"""Create signal/slot connections for the BlendShape interface"""
|
|
# Connect buttons
|
|
self.refresh_blendshape_button.clicked.connect(self._refresh_blendshapes)
|
|
self.create_blendshape_button.clicked.connect(self._create_blendshape)
|
|
self.refresh_target_button.clicked.connect(self._refresh_targets)
|
|
self.select_target_button.clicked.connect(self._select_target)
|
|
|
|
self.add_target_button.clicked.connect(self._add_target)
|
|
self.remove_target_button.clicked.connect(self._remove_target)
|
|
self.edit_target_button.clicked.connect(self._edit_target)
|
|
self.duplicate_target_button.clicked.connect(self._duplicate_target)
|
|
|
|
self.export_all_button.clicked.connect(self._export_all_targets)
|
|
self.import_targets_button.clicked.connect(self._import_targets)
|
|
self.connect_to_rig_button.clicked.connect(self._connect_to_rig)
|
|
|
|
# Connect combo boxes
|
|
self.blendshape_combo.currentIndexChanged.connect(self._on_blendshape_changed)
|
|
self.target_combo.currentIndexChanged.connect(self._on_target_changed)
|
|
|
|
# Connect tree widget
|
|
self.targets_tree.itemClicked.connect(self._on_target_selected)
|
|
self.targets_tree.itemDoubleClicked.connect(self._on_target_double_clicked)
|
|
|
|
# Initial refresh
|
|
self._refresh_blendshapes()
|
|
self._refresh_targets()
|
|
|
|
def _refresh_blendshapes(self):
|
|
"""Refresh the list of BlendShape nodes in the scene"""
|
|
current_text = self.blendshape_combo.currentText()
|
|
|
|
self.blendshape_combo.clear()
|
|
|
|
# Get all BlendShape nodes in the scene
|
|
blendshapes = cmds.ls(type="blendShape")
|
|
blendshapes.sort()
|
|
|
|
self.blendshape_combo.addItems(blendshapes)
|
|
|
|
# Restore previous selection if possible
|
|
index = self.blendshape_combo.findText(current_text)
|
|
if index >= 0:
|
|
self.blendshape_combo.setCurrentIndex(index)
|
|
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
def _create_blendshape(self):
|
|
"""Create a new BlendShape node"""
|
|
target = self.target_combo.currentText()
|
|
if not target:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"请先选择一个目标网格"
|
|
)
|
|
return
|
|
|
|
# Get a name for the new BlendShape node
|
|
name, ok = QtWidgets.QInputDialog.getText(
|
|
self,
|
|
"创建 BlendShape",
|
|
"BlendShape 名称:",
|
|
QtWidgets.QLineEdit.Normal,
|
|
f"{target}_blendShape"
|
|
)
|
|
|
|
if ok and name:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.create_blendshape(target, name)
|
|
if result:
|
|
# Refresh BlendShape list
|
|
self._refresh_blendshapes()
|
|
|
|
# Select the new BlendShape
|
|
index = self.blendshape_combo.findText(name)
|
|
if index >= 0:
|
|
self.blendshape_combo.setCurrentIndex(index)
|
|
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"成功创建 BlendShape: {name}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"创建 BlendShape 失败: {name}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"创建 BlendShape 出错: {str(e)}"
|
|
)
|
|
|
|
def _refresh_targets(self):
|
|
"""Refresh the list of target meshes in the scene"""
|
|
current_text = self.target_combo.currentText()
|
|
|
|
self.target_combo.clear()
|
|
|
|
# Get all mesh shapes in the scene
|
|
meshes = cmds.ls(type="mesh")
|
|
mesh_transforms = []
|
|
|
|
for mesh in meshes:
|
|
# Get the transform node for the mesh
|
|
transform = cmds.listRelatives(mesh, parent=True)
|
|
if transform:
|
|
mesh_transforms.append(transform[0])
|
|
|
|
# Add unique mesh transforms to the combo box
|
|
unique_transforms = list(set(mesh_transforms))
|
|
unique_transforms.sort()
|
|
|
|
self.target_combo.addItems(unique_transforms)
|
|
|
|
# Restore previous selection if possible
|
|
index = self.target_combo.findText(current_text)
|
|
if index >= 0:
|
|
self.target_combo.setCurrentIndex(index)
|
|
|
|
def _select_target(self):
|
|
"""Select the current target mesh in the scene"""
|
|
target = self.target_combo.currentText()
|
|
if target:
|
|
cmds.select(target)
|
|
|
|
def _on_blendshape_changed(self, index):
|
|
"""Handle BlendShape combo box change"""
|
|
if index >= 0:
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
def _on_target_changed(self, index):
|
|
"""Handle target combo box change"""
|
|
if index >= 0:
|
|
target = self.target_combo.itemText(index)
|
|
# Select the target in the scene
|
|
cmds.select(target)
|
|
|
|
def _update_targets_tree(self):
|
|
"""Update the targets tree with current BlendShape information"""
|
|
self.targets_tree.clear()
|
|
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
return
|
|
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
# Get targets information
|
|
targets_info = blendshape_utils.get_targets_info(blendshape)
|
|
|
|
if targets_info:
|
|
# Add targets to tree
|
|
for target_name, target_info in targets_info.items():
|
|
weight = target_info.get("weight", 0.0)
|
|
status = target_info.get("status", "正常")
|
|
|
|
item = QtWidgets.QTreeWidgetItem(self.targets_tree, [
|
|
target_name,
|
|
str(weight),
|
|
status
|
|
])
|
|
|
|
# Create weight slider
|
|
slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
|
|
slider.setRange(0, 100)
|
|
slider.setValue(int(weight * 100))
|
|
slider.setProperty("target_name", target_name)
|
|
slider.valueChanged.connect(self._on_weight_changed)
|
|
|
|
self.targets_tree.setItemWidget(item, 1, slider)
|
|
except Exception as e:
|
|
print(f"更新目标树出错: {str(e)}")
|
|
|
|
def _on_target_selected(self, item, column):
|
|
"""Handle target tree item selection"""
|
|
target_name = item.text(0)
|
|
blendshape = self.blendshape_combo.currentText()
|
|
|
|
if blendshape and target_name:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
# Select the target in the scene if it exists as a separate mesh
|
|
target_mesh = blendshape_utils.get_target_mesh(blendshape, target_name)
|
|
if target_mesh:
|
|
cmds.select(target_mesh)
|
|
except Exception:
|
|
pass
|
|
|
|
def _on_target_double_clicked(self, item, column):
|
|
"""Handle target tree item double click"""
|
|
if column == 0: # Target name column
|
|
self._edit_target()
|
|
|
|
def _on_weight_changed(self, value):
|
|
"""Handle weight slider change"""
|
|
sender = self.sender()
|
|
if not sender:
|
|
return
|
|
|
|
target_name = sender.property("target_name")
|
|
blendshape = self.blendshape_combo.currentText()
|
|
|
|
if blendshape and target_name:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
weight = value / 100.0
|
|
blendshape_utils.set_target_weight(blendshape, target_name, weight)
|
|
except Exception as e:
|
|
print(f"设置目标权重出错: {str(e)}")
|
|
|
|
def _add_target(self):
|
|
"""Add a new target to the BlendShape"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
# Get selected mesh
|
|
selection = cmds.ls(selection=True)
|
|
if not selection:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"请先选择一个网格模型作为目标"
|
|
)
|
|
return
|
|
|
|
target_mesh = selection[0]
|
|
|
|
# Get a name for the new target
|
|
name, ok = QtWidgets.QInputDialog.getText(
|
|
self,
|
|
"添加目标",
|
|
"目标名称:",
|
|
QtWidgets.QLineEdit.Normal,
|
|
f"Target_{len(cmds.blendShape(blendshape, query=True, target=True) or [])}"
|
|
)
|
|
|
|
if ok and name:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.add_target(blendshape, target_mesh, name)
|
|
if result:
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"成功添加目标: {name}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"添加目标失败: {name}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"添加目标出错: {str(e)}"
|
|
)
|
|
|
|
def _remove_target(self):
|
|
"""Remove a target from the BlendShape"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
# Get selected target
|
|
selected_items = self.targets_tree.selectedItems()
|
|
if not selected_items:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"请先在目标列表中选择一个目标"
|
|
)
|
|
return
|
|
|
|
target_name = selected_items[0].text(0)
|
|
|
|
# Confirm removal
|
|
result = QtWidgets.QMessageBox.question(
|
|
self,
|
|
"确认",
|
|
f"确定要移除目标 {target_name} 吗?",
|
|
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
|
|
)
|
|
|
|
if result == QtWidgets.QMessageBox.Yes:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.remove_target(blendshape, target_name)
|
|
if result:
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"成功移除目标: {target_name}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"移除目标失败: {target_name}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"移除目标出错: {str(e)}"
|
|
)
|
|
|
|
def _edit_target(self):
|
|
"""Edit a target in the BlendShape"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
# Get selected target
|
|
selected_items = self.targets_tree.selectedItems()
|
|
if not selected_items:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"请先在目标列表中选择一个目标"
|
|
)
|
|
return
|
|
|
|
target_name = selected_items[0].text(0)
|
|
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.edit_target(blendshape, target_name)
|
|
if result:
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"成功编辑目标: {target_name}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"编辑目标失败: {target_name}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"编辑目标出错: {str(e)}"
|
|
)
|
|
|
|
def _duplicate_target(self):
|
|
"""Duplicate a target in the BlendShape"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
# Get selected target
|
|
selected_items = self.targets_tree.selectedItems()
|
|
if not selected_items:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"请先在目标列表中选择一个目标"
|
|
)
|
|
return
|
|
|
|
target_name = selected_items[0].text(0)
|
|
|
|
# Get a name for the new target
|
|
new_name, ok = QtWidgets.QInputDialog.getText(
|
|
self,
|
|
"复制目标",
|
|
"新目标名称:",
|
|
QtWidgets.QLineEdit.Normal,
|
|
f"{target_name}_copy"
|
|
)
|
|
|
|
if ok and new_name:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.duplicate_target(blendshape, target_name, new_name)
|
|
if result:
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"成功复制目标 {target_name} 为 {new_name}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"复制目标失败: {target_name}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"复制目标出错: {str(e)}"
|
|
)
|
|
|
|
def _export_all_targets(self):
|
|
"""Export all targets to separate files"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
dir_path = QtWidgets.QFileDialog.getExistingDirectory(
|
|
self,
|
|
"选择导出目录",
|
|
""
|
|
)
|
|
|
|
if dir_path:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.export_all_targets(blendshape, dir_path)
|
|
if result:
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"Successfully exported all targets to: {dir_path}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"Failed to export targets: {dir_path}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"Error exporting targets: {str(e)}"
|
|
)
|
|
|
|
def _import_targets(self):
|
|
"""Import targets from files"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
file_paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
|
|
self,
|
|
"Select Target Files",
|
|
"",
|
|
"Maya Files (*.ma *.mb);;OBJ Files (*.obj);;All Files (*.*)"
|
|
)
|
|
|
|
if file_paths:
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.import_targets(blendshape, file_paths)
|
|
if result:
|
|
# Update targets tree
|
|
self._update_targets_tree()
|
|
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"Successfully imported {len(file_paths)} targets"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Failed to import targets"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"Error importing targets: {str(e)}"
|
|
)
|
|
|
|
def _connect_to_rig(self):
|
|
"""Connect BlendShape targets to rig controls"""
|
|
blendshape = self.blendshape_combo.currentText()
|
|
if not blendshape:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a BlendShape node first"
|
|
)
|
|
return
|
|
|
|
# Get the rig control
|
|
selection = cmds.ls(selection=True)
|
|
if not selection:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
"Please select a controller first"
|
|
)
|
|
return
|
|
|
|
control = selection[0]
|
|
|
|
# Import the blendshape utils here to avoid circular imports
|
|
from utils.blendshape_utils import BlendShapeUtils
|
|
blendshape_utils = BlendShapeUtils()
|
|
|
|
try:
|
|
result = blendshape_utils.connect_to_rig(blendshape, control)
|
|
if result:
|
|
QtWidgets.QMessageBox.information(
|
|
self,
|
|
"Success",
|
|
f"Successfully connected {blendshape} to {control}"
|
|
)
|
|
else:
|
|
QtWidgets.QMessageBox.warning(
|
|
self,
|
|
"Warning",
|
|
f"Failed to connect to controller: {control}"
|
|
)
|
|
except Exception as e:
|
|
QtWidgets.QMessageBox.critical(
|
|
self,
|
|
"Error",
|
|
f"Error connecting to controller: {str(e)}"
|
|
)
|