Multi Attributes Lose Values On Save

I have been adding some attributes to network nodes in scenes to track rigging data. When I use attributes like bool or short in their multi mode I am able to save values to different indices but on save if the starting values are equal to the default value they are not saved. On load the values start at the index of the first non-default value or it has no values if it was saved with ALL default values. I have been turning different addAttr flags on/off and saving to ascii but nothing seems to save my default value at the first index! Any suggestions would be appreciated.

We’ve had that exact problem at work, and the solution we came up with was to just save everything to a single string attribute as json.

There are some major drawbacks to that, like you can’t connect those attributes, and it can be very slow if you don’t add some kind of buffering mechanism, but it’s the best we came up with.


The other way to handle that is to set crazy defaults.
So for example on bool values, just use an int attr and use 0 and 1 with a default of something like 99999999.

1 Like

Thanks for the insight. I played around with weird default values but then had to add protections in my UI against those weird values because the attribute would push the default as it got indices removed. I think my strategy is going to be to not use any multi attributes and stick with the standard array attributes since they seem more predictable. The arrays aren’t human readable in the Attribute Editor but that will be okay for this usage.

1 Like

I went into a deep dive on this fairly recently and I have some info that could be helpful.

What you are discovering is that multi attributes are stored as sparse arrays. Maya does that to optimize memory usage, since technically there is no minimum or maximum number of values they can hold.

When you’re seeing plugs with default values not be loaded, it is technically true that they weren’t saved, but what you’re noticing is more that the plugs are not being physically created on the node. Their values can still be accessed even when they are not physically present. So in your case you’re seeing that the multi array doesn’t always start at index 0, but this is only partially true. The first physical index may not always be 0, but logical index 0 is always valid even if that plug is not physically present.

So you can do cmds.getAttr('myMultiAttr[1000]') or cmds.setAttr('myMultiAttr[1000], 1) and both will still work even if you don’t have 1000 physically present index plugs, since all logical indices of the plug technically are valid, but physically those plugs aren’t present until you access them.

In other words, it doesn’t actually matter that the plugs aren’t saved when their value matches the default, because when you call getAttr on them you get back the default value regardless.

All is well and good with that as long as you know exactly how many values you are meaning to store / access. If it’s a fixed number, just have your code read / write indices in that range. If it’s a variable number, you probably will have to create a second attribute to store the length of your multi attribute whenever you write it, so you know how many values to read the next time you want to read them.

My preference is to use separate attributes per value if I know I have a fixed number of attributes I’m trying store (e.g. myValue_1, myValue_2, myValue#), and to use the “store length as a separate attribute” method for value storage that requires more flexibility, like for storing arbitrary python lists.

1 Like

Very good breakdown Joshua. Most my variables don’t have a fixed length so I’m intrigued by the idea of a length attribute. One other complication I have run into is using attribute change callbacks in Maya. When setting a multi attribute you end up creating a lot of events for your UI to process. At least you do when you set them the dumb way like me and clear all previous values then set the new ones. I could be setting this data more intelligently but I value simplicity in my code in this instance. So for this reason I find using stringArray, doubleArray, floatArray, and Int32Array so much more sensible.
Thanks for contributing to the conversation. I see you’re around Seattle. If you ever want to come talk shop we do a Tech Art night every first Thursday at a bar in Seattle.

Hey I really appreciate the invite! Sadly though, I’m not actually in the Seattle area or I’d take you up on that.


By the way, you could solve the event problem by passing the maya callback through a simple delegate class which allows you to temporarily disable the event.

class EventDelegate:
    def __init__(self, callbackFn):
        self.enabled = True
        self.callbackFn = callbackFn

    def emit(self):
        if self.enabled:
            self.callbackFn()

    @contextmanager
    def suspended(self):
        self.enabled = False
        try:
            yield
        finally:
            self.enabled = True


##### Then in your UI code
delegate = EventDelegate(uiUpdateHandler)
# << bind maya event to delegate.emit

with delegate.suspended():
    # Set all your attributes while the event is suspended

# or you can trigger it explicitly
delegate.emit()

Or of course the array type attributes you mentioned are just simpler to deal with, though I personally still opt for multi attributes since they can be viewed in the attribute editor. For my use cases I need that debuggability since most values I’m storing are rig-system or export pipeline related.

1 Like