3
$\begingroup$

Hey I am trying to remove anything that isn't a mesh or curve from a list and it is missing items, does anyone know what I am doing wrong? For example if I add 3 sun lights I would expect it to remove all 3 from the list but it removes 2.

import bpy 


selected_objects = bpy.context.selected_objects


print("-----------------------------")
print("List len before: " + str(len(selected_objects)))


for obj in selected_objects:

    if obj.type not in ['MESH','CURVE']:
        selected_objects.remove(obj)


print("List len after: " + str(len(selected_objects)))
$\endgroup$
4
$\begingroup$

The problem is that you are changing the list that you are reading. For example, consider the first one read... you delete it, shuffling the second item into the first ‘slot’. However, next time around the loop it is the second item in the list that is picked up - which was originally the third since they have now all shuffled up - so each second item slips through the net. There are various solutions to this - such as processing the list backwards or building a list of items to delete on the first pass and removing them after all items have been processed.

Try :

for obj in reversed(selected_objects):

    if obj.type not in ['MESH','CURVE']:
        selected_objects.remove(obj)

Another option is to use a copy of the list for the loop - rather than the list itself - by using '[:]' to indicate all elements :

for obj in selected_objects[:]:

    if obj.type not in ['MESH','CURVE']:
        selected_objects.remove(obj)

This would allow you to process them in the original sequence but since the loop is acting on a copy of the original list it is not affected by the removal of objects from the list.

$\endgroup$
4
$\begingroup$

Instead of subtracting from a list, build a list with what you want -

objs = [o for o in bpy.context.selected_objects if o.type in ['MESH','CURVE']]
$\endgroup$
  • $\begingroup$ Thank you, this was how I had it originally, I just wanted to try and be optimized and use one variable. $\endgroup$ – Way2Close Feb 14 '18 at 13:37
  • $\begingroup$ @Way2Close: A list comprehension is usually faster than a "manual" for loop. You also won't save much memory with a manual loop because all of the list entries are references. $\endgroup$ – B.Y.O.B. Feb 15 '18 at 7:15
3
$\begingroup$

Don't consider context.selected_objects a normal python list.

Some points I thought it was worth mentioning: In your question setting select_objects = context.selected_objects returns a copy of the list instead of a reference.

>>> C.selected_objects
[bpy.data.objects['Cube'], bpy.data.objects['Lamp'], bpy.data.objects['Camera']]

>>> s = C.selected_objects # s is a copy
>>> l = s  # l is a reference
>>> s.pop() # remove last element from s
bpy.data.objects['Camera']

>>> s
[bpy.data.objects['Cube'], bpy.data.objects['Lamp']]

>>> l
[bpy.data.objects['Cube'], bpy.data.objects['Lamp']]

>>> C.selected_objects
[bpy.data.objects['Cube'], bpy.data.objects['Lamp'], bpy.data.objects['Camera']]

>>> 

If you wish to manipulate the context.selected_objects list, Don't use .remove(obj) to remove objects. Objects in the this list have their select property set to True. It is useful to be able to manipulate this list as it is used directly by a lot of operators.

As an example in the py console.

>>> C.selected_objects
[bpy.data.objects['Cube']]

>>> C.selected_objects.remove(C.object)
>>> C.selected_objects
[bpy.data.objects['Cube']]

>>> C.object.select
True

>>> C.selected_objects.clear()
>>> C.selected_objects
[bpy.data.objects['Cube']]

Notice the "Cube" object is neither removed, nor set as selected. Can remove from the selected objects list by negating select.

>>> for o in C.selected_objects:
...     o.select = False
...     
>>> C.selected_objects
[]

>>> C.object
bpy.data.objects['Cube']

which removes the object from the list. Note it is still the active object.

This will remove all but mesh and curve objects from selected objects.

import bpy
context = bpy.context
for o in context.selected_objects:
    o.select = o.type in ('MESH', 'CURVE')

but remember you may need to set the active object context.scene.objects.active = obj to one that is still in the list after deselecting if you are going to call an operator.

$\endgroup$
  • $\begingroup$ very enlightening explanations there. Never realised that s = context.selected_objects would pass on a copy instead of a reference. Same with the selected_objects and setting .select to False. $\endgroup$ – aliasguru Feb 21 '18 at 20:12
  • $\begingroup$ @aliasguru Cheers, started writing answer thinking it was a ref, (usually use list comprehension) Thought it was worth mentioning. $\endgroup$ – batFINGER Feb 23 '18 at 14:41

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.