7
$\begingroup$

Is there a way to "lock" an object so that it can't be deleted without unlocking it? Kind of like the option often present on digital cameras.

If not, any other way of preventing the user from deleting an object?

Also, this is for an addon, so no problem with script-based answers as a last resort.

$\endgroup$
10
$\begingroup$

You can override the delete function to achieve this :

  • add protected boolean property for the object type
  • set this property to true to prevent the object from deletion
  • the new delete function will only delete unprotected objects

this is the new delete operator no other changes are needed just include it in your addon and it will override the default delete function :

import bpy
from bpy.props import BoolProperty

bpy.types.Object.protected = BoolProperty(name = 'protected', default = False)
def main(context):
    for obj in context.selected_objects:
        if not obj.protected :
            bpy.context.scene.objects.unlink(obj)
            bpy.data.objects.remove(obj)
        else :
            print(obj.name +' is protected')


class delete_override(bpy.types.Operator):
    """delete unprotected objects"""
    bl_idname = "object.delete"
    bl_label = "Object Delete Operator"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        main(context)
        return {'FINISHED'}


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

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

if __name__ == "__main__":
    register()
| improve this answer | |
$\endgroup$
  • $\begingroup$ However, after reading the code a few times, would you mind explaining how it works? I can't seem to find where and how you override the default deletion operator... $\endgroup$ – Ludwik Apr 25 '15 at 11:07
  • 3
    $\begingroup$ they have the same Id name bl_idname = "object.delete" so when you register it the old definition gets overwritten by this one $\endgroup$ – Chebhou Apr 25 '15 at 11:09
  • 1
    $\begingroup$ I know this is old question but - how to get the "OK?" prompt from the original delete operator? $\endgroup$ – spacer May 19 '16 at 10:54
1
$\begingroup$

ok the script above is very interesting. this is not overriding the operator context but replacing it. I'm not 100% sure about interactions but for example you have anymore the register menu (F9) where you can set options like confirm and use global there > bpy.ops.object.delete(use_global=False, confirm=False) so I wrote an addon using the normal delete with an override and a new key ctrl+del (ctrl+suppr azerty)

import bpy
from bpy.props import BoolProperty

bl_info = {
    "name": "protected_delete_addon",
    "author": "1C0D",
    "version": (0, 0, 1),
    "blender": (2, 80, 0),
    "location": "property>object>visibility",
    "description": "protect from deleting with a key ctrl+del",
    "category": "Object",
}

bpy.types.Object.protected_delete = BoolProperty(
name = 'protected_delete', 
default =False,
description='to apply to several obj from active-> right clic on checkbox > copy to selected',
)

class Delete_Override(bpy.types.Operator):
    """delete unprotected objects"""
    bl_idname = "object.delete_mod"
    bl_label = "Object Delete dd Operator"
    bl_options = {'REGISTER', 'UNDO'} 

    @classmethod
    def poll(cls, context):
        return context.mode=='OBJECT'

    def execute(self, context):

        override = bpy.context.copy()  
        selected=bpy.context.selected_objects
#we override the context with a comprehensive list taking into account the property
        override['selected_objects'] = [obj for obj in selected if not obj.protected_delete]    
        bpy.ops.object.delete(override)

        return {'FINISHED'}

# a select operator to find back in a scene what object are protected      
class Delete_Override_Property_Select_Toggle(bpy.types.Operator):
        bl_idname = "object.del_override_toggle"
        bl_label = "Object Delete Operator"
        bl_options = {'REGISTER', 'UNDO'} 
        bl_description= "to select all propected objects" 

        def execute(self, context):        

            bpy.ops.object.select_all(action='DESELECT')    
            for obj in bpy.context.scene.objects:
                if obj.protected_delete:  
                        obj.select_set(True)
                        context.view_layer.objects.active=obj

            return {'FINISHED'}    


def draw_delete_override(self, context):
    layout = self.layout
#based on the code in visibility panel (checkbox with name after)
    layout.use_property_split = True  
    flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)

    obj = context.object
    col = flow.column()
    col.prop(obj, "protected_delete", text="protected_delete") ##property
    row=layout.row() 
    row.label(text='select all protected_delete') ##select by property
    row.operator("object.del_override_toggle", text="",icon='ARROW_LEFTRIGHT')

addon_keymaps = []
def register():
    #Class
    bpy.utils.register_class(Delete_Override_Property_Select_Toggle)
    bpy.types.OBJECT_PT_visibility.append(draw_delete_override) 
    bpy.utils.register_class(Delete_Override)

    #key
    wm = bpy.context.window_manager       
    km = wm.keyconfigs.addon.keymaps.new(name = 'Object Mode')
    kmi = km.keymap_items.new('object.delete_mod', 'DEL', 'PRESS',ctrl=True)
    addon_keymaps.append((km, kmi))

def unregister():

    #key
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()
    #class
    bpy.utils.unregister_class(Delete_Override_Property_Select_Toggle)
    bpy.utils.unregister_class(Delete_Override)
    bpy.types.OBJECT_PT_visibility.remove(draw_delete_override) 

if __name__ == "__main__":
    register()
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.