pythonfor-loopmayapymel

Pymel: How do I close For loop after deleting null groups?


As I continue to study For Loops: I've run into some annoying errors. The problem is the script does exactly what I want it to. It deletes the null groups under the demo joints: but unlike other loops I've made for renaming which can be closed with a transform flag in the cmds.ls command: cmds.listRelatives doesn't allow a transform flag to close out the loop. You run the script by simply clicking Build Examples then hitting Delete Waste Groups

I've tried every flag according to the Maya documentation: but nothing seems to be closing the loop. I dont know if I need another variable, or a combination of some flags: or if I am using the wrong type of wording: but ideally what I would like this script to do is simply close out the loop so I dont get the error Error: No object matches name: curve

'''
import DS_wasteGroup_cleanerDemo
reload (DS_wasteGroup_cleanerDemo)
DS_wasteGroup_cleanerDemo.gui()
'''

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

if cmds.window("renameWin", exists =True):
    cmds.deleteUI("renameWin", window = True)

myWindow = cmds.window("renameWin",t='DS_wasteGroup_cleanerDemo',w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)

def gui():

    cmds.button( label="Build Examples", c = buildExamples)
    cmds.separator( w=200, h=3)
    cmds.button( label="Delete Waste Groups", c = deleteWasteGrp)
    cmds.separator( w=200, h=9)

    cmds.setParent('..')
    cmds.showWindow(myWindow)

def buildExamples(*args):

    cmds.group(n='exampleGroup1',world=True,empty=True)
    cmds.joint(n='demoJoint1')
    cmds.group(n='curve1',world=True,empty=True)
    cmds.parent('curve1','demoJoint1')

    cmds.joint(n='demoJoint2')
    cmds.parent('demoJoint2','exampleGroup1')
    cmds.group(n='curve2',world=True,empty=True)
    cmds.parent('curve2','demoJoint2')

    cmds.joint(n='demoJoint3')
    cmds.parent('demoJoint3','exampleGroup1')
    cmds.group(n='curve3',world=True,empty=True)
    cmds.parent('curve3','demoJoint3')

    cmds.joint(n='demoJoint4')
    cmds.parent('demoJoint4','exampleGroup1')
    cmds.group(n='curve4',world=True,empty=True)
    cmds.parent('curve4','demoJoint4')

    cmds.joint(n='demoJoint5')
    cmds.parent('demoJoint5','exampleGroup1')
    cmds.group(n='curve5',world=True,empty=True)
    cmds.parent('curve5','demoJoint5')


def deleteWasteGrp(*args):
    grpList = cmds.listRelatives('demoJoint*',p=True,f=True)
    for name in grpList:
        print(grpList)
        cmds.delete('curve*')

My apologies if I'm posting simple questions. I do write Python scripts to automate the most tedious tasks in rigging: but my knowledge is only intermediate. I want to learn more python so my scripts arent so clunky and brute forced: as well as the fact that I need them to be more adaptable to various types of characters: so any resources that dumb all this down would also be appreciated. Thank you for your help.


Solution

  • Honestly I would take a whole different approach as you're hard-coding everything which could easily lead to disaster. When I mean hard-code, I mean you're trying to parent, let's say, "demoJoint2" to an object. This is bad because why are you assuming that "demoJoint2" even exists? If you create an object with a specific name that already exists, Maya will auto-rename the new object, and now you're referencing the wrong one right off the bat! Instead when you create your objects, capture their names in a variable then work with that, otherwise you'll be constantly shooting yourself in the foot.

    Here's the same script with an approach I would try instead:

    import maya.cmds as cmds
    
    
    def gui():
        if cmds.window("renameWin", exists=True):
            cmds.deleteUI("renameWin", window=True)
    
        myWindow = cmds.window("renameWin", t="DS_wasteGroup_cleanerDemo", w=200, h=500, toolbox=True)
        column = cmds.columnLayout(adj=True)
    
        cmds.button(label="Build Examples", c=buildExamples)
        cmds.separator(w=200, h=3)
        cmds.button(label="Delete Waste Groups", c=deleteWasteGrp)
        cmds.separator(w=200, h=9)
    
        cmds.setParent("..")
        cmds.showWindow(myWindow)
    
    
    def buildExamples(*args):
        root = cmds.group(n="exampleGroup1", world=True, empty=True)
    
        for i in range(5):  # Loop to the amount of joints you want to create.
            jnt = cmds.createNode("joint", name="demoJoint" + str(i + 1))  # Use `i` to help name the object.
            jnt = cmds.parent(jnt, root)[0]  # Parenting changes its long name, so recapture the joint in a variable.
            crv = cmds.group(n="curve" + str(i + 1), world=True, empty=True)  # Create empty group.
            cmds.parent(crv, jnt)  # Parent curve to joint.
    
    
    def deleteWasteGrp(*args):
        jnts = cmds.ls("demoJoint*", long=True, type="joint")  # Get all `demoJoints`.
        children = cmds.listRelatives(jnts, f=True, children=True, type="transform") or []  # Get all of their children, and only get transform types.
        curves = [obj for obj in children if obj.split("|")[-1].startswith("curve")]  # Don't assume we got the right objects. Run a final loop to collect any object that starts with `curve`. Need to use split as we're looping through long names but need to check its short name.
        if curves:  # `cmds.delete` will error if this list is empty, so don't assume.
            cmds.delete(curves)  # Delete all curves at once.
    
    
    gui()
    

    Now I can hit the build button as much as I want with no issues, and delete all the curves when pressing the delete button.

    A few more notes:

    Notice in buildExamples I'm using a loop to create all the objects instead of reusing redundant code that does the same thing. You could even have a spinbox in your gui that defines how many joints it creates now, where as before it wasn't possible because the count was hard-coded.

    cmds.listRelatives does have a way to filter objects by transforms by setting parameter type="transform". In fact you'll see many commands have this same parameter (again start checking docs).

    cmds.listRelatives('demoJoint*',p=True,f=True) was grabbing the joint's parent, not its children. The docs clearly state this.

    Running cmds.delete('curve*') is going to delete ALL objects with names that start with curve, and since you're running this in a loop it's trying to do this multiple times.

    maya.cmds is not pymel. There's a whole separate module called pymel.

    If you're unsure with any parts of the code try adding in a print statement to see what it's doing.