most bulletproof and safe way of adding custom attributes?


#1

I’m making a Mari / Maya bridge that is working nicely but I need to start thinking about stable ways of tracking objects to receive updates that will work across work sessions (ie: I can’t use global variables or easy stuff like object names, which is how I have it set up now). So I have external text files and Python is reading values from there, which is half the battle but now I need to think about the most effective way to add locked attribute tags to objects to track them within Maya. I am wary of doing this wrong since I don’t like the idea of putting things in people’s scenes. Does someone have a good tutorial or tips on how to best handle this stuff? It’s safe to assume that people aren’t going to go completely crazy changing grouping, topology and things since, at that point, they are basically making it too different from what they sent to Mari from Maya to paint, so I can keep this pretty simple, I think

So basically I think I’m looking to:

  • add a numerical/time-based index to an object.
  • that index is written to our external index file
  • mesh is sent to Mari at the same time
  • painting done
  • Mari script sends baked paint textures back to Maya, consulting our index and project directory (I’ll have to find a way to embed similar index data in Mari scenes)
  • Maya applies or updates textures on our indexed object.

I’m good for all of that except how to add attributes and select objects with those attributes.


#2

You could try something like:

import pymel.core as pm

pCube = pm.polyCube()[0]

pCube.addAttr('timeBasedAttr', keyable=True, attributeType='float', min=0.0, max=1.0)

for obj in pm.ls():
    if obj.hasAttr('timeBasedAttr'): #this would return True
        doStuffToObjectsWithAttr(obj)


#3

There is a way add attribute to all nodes of type ‘mesh’ if you like with the “nodeExtensions” command. But I suppose you want these attributes not for all objects, but only for a few ones. So the addAttr() approach seems to be appropriate.

Querying the attributes can made a bit more simple with:

attributes = pm.ls("*.timeBaseAttr")

Here you get pymel Attribute nodes with can be evaluated with:

attributes = pm.ls("*.timeBaseAttr")
for att in attributes:
	value = att.get()


#4

thanks - I’ll look into this and get back with any questions


#5

Querying the attributes can made a bit more simple with:

attributes = pm.ls("*.timeBaseAttr")

The ls() command never ceases to amaze me with functionality…thanks for that…


#6

ok - for the life of me, I can’t find the syntax or examples for attaching the actual string to the object. I want something like this unique name:

timeStamper = cmds.date(format=‘YYYYYYMMDDhhmmss’)

pm.addAttr(shortName=‘mID’, longName=‘mariMeBridgeID’, attributeType=‘string’, defaultValue=(cmds.date(format=‘YYYYYYMMDDhhmmss’)+mySelection[0]) )

but defaultValue wants a float.


#7

as i understood you want:

be able to mark some specific nodes in the scene (not all scene) by a custom identifier

the mark has to be invisible as much as possible for scene owners

the mark has to be locked for a general user

correct?

now there are my questions:

where is a moment when you set this mark (manual setup, on scene save, on object created, on export, etc.)?

does the mark has to migrate with its owner object from scene to scene (referencing, merging, etc.)?

does the mark has to be cloned with the object or has to be unique unique?

technically i see only two rival solutions:
#1 hidden and locked attribute
#2 metadata

i would prefer the second one. because it’s fancy :slight_smile:


#8

ya, that’s exactly what I’m looking to do.

where is a moment when you set this mark (manual setup, on scene save, on object created, on export, etc.)?

I export the selected geometry to Mari in a for loop so I would tag it within the scene then.

does the mark has to migrate with its owner object from scene to scene (referencing, merging, etc.)?

I’m not too concerned with making it completely portable but if it’s easy to do, then yes.

does the mark has to be cloned with the object or has to be unique unique?

not cloned. It’s safe to assume they are going to update the scene as it existed when it was sent to Mari, and stay in that static state. New geometry can be sent independently so that will get different tags.

technically i see only two rival solutions:
#1 hidden and locked attribute
#2 metadata

whatever’s clean and/or easy. I just don’t want it getting in people’s way, so invisible tags might be nice. I don’t want people thinking that I’ve created a dependency on the script, like a plug-in might.


#9

boring… so it’s about an attribute. (sure you can make an attribute hidden and locked for lamer)

I don’t want people thinking that I’ve created a dependency on the script, like a plug-in might.

exactly. every crap … sorry marks, tags… left by developer has to be at least invisible for the user. :wink: that’s my point too


#10

all attributes are transparent. there is no problem to find them all (and easily kick them all out).
the metadata is a real problem to clean. honestly i couldn’t find an easy way to collect all metadata in scene if you don’t know exactly what you are looking for.


#11

so what’s the command to add one of those strings to my object? I can’t find an example of string attribute use in the docs for pymel or py


#12

with attributes everything is simple and straightforward.
guys already showed you all. there are samples in mel/python help for addAttr command. to lock an existed attributes use setAttr command. to find all attributes by name (as shown already above) ls command. i don’t have maya around… so i don’t want to confuse you by posting ‘kinda’ working code but… well… i wil try to do it blindffold:

cmds.addAttr(nodename, longName='dateidorwhatever', shortName='did', attributeType='string', hidden=1)
cmds.setAttr(nodename+".did", dateinyourformat, type='string', lock=1)

sorry if i’ve missed anything


#13

thanks but I get an error:

NameError: name ‘nodename’ is not defined

if I use a string literal like ‘pSphere1’, I get:

RuntimeError: Type specified for new attribute is unknown.

Also, how would I go about editing the object later? If I wanted to add a new shader to the parent shape, how would I get it from knowing just the custom attribute name?


#14

ok … i don’t use it often and so can forget something.
here is it:

# just make a test node
 cmds.polyCube(name="dateCube")
 
 # add new attribute with specified name and make it hidded
 cmds.addAttr("dateCube", longName='dateID', shortName='did', dataType='string', hidden=1)
 
 # set attribute by its name and set it locked 
 cmds.setAttr("dateCube"+".did", cmds.date(format='MM.DD.YYYY hh:mm:ss'), type='string', lock=1)
 
 # check the value
 print (cmds.getAttr("dateCube.did"))
 
 # reset the value: unlock first, set new value, and lock it back
 cmds.setAttr("dateCube.did", lock=0)
 
 cmds.setAttr("dateCube.did", cmds.date(format='MM.DD.YYYY hh:mm:ss'), type='string', lock=1)
 print (cmds.getAttr("dateCube.did"))

#15

nodename is a placeholder variable that you would fill in with the name of the node you want to add your attribute to (as you’ve found by replacing it with a literal).

eg.


nodename = 'pSphere1'
// Now run the code.

The actual attribute type denisT specified wasn’t quite right. It was off the top of his head. If you read the addAttr command documentation, you’ll see the full list of attribute types (and examples). In this case you want to specify:


# Example data
nodename = 'pSphere1'
dateinyourformat = '201402111342' 
# Commands
cmds.addAttr(nodename, longName='dateidorwhatever', shortName='did', dataType='string', hidden=1)
cmds.setAttr(nodename+".did", dateinyourformat, type='string', lock=1)

Note that we changed the name of the flag from attributeType to dataType. See the documentation for the table on what flag combination to use for a given type.

Also, how would I go about editing the object later? If I wanted to add a new shader to the parent shape, how would I get it from knowing just the custom attribute name?

I don’t really understand what you’re asking here. You’ve set the attribute on the node and you want to go back and find all nodes with that custom attribute on it? Is that correct?

If so, a braindead solution would be, search through all the nodes in the scene and do an attrExists check for your custom attribute. This can be slow if you have a lot of nodes in your scene. It would help if you could filter that search by knowing what types of nodes you might potentially add this custom attribute to (eg. only transforms, only DAG nodes?, etc.)


#16

ok - thanks for the help everyone. I am obviously new to this custom attribute stuff but I now have the tag and the check procedures:

def applyUniqueIDToMesh(meshname):
	##timeStamper = cmds.date(format='YYYYYYMMDDhhmmss')
	identifierStamp = cmds.date(format='YYYYYYMMDDhhmmss')
	cmds.addAttr(meshname, longName='dateidorwhatever', shortName='did', dataType='string', hidden=0)
	cmds.setAttr(meshname+".did", (identifierStamp+meshname), type='string', lock=1)

def checkForMeshID(meshname):
	test=mel.eval('attributeExists "dateidorwhatever" '+meshname+'')
	if (test):
		eyeDee = cmds.getAttr(meshname+".did")
		##print eyeDee

I’ll hide the tag later when it’s ready for consumption

I’m not worried about it being slow to evaluate since it’s going to be assumed you’re doing this at the asset building phase (smaller scenes) and not with giant finished scenes.


#17

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.