Maya Python Nodes – Compound Attributes

If you are anything like me, you can’t always find what you need in the Maya Python API documentation. Sometimes the information in the docs does not match what actually exists in the header files. Sometimes you cannot find sufficient examples of what you want to accomplish in Autodesk’s sample scripts. When you really hit the bottom and have to scour the dregs of the internet, you will typically find a dearth of information.

Because developing Maya Nodes using the Python API has consistently been an excruciating process for me, I want to save you the headache and post things here as I come across them. Most things turn out to be fairly simple, yet require a lot of hair-ripping to sort out. One issue I came across today was setting values for compound attributes.

Let’s say you have an output connection for your node that looks like this:

# Output
nAttr = OpenMaya.MFnNumericAttribute()
customNode.out = nAttr.create( "output", "out", OpenMaya.MFnNumericData.kDouble)
nAttr.setStorable(1)
nAttr.setWritable(1)

Using the examples that ship with the software, you can easily figure out how to set this attribute in your compute function. It will look something like this:

def compute(self,plug,dataBlock):
	if ( plug == customNode.out ):
		# Get the incoming data here
		# Perform some computations here
		# Our result is defined here somewhere as dResult
		# Output the data
		outputHandle = dataBlock.outputValue( customNode.out )
		outputHandle.setDouble( dResult )
		dataBlock.setClean( plug )

This is all well and good for those of you who want to make nodes that output a sine function. There is a good chance, however, that you will need to do something more complicated, like output values to control the translation of something. In such a case, you need a compound attribute—something like k3Float, k3Double, k3Int, etc. So you will set up your output like this:

# Output
nAttr = OpenMaya.MFnNumericAttribute()
customNode.out = nAttr.create( "output", "out", OpenMaya.MFnNumericData.k3Double)
nAttr.setStorable(1)
nAttr.setWritable(1)

A resulting compute function may look like this:

def compute(self,plug,dataBlock):
	if ( plug == customNode.out ):
		# Get the incoming data here
		# Perform some computations here
		# Our result is defined here somewhere as dResult[3]
		# Output the data
		outputHandle = dataBlock.outputValue( customNode.out )
		outputHandle.set3Double( dResult[0], dResult[1], dResult[2] )
		dataBlock.setClean( plug )

Now, this seems to make sense. Unfortunately, this will not always work. I did a little bit of troubleshooting and found that setting up the compute function like this will only work if you have multiple compound attributes as outgoing connections. In fact, if you have a single compound attribute as an outgoing connection, a compute function like this is not called at all. This is because, as far as I can tell, when there is a single outgoing compound attribute, it is not the plug. Rather, its children are plugs which will activate the compute function. In short, when you have a single outgoing compound attribute, you need to perform a test to see if the plug you are testing is a child of the actual compound attribute you are wanting to output. The solution therefore looks something like this:

def compute(self,plug,dataBlock):
	if ( plug == customNode.out or plug.parent() == customNode.out ):

Hopefully this will save you some headaches and troubleshooting. I know I would have liked to have seen some examples in the docs, and certainly at least hoped some other people on the internet had run into this problem. Here’s to all of you Maya Python users out there—I know you’re out there!

5 thoughts on “Maya Python Nodes – Compound Attributes”

  1. Another option would be to “flatten” your dependencies, like:
    attributeAffects(in, outX)
    attributeAffects(in, outY)
    attributeAffects(in, outZ)
    attributeAffects(in, out)
    But in a first look the solution you presented seems to be better.

  2. As a note, if your command contains multiple attributes, some of which are compound and some of which are not, you should include a further test. Otherwise, a call to plug.parent() on a non-compound plug will error out. For example:

    def compute(self, plug, dataBlock):
    	if ( plug == customNode.out or (plug.isChild() and plug.parent() == customNode.out )):
    		# Do cool stuff
  3. Sorry about that! Last time I redid the site I removed the links since it hasn’t been updated for several years now. I went ahead and re-added it in the Legacy section on the Tools page.

Leave a Reply

Your email address will not be published. Required fields are marked *