Select Next Object

Hi all,

I’m converting an imported .svg from curve to mesh. There are hundreds of objects and all can be in a different color. I’ve created a script that does the conversion and cleanup for me but I need to select the objects from the list by hand. This is quite labour-intensive. It would be ideal if the script can select the next object in the list so I only have to change the material name in the script and execute again without having to move the mouse back and forth from object-list to script-window. Ideally it would be great if the script could also prompt me for a material name and use that to make it even easier but just selecting the next object by script would speed up my work already.

The script I have so far (and working):

import bpy

for x in bpy.context.selected_objects:
    bpy.ops.object.convert(target='MESH')
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='TOGGLE')
    bpy.ops.mesh.remove_doubles()
    bpy.ops.mesh.flip_normals()
    bpy.ops.object.mode_set(mode='OBJECT')
    mesh = bpy.context.active_object.data
    mesh.materials[0] = bpy.data.materials['Color26']
    mesh.update()

But how to move on to the next automatically?

Thanks in advance for your time and suggestions.

You need to change selection and active object.


selected = bpy.context.selected_objects.copy()

for o in selected:
    bpy.ops.object.select_all(action='DESELECT')
    o.select = True
    bpy.context.scene.objects.active = o
    bpy.ops.object.convert(target='MESH')
    # ...

Stan, thanks for the rapid response.

Your code does exactly the same thing. When I execute on 1 selected, it’s applied to 1 selected (and keeps it selected), when I select multiple, it applies to all selected at once without me having a chance to change the material.

If I understand your script correctly: for every o(bject) selected, do a deselect of all, then a select of the o. Then set the o active in the current scene and apply conversion, cleaning, material etc.

Maybe my bad in explaining. I’m not looking for a way to change them all at once. I need to check the material per object as the import of the svg gives every object its own material. So I need to go through them all to see what their original material should be and apply that. What I’m hoping to achieve is a script that I can execute when the first object from my list is selected. Then the script will automatically jump to the next object in the list. If it needs to have the same material as the previous one I can execute the script straight away, if it needs to have a different material, I can change the [‘Color26’] to something else and execute the script again. This will convert and change the material and jumps to the next object in the list. If it has the same material I can execute straight away if not, change the material etc…

Hope this helps…?

Ah, so in fact what you need is a way to store state between script invocations? That’s tricky.

One option would be to create a modal operator instead of a plain script. This way you can save the initial selection once in the invoke() function, and then do the advance/checking/material changing in the modal() function, but you’ll need a UI to set next material name, since obviously you can’t edit the operator code mid-execution.

Another would be to not use loops at all, but layers instead: i.e. put all the objects in one layer, use bpy.context.visible_objects[0] as the current object, perform everything you need on it, and then move it onto another layer, repeat executing until bpy.context.visible_objects is empty.

EDIT: if you’re willing to go through this manual checking for every object anyway, wouldn’t it be easier to just manually select groups of objects that should have the same material and then link their materials? I.e. select N objects, change the material on the last selected one (active) if needed, Ctrl+L -> Materials.

Selecting all similar at one go and change the material then is something I’ve tried but in practice doesn’t really work (with me anyway). One miss click or sticky shift key and the selection has to be done again.

The layer trick is something I hadn’t thought about and looks like a very feasible option. I’ll have a go at that and see what I can come up with.

Thanks for the push.

You can use class variables:

import bpy

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    count = 0

    def execute(self, context):
        self.__class__.count += 1
        self.report({'INFO'}, "Count: {}".format(self.__class__.count))
        return {'FINISHED'}


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


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


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.simple_operator()


They are globals. But note that counter in the example will reset whenever you re-run the script from Text editor, because it re-registers the class, which effectively replaces the original class and therefore executes counter = 0.

If you want to keep it at all costs, you can abuse bpy as a global:

import bpy

if hasattr(bpy, "_noteworthy"):
    bpy._noteworthy += 1
else:
    bpy._noteworthy = 1337
    
print(bpy._noteworthy)

A third option is to create a text datablock and push all object names there, then consecutively read a line and delete it afterwards. If the text block name starts with a dot, it will not appear in the datablock selector in the UI.