You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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,默认0
    • rightArmOffset:右臂相对父滑块的偏移值,范围-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 07:45:32