MaxPlus: Add Custom Attributes to Attribute Holder



I’m trying to figure out how to add custom attributes to an Attribute Holder modifier and am coming up empty. I’d like to stay in MaxPlus Python world but if necessary can go to pymxs but just not sure what the code would look like for that. Just diving into MaxPlus on a project and I’ve spent most of my time in Maya / Softimage land. Any help is appreciated.

Code so far:

import MaxPlus

obj = MaxPlus.Factory.CreateHelperObject(MaxPlus.ClassIds.Point)
node = MaxPlus.Factory.CreateNode(obj, 'myNode')

attrHolder = MaxPlus.Factory.CreateObjectModifier(MaxPlus.ClassIds.EmptyModifier)

# How do I create & add custom attributes to the attrHolder?


Not happening with MaxPlus as far as I’m aware, unless something changed since 2014/2015.

“The attriibute def itself is maxscript and can contain thing like rollouts etc and dealing with that would not be possible from the Python API” - or something to that effect, courtesy of Kevin Vandecar, ADN.

In a multi dcc/fbx world Autodesk really should be providing high level functions to add, remove and update custom attributes. Since they do not, I ended up creating functions that parse the definiting string and hopes to modify the correct portions of it… It’s worked so far, but it’s got to be some of then dirtiest code I’ve written in a while (maybe that’s why they don’t want to do it).

If you figure a nice way to add them, let us know!


Haven’t found one yet either unfortunately. It’s so weird because I can find and pull info on them via MaxPlus but I can’t actually create them.

At this point I’m going to end up using pymxs if I ever find out how to do it that way as well. Probably just going to evaluate a Max Script code block unless someone has some examples of using pymxs which I would be eternally grateful for as well.


Here is a code snippet of how to query and print out the custom attribute containers and parameter info

import MaxPlus

node = MaxPlus.SelectionManager.GetNodes()[0]

# Add Attr Holder to selected object
# attrHolder = MaxPlus.Factory.CreateObjectModifier(MaxPlus.ClassIds.EmptyModifier)
# attrHolder.SetName(MaxPlus.WStr('myObjAttrs'))
# node.AddModifier(attrHolder)

# NOTE: You'll have to use the Animation > Parameter Editor OR MaxScript to add parameters

# Find Modifier Programmatically
mod = None
for i in xrange(node.NumModifiers):
	currMod = node.GetModifier(i)
	if currMod.GetName() == 'myObjAttrs':
		mod = currMod
# Get Attr Containers and Param Blocks
for i in xrange(len(mod.GetCustomAttributeContainer())):
	attrCntr = mod.GetCustomAttributeContainer()[i]
	print attrCntr.GetName()
	paramBlock = attrCntr.GetParameterBlock()
	for i in range(paramBlock.NumParameters):
		param = paramBlock.GetItem(i)
		paramName = param.GetName()
		paramValue = param.Value
		paramType = param.GetParamType()
		print '- {0} : value({1}) : type({2})'.format(paramName, paramValue, paramType)


The problem is that you can’t ASSIGN a new custom attribute container using MaxPlus.

I read/write info through MaxPlus but assign custom attributes by evaluating a maxscript string command. It’s annoying to say the least.

Note that the exception is a string array; you cannot write if your version is 2016 or lower. In 2017+ it’s fixed.

I’ve not yet tried pymxs in a production environment, but my real concern is trading values between pymxs and MaxPlus; I’m hoping they match but my fear is that other than base types they don’t.


[QUOTE=Claudio A;30652]
I’ve not yet tried pymxs in a production environment, but my real concern is trading values between pymxs and MaxPlus; I’m hoping they match but my fear is that other than base types they don’t.[/QUOTE]

They are definitely NOT the same types. You may be able to get them to be but I’ve found no info on how to accomplish that.

In the mean time, I’ve had to resort to pymxs to add custom attributes and parsing the attribute def and injecting new data to update it.

# Python Add Custom Attribute Def
import MaxPlus
import pymxs

rt = pymxs.runtime

node = MaxPlus.SelectionManager.GetNodes()[0]

# Find Modifier Programmatically
mod = None
for i in xrange(node.NumModifiers):
	currMod = node.GetModifier(i)
	if currMod.GetName() == 'myAttrHolder':
		mod = currMod

attrDef = """
custAttrDef=attributes testAttrib 
	parameters main rollout:{0}Rollout
        -- Param Def Begin
        -- Param Def End

        rollout {0}Rollout "{0}"
        -- Rollout Def Begin
        -- Rollout Def End

count = rt.CustAttributes.count(rt.myMod)

rt.CustAttributes.add(rt.myMod, rt.custAttrDef)
rt.CustAttributes.makeUnique(rt.myMod, count + 1)

Here is also a snippet for redefining the Custom Attr Def after the fact. I’ve left some bread crumbs to easily parse / insert new data

# Python Get / Set Custom Attr Def
import MaxPlus
import pymxs

rt = pymxs.runtime

count = rt.CustAttributes.count(rt.targetObj)

res = MaxPlus.FPValue()
dataDef = rt.CustAttributes.getDef(rt.targetObj.DisplayInfo_ArmSettings)
defSource = dataDef.source

defLines = defSource.splitlines()
for each in defLines:
	print each

endParamIndex = defLines.index('            -- Param Def End')

defLines.insert(endParamIndex, '			param1 type: #float ui:param1 default:10')

endRolloutIndex = defLines.index('            -- Rollout Def End')
defLines.insert(endRolloutIndex, '	\st	spinner param1 \"floatparam\" type: #float')

newDef = '

rt.CustAttributes.redefine(dataDef, newDef)


I would be pretty interested, if there is a way of adding, removing and updating custom attributes, without messing with the attribute definition string, regardless of language!

I looked over the MaxScript and MaxPlus docs on the subject, at it seems there could be something… But think I might be too burnt from previous attempts to have another go (plus I just started a weeks vacation, no way I’m booting Max).

If someone is in the know, just spill the beans already :slight_smile:


I got word from another list that one of the Max devs on ADN told them it wasn’t possible without editing the def.


In an effort to avoid spreading too much mis-information, I went back and looked over some correspondence with AD Support from 2014. Seem like I got some things mixed up.

  1. Case: I’m asking if it’s at all possible to work with custom attributes from Python. I get a reply that it will not be possible to expose these to Python, only to MaxScript and C++. I now understand that they assumed I was trying to work with attributes exposed from a C++ plug-in :):

  2. Case: I’m hitting a bug when trying to work with attributeDefinitions using MaxPlus and EvalMaxScript.

In that context I’m arguing that it’s too complex having to parse the attributeDefinition to simply set, get, create and delete custom attributes. Custom attributes are a stable of working with cross DCC and FBX (everything they pushed) and it would be resonable for Autodesk to provide some higher level API. Here is the meat of their reply:

“Scripted custom attributes are, well, scripted custom attributes. Their definition is created by MaxScript parsing the script string. The CA definition contains more than just the parameters it includes the rollout definition, various event handlers, and other MaxScript specific things.”
(That’s a no then?)

In summary, I don’t know what’s possible outside of working with the attribute definition directly and it’s been a few years since I dealt with it.

Sorry for any confusion caused


If you want to add a slider to a definition, the only way (other than the parameter editor) to do it is to edit its definition string, which is in mxs. This has been pretty annoying in mxs even before python was introduced, but I see AD’s point: it works as a full featured rollout, not just a set of fields. This is why some rigs can have fancy dotNet UIs in attribute holders without resorting to plugins.

If you want to add a new or copy definition (which will appear as a new rollout in the attribute holder) with all its sliders and such to another modifier, you may want to look into the custAttributes virtual array; working with it is as simple as adding or deleting items from an array. I have no idea if it’s usable from python, but it may be…