Reputation: 103
I want to do something like this,
things = [ node().path = x for x in cmds.ls(sl=1,l=1)]
but I'm getting an invalid syntax error. So I've had to use this instead,
things = []
for i, thing in enumerate(cmds.ls(sl=1,l=1)):
things.append(node())
things[i].path = thing
The first invalid code is nice clean and short. The second one is tedious. How can I get some code that allows me to assign a class variable in the same creation line WITHOUT using an initialization. I'm avoiding initialization because this class is going to be inherited into many other classes across multiple files, and my previous version that uses initializations breaks horrible when imported into too many packages, causing unbound method errors.
Upvotes: 1
Views: 1062
Reputation: 103
The reasons for the original question are related to this problem: Maya Python: Unbound Method due to Reload(). The solution I've found is essentially to rework my tracking method to not need an initialization, and then, to avoid the unbound error, I create the initialization sequence nearest to its use.
trackingMethod file:
import maya.cmds as cmds
import maya.OpenMaya as om
class pathTracking(object):
def setPathing(self, instance):
sel = om.MSelectionList()
sel.add(instance.nodeName)
try:
instance.obj = om.MObject()
sel.getDependNode(0, instance.obj)
except:
cmds.warning(instance.nodeName + " is somehow invalid as an openMaya obj node.")
try:
instance.dag = om.MDagPath()
sel.getDagPath(0,instance.dag)
except:
pass
def __set__(self, instance, value):
if isinstance(value,dict):
if "dag" in value:
instance.dag = value["dag"]
if "obj" in value:
instance.obj = value["obj"]
if "nodeName" in value:
instance.nodeName = value["nodeName"]
self.setPathing(instance)
else:
if isinstance(value, basestring):
instance.nodeName = value
self.setPathing(instance)
def __get__(self, instance, owner):
if instance.dag and instance.dag.fullPathName():
return instance.dag.fullPathName()
return om.MFnDependencyNode(instance.obj).name()
class exampleNode(object):
path = pathTracking()
dag = None
obj = None
nodeName = ""
someVar1 = "blah blah"
def initialize(self,nodeName,obj,dag):
if obj or dag:
self.obj = obj
self.dag = dag
elif nodeName:
self.path = nodeName
else:
return False
return True
other file:
import trackingMethod as trm
circleExample(trm.exampleNode):
def __init__(self,nodeName="",dag=None,obj=None):
if not self.initialize(nodeName,obj,dag)
self.path = cmds.circle()[0]
and with this method I can do
circles = [circleExample(nodeName=x) for x in cmds.ls(sl=1,l=1)]
PS. I ran into some stuff that required the class to be initialized before some of its bits could be created. Below is a custom dict that required I pass an instance of self to it at the time the dict is created. Recreating these dict structures in every class that interacts with a transform would have been tedious. The solution was to put these dependent class initializations into a function in the transform class. That way the final class inherits the function to create the dicts and can call it in their init. This avoids a whole russian nesting doll of init statements that breaks when you have multiple files inheriting from a single class.
While this solution may seem obvious to some, I was just about pulling out hair to think of a way around a chicken egg situation of needing to init the class to get self, but not being able to init class due to unbound method errors.
class sqetDict(dict):
def __init__(self,instance,*args,**kwargs):
self.instance = instance
dict.__init__(self,*args,**kwargs)
def __getitem__(self, key):
thing = dict.__getitem__(self,key)
if key in self and isinstance(thing,(connection,Attribute,xform)):
return thing.__get__(self.instance,None)
else:
return dict.__getitem__(self,key)
def __setitem__(self, key, value):
thing = dict.__getitem__(self,key)
if key in self and isinstance(thing,(connection,Attribute,xform)):
thing.__set__(self.instance,value)
else:
dict.__setitem__(self,key,value)
These dicts would be initialized like this:
def enableDicts(self):
self.connection = sqetDict(self, {"txyz": connection("translate"), "tx": connection("tx"),
"ty": connection("ty"), "tz": connection("tz"),
"rxyz": connection("rotate"),
"rx": connection("rx"), "ry": connection("ry"), "rz": connection("rz"),
"sxyz": connection("scale"),
"sx": connection("sx"), "sy": connection("sy"), "sz": connection("sz"),
"joxyz": connection("jointOrient"),
"jox": connection("jox"), "joy": connection("joy"), "joz": connection("joz"),
"worldMatrix": connection("worldMatrix"),
"worldInvMatrix": connection("worldInverseMatrix"),
"parentInvMatrix": connection("parentInverseMatrix")})
self.value = sqetDict(self, {"txyz": Attribute("translate", "double3"),
"tx": Attribute("tx", "float"), "ty": Attribute("ty", "float"),
"tz": Attribute("tz", "float"),
"rxyz": Attribute("rotate", "double3"),
"rx": Attribute("rx", "float"), "ry": Attribute("ry", "float"),
"rz": Attribute("rz", "float"),
"sxyz": Attribute("scale", "double3"),
"sx": Attribute("sx", "float"), "sy": Attribute("sy", "float"),
"sz": Attribute("sz", "float"),
"joxyz": Attribute("jointOrient", "double3"),
"jox": Attribute("jox", "float"), "joy": Attribute("joy", "float"),
"joz": Attribute("joz", "float"),
"rotOrder": Attribute("rotateOrder", "string"),
"worldMatrix": Attribute("worldMatrix", "matrix"),
"worldInvMatrix": Attribute("worldInverseMatrix", "matrix"),
"parentInvMatrix": Attribute("parentInverseMatrix", "matrix"),
"rotatePivot": Attribute("rotatePivot", "double3"),
"visibility": Attribute("visibility", "long")})
self.xform = sqetDict(self, {"t": xform("t"), "ro": xform("ro"), "s": xform("s")})
my connection class does the cmds.connectAttr when sent a value, and it returns the various attributes of a connection as a dict like {"in": "in connection", "out":["outConn1","outCon2",etc..], "path":"fullpath name to attribute"}. So you could do something like, thingA.connection["txyz"] = thingB.connection["txyz"]["path"] to connect the relative translates of two objects.
My Attribute class allows set and get of attribute values, like temp = thing.value["txyz"] results in temp = (value,value,value), and thing.value["txyz"]=(0,0,0) would zero the translate.
xform does the values thing but in absolute world space values.
Upvotes: 0
Reputation: 96172
Not only is the first example invalid syntax, what you are attempting to do is fundamentally unsound: do not mix list-comprehensions with state-change (i.e. assigning to an object attribute). Whatever node
is, it seems the best way to deal with your issue is to add a parameter to node.__init__
that allows you to set path
when you instantiate a node
object. And then you could do things = [node(x) for x in cmds.ls(sl=1, l=1)]
So, the most basic approach using a single positional argument to __init__
:
class Node(object):
def __init__(self, path):
self.path = path
...
things = [Node(x) for x in cmds.ls(sl=1, l=1)]
More importantly, though, using a for-loop is perfectly Pythonic. Trying to get your code to be all one-liners is a fundamentally misguided. Here is how I would work with what you have already and make it more Pythonic:
things = []
for path in cmds.ls(sl=1,l=1):
n = node()
n.path = path
things.append(n)
The above is perfectly pythonic as it is...
Upvotes: 1