如何在Maya属性编辑器AE模板中创建父子联动滑块组
Maya Attribute Editor 父子滑块联动适配方案
你现有独立窗口的逻辑不能直接搬到AE里,核心原因是AE的UI生命周期是Maya托管的——切换节点、刷新属性时AE会自动销毁重建控件,你存在类实例里的slider/parentval/offsetval字典会全部失效,而且你目前删除重建子滑块的逻辑在AE里会导致UI闪烁、引用错乱,必须先调整核心逻辑再做适配。
第一步:把状态存储从UI迁移到节点属性
UI只做值的输入输出入口,所有联动状态全部存在节点自定义属性上,从根源上避免UI重建导致的状态丢失:
- 给控制节点新增3个自定义属性:
bothArmsUp:父滑块值,范围-100~100,默认0,控制双臂同时上抬leftArmOffset:左臂相对父滑块的偏移值,范围-200~200,默认0rightArmOffset:右臂相对父滑块的偏移值,范围-200~200,默认0
- 骨骼驱动逻辑直接用表达式绑定,不要写在UI回调里,这样即使用户在通道盒改属性,联动也能正常生效
import pymel.core as pm # 选中你的总控制器节点后运行 ctrl = pm.selected()[0] # 批量加自定义属性 for attr_name, min_val, max_val, default_val in [ ("bothArmsUp", -100, 100, 0), ("leftArmOffset", -200, 200, 0), ("rightArmOffset", -200, 200, 0) ]: if not pm.attributeQuery(attr_name, node=ctrl, exists=True): pm.addAttr(ctrl, longName=attr_name, at='double', min=min_val, max=max_val, dv=default_val, k=True) # 绑定骨骼驱动表达式 pm.expression( s=f"lShldrBend.rotateZ = {ctrl}.bothArmsUp + {ctrl}.leftArmOffset", ae=True, uc='all' ) pm.expression( s=f"rShldrBend.rotateZ = -({ctrl}.bothArmsUp + {ctrl}.rightArmOffset)", ae=True, uc='all' )
第二步:编写AE滑块组逻辑
完全删掉原来删除重建子滑块的逻辑,子滑块值变化时只更新对应偏移属性,父滑块值变化时只需要刷新子滑块的显示值即可,不需要重建控件。
注意AE回调不要绑定类实例引用,直接传节点名字符串,避免上下文丢失报错:
import maya.cmds as cmds def build_arm_slider_group(node_name): # 父滑块值变化回调 def parent_slider_changed(*args): parent_val = cmds.floatSliderGrp(parent_slider, q=True, v=True) cmds.setAttr(f"{node_name}.bothArmsUp", parent_val) # 直接更新子滑块显示值,无需重建UI left_offset = cmds.getAttr(f"{node_name}.leftArmOffset") right_offset = cmds.getAttr(f"{node_name}.rightArmOffset") cmds.floatSliderGrp(left_slider, e=True, v=parent_val + left_offset) cmds.floatSliderGrp(right_slider, e=True, v=parent_val + right_offset) # 左子滑块值变化回调 def left_slider_changed(*args): current_val = cmds.floatSliderGrp(left_slider, q=True, v=True) parent_val = cmds.getAttr(f"{node_name}.bothArmsUp") cmds.setAttr(f"{node_name}.leftArmOffset", current_val - parent_val) # 右子滑块值变化回调 def right_slider_changed(*args): current_val = cmds.floatSliderGrp(right_slider, q=True, v=True) parent_val = cmds.getAttr(f"{node_name}.bothArmsUp") cmds.setAttr(f"{node_name}.rightArmOffset", current_val - parent_val) # 按AE默认样式建控件 cmds.frameLayout(label="Arm Pose Control", collapsable=True, mw=5, mh=5) parent_slider = cmds.floatSliderGrp( l='Both Arms Up-Down', f=True, min=-100.0, max=100.0, v=cmds.getAttr(f"{node_name}.bothArmsUp"), cc=parent_slider_changed, dc=parent_slider_changed ) left_slider = cmds.floatSliderGrp( l='Left Arm Up-Down', f=True, min=-100.0, max=100.0, v=cmds.getAttr(f"{node_name}.bothArmsUp") + cmds.getAttr(f"{node_name}.leftArmOffset"), cc=left_slider_changed, dc=left_slider_changed ) right_slider = cmds.floatSliderGrp( l='Right Arm Up-Down', f=True, min=-100.0, max=100.0, v=cmds.getAttr(f"{node_name}.bothArmsUp") + cmds.getAttr(f"{node_name}.rightArmOffset"), cc=right_slider_changed, dc=right_slider_changed ) cmds.setParent('..') # 注册AE自定义内容,Maya启动时运行一次即可 def register_ae_arm_control(): cmds.callbacks( hook='AETemplateCustomContent', addCallback=build_arm_slider_group, owner='armRigSliderGroup', nodeType='transform' # 替换为你实际要显示滑块组的节点类型,自定义节点填对应类型名 ) register_ae_arm_control()
适配注意事项
- 不要在AE逻辑里持有UI控件的全局/类实例长期引用,AE刷新时旧控件会被销毁,残留引用会导致报错
- 尽量不要用lambda绑定带
self的实例方法做AE回调,AE执行回调时的上下文和独立窗口不同,很容易出现变量找不到的问题 - 如果需要做更规范的自定义节点AE模板,可以继承
maya.app.general.mayaMixin.MayaTemplateMixin写标准模板类,上述callback方案足够快速实现滑块组需求,代码量更小 - 你原来删除重建子滑块的逻辑完全可以废弃,直接编辑现有控件的值即可,既不会出现UI闪烁,性能也更好
内容的提问来源于stack exchange,提问作者citybybay1
相关产品推荐
相关产品推荐

