3
$\begingroup$

How can I pass a variable (if possible a tuple) to an operator class I'm calling by it's invoke?

class MyOperator(bpy.types.Operator):
    """Operate"""
    bl_idname = "object.myoperator"
    bl_label = "Operate"

    myvar = None
    filepath = bpy.props.StringProperty(subtype="FILE_PATH")

    def invoke(self, context, event): 
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

    def execute(self, context):   
        print(self.myvar)

...

prop = bpy.ops.object.myoperator('INVOKE_DEFAULT')
prop.myvar = "Hello World"

Or:

bpy.ops.object.myoperator('INVOKE_DEFAULT').myvar = "Hello World"

Error:

Traceback (most recent call last):
  File "\myoperator.py", line 151, in execute
AttributeError: 'set' object has no attribute 'myvar'

location: <unknown location>:-1

I've tried everything I could think of for hours now, but it seems I'm incapable of doing that.

$\endgroup$

1 Answer 1

4
$\begingroup$

I changed your code snippet a bit and made some comment. I hope that solves your problem.

import bpy

class MyOperator(bpy.types.Operator):
    """Operate"""
    bl_idname = "object.myoperator"
    bl_label = "Operate"

    # make the variable a property of the operator
    myvar = bpy.props.StringProperty()
    filepath = bpy.props.StringProperty(subtype="FILE_PATH")

    def invoke(self, context, event): 
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

    def execute(self, context):   
        print(self.myvar)

        # I added this here because otherwise it will through an exception at the end
        return {"FINISHED"}    

# this is how you invoke the operator, but you have to register it before
bpy.ops.object.myoperator("INVOKE_DEFAULT", myvar = "Hello World")  

Unfortunally you can't do the same thing with tuples (except you have numbers)... Here is some more information on how to define properties: https://www.blender.org/api/blender_python_api_2_76_2/bpy.props.html

About tuples as parameters:

When the tuple only consists of numbers or booleans, the tuple always has the same length <= 32, you can use either a BoolVectorProperty, IntVectorProperty or FloatVectorProperty.

A more generic solution would be to use the CollectionProperty. Therefor you first have to make a property group that contains the structure of the individual elements in the collection. In your case you only have a tuple with multiple numbers, so the PropertyGroup would look like so:

import bpy
from bpy.props import *

class SingleNumberProperty(bpy.types.PropertyGroup):
    value = FloatProperty()

In your operator you can define the collection property that uses this type.

class MyOperator(bpy.types.Operator):
    bl_idname = "my_operator.my_operator"
    bl_label = "My Operator"

    numbers_collection= CollectionProperty(type = SingleNumberProperty)

In the execute function you can unpack this collection in the following way:

def execute(self, context):
    my_numbers = []
    for item in self.numbers_collection:
        my_numbers.append(item.value)
    return {"FINISHED"}

The part that was the most difficult to find out is how to pass the tuple as collection. Basicly you have to create a list of dictionaries. Each of these dictionaries need to have a "name" key. In addition to that they can contain the data you want to set on the properties ("value" in this case).

The list of dicts can look like so:

[ {"name" : "", "value" : 12},
  {"name" : "", "value" : 15},
  {"name" : "", "value" : 20} ]

So lets say you have a list of numbers and want to transform it to this data structure:

numbers = [12, 15, 20]

# long way
packed_numbers = []     
for number in numbers:
    packed_numbers.append({"name" : "", "value" : number}) 

# more pythonic way
packed_numbers = [{"name" : "", "value" : number} for number in numbers]

The final step is to call the operator and pass these packed numbers as parameter to the collection property:

bpy.ops.my_operator.my_operator(numbers_collection = packed_numbers)
$\endgroup$
8
  • $\begingroup$ Yeah that's really a problem, as there is no tuple property. I actually have numbers in the tuple, so what was that workaround you suggested? $\endgroup$
    – bortran
    Feb 22, 2016 at 15:55
  • $\begingroup$ Do you always have the same amount of numbers in that tuple and less than 32? If yes you can use the FloatVector or IntVectorProperty. Otherwise you need another work around which I will try to figure out now. $\endgroup$ Feb 22, 2016 at 15:58
  • $\begingroup$ I actually do have a limited amount of numbers, but if you manage to find a more generic solution that works for other cases, too, I'd be pretty happy. $\endgroup$
    – bortran
    Feb 22, 2016 at 16:03
  • $\begingroup$ I added the generic solution to the answer. $\endgroup$ Feb 22, 2016 at 16:36
  • $\begingroup$ Sorry, but I'm struggling again to pass the the collection, where you wrote bpy.ops.my_operator.my_operator(numbers = packed_numbers) I suppose you meant numbers_collection instead of numbers? Still, it doesn't work calling it and I get this error: TypeError: CollectionProperty(...): expected an RNA type derived from PropertyGroup, failed with: RuntimeError: , missing bl_rna attribute from 'RNAMetaPropGroup' instance (may not be registered) $\endgroup$
    – bortran
    Feb 23, 2016 at 8:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .