0
$\begingroup$

I found several threads on this subject but could not distill a solution. I am trying to delete .001, .002, etc.

The code should delete these, but every time I rerun my code it is as if nothing was ever deleted; the count is now >0.250 and is rising rapidly.

Full disclosure: The solution below is inefficient; it is the culmination of several efforts to solve my problem and represents my final "brute force" approach.

Thanks in advance for your suggestions:

def delete_object_iteration(objname):
    for x in range(1, 999):
        xs=str(x)
        on2=objname+'.'+xs.zfill(3)
        try:
            bpy.data.objects.remove(bpy.context.scene.objects[on2], do_unlink = True)
        except:
            pass
        delete_object_instance(on2)

def delete_object_instance(objname):  
    try:
        bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
    except:
        pass
    try:
        bpy.ops.object.select_all(action='DESELECT')
        bpy.data.objects[objname].select_set(True)
        bpy.ops.object.delete()
    except:
        pass
```
$\endgroup$
  • $\begingroup$ I'm a bit confused why you call delete_object_instance after you've already removed the object. $\endgroup$ – rjg Jul 29 at 20:20
2
$\begingroup$

The following script works fast and removes the objects.

import bpy


def delete_object_iteration(obj_name):
    for x in range(1, 999):
        obj = bpy.context.scene.objects.get(obj_name + '.' + str(x).zfill(3))
        if obj is not None:
            bpy.data.objects.remove(obj, do_unlink=True) 

delete_object_iteration("Cube")

The alternative approach would be using the operator with context override.

import bpy


def delete_object_iteration(obj_name):
    objs = []
    for x in range(1, 999):
        obj = bpy.context.scene.objects.get(obj_name + '.' + str(x).zfill(3))
        if obj is not None:
            objs.append(obj)
    bpy.ops.object.delete({"selected_objects": objs})

delete_object_iteration("Cube")

In case you don't want to remove only between 1 and 999:

import bpy

def is_match(prefix, name):
    split_name = name.split('.')      
    if len(split_name) == 2 and split_name[0] == prefix and split_name[1].isdigit():
        return True
    else:
        return False

def delete_object_iteration(obj_name):
    for obj in bpy.context.scene.objects:
        if is_match(obj_name, obj.name):
            bpy.data.objects.remove(obj, do_unlink=True) 

delete_object_iteration("Cube")

Purging orphans as well:

import bpy

def is_match(prefix, name):
    split_name = name.split('.')     
    if len(split_name) == 2 and split_name[0] == prefix and split_name[1].isdigit():
        return True
    else:
        return False

def purge_orphans():
    old_type = bpy.context.area.type
    bpy.context.area.type = 'OUTLINER'
    bpy.context.space_data.display_mode = 'ORPHAN_DATA'
    bpy.ops.outliner.orphans_purge()
    bpy.context.area.type = old_type

def delete_object_iteration(obj_name):
    for obj in bpy.context.scene.objects:
        if is_match(obj_name, obj.name):
            bpy.data.objects.remove(obj, do_unlink=True)
    purge_orphans()

delete_object_iteration("Cube")
$\endgroup$
  • 1
    $\begingroup$ Oops... posted without noticing you had edited answer to include similar. $\endgroup$ – batFINGER Jul 30 at 9:52
  • $\begingroup$ @batFINGER no problem. You're answer also includes variations that mine doesn't. $\endgroup$ – rjg Jul 30 at 9:54
2
$\begingroup$

Finding with re

Alternatively, find all objects that match a pattern using active objects name. With object "Cube" active a simple re could be

regexp = r'Cube.\d+'

any object with a name starting with "Cube." and ending only with digits past that point will match. Need only iterate over scene.objects, or any collection containing objects eg all in blend bpy.data.objects, and see if they match, as opposed to finding if any of each of a 1000 exist.

There are other ways without re, eg split or rpartition on "." and test if part is numeric.

>>> "Cube.001".rpartition(".")
('Cube', '.', '001')

Test script finds the matches.

import bpy
import re

context = bpy.context
scene = context.scene
ob = context.object

rexp = f"{ob.name}.\d+"

matches = [o for o in scene.objects if re.fullmatch(rexp, o.name)]

print(matches)

Ok if we have matches, can iterate and delete from blend.

while matches:
    bpy.data.objects.remove(matches.pop())

Or with the operator, passing a context override.

if matches:
    bpy.ops.object.delete({"selected_objects" : matches})

There is a lot of over zealous hoo-ha re not using operators. If you had 1000s of matches above, the operator is still called only once... IMO this is Ok.

but Don't!!!! do this

for o in matches:
    bpy.ops.some.op(....)

and call the operator 1000s of times.

Python performance with Blender operators

Why avoid bpy.ops?

Please also note that bpy.ops.object.select_pattern(...) has similar functionality, and this could be a 2 liner.

bpy.ops.object.select_pattern(pattern="Cube.*", extend=False)
bpy.ops.object.delete()

Which will delete "Cube.001.001" also.

$\endgroup$
  • 1
    $\begingroup$ Sorry to intrude .. but this is the sort of place I'm getting stuck trying to figure out the API. The only ref. I have to bpy.ops.object.delete is this one .. I assume you're replacing a context parameter with a dictionary of your own ... where would I find the docs for that? Is context an default parameter for all ops calls, or something? $\endgroup$ – Robin Betts Jul 30 at 12:00
  • 1
    $\begingroup$ blender.stackexchange.com/questions/35653/… Would love to see the required context members in the docs. $\endgroup$ – batFINGER Jul 30 at 12:31
  • $\begingroup$ Thanks. It's nice to know I'm not he only one. With the occasional help of folks like you, I'll muddle through.. :) $\endgroup$ – Robin Betts Jul 30 at 12:49

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.