Sunday, 29 May 2022

Scripting: ls() command

This post is intended as a follow-up to: Don't use Cmds, use Pymel


You'll use this guy a lot, especially if you're using Pymel (why aren't you using Pymel?)

The ls() command.
Pretty sure the ls stands for "list". I never bothered to check, actually, but I assume they abbreviated it because list is already a native python function used to turn variables into a list of variables.

ls() is useful in a number of situations. It can be used to create a python list of specific objects based on the arguments you input.

Check out all the available arguments here:
https://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.core.general/pymel.core.general.ls.html

But I shall go over the commonly useful ones.

Suppose I want to create a python list of all of a certain kind of object in the scene.
More specific? Okay, suppose I want to make all joint objects invisible. All of them. And I don't want to have to dig through the hierarchy to find them all.
I can do this using ls() and the type parameter:

import pymel.core as pm

allJnts = pm.ls(type="joint")

Doing this finds every single joint throughout the scene, composes them into a list and assigns that list to the variable allJnts.
Let's print the variable to see:

print(allJnts)

and uuuh... FWI, for a scene with a lot of joints, that will return a really long list which can be a pain to read when it's all printed on a single line, so I'll print it again but this time inside a for loop that prints the list one entry at a time:

for entry in allJnts:

     print(entry)


That looks like a success. We got the joints, all of the joints, and nothing but the joints.
And now that I have them neatly assembled before me, I can do stuff to them, knowing that no joint will be left behind:

for jnt in allJnts:

    pm.setAttr(jnt+".visibility", False)

To do this - to get all of a certain kind of joint - you need to know of course what string to feed to the type parameter. If you ever want to know what an object's type is, with the object selected, you can see the type at the top of the attribute editor:


Or you can find out with the  nodeType function, inputing the object in question as an argument:

obj = pm.group()

objNodeType =  pm.nodeType(obj)

print(objNodeType)

So assembling a list of all of a certain node type is one option. Another is to assemble a list of all DAG nodes in the scene using the dagObjects parameter, which accepts True or False as inputs (alternatively: 1 or 0):

allDagNodes = pm.ls(dagObjects=True)

There are many other parameters you can use to look for a certain kind of node.
Another option it to assemble a list of nodes from what is currently selected in the scene. Very useful if you're writing a reusable function to do a certain series of things to whatever you have selected.

To do this, use the selection parameter:


sel = pm.ls(selection=1)

print(sel)


Don't worry about those objects bring wrapped inside nt.Transform(), by the way. Maya just does that when it prints maya objects as list entries. If we printed them one by one we'd get more conventional names:

Just a little console weirdness to ignore.


Yet another option is to search for nodes with certain sub-string in their name by entering that sub-string as an argument.
Often, to use this successfully, it helps to know some python formatting syntax, such as the wild card: *

Check it out:


armNodes = pm.ls("arm_*")

for node in armNodes:

    print(node)


You can even use various parameters together to narrow down the qualifying nodes.
Suppose in that same scenario I only wanted the nodes whose names begin with "arm_" and are joints:

armJnts = pm.ls("arm_*", type="joint")

for node in armJnts:

    print(node)


Using ls() to assign Maya objects to variables

If you are using Cmds, ls() will return a list of strings based on the names of the nodes, but in Pymel ls() is more useful than that. In Pymel, ls() will return a list of the Maya objects themselves. And you need to get an object in python before you can assign it to a variable, so what better way than by using ls().


cubes = pm.ls(="*cube*", type="transform")

print(cubes)

 


That will create a list of maya objects out of any transform node int he scene with "cube" in its name. Each of these list entries can be accessed via its list index.

Alright, that's a slick way of getting all the poly cube objects, but what if we want a specific one?
Well surely it's just a matter of being more specific with the sub-string argument. Or hell, just selecting the cube we want in the scene and using the selection parameter:


cubeA = pm.ls(selection=True)

print(cubeA) 


And like that, we can convert a selection to a Maya object held in a variable with the script can pass around and act upon.

Nice. Nice. But notice even if there's only a single object that meets the criteria we set, ls() still returns a list, even if it's just a list of one object. This is a problem. If next we attempted to parent the cube object using the cubeA variable, we'd get an error telling us "You fool! You can't parent a python list. Only Maya objects can be parented, and this ain't no Maya object - it's a list CONTAINING a Maya object!"

So first, we need to extract that object from the list and give it its own variable. If we can trust that the list is only one object long, then we know the object is at list entry [0]:

cubeA_list = pm.ls(selection=True)

cubeA = cubeA_list[0]

print(cubeA)


Actually, Python is slick enough to let you reassign a variable with a derivative of itself all in one line. No need to come up with two different variables if one you've extracted the object you're finished with the first variable:

cubeA = pm.ls(selection=True)

cubeA = cubeA[0]

print(cubeA)


We can condense this down to even fewer lines, even. You can access the entry of the variable on the same line as the variable is being defined as the function's output:

cubeA = pm.ls(selection=True)[0]

print(cubeA)

So get good and comfortable with the ls() function. It'll be one of your most used if you're using Pymel (Seriously? Why aren't you using Pymel? Why does Cmds still even exist?!)

No comments:

Post a Comment