Functions Undefined

In the following code i have defined a function MoveCG(obj) where i pass it and object. In the LineUpLetters function i try to call MoveCG but i get an error (MoveCG is not defined). I don’t understand why not.


import bpy
import math
import mathutils
from mathutils import Vector
    
class FollowingLetters(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Following Letters"
    bl_idname = "Following Letters"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
#    bl_context = "object"
       
    #
    #=================================================================
    # 
    def draw(self, context):
        
        layout = self.layout
        obj = context.object


 #       row = layout.row()
 #       row.label(text="Hello world!", icon='WORLD_DATA')


        row = layout.row()
        row.label(text="Active Text object: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")
        
    def GetCurveLength(name):
        
        #deselect everything
        bpy.ops.object.select_all(action='DESELECT')


        curveObj = bpy.data.objects[name] 
        curveObj.select = True    


        bpy.context.scene.objects.active = curveObj


        #       obj_name_original = context.active_object.name
        bpy.ops.object.duplicate_move()
           
        # the duplicate is active, apply all transforms to get global coordinates
        bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)


        # convert to mesh
        bpy.ops.object.convert(target='MESH', keep_original=False)


        #curveObj = bpy.data.objects["NurbsPath.001"] 
        curveData = bpy.context.active_object.data
           
        edge_length = 0
        for edge in curveData.edges:
           vert0 = curveData.vertices[edge.vertices[0]].co
           vert1 = curveData.vertices[edge.vertices[1]].co
           edge_length += (vert0-vert1).length


        # deal with trailing float smear
        edge_length = '{:.6f}'.format(edge_length)
        
        #delete extras
        bpy.ops.object.delete()
        
        #re-install original object
        bpy.context.scene.objects.active = bpy.data.objects[name]
        
        return edge_length


    #move cg to center of letter at base
    def MoveCG(obj):
    
        obj.select = True
        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
        
        bpy.context.area.type = 'VIEW_3D'
        bpy.ops.view3d.snap_cursor_to_selected()
        cursor = bpy.context.scene.cursor_location
        bpy.context.area.type = 'TEXT_EDITOR'   
        
         #get cursor loaction
        print("Object ",obj.name)
        print("Cursor location ",cursor[0],cursor[1],cursor[2])
        obj.select = False   


    def LineUpLetters(selected):
        
        origTextObject = selected


        #get list of letters
        letters = list(selected.data.body)
        numLetters = len(letters)
           
        print(numLetters)
        print(letters)


        # convert letters to mesh
        bpy.ops.object.convert()


        #remove doubles
        bpy.ops.object.mode_set(mode='EDIT')


        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.mesh.remove_doubles()


        bpy.ops.object.mode_set(mode='OBJECT')




        # seperate into individual letters
        bpy.ops.mesh.separate(type='LOOSE')


        selected=bpy.context.selected_objects
        numObj=len(selected)
        last=selected[numObj-1]
        last.name = letters[0]
        MoveCG(last)


        count = 1
        for obj in selected:
            if count >= numObj:
                break
            obj.name = letters[count]
            MoveCG(obj)
            
            count += 1


            
        print("Number of Individual Objects ",numObj)


        print("Finished ",numObj," objects")       
        
#
#=================================================================
#


    # Get the current selected text object
    selected=bpy.context.scene.objects.active
    objType = getattr(selected, 'type', '')
    if objType == 'FONT':
       LineUpLetters(selected)
       bpy.ops.object.select_all(action='DESELECT')
       
       curveLength = GetCurveLength("NurbsPath")
       print("Curve Length = ",curveLength)
        




def register():
    bpy.utils.register_class(FollowingLetters)




def unregister():
    bpy.utils.unregister_class(FollowingLetters)




if __name__ == "__main__":
    register()



Since the function and the call are inside the FollowingLetters class, use self.MoveCG() instead :wink:

I tried that and get ‘self’ not defined. What i don’t understand is that MoveCG is a member of the FollowingLetters class, so why can’t you call it from some other function in that same class.

The use of “self” seems very confusing, as to when to use


import bpy
import math
import mathutils
from mathutils import Vector


cgXLoc = []


class FollowingLetters(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Following Letters"
    bl_idname = "Following Letters"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
#    bl_context = "object"
       
    #
    #=================================================================
    # 
    def draw(self, context):
        
        layout = self.layout
        obj = context.object


 #       row = layout.row()
 #       row.label(text="Hello world!", icon='WORLD_DATA')


        row = layout.row()
        row.label(text="Active Text object: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")
        
    def GetCurveLength(name):
        
        #deselect everything
        bpy.ops.object.select_all(action='DESELECT')


        curveObj = bpy.data.objects[name] 
        curveObj.select = True    


        bpy.context.scene.objects.active = curveObj


        #       obj_name_original = context.active_object.name
        bpy.ops.object.duplicate_move()
           
        # the duplicate is active, apply all transforms to get global coordinates
        bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)


        # convert to mesh
        bpy.ops.object.convert(target='MESH', keep_original=False)


        curveData = bpy.context.active_object.data
           
        edge_length = 0
        for edge in curveData.edges:
           vert0 = curveData.vertices[edge.vertices[0]].co
           vert1 = curveData.vertices[edge.vertices[1]].co
           edge_length += (vert0-vert1).length


        # deal with trailing float smear
        edge_length = '{:.6f}'.format(edge_length)
        
        #delete extras
        bpy.ops.object.delete()
        
        #re-install original object
        bpy.context.scene.objects.active = bpy.data.objects[name]
        
        return edge_length


    #move cg to center of letter at base
    def MoveCG(obj):
    
        obj.select = True
        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
        
        bpy.context.area.type = 'VIEW_3D'
        bpy.ops.view3d.snap_cursor_to_selected()
        cursor = bpy.context.scene.cursor_location
        bpy.context.area.type = 'TEXT_EDITOR'   
        
         #get cursor loaction
        print("Object ",obj.name)
        print("Cursor location ",cursor[0],cursor[1],cursor[2])
        obj.select = False   
        
    def LineUpLetters(selected,textThickness,curveName):
        
        origTextObject = selected


        #get list of letters
        letters = list(selected.data.body)
        numLetters = len(letters)
           
        print(numLetters)
        print(letters)
        
        LettersData = selected.data
        LettersData.extrude = textThickness
        
        # convert letters to mesh
        bpy.ops.object.convert()


        #remove doubles
        bpy.ops.object.mode_set(mode='EDIT')


        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.mesh.remove_doubles()


        bpy.ops.object.mode_set(mode='OBJECT')
        
        # seperate into individual letters
        bpy.ops.mesh.separate(type='LOOSE')


        selected=bpy.context.selected_objects
        numObj=len(selected)
        last=selected[numObj-1]
        last.name = letters[0]
        #add edge split modifier
        obj = bpy.context.active_object 
        bpy.ops.object.modifier_add(type='EDGE_SPLIT')   
            
        obj = bpy.context.object
        cns = obj.constraints.new(type='FOLLOW_PATH')
        cns.target = bpy.data.objects[curveName]
        cns.use_curve_follow = True
        cns.forward_axis = 'FORWARD_X'
        cns.up_axis = 'UP_Y'
             
        obj = last
        obj.select = True
        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
        
        bpy.context.area.type = 'VIEW_3D'
        bpy.ops.view3d.snap_cursor_to_selected()
        cursor = bpy.context.scene.cursor_location
        bpy.context.area.type = 'TEXT_EDITOR'   
        
         #get cursor loaction
        print("Object ",obj.name)
        print("Cursor location ",cursor[0],cursor[1],cursor[2])
        cgXLoc.append(cursor[0])
        obj.select = False   
        self.MoveCG(last)


        #deselect everything
        bpy.ops.object.select_all(action='DESELECT')


        count = 1
        for obj in selected:
            if count >= numObj:
                break
            obj.name = letters[count]
           
            bpy.context.scene.objects.active = bpy.data.objects[letters[count]]
            bpy.ops.object.modifier_add(type='EDGE_SPLIT')   


            obj = bpy.context.object
            cns = obj.constraints.new(type='FOLLOW_PATH')
            cns.target = bpy.data.objects[curveName]
            cns.use_curve_follow = True
            cns.forward_axis = 'FORWARD_X'
            cns.up_axis = 'UP_Y'
            
            obj.select = True
            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
            
            bpy.context.area.type = 'VIEW_3D'
            bpy.ops.view3d.snap_cursor_to_selected()
            cursor = bpy.context.scene.cursor_location
            bpy.context.area.type = 'TEXT_EDITOR'   
            
             #get cursor loaction
            print("Object ",obj.name)
            print("Cursor location ",cursor[0],cursor[1],cursor[2])
            cgXLoc.append(cursor[0])
            obj.select = False  
 
            self.MoveCG(obj)
            
            count += 1
            obj.select = False
       
        print("Number of Individual Objects ",numObj)


        print("Finished ",numObj," objects")  
        
        print("Length of CG vector ",len(cgXLoc))
        print(cgXLoc)
        
        return numObj     
        
#
#=================================================================
#
# --------------------------------------------------------------------------
#
#   Input Variables
#
    textName = "Text"
    textThickness = 0.05
    curveName = "NurbsPath"
    pathDuration = 100
    firstFrame = 1
    lastFrame = 250
# --------------------------------------------------------------------------


    # Get the current selected text object
    
    bpy.context.scene.objects.active = bpy.data.objects[textName]
    
    selected=bpy.context.scene.objects.active
    
    objType = getattr(selected, 'type', '')
    if objType == 'FONT':
        numLetters = LineUpLetters(selected,textThickness,curveName)
        bpy.ops.object.select_all(action='DESELECT')
           
        curveLength = GetCurveLength("NurbsPath")
        print("Curve Length = ",curveLength)
        print("Number of letters = ",numLetters)
           
        path = bpy.data.curves[curveName]
        path.use_path = True
        path.use_path_follow = True
        path.path_duration = pathDuration
         
        # Animate path
        path.eval_time = 0
        path.keyframe_insert(data_path="eval_time", frame=firstFrame)
        path.eval_time = pathDuration
        path.keyframe_insert(data_path="eval_time", frame=lastFrame)   
        
        bpy.ops.screen.frame_jump(end=False)


def register():
    bpy.utils.register_class(FollowingLetters)




def unregister():
    bpy.utils.unregister_class(FollowingLetters)




if __name__ == "__main__":
    register()



Ohh… the class is derived from bpy.types.Panel… that’s probably the reason self doesn’t work…

Another option can be placing the MoveCG() function inside the LineUpLetters, since it’s only called from there… This will make the MoveCG work, but then it throws an ‘out of range’ error at «obj.name=Letters[count]»