String passed as parameter to blender object function not working

Hello, I’m having an issue and I’m not sure if its a python problem or a blender API problem that I’m missing.

I have function that accepts an object struct and a string,
function parameters:

def changeAttributeToUV(obj, att_name):

and inside that function I’m doing:

uvmap = obj.data.uv_layers.new(name=att_name)

And I’m using that function like this:

# transform splineUvs to UVMaps
changeAttributeToUV(ob, "UVMap")
changeAttributeToUV(ob, "splineUvs")

When the above runs, there is no new UVMap layer at all, printing the following to the console for the UVMap created MeshUVLoopLayer("NGon Face-Vertex"). But if I do the following code:

uvmap = obj.data.uv_layers.new(name="SomeLayerName")

It generates the new uv layer just fine, and shows up in the attribute tab in UVmaps. That code can be inside or outside the function (in the operator run method) that it does the same thing if written that way.

So, Am I missing something? :frowning:
Thank you for the help!

When I run this snippet it works:

import bpy

def changeAttributeToUV(obj, att_name):
    uvmap = obj.data.uv_layers.new(name=att_name)

ob = bpy.context.object

# transform splineUvs to UVMaps
changeAttributeToUV(ob, "UVMap")
changeAttributeToUV(ob, "splineUvs")

I did notice that in your function argument you’re looking for obj, and when you call it you’re just looking for ob, maybe it’s just a typo?

The ob variable is just my object reference from outside the function call, passed as the obj variable.

Doesn’t really look like you are doing anything different in your sample, but it’s not working on my code?

Can you post your complete code in one block, or at least the entire changeAttributeToUV() function definition and its example invokations?

1 Like

Here is the code and the function that is calling it, it’s not a hard thing, but this interaction seems buggy? That or it’s just my stupidity showing

import bpy

def changeAttributeToUV(obj, att_name):
    
    # Create UVMap
    uvmap = obj.data.uv_layers.new(name=att_name)
    
    # UV attribute to store
    uv_attrib = None

    # Try to find valid face corner attribute
    for attribute in obj.data.attributes:
        if attribute.name == att_name:
            uv_attrib = attribute
            break

    # Check if face corner attribute was found
    if uv_attrib is None:
        if context.active_object is obj:
            bpy.ops.object.delete()
        self.report({'ERROR'}, 'No Face Corner attribute found!')
        return {'CANCELLED'}

    for loop in obj.data.loops:
        uvmap.data[loop.index].uv[0] = uv_attrib.data[loop.index].vector[0]
        uvmap.data[loop.index].uv[1] = uv_attrib.data[loop.index].vector[1]
    
    print(uv_attrib)
    obj.data.attributes.remove(uv_attrib)
    print(uvmap)

def generateRailSelection(context):
    selected = []
    # get all selected
    selected.extend(context.selected_objects)
    
    # deselect all
    bpy.ops.object.select_all(action='DESELECT')
    
    # run through selection
    for ob in selected:
        
        if (ob.type != 'CURVE'):
            continue
     
        # select
        ob.select_set(True)
        context.view_layer.objects.active = ob
        print(ob)
        source_name = ob.name
        
        # duplicate selection
        bpy.ops.object.convert(target='MESH', keep_original=True)
        cloneObj = context.active_object
        cloneObj.name = source_name + "GeneratedRail"
        
        # transform splineUvs to UVMaps
        changeAttributeToUV(cloneObj, "UVMap")
        changeAttributeToUV(cloneObj, "splineUvs")
        # seperate mesh by material
        
        # delete extra set that doesnt make sense with section
        
        # join all
        
        cloneObj.select_set(False)

This works for me. Something in the commented section is not working as intended probably

import bpy

def changeAttributeToUV(obj, att_name):
    
    # Create UVMap
    uvmap = obj.data.uv_layers.new(name=att_name)
    
    # UV attribute to store
#    uv_attrib = None

#    # Try to find valid face corner attribute
#    for attribute in obj.data.attributes:
#        if attribute.name == att_name:
#            uv_attrib = attribute
#            break

#    # Check if face corner attribute was found
#    if uv_attrib is None:
#        if context.active_object is obj:
#            bpy.ops.object.delete()
#        self.report({'ERROR'}, 'No Face Corner attribute found!')
#        return {'CANCELLED'}

#    for loop in obj.data.loops:
#        uvmap.data[loop.index].uv[0] = uv_attrib.data[loop.index].vector[0]
#        uvmap.data[loop.index].uv[1] = uv_attrib.data[loop.index].vector[1]
#    
#    print(uv_attrib)
#    obj.data.attributes.remove(uv_attrib)
    print(uvmap)


ob=bpy.data.objects["Torus"]        
changeAttributeToUV(ob, "UVMap")

What exactly is the goal, here? As far as I can tell, you are…

  1. Adding a UV Map layer to the mesh
  2. Locating the attribute interpretation of that UV Map layer
  3. …?
  4. Removing the attribute, which also removes the UV Map which is represented by that attribute?

If you comment out the obj.data.attributes.remove(uv_attrib) call, look at what’s printed on my machine on one particular run…

<bpy_struct, Float2Attribute("UVMap") at 0x000002A29E171488>
<bpy_struct, MeshUVLoopLayer("UVMap") at 0x000002A29E171488>

That is: two interpretations of an array of data, and that data is located at the same address in memory. They are different views of the same data, because UV coordinates are Float2 face corner attributes. When you remove the one, you also remove the other, because they are the same thing.

this might me the actual problem!

is there a way that I can remove the attribute and not remove the created UVMap?
Should I give them a different name?

maybe there is a function that I’m missing that I can use to turn my face corner attributes into UVmaps

If the attributes have different names, they are different attributes. And if they have compatible data types (Float2, in this case), you can copy their values from one attribute channel to the other.

Where is the “splineUV” attribute coming from, though, which you want to convert to a UV Map? And why do you try to convert the attribute “UVMap” into to a UV Map called “UVMap” first? I don’t understand the code, so I don’t understand your goal.

Those attributes are all coming from a geometry nodes tree, because they are automatically generated UVs