Naming Things Uniquely in Maya

This is a pretty simple question, but how do you ensure that objects get named uniquely in the tools you write?

I’ve written a few scripts to automate rig set-ups in Maya and the only thing I’ve come up with is to include a utility function like this in each script:


def nameUnique(baseName, aObject):
	tryName = baseName    
	sfx = 1
	while (mc.objExists(tryName)):
		tryName = baseName + "_" + str(sfx)
		sfx += 1
	
	return mc.rename(aObject, tryName)

I was curious if anyone knows of a simpler/better way to achieve this.

-Cheers

well, that’s kind of it – alas. Maya’s decision to use names as ids has lots of negative consequences, but there’s no easy way around it.

I think your example code may be redundant: if you just capture the rename, you’ll get back a numeric increment automatically (although with no suffix).

The truly sucky thing, however , is that Maya will mess with your objects as you move them around in the hierarchy. So, you can make an object, call it anything you like – but when if you parent it into a location where there’s an existing object with the same short name, your original will get renamed on you whether you like it or not.

Some rules for living in a universe where this happens:

#1: Don’t use names for anything important. Naming conventions don’t work as a primary tools in a world where stuff changes names just because somebody did something as simple as parenting or unparenting.

#2: always, always use the long form of names for anything longer than a 3 line script – and doubly always if you’re passing stuff between functions. ls(l=True) is and listRelatives (f=True) are your friends

#2: Use sets or selections as name-independent ways to keep track of things. If you need to work on a bunch of stuff whose names may be in flux due to renaming or hierarchy changes. If you have a selection, you can iterate over it getting names at the last second and removing items as you process them:


sel = cmds.ls(sl=True)
while sel:
    last = sel.pop()
    cmds.select(cmds.rename(last, 'renamed'), d=True)
    sel = cmd.ls(sl=True)

of course in that case, the names are all different so there’s no way to recover the end result. Sets is a nice way to handle that:


def rename_to_set(*objs):
    sel = cmds.ls(sl=True)
    result = cmds.sets(n='my_stuff') # use a variable - you don't know what name you'll get !!
    while sel:
        last = sel.pop()
        cmds.select(cmds.rename(last, 'renamed'), d=True)
        sel = cmd.ls(sl=True)
    return result

#3: To identify stuff permanently, attributes are a good way to go. You have control over the visibility and editability. Don’t want your users to change a ‘left_hand’ to ‘right_hand’ by accident? just lock the ‘.side’ attribute, etc. ls(o=True, “*.myAttrib”) is a simple cheap way of finding things again.

#4: Never assume stuff got the name you expected (your example shows you already get that part…) If you need to be sure something came out the way you wanted, always stick the result into a variable:


newNode = createNode('transform', name='newnode')

and you can verify it with ls:


newnode = cmds.createNode('transform', name= 'i_want_this_name')# item is selected after creation
assert 'i_want_this_name' in cmds.ls(sl=True) # raises if the name didn't turn out as expected

I had this problem as well when I wanted to make a rigged lig, and then make another one an another one and so on. Unique naming became an issue. This works - add # to the end of the name and maya will auto gen a unique number at the end. Then, capture it. Tell me if it works for you.

import maya.cmds as cmds 
import maya.mel as mel		


def stripPath(self, obj):
	# returns transform without |
	objToFilter = '%s%s%s' %('stringToStringArray("',obj,'", "|");')
	strippedName = mel.eval(objToFilter)

	return (strippedName[-1])


cmds.joint( p=(0, 8, 0), n='upLeg#')
self.my_upLeg = cmds.ls(sl=1)[0]
self.my_upLeg = self.stripPath(self.my_upLeg)

Thanks for the tips.

I really like your suggestion for using attributes Theodox. And the ability to grab everything quickly with ls(o=True, “*.myAttrib”) is pure brilliance!

I did a quick test and it looks like rename doesn’t automatically increment names unless the objects are on the same level in the scene’s hierarchy–or you stick # to the end of the new name like bergj says. So, if you have two groups at the root level, one named ‘group1’ the other named ‘null1’ and you call rename(‘null1’, ‘group1’) it’ll rename ‘null1’ to ‘group2’, but if you have null1 under the root and group1 parented to another node, then calling rename(‘null1’, ‘group1’) changes ‘null1’ to ‘group1’.

Bergj, I ran the code you provided and it did work. I follow it for the most part but I’m not quite getting the line: objToFilter = ‘%s%s%s’ %(‘stringToStringArray("’,obj,’", “|”);’) Would you mind going into a bit more detail about what it’s doing?

Also, I never knew about using # in rename. That’s really cool, and it’s a lot easier than what I was doing before.
It does cause a bit of a problem in my Python editor though, since # is reserved for single line comments, it looks like I’m commenting out the ends of the rename statement. Does anyone know of a way to escape the # character to stop that annoying syntax highlighting?

Thanks again for the help!

[QUOTE=JonStrong;19085]I’m not quite getting the line: objToFilter = ‘%s%s%s’ %(‘stringToStringArray("’,obj,‘", “|”);’) Would you mind going into a bit more detail about what it’s doing?

[/QUOTE]

I’m splitting the | char from the obj string which you can get if the object is in a hierarchy, but with mel. Its the same as obj.split(‘|’)

[QUOTE=JonStrong;19085]
Also, I never knew about using # in rename. That’s really cool, and it’s a lot easier than what I was doing before.
It does cause a bit of a problem in my Python editor though, since # is reserved for single line comments, it looks like I’m commenting out the ends of the rename statement. Does anyone know of a way to escape the # character to stop that annoying syntax highlighting?
[/QUOTE]

If you have written cmd.createSomething(n=‘#’) it should escape. It does in sublime text 2.

Yah, like i said, the autoname behavior is local to context. Which is why is sort of stinks, since the results of the same script can vary based on circumstances :frowning:

FWIW, I like to keep the pipe characters. There’s some good tricks you get that way, and you can be sure what you’re dealing with since it’s the format that ls -l returns (I suppose most of these work w/o the initial slash too but I don’t get any extra value from stripping it. To each his own !)


# stupid maya object path tricks

hierarchy_depth = "|path|to|object".count("|") - 1 

path, sep, short_name = "|path|to|object".rpartition("|") # sep is always "|"

parent = "|path|to|object".rpartition("|")[0]

root_path = "|path|to|object".split("|")[1]  # this would be [0] w/o the initial pipe

is_child_of  = lambda parent, child: child.startswith(parent)

is_below = lambda parent, child: child.find("|" + parent + "|")  > -1 

relative_path= lambda parent, child: child[len(parent) + 1 :] if is_child_of (parent, child) else ""
# note: this does not have a leading pipe. so...

is_abs = lambda p: p[0] == "|"  # this is why I don't strip first pipes, I use no-first-pipe to mean "relative path"
# so path join is just "|".join('|all',"my",'paths')


common_ancestor  = lambda a, b : os.commonprefix(a, b)  # from the os module


def walk_up(path):
   recurse_up = path.split("|")
   while len(recurse_up) > 1:
        yield"|".join(recurse_up)
        recurse_up.pop()


def walk_down(path):
    recurse_down = path.split("|")
    for lvl in range(1, len(recurse_down) +1):
        yield "|".join(recurse_down[:lvl])

Thanks again for all the help and suggestions. I’ve come across a related issue that I’d appreciate getting some input on.

In my scripts I’ve started adding attributes to objects to label them and it’s working out well. But, I keep finding that I consistently want to grab all the objects with a given attribute that are descendants of another object–like all the controls that belong to a given character’s rig for example.

So–using that example–say all the controls for my rig are labeled with an attribute “.isControl”. Then ls("*.isControl", l=True, o=True) is enough to grab all its controls. But that causes a bit of a problem if there are multiple characters in a scene, since I’ll be grabbing all the controls in every rig.

My current solution would be to run two list commands–an ls command to grab every control and a listRelatives command to get all the objects that are part of the rig–and then combine their results using sets, like so:

rigStuffs = listRelatives(rigRoot, allDescendents=True, f=True)
allControls = ls("*.isControl", l=True, o=True)
rigControls = list(set(rigStuffs) & set(allControls))

This works well, but I tend to do this a lot throughout my scripts and typing the same three lines of code gets annoying.

I was wondering if anyone knows how to do the same job in a single line by combining ls and listRelatives directly. I’m certain someone showed me how before, but for the life of me I can’t remember how they did it…

I suppose I could always just write a helper function to do the same job, but I’d really like to do it just using Maya’s commands if that’s possible.

Any help with this would be greatly appreciated :slight_smile:

Have a look in the Red9 StudioPack, there’s a number of ways of managing node ID’s, finding nodes in hierarchies and generally managing nodes in complex systems. If you’re looking for something that can ID any node in a hierarchy, or group of nodes then take a look at the r9Core.FilterNode class.

Why? it’s a class filter than id’s anything based on 4 intersecting filters:

metaNode - nodes that are part of a metaSystem
nodeType - obvious really, find all children (or scene level) of type x BUT it’s capable of always clamping to the transform node
searchAttrs - what you’re after really, find all children with attr x, BUT it also has and = and NOT operator on in, so if your attr were a string you could do ‘isControl=L’ or ‘NOT:isControl=L’
searchPattern - in built regex search filter

basically with this you can find and pinpoint anything in the scene in one easy go. In the next week or so I’m going to do a video taking people through the backendcode, but you can get an idea of it’s capability with this:

Mark

[QUOTE=Mark-J;20893]Have a look in the Red9 StudioPack, there’s a number of ways of managing node ID’s, finding nodes in hierarchies and generally managing nodes in complex systems. If you’re looking for something that can ID any node in a hierarchy, or group of nodes then take a look at the r9Core.FilterNode class.

basically with this you can find and pinpoint anything in the scene in one easy go. In the next week or so I’m going to do a video taking people through the backendcode, but you can get an idea of it’s capability with this:

Mark[/QUOTE]

I keep hearing about the red9 tools, but don’t know if they can work on generic rigs, or what “step #1” is to start using 'em. The FilterNode class sounds like something Maya couldn’t live without!

That was the entire point of the pack, to be generic and capable of hooking and working on any rig you throw at it. There’s nothing in there hard-coded, it all hooks to your setups either via the metaData (recommended and by far the best if you’re coding around) or it hooks via the filterNode. The main UI (animUI) takes a filter which is what binds the code to your setup, either filtering through hierarchies for specific markers,names,nodes etc, or walking a metaData network. Have a good browse round the vimeo channel, should give you a good insight.

Part4 of the metaData I’m going to try and do this weekend, and thats the one that’ll go through subclassing which is where the real power lies

Mark

Thanks for the info!