Home Website Youtube GitHub

Script to Rename Guides

Hello! Pretty often, I find that I need to adjust the names of a lot of guides (like 50 or so at a time). This is pretty tedious since one needs to open the properties and adjust the name through the mGear interface. Is there a simple way to script this that I’m missing?

Whew, I really had to hunt for this! :sweat_smile:

You can find the rename method in mgear_X.X.X/release/scripts/mgear/shifter/guide.py

In the Rig class. Rig.updateProperties(root, newName, newSide, newIndex)

And the comment in the script is wrong. It says “newIndex” should be a str. But it actually needs to be an integer, as far as I can tell.

import pymel.core as pm
import mgear.shifter

rig = shifter.guide.Rig()

# Select your component root
componentRoot = pm.selected()[0]

newName = 'legRenamed'
newSide = 'L'
newIndex = 2

# Rename the component:
rig.updateProperties(componentRoot, newName, newSide, newIndex)

In this above example script “componentRoot” would be the root of each component you want to rename. For example, leg_L0_root. There is some code in guide.py that exists to find the component root of any selected node. It’s basically a while loop that keeps looking at the parent until it finds the .comp_type attribute. Every component root has that attribute.

But here is a way to find the root that I prefer, because I kind of hate while loops, unless they are really necessary! But when we know the hierarchy depth, there is no reason for a while loop.

import pymel.core as pm

def find_component_root(node):
    '''
    March UP the hierarchy until you find the first one that has .comp_type
    That will be your component root.
    '''
    componentRoot = False
    hierarchyDepth = len(node.longName().split('|'))
    allParents = [
        node.getParent(ii) for ii in range(hierarchyDepth)
        if node.getParent(ii)
        if node.getParent(ii).hasAttr('comp_type')
        ]
    if allParents:
        componentRoot = allParents[0]
    return componentRoot
    

# Select some guide nodes, and run this.
# It will return the component root for each.
for each in pm.selected():
    componentRoot = find_component_root(each)
    print(componentRoot)

Chris, that’s really helpful. Thank you.

In case it’s useful to anyone, here is a super quick toolkit I made for renaming, duplicating, and mirroring multiple guides at once.

from pymel.core import *
from mgear import shifter

##### mGear Tools #####

def getRoot(node):
    componentRoot = False
    hierarchyDepth = len(node.longName().split('|'))
    allParents = [
        node.getParent(ii) for ii in range(hierarchyDepth)
        if node.getParent(ii)
        if node.getParent(ii).hasAttr('comp_type')
        ]
    if allParents:
        componentRoot = allParents[0]
    return(componentRoot)    

def renameGuides():
    rig = shifter.guide.Rig()
    
    newName = textField(nameFld, q=1, tx=1)
    sel = selected()
    if newName:
        for node in sel:
            root = getRoot(node)
            
            cname = root.comp_name.get()
            cside = root.comp_side.get()
            cindex = root.comp_index.get()
            
            rig.updateProperties(root=root, newName=newName, newSide=cside, newIndex=cindex)
    
def replaceGuides():
    rig = shifter.guide.Rig()
    
    repName = textField(repFld, q=1, tx=1)
    withName = textField(withFld, q=1, tx=1)
    sel = selected()
    if repName:
        for node in sel:
            root = getRoot(node)
            
            cname = root.comp_name.get()
            cside = root.comp_side.get()
            cindex = root.comp_index.get()
            
            newName = cname.replace(repName, withName)
            
            rig.updateProperties(root=root, newName=newName, newSide=cside, newIndex=cindex)
            
def presufGuides():
    rig = shifter.guide.Rig()
    
    preName = textField(preFld, q=1, tx=1)
    sufName = textField(sufFld, q=1, tx=1)
    sel = selected()
    for node in sel:
        root = getRoot(node)
        
        cname = root.comp_name.get()
        cside = root.comp_side.get()
        cindex = root.comp_index.get()
        
        newName = '{}{}{}'.format(preName, cname, sufName)
        
        rig.updateProperties(root=root, newName=newName, newSide=cside, newIndex=cindex)
    
def duplicateGuides():
    rig = shifter.guide.Rig()
    sel = selected()
    for node in sel:
        root = getRoot(node)
        rig.duplicate(root=root, symmetrize=0)
    
def mirrorGuides():
    rig = shifter.guide.Rig()
    sel = selected()
    for node in sel:
        root = getRoot(node)
        rig.duplicate(root=root, symmetrize=1)

##### UI

if window('vrMGTools', ex=1):
	deleteUI('vrMGTools')

ui = window('vrMGTools', t='mGear Tools', wh=(250, 185))
mlt = columnLayout()
btnColor = [.94, .82, .30]

rowLayout(p=mlt)
text(l='Rename guides')

rowLayout(p=mlt, nc=2)
nameFld = textField('nameField', w=158, en=1, pht='Name')
nameButt = iconTextButton('nameButton', l='Rename', bgc=btnColor, rpt=1, w=88,  
                          st='textOnly', ann='Rename selected guides.',
                          c=renameGuides)
rowLayout(p=mlt, nc=3)
repFld = textField('replaceField', w=78, en=1, pht='Replace...')
withFld = textField('withField', w=78, en=1, pht='...with')
repButt = iconTextButton('repButton', l='Replace', bgc=btnColor, rpt=1, w=88,  
                          st='textOnly', ann='Replace text in selected guide names.',
                          c=replaceGuides)
rowLayout(p=mlt, nc=3)
preFld = textField('prefixField', w=100, en=1, pht='Prefix')
presufButt = iconTextButton('prefixSuffixButton', l='Add', bgc=btnColor, rpt=1, w=46,
                            st='textOnly', ann='Add prefix / suffix to selected guide names.',
                            c=presufGuides)
sufFld = textField('suffixField', w=100, en=1, pht='Prefix')

rowLayout(p=mlt)
text(l='Duplicate guides')

rowLayout(p=mlt, nc=2)
dupButt = iconTextButton('dupButton', l='Duplicate', bgc=btnColor, rpt=1, w=123,  
                         st='textOnly', ann='Duplicate selected guides.',
                         c=duplicateGuides)
mirButt = iconTextButton('mirButton', l='Mirror', bgc=btnColor, rpt=1, w=123,  
                         st='textOnly', ann='Mirror selected guides.',
                         c=mirrorGuides)

showWindow(ui)
2 Likes

Ok, there’s something odd going on when I try to run the script that I posted above. It seems like there is some sort of recursion in the rig.updateProperties command. When I run this on multiple selected guides, it sometimes performs a given operation multiple times.

For example, if I mirror a handful of guides, some of them get mirrored more than once. I also added a function to add a prefix / suffix to a guide’s name, and if I select multiple guides where some are children of others, they get the suffix added multiple times.

Any thoughts on how to address this issue?

  1. I only quickly glanced at your code and found one example. There are likely more. Look at this loop. You are looping over every single selected node, and then finding the root. So you are doing an operation on the root for every node. So if you have 3 children selected, 3 times it will find the root and run the operation on the same root.

You should first find the roots of all selected items. And then iterate over that SET. (Not a list, but a set, so any duplicate nodes get filtered out.) And then sometimes depending on the operation, you might need to do children first, if operating on a parent would affect the child. Or vice versa.

  1. Unsolicited bonus tip: I highly discourage you from doing from pymel.core import * - This is generally bad practice. And you create a higher chance of creating naming conflicts. For example if you ever define a variable “button”, you’ll override pm.button.

I used to do the same thing for the first year in PyMEL, and then I realized it also made it much harder to search and refactor my code, because instead of being able to search “pm.something” I had to search for “something”. Put it in a namespace, and it will make your code easier to read, and easier to refactor and search.

Thanks for the pointer in the right direction, Chris. I’ll dig into that tomorrow, but what you’re saying makes perfect sense.

Also thank you for the tip about importing PyMel. I’ve had no issue doing it this way for years and years, but I confess that typically I am the only one running my code (and I’m careful to avoid conflicts). No one ever explained to me what the downside could be.