Asaph Kim
Asaph Kim

Reputation: 33

Maya Python: How do i call on and edit attributes of an object in one function when that object was created in another function?

The Plan:

I've been working on a script for measuring the distance between 2 vertices that the user selects and scaling up or down that the object based on a desired length between those vertices.

The Problem:

Error states that it cannot locate the textFieldButtonGrp object that I create in one of my functions.

i basically put my window format stuff into a single function:

def window_presets():
    '''
    presets for UI window
    '''
    if mc.window("Distance Scale Tool", exists=True):
        mc.deleteUI("Distance Scale Tool")
    mc.window("Distance Scale Tool", t="Distance Based Scale Tool")
    mc.rowColumnLayout(numberOfColumns=2, 
                      columnAttach=(1, 'left', 0), 
                      columnWidth=[(1,100), (2,300)])
    mc.text(l="Current Length")
    current_length = mc.textFieldButtonGrp("Current Length", 
                                          editable=False, 
                                          text="{0}".format(refresh_current_dist()), 
                                          buttonLabel="Refresh", 
                                          buttonCommand=refresh_current_dist)

    mc.text(l="Desired Length")
    desired_length = mc.textFieldButtonGrp("Desired Length", 
                                          buttonLabel="Scale",  
                                          buttonCommand=scale_dist, 
                                          tcc=refresh_scale_factor)
                                 
    mc.showWindow()

i want the refresh button to call another function that edits the textFieldButtonGrp that i created:

def refresh_textfield(distance):
    if mc.textFieldButtonGrp("Current Length", exists=True):
        mc.textFieldButtonGrp("Current Length", 
                              edit=True, 
                              text="{0}".format(distance))   
    else:
        print "Current Length dont exist"

but "Current Length".... it doesnt seem to exist....

same with "Desired Length"....

Heres the full script:

##  ((Ax - Bx)**2 + (Ay - By)**2 + (Az - Bz)**2)**0.5

import maya.cmds as mc
import math

def window_presets():
    '''
    presets for UI window
    '''
    if mc.window("Distance Scale Tool", exists=True):
        mc.deleteUI("Distance Scale Tool")
    mc.window("Distance Scale Tool", t="Distance Based Scale Tool")
    mc.rowColumnLayout(numberOfColumns=2, 
                      columnAttach=(1, 'left', 0), 
                      columnWidth=[(1,100), (2,300)])
    mc.text(l="Current Length")
    current_length = mc.textFieldButtonGrp("Current Length", 
                                          editable=False, 
                                          text="{0}".format(refresh_current_dist()), 
                                          buttonLabel="Refresh",                          buttonCommand=refresh_current_dist)
    
    mc.text(l="Desired Length")
    desired_length = mc.textFieldButtonGrp("Desired Length", 
                                          buttonLabel="Scale",  
                                          buttonCommand=scale_dist, 
                                          tcc=refresh_scale_factor)
                                     
    mc.showWindow()
    

def get_object_name():
    selPoints = mc.ls(sl=True)
    obj_name = selPoints[0].split('.')[0]
    return obj_name
    
def get_coordinates():
    '''
    Gets coordinates of selected points and gets distance between them
    '''
    selPoints = mc.ls(sl=True)
    obj_name = get_object_name()
    print obj_name
    vtxCoordList = mc.xform(selPoints, 
                            query=True, 
                            translation=True, 
                            ws=True)
    Ax, Ay, Az = vtxCoordList[:-3]
    Bx, By, Bz = vtxCoordList[3:]
     
    return (Ax, Bx, Ay, By, Az, Bz)

def calculate_distance(Ax, Bx, Ay, By, Az, Bz):
    '''
    Determines distance between 2 coordinates on single mesh.
    
    Below are formulas for distance based on single axis:
     
    dx = ((Ax - Bx)**2)**0.5
    print "Distance on X axis is: {0}".format(dx) #distance on X axis
    dy = ((Ay - By)**2)**0.5
    print "Distance on Y axis is: {0}".format(dy) #distance on Y axis
    dz = ((Az - Bz)**2)**0.5
    print "Distance on Z axis is: {0}".format(dz) #distance on Z axis
    
    '''
    distance = math.sqrt((Ax - Bx)**2 + (Ay - By)**2 + (Az - Bz)**2)
         
    print "the distance between points is {0}".format(distance)
    
    return distance

def refresh_textfield(distance):
    if mc.textFieldButtonGrp("Current Length", exists=True):
        mc.textFieldButtonGrp("Current Length", 
                              edit=True, 
                              text="{0}".format(distance))   
    else:
        print "Current Length dont exist"
def refresh_current_dist():
    '''
    returns current distance
    '''

    current_coordinates = get_coordinates()
    current_distance = calculate_distance(*current_coordinates)

    refresh_textfield(current_distance)

    return current_distance


    
def refresh_scale_factor(sf):
    '''
    returns factor by which object will be scaled 
    ''' 
   
    current_distance = refresh_current_dist()
          
    scale_factor = (float(sf))/(float(current_distance))
    print "dist btwn pnts is d: {0}".format(current_distance)
    print "sf is {0}".format(sf)
    print "user input is {0}".format(sf)
    print "scale factor is {0}".format(scale_factor)
    print "-"*10
    return scale_factor

 
def scale_dist():
    '''
    scale object to match measurement
    '''
    user_input = float(mc.textFieldButtonGrp("Desired Length",
                                             query=True, 
                                             text=True))
    scale_factor = refreshScaleFactor(user_input)                                                                                          
    mc.makeIdentity(get_object_name(), 
                    apply=True, 
                    translate=1, 
                    rotate=1, 
                    scale=1, 
                    normal=0, 
                    preserveNormals=1)#freeze transformations
    mc.DeleteAllHistory()     
    mc.scale(scale_factor, scale_factor, scale_factor, get_object_name())
    print "you scaled by {0}".format(scale_factor)
    mc.makeIdentity(get_object_name(), 
                    apply = True, 
                    translate=1, 
                    rotate=1, 
                    scale=1, 
                    normal=0, 
                    preserveNormals=1)#freeze transformations



if __name__ == '__main__':
    window_presets()

Upvotes: 0

Views: 971

Answers (3)

theodox
theodox

Reputation: 12218

@DrHaze is correct, your UI elements will not have spaces in their names: when you create

In general,. using a class is the easiest way to make sure that the UI elements you need are accessible to your functions: see here or here and extended discussion here

If you don't want to use a class you will need to make sure that the functions get the real name of your UI elements, not the name you are expecting.

Upvotes: 0

DrHaze
DrHaze

Reputation: 1318

Solution:

Remove the space in "Current Length" and this will fix your error.

Naming notes:

Consider applying labels the same naming as the one you are using on functions. I usually name them this way:

"<mine or company initials>_<ToolName>_<WidgetName>"

In you case this will be something like "ak_VertexDistance_InputCurrentLength".

Why this naming? Few months ago I was writing a script to save a Maya scene somewhere on the network. I was trying to add some items to an optionMenu to my window, but whatever I was trying, the optionMenu remained empty. After two hours of unsuccessful researches, I realised that the item were added to an other optionMenu in an other of my tools. The widgets had the same generic name.

Your initials are optionnal but adding a <ToolName> is in my opinion mandatory if you want to differenciate the widgets of your different tools.

Upvotes: 1

Darkgaze
Darkgaze

Reputation: 2573

This can be resolved two ways I think of:

The first is to create a CLASS surrounding all your functions and saving the textFieldButtonGrp in a class variable self.textField = textFieldButtonGrp(...). This means creating a class encapsulating all of your functions, adding an __init__(self,*args) function together with the rest, and probably adding self. everywhere it fails (function calls, calling variables...). And, as a plus, you will be able to save every item from the UI in a class variable, and use it in any funcion using self.variableName

The second aproach is to create the textField, keep it in a variable, and then pass it as an argument using that buttonCommand=partial(functionname,variable)

Where partial comes from:

from functools import partial

But the way to do this is first create the text field without the buttonCommand, and then execute the creation of the textField using edit mode (not create mode) Short explanation about execution modes

textFieldButtonGrp(variableName, edit=True, buttonCommand=partial(function,variableName))

So you edit the text field to add this buttonCommand afterwards.

I haven't tested this, but I'm sure any of them will work, specially the first. Hope this helps.

Upvotes: 0

Related Questions