How to use Python gradientControlNoAttr

How to use Python gradientControlNoAttr

1140534355
Enthusiast Enthusiast
2,630 Views
32 Replies
Message 1 of 33

How to use Python gradientControlNoAttr

1140534355
Enthusiast
Enthusiast

Hello, everyone, I'm new to Python. I found gradientControlNoAttr layout in Python document today, but I don't know how to define this. I've been using def source_v(source_cv) function before, and I want to know how I can change it to gradientControlNoAttr, or if there is a case for reference.

####################################
def source_v(source_cv):
    attraction_profile = cmds.getAttr(source_cv + ".attractionProfile[*]")
    for cv in cmds.ls(sl=True, dag=True, typ="nurbsCurve"):
        point_num = len(cmds.getAttr(cv + ".attractionProfile[*]")) or 0  
        for i in range(point_num)[len(attraction_profile):]:
            cmds.removeMultiInstance("{}.attractionProfile[{}]".format(cv, i), b=True)
        for i, (pos, value, interp) in enumerate(attraction_profile):
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(cv, i), pos)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(cv, i), value)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(cv, i), interp)
source_cv = cmds.ls(sl=True, dag=True, typ="nurbsCurve")[0]

#####################################
cmds.gradientControlNoAttr('attractionProfile', h=120)
cmds.gradientControlNoAttr('attractionProfile', ck=0,e=1)
cmds.rowLayout(nc=3)
cmds.floatFieldGrp('posFieldRadius', pre=3, l="Selected Position", nf=1, v1=0.000, s=1.0, cw=(1, 70))
cmds.floatFieldGrp('valFieldRadius', pre=3, l="Selected Value", nf=1, v1=1.000, s=1.0, cw=(1, 70))

cmds.optionMenuGrp('interpMenuRadius', cw=(1, 50), l="Interpolation")
cmds.menuItem(l="  None  ")
cmds.menuItem(l="  Linear  ")
cmds.menuItem(l="  Smooth  ")
cmds.menuItem(l="  Spline  ")
cmds.setParent( '..' )

04af9e59b891b09710cec41a2dc2b79.png 

0 Likes
Accepted solutions (4)
2,631 Views
32 Replies
Replies (32)
Message 2 of 33

jmreinhart
Advisor
Advisor
Accepted solution

So you could use the gradientControlNoAttr to get what you want like this 

class AttractionProfileWindow():
    # If your working on a UI it can be annoying to pass lots of variables
    # between different functions. So it's often better to use a python class
    # that contains the data and the functions.
    def __init__(self):
        # This is called when we first create the class
        
        # self.x means the variable belongs to the class
        # not just the function.
        self.source_curve = None
        self.getTheNurbsCurve()
        if not self.source_curve:
            return
            
        self.createWindow()
                        
    def getTheNurbsCurve(self):
        # Any function that belongs to a class should have "self"
        # as the first input. This tells the function 
        # "you have access to all the variables" held by the class.
        # Get the nurbs curve we will be using.
        
        self.source_curve = cmds.ls(sl=True, dag=True, typ="nurbsCurve")
        if not self.source_curve:
            cmds.warning('No curve was selected')
            return
        else:
            self.source_curve = self.source_curve[0]
        
        # Check if the curve has "attractionProfile"
        if not cmds.objExists(self.source_curve + '.attractionProfile'):
            cmds.warning('{} is missing "attractionProfile"'.format(self.source_curve))
            self.source_curve = None
            
    def createWindow(self):
        # Create the window
        cmds.window( title='Gradient Control For OptionVar')
        cmds.columnLayout()
        
        # Add the ramp widget
        cmds.gradientControlNoAttr(
            'attractionProfile', 
            h = 120,
            ck=0,
            changeCommand = self.rampModified,
        )
        cmds.setParent( '..' )
        self.setUpRamp()

        # Add the other widgets
        cmds.rowLayout(nc=3)
        cmds.floatFieldGrp('posFieldRadius', pre=3, l="Selected Position", nf=1, v1=0.000, s=1.0, cw=(1, 70), changeCommand = self.posModified)
        cmds.floatFieldGrp('valFieldRadius', pre=3, l="Selected Value", nf=1, v1=1.000, s=1.0, cw=(1, 70), changeCommand = self.valModified)
        # Update these float sliders to show the value from the selected key
        cmds.gradientControlNoAttr(
            'attractionProfile', 
            e = True,
            currentKeyChanged = self.currentKeyChanged,
        )
        
        self.currentKeyChanged('')

        cmds.optionMenuGrp('interpMenuRadius', cw=(1, 50), l="Interpolation")
        cmds.menuItem(l="  None  ")
        cmds.menuItem(l="  Linear  ")
        cmds.menuItem(l="  Smooth  ")
        cmds.menuItem(l="  Spline  ")
        cmds.setParent( '..' )
        cmds.showWindow()
    
    def currentKeyChanged(self, currentData):
        currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
        if currentIndex == -1:
            return

        # Get the values for that key
        currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
        if not currentData:
            return         

        a = currentData.split(',')
        posVal = float(a[currentIndex*3])
        valVal = float(a[currentIndex*3+1])
        
        # Update the two floatFields
        cmds.floatFieldGrp('posFieldRadius', e = True, v =[posVal]*4)
        cmds.floatFieldGrp('valFieldRadius', e = True, v =[valVal]*4)
    
    def posModified(self, val):
        # Change the position of the selected key
        currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
        if currentIndex == -1:
            return
        
        currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
        a = currentData.split(',')
        a[currentIndex*3+1] = str(val)
        newData = ','.join(a)
        currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)

    def valModified(self, val):
        # Change the position of the selected key
        currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
        if currentIndex == -1:
            return
        
        currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
        a = currentData.split(',')
        a[currentIndex*3] = str(val)
        newData = ','.join(a)
        currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)
    
    def setUpRamp(self):
        # This function will make the ramp in the UI match the existing values of the ramp curve
        attributeData = []
        for i in cmds.getAttr(self.source_curve + ".attractionProfile", mi = True) or []:
            attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Position".format(self.source_curve, i)))
            attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(self.source_curve, i)))
            attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(self.source_curve, i)))
        
        # Set the data
        currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = str(attributeData))

    def rampModified(self, gradient_data):
        # Convert the gradient_data from a unicode (string) to a list 
        gradient_data = gradient_data.split(',')# Split the string up 
        
        # We will convert each element from a string to a float or int
        data_list = []
        
        for i,s in enumerate(gradient_data):
            # If the elements index is a multiple of 3
            # we want it to be an int
            # since every third element represents the interpolation
            # we check for this by using modular arithmetic (the % sysmbol)
            if (i+1)%3 == 0:
                data_list.append(float(s))
            else:
                data_list.append(float(s))
                
        # Then we will split up the data into chunks of 3
        # so instead of having 1 list of data, we have a list of lists
        # where each element contains all the data for a specific cv
        attraction_profile = []
        for i in range(len(data_list)/3):
            attraction_profile.append(data_list[i*3:(i*3)+3])
        
        # Do whatever it is you were doing with that data
        # THIS WILL AFFECT ALL THE CURVES NOT JUST THE ONE YOU 
        # SELECTED WHEN YOU LAUNCHED THE WINDOW
        for eachCurve in (cmds.ls(sl=True, dag=True, typ="nurbsCurve") or []) + [self.source_curve]:
            for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
                cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True)
                
            for i, (pos, value, interp) in enumerate(attraction_profile):
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(eachCurve, i), pos)
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(eachCurve, i), value)
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(eachCurve, i), interp)

AttractionProfileWindow()

This code is mostly complete, except for setting the interpolation when you change the enum widget in the UI.

 

 

 

 

But what could be easier is using the gradientControl. The difference between the two is that the gradientControl gets linked to a specified attribute, and the gradientControlNoAttr does not (hence the NoAttr). Having them linked allows you to use undo and redo without messing up the UI but doesn't give you floatField for setting precise values. gradientControl only links to one attribute one one node you would need to use the "visbleChangeCommand" to update the attributes on the other selected curves but that's something that we already do in the code above.

That's a lot of info, so let me know if things don't make sense.

 

class AttractionProfileWindow():
    # If your working on a UI it can be annoying to pass lots of variables
    # between different functions. So it's often better to use a python class
    # that contains the data and the functions.
    def __init__(self):
        # This is called when we first create the class
        
        # self.x means the variable belongs to the class
        # not just the function.
        self.source_curve = None
        self.getTheNurbsCurve()
        if not self.source_curve:
            return
            
        self.createWindow()
            
    def getTheNurbsCurve(self):
        # Any function that belongs to a class should have "self"
        # as the first input. This tells the function 
        # "you have access to all the variables" held by the class.
        # Get the nurbs curve we will be using.
        
        self.source_curve = cmds.ls(sl=True, dag=True, typ="nurbsCurve")
        if not self.source_curve:
            cmds.warning('No curve was selected')
        else:
            self.source_curve = self.source_curve[0]
        
        # Check if the curve has "attractionProfile"
        if not cmds.objExists(self.source_curve + '.attractionProfile'):
            cmds.warning('{} is missing "attractionProfile"'.format(self.source_curve))
            self.source_curve = None

    def createWindow(self):
        # Create the window
        cmds.window( title='Gradient Control For OptionVar')
        cmds.columnLayout()
        
        # Add the ramp widget
        cmds.gradientControl(
            'attractionProfile', 
            h=120, 
            at='{}.attractionProfile'.format(self.source_curve)
        )
        cmds.setParent( '..' )
        
        x = cmds.attrEnumOptionMenuGrp( l='Output Format',
                            at='defaultRenderGlobals.imageFormat',
                            ei=[(0, 'None'),(1, 'Linear'), (2, 'Smooth'),
                                (3, 'Spline')])

        cmds.setParent( '..' )
        
        cmds.gradientControl(
            'attractionProfile', 
            e = True,
            selectedInterpControl=x
        )        
        cmds.showWindow()
           
    def addAttributes(self):
        cmds.addAttr(self.source_curve, ln = 'attractionProfile', nc = 3, at = 'compound', multi = True)
        cmds.addAttr(self.source_curve, ln = 'attractionProfile_Position', at = 'float', parent = 'attractionProfile')
        cmds.addAttr(self.source_curve, ln = 'attractionProfile_FloatValue', at = 'float', parent = 'attractionProfile')
        cmds.addAttr(
            self.source_curve,
            ln = 'attractionProfile_Interp', 
            at = 'enum', 
            p = 'attractionProfile',
            enumName = 'None:Linear:Smooth:Spline'
        )

AttractionProfileWindow()

 

0 Likes
Message 3 of 33

1140534355
Enthusiast
Enthusiast

Thank you so much, but it seems that batch modification can't be done now. What's more, if I make some minor modifications and put them in my defined window, I will report an error name 'self' is not defined #. Why? Finally, I want to let the curve return to the default after I open the script through clear. I don't know if I did it right.

import maya.cmds as cmds
import maya.mel as mel
##########################
def getTheNurbsCurve(self):
 
    self.source_curve = cmds.ls(sl=True, dag=True, typ="nurbsCurve")
    if not self.source_curve:
        cmds.warning('No curve was selected')
    else:
        self.source_curve = self.source_curve[0]
    
    # Check if the curve has "attractionProfile"
    if not cmds.objExists(self.source_curve + '.attractionProfile'):
        cmds.warning('{} is missing "attractionProfile"'.format(self.source_curve))
        self.source_curve = None

def addAttributes(self):
    cmds.addAttr(self.source_curve, ln = 'attractionProfile', nc = 3, at = 'compound', multi = True)
    cmds.addAttr(self.source_curve, ln = 'attractionProfile_Position', at = 'float', parent = 'attractionProfile')
    cmds.addAttr(self.source_curve, ln = 'attractionProfile_FloatValue', at = 'float', parent = 'attractionProfile')
    cmds.addAttr(self.source_curve,ln = 'attractionProfile_Interp', at = 'enum', p = 'attractionProfile',enumName = 'None:Linear:Smooth:Spline')
        
####clear                
###cmds.optionVar(clearArray = 'attractionProfile')
###cmds.optionVar(stringValueAppend=['attractionProfile', '1,1,2'])
###cmds.optionVar(stringValueAppend=['attractionProfile', '1,0,2'])        
             


cmds.gradientControl('attractionProfile', h=120, at='getTheNurbsCurve(self)'.format(self.source_curve))
x = cmds.attrEnumOptionMenuGrp( l='Output Format',at='defaultRenderGlobals.imageFormat',ei=[(0, 'None'),(1, 'Linear'), (2, 'Smooth'),(3, 'Spline')])    
cmds.gradientControl('attractionProfile', e = True,selectedInterpControl=x)  


​
0 Likes
Message 4 of 33

jmreinhart
Advisor
Advisor
Thank you so much, but it seems that batch modification can't be done now.

GradientControl only links to one attribute one one node. I thought you could use the "visibleChangeCommand" to trigger an update on the other selected curves when you edit a curve in the UI. But that's actually not what it does.

I tried using the dragCallback and dropCallback, but they seem nonfunctional. I have a few ideas that could get around this issue:

  • You could create an attributeChanged callback using the Maya API and use that to update the other curves when the UI updates the attributes that are linked to the UI.
    • That's getting very complicated and probably too much since you are new to Python.
  • You could use the first code from my previous reply instead, since that retained batch editing.
    • That would not having working undo and redo
  • You could make the UI update all the curves when you press a button, or maybe when you close the UI.
    • This might be an issue if you want to see the attributes change on all the curves as you use the UI.
    • This is the easiest solution and it keeps the simple GradientControl and therefore undo and redo.
  • Maybe a scriptJob could work too.
What's more, if I make some minor modifications and put them in my defined window, I will report an error name 'self' is not defined #. Why? 

So I think you should look into how to use "classes" in python because the way you are organizing your code is not correct. I won't go into details because there's lots of good resources on "classes".

 https://realpython.com/lessons/classes-python/ 

But basically the correct way of doing it is like this.

# A class is a collection of variables and functions
# that are all grouped together and can share data
# you can have multiple "instances" of a python "class"

class YourClass():
    #  __init__ is called when we first create the class
    def __init__(self):
        # Define some variables your class will need
        self.test_variable = None
        # Do some stuff
        ...
            
    def classFunctionA(self):
        ...

    def classFunctionB(self):
        ...   

# This creates an instance of the class
YourClass()

Each function that belongs to the class, and uses data that belongs to the class needs to be created underneath the class itself. So your error is happening because you did not create a class, but you from using "self" which looks for the class.

 


Finally, I want to let the curve return to the default after I open the script through clear. I don't know if I did it right.

So the gradientControl doesn't use optionVars, and optionVars aren't really what you need since they are intended to stay stored after you close the UI, but you will have new data from the loaded curve each time you use the UI. So the code for resetting the ramp would be like this.

    def clear(self):
        # To do the clear we need to edit the attribute not the ui
        # since gradientControl keys can't be edited via command
        for eachCurve in (cmds.ls(sl=True, dag=True, typ="nurbsCurve") or []) + [self.source_curve]:
            # Remove all the data
            for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
                cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True)
                
            # Add the two standard keys back in with linear interpolation
            attraction_profiled = [[0,0,1],[1,1,1]]
            for i, (pos, value, interp) in enumerate(attraction_profile):
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(eachCurve, i), pos)
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(eachCurve, i), value)
                cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(eachCurve, i), interp)

 

Hope that helps 🙂

 

 

 

 


Message 5 of 33

1140534355
Enthusiast
Enthusiast

Thank you. Actually, I also want to use the first method, but I will report an error # error: typeerror: file < Maya Console > line145: unsupported opera and type (s) for/:'int' and' list' #
I tried to change /3, but the control would be disrupted. By the way, I want to make a key frame above each point. I can only make one point using the Set_Key function, but I can't make any one. I wonder if it would be complicated to make a key frame.

##SET_KEY
def SET_KEY(CURVE = None, attr01 = ""):
    if CURVE== None:
        CURVE = cmds.ls(sl = True, dag = True, type = 'transform')
    elif type(CURVE) != list:
        CURVE = list(CURVE)   
    for i in CURVE:
                cmds.setKeyframe(i + attr01)
Message 6 of 33

jmreinhart
Advisor
Advisor
Thank you. Actually, I also want to use the first method, but I will report an error # error: typeerror: file < Maya Console > line145: unsupported opera and type (s) for/:'int' and' list' #

I tried to change /3, but the control would be disrupted. 


Well line 145 in my example code is 

 

for i in range(len(data_list)/3):

 

So it should be an integer divided by an integer. So I don't know if you have a typo or something, but the code works fine for me.

 

By the way, I want to make a key frame above each point. I can only make one point using the Set_Key function, but I can't make any one. I wonder if it would be complicated to make a key frame.

 

##SET_KEY
def SET_KEY(CURVE = None, attr01 = ""):
    if CURVE== None:
        CURVE = cmds.ls(sl = True, dag = True, type = 'transform')
    elif type(CURVE) != list:
        CURVE = list(CURVE)   
    for i in CURVE:
                cmds.setKeyframe(i + attr01)

 


You want to key these attributes?

jmreinhart_0-1665934355729.png

Just use something like this. 

 

source_curve = 'nurbsCircleShape2'
for i in cmds.getAttr(source_curve + '.attractionProfile', mi = True):
    cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_Position'.format(i))
    cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_FloatValue'.format(i))
    cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_Interp'.format(i))

 

 

 

Message 7 of 33

1140534355
Enthusiast
Enthusiast

I gave up the second method. The first method, I copy the code directly, is still wrong. Interpolation doesn't seem to work either. I tried to delete /3. The script can run, but it doesn't run correctly. And batch setting cannot be realized.

0 Likes
Message 8 of 33

jmreinhart
Advisor
Advisor
I gave up the second method. The first method, I copy the code directly, is still wrong. Interpolation doesn't seem to work either. I tried to delete /3. The script can run, but it doesn't run correctly. And batch setting cannot be realized.

I said in my first response "This code is mostly complete, except for setting the interpolation when you change the enum widget in the UI." So you still need to make the widget update the interpolation the same way I did for the two float fields. Use that part of the code for reference.

 

Try adding this to line 144. This won't fix this issue, but it will help you figure out what part of the operation python thinks is a float instead of an int.

print type(len(data_list)/3)

 

Message 9 of 33

jmreinhart
Advisor
Advisor

jmreinhart_0-1665938212205.png

 

Actually I found the issue. Python 3 produces a float if you divide two ints.

Just put an int() around the 

len(data_list)/3

and then it will work

0 Likes
Message 10 of 33

1140534355
Enthusiast
Enthusiast

我试着在maya2020上运行,发现可以,但是需要在Maya 2022上运行。而为什么这条曲线是垂直反转的呢?怎么才能调整到正常状态?

Message 11 of 33

jmreinhart
Advisor
Advisor

The X and Y axis on the ramp seem like they are switched. Try switching the setAttr values on line 155 and 156.

Message 12 of 33

1140534355
Enthusiast
Enthusiast

Thank you, it can run now. When I copy the code to my custom window, two errors will appear, but it will not affect the use. I wonder if this mistake can be removed?     If it's convenient, you can leave a Paypal number. Let me buy you a cup of coffee.

import maya.cmds as cmds
import maya.mel as mel
#################
  
def getTheNurbsCurve():      
    source_curve = cmds.ls(sl=True, dag=True, typ="nurbsCurve")
    if not source_curve:
        cmds.warning('No curve was selected')
        return
    else:
        source_curve = source_curve[0]
    
    # Check if the curve has "attractionProfile"
    if not cmds.objExists(source_curve + '.attractionProfile'):
        cmds.warning('{} is missing "attractionProfile"'.format(source_curve))
        source_curve = None
            

    
def currentKeyChanged(currentData):
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return

    # Get the values for that key
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    if not currentData:
        return         

    a = currentData.split(',')
    posVal = float(a[currentIndex*3])
    valVal = float(a[currentIndex*3+1])
        
    # Update the two floatFields
    
    cmds.floatFieldGrp('posFieldRadius', e = True, v =[posVal]*4)
    cmds.floatFieldGrp('valFieldRadius', e = True, v =[valVal]*4)
    
def posModified(val):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3+1] = str(val)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)

def valModified(val):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3] = str(val)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)
    
def setUpRamp():
    # This function will make the ramp in the UI match the existing values of the ramp curve
    attributeData = []
    for i in cmds.getAttr(source_curve + ".attractionProfile", mi = True) or []:
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Position".format(source_curve, i)))
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(source_curve, i)))
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(source_curve, i)))
    
    # Set the data
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = str(attributeData))

def rampModified(gradient_data):
    # Convert the gradient_data from a unicode (string) to a list 
    gradient_data = gradient_data.split(',')# Split the string up 
    
    # We will convert each element from a string to a float or int
    data_list = []
    
    for i,s in enumerate(gradient_data):
        # If the elements index is a multiple of 3
        # we want it to be an int
        # since every third element represents the interpolation
        # we check for this by using modular arithmetic (the % sysmbol)
        if (i+1)%3 == 0:
            data_list.append(float(s))
        else:
            data_list.append(float(s))
            
    # Then we will split up the data into chunks of 3
    # so instead of having 1 list of data, we have a list of lists
    # where each element contains all the data for a specific cv
    print (type(len(data_list)/3))    
    attraction_profile = []
    for i in range(int(len(data_list)/3)):
        attraction_profile.append(data_list[i*3:(i*3)+3])
                
    source_curve=getTheNurbsCurve()
    # Do whatever it is you were doing with that data
    # THIS WILL AFFECT ALL THE CURVES NOT JUST THE ONE YOU 
    # SELECTED WHEN YOU LAUNCHED THE WINDOW
    for eachCurve in (cmds.ls(sl=True, dag=True, typ="nurbsCurve") or []) + [source_curve]:
        for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
            cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True)
            
        for i, (pos, value, interp) in enumerate(attraction_profile):
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(eachCurve, i), value)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(eachCurve, i), pos)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(eachCurve, i), interp)                                                                                            

########wingdow
     
if cmds.window("YetiTool", exists=True):
    cmds.deleteUI("YetiTool")

cmds.window("YetiTool", t="YetiTool",
          w=460, h=500, sizeable=True, mnb=True, mxb=False ,menuBar=True)
                    
cmds.scrollLayout('scrollLayout')

############################

# Add the ramp widget

cmds.gradientControlNoAttr('attractionProfile',w=460, h = 120,ck=0,changeCommand = rampModified,)

cmds.separator(h=5)
# Add the other widgets
cmds.rowLayout(nc=3)
cmds.floatFieldGrp('posFieldRadius', pre=3, l="Selected Position", nf=1, v1=0.000, s=1.0, cw=(1, 100), changeCommand = posModified)

cmds.setParent( '..' )
cmds.rowLayout(nc=2)
cmds.floatFieldGrp('valFieldRadius', pre=3, l="Selected Value", nf=1, v1=1.000, s=1.0, cw=(1, 100), changeCommand = valModified)
# Update these float sliders to show the value from the selected key
cmds.gradientControlNoAttr('attractionProfile', e = True,currentKeyChanged = currentKeyChanged,)

cmds.optionMenuGrp('attractionProfile', cw=(1, 153), l="Interpolation")
cmds.menuItem(l="  None  ")
cmds.menuItem(l="  Linear  ")
cmds.menuItem(l="  Smooth  ")
cmds.menuItem(l="  Spline  ")
cmds.setParent( '..' )

cmds.showWindow("YetiTool")

#########
# Error: line 1: No object matches name: AttributeEditor|MainAttributeEditorLayout|formLayout98|AEmenuBarLayout|AErootLayout|AEStackLayout|AErootLayoutPane|AEbaseFormLayout|AEcontrolFormLayout|AttrEdnurbsCurveFormLayout|scrollLayout3|columnLayout4|frameLayout65|columnLayout323|columnLayout324|attractionProfileRampForm|attractionProfileSi # 
# Error: TypeError: file <maya console> line 104: unsupported operand type(s) for +: 'NoneType' and 'str' # 

 

0 Likes
Message 13 of 33

jmreinhart
Advisor
Advisor
Accepted solution

So the error is coming from this part, specifically the first line.

    for eachCurve in (cmds.ls(sl=True, dag=True, typ="nurbsCurve") or []) + [source_curve]:
        for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
            cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True)

Basically the first line creates a list of the selected curves in the scene plus the "source_curve".

But where is source curve getting defined?

Its supposed to come from this line 

source_curve=getTheNurbsCurve()

which calls this code. 

def getTheNurbsCurve():      
    source_curve = cmds.ls(sl=True, dag=True, typ="nurbsCurve")
    print source_curve
    if not source_curve:
        cmds.warning('No curve was selected')
        return
    else:
        source_curve = source_curve[0]
    
    # Check if the curve has "attractionProfile"
    if not cmds.objExists(source_curve + '.attractionProfile'):
        cmds.warning('{} is missing "attractionProfile"'.format(source_curve))
        source_curve = None
            

Notice that this function gets the source curve but it doesn't return it.  Just add 

return source_curve

 to the end of the function.

0 Likes
Message 14 of 33

1140534355
Enthusiast
Enthusiast

Hi, excuse me again, I set interp with reference to the selected parameter, but it can only be set at present, and I made a mistake there.

def interpModified(Interp):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3+2] = str(interp)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)

        
def currentKeyChanged(currentData):
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return

    # Get the values for that key
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    if not currentData:
        return         

    a = currentData.split(',')
    posVal = float(a[currentIndex*3])
    valVal = float(a[currentIndex*3+1])
    interp = int(a[currentIndex*3+2])   
    # Update the two floatFields                            


cmds.optionMenuGrp('Interpolation', cw=(1, 153), l="Interpolation", changeCommand = InterpModified)    
cmds.menuItem(l="  None  ")
cmds.menuItem(l="  Linear  ")
cmds.menuItem(l="  Smooth  ")
cmds.menuItem(l="  Spline  ")
0 Likes
Message 15 of 33

jmreinhart
Advisor
Advisor

Sorry, what's your question?

0 Likes
Message 16 of 33

1140534355
Enthusiast
Enthusiast

Sorry, I only recently learned about the optionMenuGrp menu. When I set the interpolation, I can only make (none) take effect. I don't know what I did wrong.530407f057607e34599ee37d5e70594.png

def interpModified(Interp):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3+2] = str(interp)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)

        
def currentKeyChanged(currentData):
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return

    # Get the values for that key
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    if not currentData:
        return         

    a = currentData.split(',')
    posVal = float(a[currentIndex*3])
    valVal = float(a[currentIndex*3+1])
    interp = int(a[currentIndex*3+2])   
    # Update the two floatFields                            
    cmds.optionMenuGrp('interpMenuRadius', e = True, v =[interp]*4)
    cmds.floatFieldGrp('posFieldRadius', e = True, v =[posVal]*4)
    cmds.floatFieldGrp('valFieldRadius', e = True, v =[valVal]*4)    
                

cmds.optionMenuGrp('interpMenuRadius', cw=(1, 153), l="Interpolation", changeCommand = InterpModified)    
cmds.menuItem(l="  None  ")
cmds.menuItem(l="  Linear  ")
cmds.menuItem(l="  Smooth  ")
cmds.menuItem(l="  Spline  ")
cmds.optionMenuGrp('interpMenuRadius', e=1, sl=3)​
0 Likes
Message 17 of 33

1140534355
Enthusiast
Enthusiast
Hello, I have tried many times, but I still can't set the interpolation of the curve. Can you help me see the reason?
0 Likes
Message 18 of 33

jmreinhart
Advisor
Advisor

Could you please post your full code?

0 Likes
Message 19 of 33

2368457978
Enthusiast
Enthusiast
import maya.cmds as cmds
import maya.mel as mel

def getTheNurbsCurve():      
    source_curve = cmds.ls(sl=True, dag=True)
    if not source_curve:
        cmds.warning('No curve was selected')
        return
    else:
        source_curve = source_curve[0]
    
    # Check if the curve has "attractionProfile"
    if not cmds.objExists(source_curve + '.attractionProfile'):
        cmds.warning('{} is missing "attractionProfile"'.format(source_curve))
        source_curve = None   
    return source_curve        
    
def currentKeyChanged(currentData):
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return

    # Get the values for that key
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    if not currentData:
        return         
    a = currentData.split(',')
    posVal = float(a[currentIndex*3])
    valVal = float(a[currentIndex*3+1])
    interp = int(a[currentIndex*3+2])    
    # Update the two floatFields    
    cmds.floatFieldGrp('posFieldRadius', e = True, v =[posVal]*4)
    cmds.floatFieldGrp('valFieldRadius', e = True, v =[valVal]*4)
    interp=cmds.optionMenu(interp, query=True, value=[interp]*4)  
              
def posModified(val):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3+1] = str(val)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)

def valModified(val):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3] = str(val)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)
        
def interpModified(val):
    # Change the position of the selected key
    currentIndex = cmds.gradientControlNoAttr('attractionProfile', q = True, currentKey = True)
    if currentIndex == -1:
        return
    
    currentData = cmds.gradientControlNoAttr('attractionProfile', q = True, asString = True)
    a = currentData.split(',')
    a[currentIndex*3+2] = str(val)
    newData = ','.join(a)
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = newData)                
                                
    
def setUpRamp():  
    # This function will make the ramp in the UI match the existing values of the ramp curve
    attributeData = []
    for i in cmds.getAttr(source_curve + ".attractionProfile", mi = True) or []:
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Position".format(source_curve, i)))
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(source_curve, i)))
        attributeData.append(cmds.getAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(source_curve, i)))    
    # Set the data
    currentData = cmds.gradientControlNoAttr('attractionProfile', e = True, asString = str(attributeData))

def rampModified(gradient_data):
    # Convert the gradient_data from a unicode (string) to a list 
    gradient_data = gradient_data.split(',')# Split the string up 
    
    # We will convert each element from a string to a float or int
    data_list = []
    
    for i,s in enumerate(gradient_data):
        # If the elements index is a multiple of 3
        # we want it to be an int
        # since every third element represents the interpolation
        # we check for this by using modular arithmetic (the % sysmbol)
        if (i+1)%3 == 0:
            data_list.append(float(s))
        else:
            data_list.append(float(s))
            
    # Then we will split up the data into chunks of 3
    # so instead of having 1 list of data, we have a list of lists
    # where each element contains all the data for a specific cv
    print (type(len(data_list)/3))    
    attraction_profile = []
    for i in range(int(len(data_list)/3)):
        attraction_profile.append(data_list[i*3:(i*3)+3])
                
    source_curve=getTheNurbsCurve()
    # Do whatever it is you were doing with that data
    # THIS WILL AFFECT ALL THE CURVES NOT JUST THE ONE YOU 
    # SELECTED WHEN YOU LAUNCHED THE WINDOW
    for eachCurve in (cmds.ls(sl=True, dag=True) or []) + [source_curve]:
        for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
            cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True) 
                
        for i, (pos, value, interp) in enumerate(attraction_profile):
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(eachCurve, i), value)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(eachCurve, i), pos)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(eachCurve, i), interp)
                        
                                                
def clear():
    # To do the clear we need to edit the attribute not the ui
    # since gradientControl keys can't be edited via command
    source_curve=getTheNurbsCurve()
    for eachCurve in (cmds.ls(sl=True, dag=True, typ="nurbsCurve") or []) + [source_curve]:
        # Remove all the data
        for i in cmds.getAttr(eachCurve + ".attractionProfile", mi = True) or []:
            cmds.removeMultiInstance("{}.attractionProfile[{}]".format(eachCurve, i), b=True)
            
        # Add the two standard keys back in with linear interpolation
        attraction_profiled = [[0,1,1],[1,1,1]]
        for i, (pos, value, interp) in enumerate(attraction_profile):
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Position".format(eachCurve, i), value)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_FloatValue".format(eachCurve, i), pos)
            cmds.setAttr("{}.attractionProfile[{}].attractionProfile_Interp".format(eachCurve, i), interp)                                                                                              

                       
def set__key():      
    source_curve = getTheNurbsCurve()
    for i in cmds.getAttr(source_curve + '.attractionProfile', mi = True):
        cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_Position'.format(i))
        cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_FloatValue'.format(i))
        cmds.setKeyframe(source_curve + '.attractionProfile[{}].attractionProfile_Interp'.format(i))                                                

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

cmds.window("mayaTool", t="mayaTool",
          w=460, h=500, sizeable=True, mnb=True, mxb=False ,menuBar=True)
                    
 
                                        
###
cmds.scrollLayout('scrollLayout')
cmds.gradientControlNoAttr('attractionProfile', w=460, h = 120,ck=0,changeCommand = rampModified,)
cmds.rowLayout(nc=2)
cmds.button("reset", w=230, h=30, c="clear()", bgc=(0.4, 0.2, 0.3))
cmds.button("Set key", w=230, h=30, c="set__key()", bgc=(1, 0.05, 0))
cmds.setParent( '..' )    
cmds.separator(h=5)
# Add the other widgets
cmds.rowLayout(nc=3)         
cmds.floatFieldGrp('posFieldRadius', pre=3, l="Selected Position", nf=1, v1=0.000, s=1.0, cw=(1, 70), changeCommand = posModified)

cmds.floatFieldGrp('valFieldRadius', pre=3, l="Selected Value", nf=1, v1=1.000, s=1.0, cw=(1, 70), changeCommand = valModified)

cmds.gradientControlNoAttr('attractionProfile', e = True,currentKeyChanged = currentKeyChanged,)
interp=cmds.optionMenuGrp( cw=(1, 70), l="Interpolation",changeCommand = interpModified)
cmds.menuItem(l="  None  ")
cmds.menuItem(l="  Linear  ")
cmds.menuItem(l="  Smooth  ")
cmds.menuItem(l="  Spline  ")
cmds.setParent( '..' )

cmds.showWindow("mayaTool")

There are still some problems with resetting. I can't seem to get the correct function. The key frame will disappear when I move the ramp curve points.

0 Likes
Message 20 of 33

1140534355
Enthusiast
Enthusiast
Thank you, I sent it with another account.
0 Likes