Colin Knueppel
Colin Knueppel

Reputation: 103

Initialization order causes class instance problems

I'm just going to post what I have so far and try to describe the problem.

The script bellow tracks a transform object with custom attributes. It creates an interface to easily __get__ and __set__ to a maya node's attributes. This makes it easy to save attribute values into a scene that can load with the scene in the future. In the script I was using OpenMaya to track a dagObject, but simplified to try to work through the problem first. If you want to use this code, look into MDagPath and MSelectionList.

Now.. Attributes() need to be declared outside of __init__ to work correctly. Attributes() as they are now, require a Transform() at creation. If I create a Transform() outside of an __init__ in the Clip(), the resulting Transform() instance will reference whatever latest Transform() was created by this method (image)

Can't post image without reputation, so descriptively: three clips called _new_anim_, _new_anim_1, _new_anim_2. When script bellow runs, the output from the list is all _new_anim_2, which is the last transform that was initialized.

__init__ happens after the Attributes are created. I need to pass a __init__ Transform instance to an already initialized Attribute instance.

How?

Saying clip.transform = correctTransform doesn't seem to replace the reference to correctTransfrom. I need a way to pass a class instance to a class variables that are initialized outside of the __init__.

import maya.cmds as cmds

class Attribute(object):
    def __init__(self,transform,attr,*args):
        self.transform = transform
        self.attr = attr
        self.string = "string" in args
    def __set__(self, instance, value):
        if self.string:
            cmds.setAttr(self.transform.path()+"."+self.attr,value,dt="string")
        else:
            cmds.setAttr(self.transform.path()+"."+self.attr,value)
    def __get__(self, instance, owner):
        if self.string:
            return     cmds.getAttr(self.transform.path()+"."+self.attr,dt="string")
        return cmds.getAttr(self.transform.path()+"."+self.attr)

class Transform(object):
    def __init__(self):
        self.parent = ""
        self.name = ""
    def setObject(self,transform):
        self.parent = cmds.listRelatives(transform,p=1)[0]
        self.name = cmds.ls(transform,sn=1)[0]
    def path(self):
        return self.parent+"|"+self.name
    def getName(self):
        return cmds.ls(self.path(),sn=1)[0]
    def rename(self,value):
        self.name = cmds.ls(cmds.rename(self.path(),value),sn=1)[0]

class Clip(object):
    transform = Transform()
    start = Attribute(transform,"STA")
    end = Attribute(transform,"END")
    loop = Attribute(transform,"Loop")
    relative = Attribute(transform,"RelativeToStart")
    speedState = Attribute(transform,"SpeedState")
    speed = Attribute(transform,"SpeedVal")

    def __init__(self,transform):
        self.transform.setObject(transform)

Upvotes: 1

Views: 80

Answers (1)

Blckknght
Blckknght

Reputation: 104762

If I am understanding your question correctly, it looks like you want the transform variable in your Clip class to be a instance-specific value, but you're having problems because it needs to be accessed by the Attribute descriptors you're also adding to Clip, which need to be declared as class variables.

I think the solution to this is to have the Attribute class's __get__ and __set__ methods look for transform on the instance they are passed in, rather than on self.

Try this:

class Attribute(object):
    def __init__(self,attr,*args): # no transform parameter or instance variable
        self.attr = attr
        self.string = "string" in args
    def __set__(self, instance, value): # look up transform on instance, rather than self
        if self.string:
            cmds.setAttr(instance.transform.path()+"."+self.attr,value,dt="string")
        else:
            cmds.setAttr(instance.transform.path()+"."+self.attr,value)
    def __get__(self, instance, owner): # here too
        if self.string:
            return     cmds.getAttr(instance.transform.path()+"."+self.attr,dt="string")
        return cmds.getAttr(instance.transform.path()+"."+self.attr)

# Transform can stay the same, though you could merge set_object into __init__

class Clip(object):
    # no more transform class variable
    start = Attribute("STA") # no more transform argument passed to the Attributes
    end = Attribute("END")
    loop = Attribute("Loop")
    relative = Attribute("RelativeToStart")
    speedState = Attribute("SpeedState")
    speed = Attribute("SpeedVal")

    def __init__(self,transform):
        self.transform = Transform() # create transform as an instance variable
        self.transform.setObject(transform)

Upvotes: 1

Related Questions