0

The issue I appear to be having is that when I run a code that uses the plusMinusAverage class more than once (in my case a for loop), the new instance of the class keeps references to the previous list of pmaDicts created by the previous plustMinusAverage creation and adds to the end of it.

Meaning (Code that results this is further down below)

things = []
for i in range(2):
    thing[i] = plusMinusAverage(count3D=2)
    print thing[i]
    print thing[i].values3D 
>>> (plusMinusAverage at 0x00NSTUFF1)
>>> [{"x":(attr.connection at 0x1234),"y":(attr.connection at 0x2345), etc..},
     {"x":(attr.connection at 0x3456),"y":(attr.connection at 0x4567), etc..}]
>>> (plusMinusAverage at 0x00NSTUFF2)
>>> [{"x":(attr.connection at 0x1234),"y":(attr.connection at 0x2345), etc..},
     {"x":(attr.connection at 0x3456),"y":(attr.connection at 0x4567), etc..},
     {"x":(attr.connection at 0x5678),"y":(attr.connection at 0x6789), etc..},
     {"x":(attr.connection at 0x7890),"y":(attr.connection at 0x8901), etc..}]

What gets me about this is that it appears to print out the object, but then the list appears to point to the original but with more entries. I don't get why the list would not be a unique one.

Sorry for the massive posting of code below, but I figured this issue has enough nuances that trying to make a simpler version would likely invite solutions that wouldn't work for my circumstance, and since I'm not certain where the issue lies exactly, I'm going to include all the pertinent parts where it might be going wrong. ..plus maybe someone who works in maya can use this to build their own shaderNode tools.

class Tracker2(object):
    dag = ""
    obj = ""
    getTime = "current"

    def setPathing(self):
        if self.nodeName == None:
            self.nodeName = cmds.createNode('transform')
            cmds.addAttr(self.nodeName, sn="type", ln="type", dt="string")
            cmds.setAttr(self.nodeName + ".type", type="string", keyable=0)
        sel = om.MSelectionList()
        sel.add(self.nodeName)
        self.obj = om.MObject()
        self.dag = om.MDagPath()
        sel.getDependNode(0, self.obj)
        try:
            sel.getDagPath(0, self.dag)
        except:
            pass

    def __init__(self):
        if not self.dag and not self.obj:
            self.setPathing()

    def fullpath(self):
        if self.dag and self.dag.fullPathName():
            return self.dag.fullPathName()
        return om.MFnDependencyNode(self.obj).name()

class shaderNode(Tracker2):
    def __init__(self):
        self.nodeName = cmds.shadingNode(self.type,au=1)
        Tracker2.__init__(self)

class connection(object):
    def __init__(self, attr, *args):
        self.attr = attr
    def __set__(self, instance, value):
        if isinstance(value,basestring):
            try:
                cmds.connectAttr(value,instance.fullpath()+"."+self.attr,f=1)
            except Exception as inst:
                cmds.warning(inst)
        elif not value:
            temp = cmds.listConnections(instance.fullpath()+"."+self.attr,s=1,d=0)
            if temp:
                cmds.disconnectAttr(instance.fullpath()+"."+self.attr, temp[0])
        else:
            cmds.warning("Set Connection: Source attribute is non-string value | "+value)
    def __get__(self, instance, owner):
        tempIn = cmds.listConnections(instance.fullpath()+"."+self.attr,s=1,d=0)
        tempIn = tempIn if tempIn else []
        tempOut = cmds.listConnections(instance.fullpath()+"."+self.attr,s=0,d=1)
        tempOut = tempOut if tempOut else []
        #returns list of [[incoming] , [outgoing]]
        return [tempIn,tempOut]

In separate py file where class containing connection is loaded as attr

class pmaDict(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(dict.__getitem__(self, key),attr.Attribute):
            return thing.__get__(self.instance,None)
        if key in self and isinstance(dict.__getitem__(self,key),attr.connection):
            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(dict.__getitem__(self,key),attr.Attribute):
            thing.__set__(self.instance,value)
        elif key in self and isinstance(dict.__getitem__(self,key),attr.connection):
            thing.__set__(self.instance, value)
        else:
            dict.__setitem__(self,key,value)

class plusMinusAvg(attr.shaderNode):
    type = "plusMinusAverage"
    values1D = []
    values2D = []
    values3D = []

    def addInput1D(self):
        i = len(self.values1D)
        print self
        cmds.setAttr(self.fullpath() + ".input1D[" + str(i) + "]", 0)
        newInput = pmaDict(self,
                           {"x": attr.Attribute("input1D[" + str(i) + "]", "float"),
                            "x_con": attr.connection("input1D[" + str(i) + "]")})
        self.values1D.append(newInput)

    def addInput2D(self):
        i = len(self.values2D)
        print self
        cmds.setAttr(self.fullpath() + ".input2D[" + str(i) + "]", 0, 0, type="double2")
        newInput = pmaDict(self,
                           {"xy": attr.Attribute("input2D[" + str(i) + "]", "float"),
                            "x": attr.Attribute("input2D[" + str(i) + "].input2Dx", "float"),
                            "y": attr.Attribute("input2D[" + str(i) + "].input2Dy", "float"),
                            "xy_con": attr.connection("input2D[" + str(i) + "]"),
                            "x_con": attr.connection("input2D[" + str(i) + "].input2Dx"),
                            "y_con": attr.connection("input2D[" + str(i) + "].input2Dy")})
        self.values2D.append(newInput)

    def addInput3D(self):
        i = len(self.values3D)
        print self
        cmds.setAttr(self.fullpath()+".input3D["+str(i)+"]",0,0,0, type="double3")
        newInput = pmaDict(self,
                           {"xyz": attr.Attribute("input3D["+str(i)+"]","double3"),
                            "x": attr.Attribute("input3D["+str(i)+"].input3Dx","float"),
                            "y": attr.Attribute("input3D["+str(i)+"].input3Dy","float"),
                            "z": attr.Attribute("input3D["+str(i)+"].input3Dz","float"),
                            "xyz_con": attr.connection("input3D["+str(i)+"]"),
                            "x_con": attr.connection("input3D["+str(i)+"].input3Dx"),
                            "y_con": attr.connection("input3D["+str(i)+"].input3Dy"),
                            "z_con": attr.connection("input3D["+str(i)+"].input3Dz")})
        self.values3D.append(newInput)

    def __init__(self, count1D=0, count2D=0, count3D=0):
        attr.shaderNode.__init__(self)
        for i in range(count1D):
            self.addInput1D()
        for i in range(count2D):
            self.addInput2D()
        for i in range(count3D):
            self.addInput3D()

1 Answers1

1

The problem lines are right here:

class plusMinusAvg(attr.shaderNode):
    type = "plusMinusAverage"
    values1D = []
    values2D = []
    values3D = []

You Are assigning the lists as class attributes. You are normally used to such an assignment working out because if you were to do something like values1d = blah in a method call, the assignment would implicitly be made on self. However, you never make another assignment: you just use the class-level list via methods such as append, __setitem__, etc. Therefore all instances use the same lists, defined in the class object.

To fix, move the three list assignments to within __init__:

self.values1D = []
self.values2D = []
self.values3D = []

This will ensure that each instance gets it's own list. Do that for all mutable attributes such as lists and dicts as a general rule to avoid the same type of problem in the future.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264