pass a list argument to an operator

I wanted to write an operator able to select a list of objects / name objects.
example from a list generated by :

lst = bpy.context.selected_objects
# or
lst = ['Cube','Camera']

it could be used to save the selected objects before to make something, then to restore the previous user selection.

so I did this :

import bpy


class obselectlist(bpy.types.Operator):
    bl_idname = "object.select_list"
    bl_label = "select a list of objects or names"
    oblist = ['Cube','Camera'] # as default just to see it work

    def execute(self,context) :
        bpy.ops.object.select_all(action='DESELECT')
        for ob in self.oblist :
            if type(ob) == bpy.types.Object :
                bpy.ops.object.select_name(name=ob.name,extend=True)
            else :
                bpy.ops.object.select_name(name=ob,extend=True)
        return {'FINISHED'}
    
bpy.utils.register_class(obselectlist)

but as there’s not a bpy.props.ListProperty, (would be cool to understand why btw:confused:) I don’t know how to do :

>>> bpy.ops.object.select_list(lst=['Cube'])
Traceback (most recent call last):
[blah]
TypeError: Converting py args to operator properties: : <b>keyword "lst" unrecognized</b>

I found no way through operators. but the code below will do the job:

import bpy

class obselect(bpy.types.PropertyGroup):

    def list(self,lst) :
        bpy.ops.object.select_all(action='DESELECT')
        for ob in lst :
            if type(ob) == bpy.types.Object :
                bpy.ops.object.select_name(name=ob.name,extend=True)
            else :
                bpy.ops.object.select_name(name=ob,extend=True)

bpy.utils.register_class(obselect)
bpy.types.Object.selection = bpy.props.PointerProperty(type=obselect)
&gt;&gt;&gt; bpy.context.object.selection.list(['Cube','Camera'])
&gt;&gt;&gt; bpy.context.object.selection.list(lst)

works.

I’d rather like to hook the function in bpy.ops but no way.

so I don’t get it about bpy concept. why is there no way to define list properties ?
operators should be able to use lists/dicts no ?
maybe I missed something thanks for any explanation.

Perhaps you could make the execute method of the operator to work on one object at a time – context.active_object , and then loop through your list of objects in the invoke part.

during invoke, you can get a list of selected objects and loop through it, making each object ‘active’ in turn,
and for each iteration, run your execute method

If it’s just intermediary data (i.e. doesn’t need to be saved with the file), then just declare a list at the top of the file and include wherever using ‘global’.

ok I find some other ways to use it in ops… using a string as argument that call a list stored elsewhere, nothing new.

it seems you cant go any other way that propertygroup classes (or normal class, or globals as you wrote rarebit, it depends) to store datas when dealing with lists or dicts.

and you’ll have to write a listToString function for operators arguments.
or something could be done with collection or enum maybe…

Here’s something I just muddled from the next one… Basically it defines a collection, then it declares a variable in the operator class and passes it about like a …


import bpy

#    LIST
class myList(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Test Prop", default="Unknown")
    value = bpy.props.IntProperty(name="Test Prop", default=7)

bpy.utils.register_class(myList)

def list_clear(li):
    my_list=li  #sce.theList
    print("List Clear")
    n=len(my_list)
    for i in range(0,n+1):
        my_list.remove(n-i)
    return

def list_set(l,li):
    for e in l:
        my_item = li.add()
        my_item.name = e
        my_item.value = 1000
    return

def list_print(li):
    for my_item in li:  #bpy.context.scene.theList:
        print(my_item.name, my_item.value)
    return

class testing(bpy.types.Operator):
    bl_idname = "testing.tester"
    bl_label = "Add Mesh Object"
    
    bl_description = "Test class function"
    bl_options = {'REGISTER', 'UNDO'}
    
    theList = bpy.props.CollectionProperty(type=myList)
    theList_index = bpy.props.IntProperty(min=-1, default=-1)
    
    def invoke(self, context, event):
        list_clear(self.theList)
        l=["one","two","three"]
        list_set(l,self.theList)
        return self.execute(context)
    
    def execute(self, context):
        print("TESTING")
        list_print(self.theList)
        return {'FINISHED'}
    

class SimplePanel(bpy.types.Panel):
    bl_label = "My Simple Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        self.layout.operator("testing.tester")

bpy.utils.register_class(testing)
bpy.utils.register_class(SimplePanel)

And this is how I usually use it… (sort of, see MyMesh)


import bpy

#    LIST
class myList(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Test Prop", default="Unknown")
    value = bpy.props.IntProperty(name="Test Prop", default=7)

bpy.utils.register_class(myList)

bpy.types.Scene.theList = bpy.props.CollectionProperty(type=myList)
bpy.types.Scene.theList_index = bpy.props.IntProperty(min=-1, default=-1)

def list_clear():
    sce= bpy.context.scene
    my_list= sce.theList
    print("List Clear")
    n=len(my_list)
    for i in range(0,n+1):
        my_list.remove(n-i)
    return

list_clear()

l=["one","two","three"]
for e in l:
    my_item = bpy.context.scene.theList.add()
    my_item.name = e
    my_item.value = 1000

for my_item in bpy.context.scene.theList:
    print(my_item.name, my_item.value)

hey thanks rarebit. it’s interesting about collection use from an operator. actually I spent the whole day playing with it. :slight_smile:
btw I’d like to update the items of an enumprop defined in an operator instance (it’s ok for those defined in property) if someone know.

import bpy, random

#    LIST
class myList(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Test Prop", default="Unknown")
    value = bpy.props.IntProperty(name="Test Prop", default=7)
    
def index(li,name) :
    #return int(self.path_from_id().split('[')[1].split(']')[0])
    for idx,field in enumerate(li) :
        if field.name == name :
             return idx
    return 0

bpy.utils.register_class(myList)

def list_clear(li):
    print("List Clear")
    while len(li) &gt; 0 :
        my_list.remove(n-i)

def list_set(l,li):
    for e in l:
        my_item = li.add()
        my_item.name = e
        my_item.value = random.randint(1000,2000)

def list_print(li):
    print(li.name, li.value)
 
def sync_enum(self,context) :
    print('sync_enum')
    self.theList_index = int(self.enum)

def sync_search(self,context) :
    print('sync_search')
    self.theList_index = index(self.theList,self.search) + 1

def menuitems(li) :
    items=[]
    for idx,field in enumerate(li) :
        items.append( ( str(idx+1), field.name, '') )
    return items

def update_list_value(self,context) :
    self.theList[self.theList_index-1].value = self.updatedvalue
    
def update_list_name(self,context) :
    self.theList[self.theList_index-1].name = self.updatedname
    #bpy.types.WM_OT_tester.enum = bpy.props.EnumProperty(
    #   items = menuitems(self.theList),
    #    update = sync_enum
    #    )
    #bpy.types.WM_OT_tester.draw(self,context)
    
class WM_testing(bpy.types.Operator):
    bl_idname = "wm.tester"
    bl_label = "Add Mesh Object"
    
    bl_description = "Test class function"
    bl_options = {'REGISTER', 'UNDO'}
    
    theList = bpy.props.CollectionProperty(type=myList)

    theList_index = bpy.props.IntProperty(
            min=1,
            max=3,
            default=1,
            )
    
    enum = bpy.props.EnumProperty( 
            items = [ ('1','one',''),('2','two',''),('3','three','') ],
            update = sync_enum
            )

    search = bpy.props.StringProperty(
            update = sync_search
            )

    updatedname = bpy.props.StringProperty(
            default='',
            update = update_list_name
            )
            
    updatedvalue = bpy.props.IntProperty(
            default=1,
            update = update_list_value
            )
            
    edit = bpy.props.BoolProperty()
    
   
    def invoke(self, context, event):
        list_clear(self.theList)
        l=["one","two","three"]
        list_set(l,self.theList)
        return self.execute(context)

    def draw(self,context) :
        print('draw')

        layout = self.layout
       
        row = layout.row()
        row.prop(self,'theList')

        row = layout.row()
        row.prop(self,'theList_index', slider=False, text='index')

        row = layout.row()
        row.props_enum(self,'enum')

        row = layout.row()
        row.prop_menu_enum(self,'enum',text="field")
        
        row = layout.row()
        row.prop_search(self,'search',self,'theList')
        
        row = layout.row()
        row.label(text='name : %s'%self.theList[self.theList_index-1].name)
        if self.edit : row.prop(self,'updatedname')
       
        row = layout.row()
        row.label(text='value : %s'%self.theList[self.theList_index-1].value)
        if self.edit : row.prop(self,'updatedvalue')

        row = layout.row()
        row.prop(self,'edit', toggle=True, text='edit')  
          
    def execute(self, context):
        print("TESTING %s %s"%(self.theList_index,self.enum))
        self.updatedname = self.theList[self.theList_index-1].name       
        self.updatedvalue = self.theList[self.theList_index-1].value
        self.enum = str(self.theList_index)
        self.search = self.theList[self.theList_index-1].name

        list_print(self.theList[self.theList_index-1])
        return {'FINISHED'}


class SimplePanel(bpy.types.Panel):
    bl_label = "My Simple Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        self.layout.operator("wm.tester")

bpy.utils.register_class(WM_testing)
bpy.utils.register_class(SimplePanel)

I’m not sure if this is a good idea, I just use a listToString function to save python lists like littleneo was suggesting. Here is append and remove implemented:

def append(item, stringTarget, context):
    if hasProp(stringTarget,context):
        list = eval(context.object[stringTarget])
    else:
        list = []
    list.append(item)
    return str(list)

def rem(item, stringTarget, context):
    list = eval(context.object[stringTarget])
    list.remove(item)
    return str(list)

also it’s funny : with my snippet and the one of rarebit I think too :
when you run the script then edit a bit the script text, then play with the panel… it restores the script as it was at initialisation… and you loose all your updates. so watch your mouse :slight_smile:

Try removing the ‘default’ when declaring the vars.

Also the script calls clear list and init list on invoke, otherwise you get what was before, plus new stuff, all depends on how your app works, that’s was just a quick example…