# ARTv2 技术细节文档 **最后更新**: 2024-12-07 --- ## 📐 架构概述 ### 模块结构 ``` ARTv2/ ├── Core/ │ ├── Scripts/ │ │ ├── System/ # 核心系统模块 │ │ ├── RigModules/ # 骨骼模块 │ │ ├── Interfaces/ # UI 界面 │ │ └── ThirdParty/ # 第三方库 │ ├── JointMover/ # 骨骼移动器 │ └── Pickers/ # 动画选择器 └── plug-ins/ └── ARTv2.py # Maya 插件入口 ``` --- ## 🔧 Python 3 迁移技术细节 ### 1. 关键语法变更 #### reload() → importlib.reload() ```python # Python 2 reload(module) # Python 3 import importlib importlib.reload(module) ``` **影响文件**: 50+ 个模块 **修复方法**: 添加 `import importlib` 并替换所有 `reload()` 调用 #### print 语句 → print() 函数 ```python # Python 2 print "message" print "format %s" % value # Python 3 print("message") print(f"format {value}") # 使用 f-string ``` **影响文件**: 40+ 个模块 **修复方法**: 统一使用 f-string 格式化 #### xrange() → range() ```python # Python 2 for i in xrange(100): pass # Python 3 for i in range(100): pass ``` **影响文件**: riggingUtils.py 及相关模块 **修复方法**: 全局替换 `xrange` 为 `range` #### except 语句 ```python # Python 2 except: pass # Python 3 except Exception: pass ``` **影响文件**: 100+ 处 **修复方法**: 明确捕获 Exception 类型 --- ## 🏗️ 核心系统架构 ### 1. ART_RigModule 基类 **文件**: `System/ART_RigModule.py` **职责**: - 所有骨骼模块的基类 - 提供通用的骨骼创建方法 - 管理模块网络节点 - 处理模块间的父子关系 **关键方法**: ```python class ART_RigModule: def __init__(self, moduleType, moduleName, userSpecifiedName) def addAttributes(self) def skeletonSettings_UI(self, name) def addJointMoverToOutliner(self) def updateSettingsUI(self) def applyModuleChanges(self, moduleInst) def resetSettings(self) def pinModule(self, state) def skinProxyGeo(self) def buildRig(self) # 已修复异常变量作用域问题 def aimMode_Setup(self, state) def returnNetworkNode(self) # 已修复 UnboundLocalError ``` **重要修复 1 - UnboundLocalError**: ```python @property def returnNetworkNode(self): networkNode = None # 初始化变量 networkNodes = cmds.ls(type="network") for node in networkNodes: attrs = cmds.listAttr(node) if "moduleName" in attrs: if cmds.getAttr(node + ".moduleName") == self.name: networkNode = node break # 找到后退出 return networkNode ``` **重要修复 2 - 异常变量作用域问题**: ```python def buildRig(self, textEdit, uiInst): """构建模块骨骼""" currentNodes = cmds.ls("*", long=True) successfulBuild = True errorMessage = "" buildException = None # ✅ 初始化异常变量 try: self.buildRigCustom(textEdit, uiInst) except Exception as e: successfulBuild = False buildException = e # ✅ 保存异常 errorMessage = str(traceback.format_exc()) # ... 处理新节点 ... if not successfulBuild: print(f"Build Rig Failed: {str(buildException)}") # ✅ 使用保存的异常 print(errorMessage) if buildException: raise buildException # ✅ 重新抛出供上层捕获 ``` **修复影响**: - ✅ 修复了 Root 模块构建失败 - ✅ 修复了 Torso 模块构建失败 - ✅ 修复了"控制器消失"的问题 - ✅ 确保异常能被 ART_BuildProgressUI 正确捕获和处理 ``` --- ### 2. 骨骼构建流程 **文件**: `Interfaces/ART_BuildProgressUI.py` #### 构建阶段 ``` 1. 预检查 (preflightCheck) ├─ 检查角色存在 ├─ 检查模块存在 ├─ 检查骨骼状态 └─ 检查场景状态 2. 设置绑定姿态 (setRigPose) ├─ 导出皮肤权重 └─ 删除旧骨骼 3. 重建骨骼 (rebuildSkeleton) ├─ 安全删除根骨骼 └─ 重新构建骨骼层级 4. 构建绑定 (buildRigs) ├─ 创建驱动骨架 ├─ 逐个构建模块绑定 └─ 设置绑定层级 5. 导入权重 (importWeights) ├─ 检查网格存在 ├─ 导入皮肤权重 └─ 生成导入摘要 6. 后处理 (postScript) └─ 清理和优化 ``` #### 预检查机制 ```python def preflightCheck(self): """构建前验证""" errors = [] warnings = [] # 检查 1: 角色存在 if not cmds.objExists("ART_RIG_ROOT"): errors.append("No character found") # 检查 2: 模块存在 modules = utils.returnRigModules() if not modules: errors.append("No modules found") # 检查 3: 骨骼存在 if not cmds.objExists("root"): errors.append("Skeleton not built") # 检查 4: 场景状态 state = cmds.getAttr("ART_RIG_ROOT.state") if state == 0: errors.append("Character in Skeleton Placement mode") return errors, warnings ``` #### 错误恢复机制 ```python def buildRigs(self): """构建绑定,支持错误恢复""" failed_modules = [] for inst in self.mainUI.moduleInstances: try: inst.buildRig() self.infoText.append(f"✓ Built: {inst.name}") except Exception as e: failed_modules.append((inst.name, str(e))) self.infoText.setTextColor(QtGui.QColor(255, 100, 100)) self.infoText.append(f"✗ Failed: {inst.name}") self.infoText.append(f" Error: {e}") self.errors += 1 # 显示摘要 if failed_modules: self.infoText.append(f"\nBuild Summary:") self.infoText.append(f" Success: {len(self.mainUI.moduleInstances) - len(failed_modules)}") self.infoText.append(f" Failed: {len(failed_modules)}") ``` --- ### 3. 权重管理系统 **文件**: `System/riggingUtils.py` #### 导出权重 ```python def export_skin_weights(filePath, mesh): """导出皮肤权重到文件""" skinCluster = mel.eval(f'findRelatedSkinCluster("{mesh}")') if not skinCluster: return False # 获取权重数据 weights = cmds.getAttr(f"{skinCluster}.weightList[*].weights[*]") # 保存到文件 with open(filePath, 'w') as f: json.dump(weights, f) return True ``` #### 导入权重(带错误处理) ```python def import_skin_weights(filePath, mesh, worldSpace=True): """导入皮肤权重,带完整错误处理""" # 检查网格存在 if not cmds.objExists(mesh): raise RuntimeError(f"Mesh not found: {mesh}") # 检查文件存在 if not os.path.exists(filePath): raise RuntimeError(f"Weight file not found: {filePath}") # 读取权重数据 try: with open(filePath, 'r') as f: weights = json.load(f) except Exception as e: raise RuntimeError(f"Failed to read weight file: {e}") # 应用权重 try: skinCluster = mel.eval(f'findRelatedSkinCluster("{mesh}")') if not skinCluster: raise RuntimeError("No skin cluster found") cmds.setAttr(f"{skinCluster}.weightList[*].weights[*]", *weights) except Exception as e: raise RuntimeError(f"Failed to apply weights: {e}") return True ``` --- ## 🎨 UI 系统架构 ### Qt 兼容层 **文件**: `ThirdParty/Qt/__init__.py` ```python # 自动检测并导入正确的 Qt 版本 try: from PySide2 import QtCore, QtGui, QtWidgets from PySide2.QtCore import Signal, Slot except ImportError: from PySide import QtCore, QtGui QtWidgets = QtGui from PySide.QtCore import Signal, Slot ``` ### 主界面结构 **文件**: `Interfaces/ART_RigCreatorUI.py` ```python class ART_RigCreatorUI(QtWidgets.QMainWindow): def __init__(self, parent=None): super(ART_RigCreatorUI, self).__init__(parent) # 设置窗口属性 self.setWindowTitle("ARTv2 - Rig Creator") self.setObjectName("ART_RigCreatorUI") # 构建 UI self.buildUI() # 连接信号 self.connectSignals() def buildUI(self): """构建主界面""" # 创建中央部件 centralWidget = QtWidgets.QWidget() self.setCentralWidget(centralWidget) # 创建布局 mainLayout = QtWidgets.QVBoxLayout(centralWidget) # 添加工具栏 self.createToolbar() # 添加模块列表 self.createModuleList() # 添加属性编辑器 self.createAttributeEditor() ``` --- ## 🔒 安全机制 ### 1. 节点锁定处理 ```python def unlock_node_hierarchy(node): """递归解锁节点及其子节点""" if cmds.objExists(node): # 解锁节点 if cmds.lockNode(node, q=True, lock=True)[0]: cmds.lockNode(node, lock=False) # 递归解锁子节点 children = cmds.listRelatives(node, children=True, fullPath=True) or [] for child in children: unlock_node_hierarchy(child) ``` ### 2. 安全删除 ```python def safe_delete(node): """安全删除节点""" try: # 解锁节点层级 unlock_node_hierarchy(node) # 删除节点 if cmds.objExists(node): cmds.delete(node) return True except Exception as e: cmds.warning(f"Failed to delete {node}: {e}") return False ``` --- ## 📊 性能优化 ### 1. 循环优化 ```python # 优化前 for node in networkNodes: if condition: networkNode = node # 继续循环所有节点 # 优化后 for node in networkNodes: if condition: networkNode = node break # 找到后立即退出 ``` ### 2. 批量操作 ```python # 优化前 - 逐个设置属性 for obj in objects: cmds.setAttr(f"{obj}.attr", value) # 优化后 - 批量设置 cmds.setAttr([f"{obj}.attr" for obj in objects], value) ``` --- ## 🧪 测试策略 ### 单元测试示例 ```python def test_returnNetworkNode(): """测试 returnNetworkNode 方法""" # 创建测试模块 module = ART_RigModule("test", "test", "test") # 测试:节点不存在 result = module.returnNetworkNode assert result is None, "Should return None when node not found" # 创建网络节点 node = cmds.createNode("network") cmds.addAttr(node, ln="moduleName", dt="string") cmds.setAttr(f"{node}.moduleName", "test", type="string") # 测试:节点存在 result = module.returnNetworkNode assert result == node, "Should return the network node" # 清理 cmds.delete(node) ``` --- ## 🔍 调试技巧 ### 1. 启用详细日志 ```python import logging # 配置日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('ARTv2') logger.debug("Debug message") ``` ### 2. Maya 脚本编辑器输出 ```python def debug_print(msg): """在 Maya 脚本编辑器中打印调试信息""" cmds.warning(f"[ARTv2 DEBUG] {msg}") ``` --- ## 📚 API 参考 ### 核心类 #### ART_RigModule - `addAttributes()` - 添加模块属性 - `buildRig()` - 构建模块绑定 - `returnNetworkNode` - 获取网络节点 #### ART_BuildProgressUI - `preflightCheck()` - 预检查 - `buildRigs()` - 构建绑定 - `importWeights()` - 导入权重 ### 工具函数 #### utils.py - `returnRigModules()` - 获取所有模块 - `returnFriendlyPath()` - 转换路径格式 #### riggingUtils.py - `export_skin_weights()` - 导出权重 - `import_skin_weights()` - 导入权重 --- ## 🔗 依赖关系 ``` ARTv2.py (插件入口) ├─ System/utils.py ├─ System/interfaceUtils.py ├─ Interfaces/ART_RigCreatorUI.py │ ├─ System/ART_RigModule.py │ ├─ RigModules/ART_*.py │ └─ Interfaces/ART_BuildProgressUI.py └─ ThirdParty/Qt/ ``` --- **维护者**: Cascade AI **技术支持**: GitHub Issues