Uvmap handling - new operators

blender does support multi-object edit mode and unwrapping.

the uvmap handling did not yet support it.
I wrote these very useful operators and want to donate its code.
would someone please add it to the code base ?
almost all you have to do:
copy the code into scripts/startup/bl_ui/properties_data_mesh.py
and register the classes (see below).
thank you!

new operators (multi object):
add uvmap to all selected objects
dis-/activate them
delete uvmap from selected objects

CODE:

import bpy



"""

add to file:
scripts/startup/bl_ui/properties_data_mesh.py

note:
add operators to classes list,  for registering

classes = [
    OT__uvmap_change_active_states,
    'OT__uvmap_delete_from_selected,
    OT__uvmap_add_to_selected,
    MESH_MT_uvmap_context_menu,
}

# OR:
bpy.utils.register_class(OT__uvmap_add_to_selected)
bpy.utils.register_class(OT__uvmap_delete_from_selected)
bpy.utils.register_class(OT__uvmap_change_active_states)
bpy.utils.register_class(MESH_MT_uvmap_context_menu')

also , for the context menu , add this line: 
`col.menu( 'MENU_MT_uvmap_context_menu', icon='DOWNARROW_HLT', text='' )`
 
to **draw function** of
`class DATA_PT_uv_texture`

"""





class MESH_MT_uvmap_context_menu(Menu):
    '''
        adds the context menu for the uvmap-section in data-panel. (properties_data_mesh.py)
    '''
    bl_label = "uvmap - special functions (applied to selection)"
    
    def draw(self, _context):
        layout = self.layout

        layout.label( text='uvmap - special functions (applied to selection)' )
        layout.separator()
        layout.operator( 'selection.uvmap_add_to_selected' , icon='ADD' )
        layout.operator( 'selection.uvmap_change_active_states' , icon='OUTLINER_DATA_FONT' )
        layout.operator( 'selection.uvmap_delete_from_selected' , icon='X' )
    #
#end class: MESH_MT_uvmap_context_menu




class OT__uvmap_change_active_states(bpy.types.Operator):
    bl_idname = "selection.uvmap_change_active_states"
    bl_label = "Change active-state or active_render of named uvmap"

    #filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    #my_float: bpy.props.FloatProperty(name="Float")
    uv_set_active: bpy.props.BoolProperty(name="Activate uvmap", default=True)
    uv_set_render: bpy.props.BoolProperty(name="Activate for rendering", default=False)
    
    uv_new_name: bpy.props.StringProperty(name="UV map name", default="new map.01")
        
    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        
        #col.label(text="Add new UV maps to selected objects")

        row = col.row()
        #row.prop(self, "my_float")
        row.prop(self, "uv_set_active")
        row.prop(self, "uv_set_render")
        col.prop(self, "uv_new_name")

    def execute(self, context):
        #
        AA = bool( self.uv_set_active )
        RR = bool( self.uv_set_render ) 
        #
        self .FUNC_uvmap_change_active_states( context , self.uv_new_name , make_active=AA , active_for_render=RR )
        return {'FINISHED'}
    
	    
    @classmethod
    def FUNC_uvmap_change_active_states( cls, context, uvmap_name, make_active=True, active_for_render=False ):
        '''
        (class independent function) 
        change the active-state of the named uvmap for all selected objects.
        '''
        # 2021-11 by Emanuel Rumpf , Germany , em-rumpf[AT]gmx.de
        slc = context.selected_objects
        
        #print( 'called: FUNC_change_uv_active_state_for_selected. count of selected obs: ', str(len(slc)) )
        #
        success = True
        #
        for obj in slc:
            #
            dt = obj.data
            ## remove same_name
            try: uvlay = dt.uv_layers[uvmap_name]
            except: uvlay = None
            #
            if uvlay is None:
                print( f'Warning: uvmap with name: {uvmap_name} does not exist for object: {obj.name} -- FUNC_uvmap_change_active_states()')
                success = False
            else:
                uvlay.active_render = True  if active_for_render  else False
                uvlay.active = True  if make_active  else False
            #
            #
        #end for
        #
        wm = context.window_manager
        #
        if success:
            def draw_message( popup, context):
                popup.layout.label( text='' )
            wm.popup_menu( draw_message, title='success', icon='INFO' )
        else:
            def draw_message( popup, context):
                popup.layout.label( text='' )
            wm.popup_menu( draw_message, title='failed, see console message', icon='ERROR' )
        #
    #
#end class: OT__uvmap_change_active_states






class OT__uvmap_delete_from_selected(bpy.types.Operator):
    bl_idname = "selection.uvmap_delete_from_selected"
    bl_label = "Delete a uvmap by name, from all selected objects"

    #filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    #my_float: bpy.props.FloatProperty(name="Float")
    uv_new_name: bpy.props.StringProperty(name="UV map name", default="new map.01")
    
    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        
        #col.label(text="Add new UV maps to selected objects")

        row = col.row()
        #row.prop(self, "my_float")
        row.prop(self, "uv_set_active")
        row.prop(self, "uv_set_render")

        col.prop(self, "uv_new_name")

    def execute(self, context):
        #
        self .FUNC_uvmap_delete_from_selected( context , self.uv_new_name )
        return {'FINISHED'}
        #
    
    @classmethod
    def FUNC_uvmap_delete_from_selected( cls, ctx, uvmap_name  ):
        '''
        (class independent function) 
        delete the uvmap with given name, from all selected objects.
        '''
        # 2021-11 by Emanuel Rumpf , Germany , em-rumpf[AT]gmx.de

        slc = ctx.selected_objects
        
        #print( 'called: FUNC_change_uv_active_state_for_selected. count of selected obs: ', str(len(slc)) )
        #
        for obj in slc:
            #
            dt = obj.data
            ## remove same_name
            try: uvlay = dt.uv_layers[uvmap_name]
            except: uvlay = None
            #
            if uvlay is None:
                print( f'Warning: uvmap with name: {uvmap_name} does not exist for object: {obj.name} -- FUNC_uvmap_delete_from_selected()')
            else:
                dt.uv_layers.remove( uvlay ) 
            #
            #
        #end for
    #
#end class: OT__uvmap_delete_from_selected 










class OT__uvmap_add_to_selected(bpy.types.Operator):
    bl_idname = "selection.uvmap_add_to_selected"
    bl_label = "Add a new UV map to all selected objects" # note: replaces uvmap with equal name

    filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    #my_float: bpy.props.FloatProperty(name="Float")
    uv_set_active: bpy.props.BoolProperty(name="Activate new UV map", default=True)
    uv_set_render: bpy.props.BoolProperty(name="Activate for rendering", default=False)
    
    uv_new_name: bpy.props.StringProperty(name="UV map name", default="new map.01")
    


    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        
        #col.label(text="Add new UV maps to selected objects")

        row = col.row()
        #row.prop(self, "my_float")
        row.prop(self, "uv_set_active")
        row.prop(self, "uv_set_render")

        col.prop(self, "uv_new_name")

    def execute(self, context):
        
        #print( 'OT__add_uv_maps_to_selected - calles: FUNC_add_uvmaps_to_selected.', self)
        #print( '  new UV name: ', self.uv_new_name )
        #print( '  set active: ', self.uv_set_active )
        #print( '  set render: ', self.uv_set_render )
        #
        self .FUNC_uvmap_add_to_selected( context , self.uv_new_name , make_active=self.uv_set_active , active_for_render=self.uv_set_render )
        return {'FINISHED'}
    
    @classmethod
    def FUNC_uvmap_add_to_selected( cls, ctx, uvmap_name, make_active=True, active_for_render=False ):
        '''
        (class independent function) 
        adds a uv-map with name to each selected object and makes it active (or not)
        -- 
        example call: 
        FUNC_add_uvmaps_to_selected( bpy.context , 'atlas.a1' , make_active=True , replace_same_name=True )
        '''
        # 2021-11 by Emanuel Rumpf , Germany , em-rumpf[AT]gmx.de
        slc = ctx.selected_objects
        
        #print( 'called: FUNC_add_uvmaps_to_selected. count of selected obs: ', str(len(slc)) )
        #print( 'len: ', len(slc) )
        #
        for obj in slc:
            #
            dt = obj.data
            #print( ' FUNC_add_uvmaps_to_selected: iterated.name: ', obj.name )
            ## remove same_name
            try: uvold = dt.uv_layers[uvmap_name]
            except: uvold = None
            if uvold is not None:
                #print( ' FUNC_add_uvmaps_to_selected: uv_map with name exists - replacing' )
                dt.uv_layers.remove( uvold )
            #
            #
            uvnew = dt.uv_layers .new( name=uvmap_name, do_init=True )  # do_init: copy active uv map
            if uvnew is None:
                print( f'Warning: : creating uvmap failed.  uvnew is None for obj_name: {obj.name} -- FUNC_uvmap_add_to_selected' )
                continue 
            #
            uvnew.active_render = True  if active_for_render  else False
            uvnew.active = True  if make_active  else False
            #
        #end for
    #
#end class: OT__uvmap_add_to_selected
2 Likes

also , for the context menu , add this line:
col.menu( 'MENU_MT_uvmap_context_menu', icon='DOWNARROW_HLT', text='' )

to draw function of
class DATA_PT_uv_texture