Store variable within blender scene?

Is there any way to save a variable with a string or other data type within a blender .blend scene?

Are you trying this from within a Python operator or from the Blender UI? Both is possible, but the way to get there is different

If you could explain both cases to me, I’d be grateful.

First, adding a prop using the Blender UI:

Go to the Scene tab in the Property Editor, down to the Custom Properties section, and click Add. This will by default add a float property. Use the Edit button to edit the new Property. The thing is that the data type of such a prop can simply be defined by giving it a different value (same as with Python, these props can change type according to the content). So if you type in a custom string, it will become of type String. You can even add a tooltip this way. Final result should look like this in the UI:

Bildschirmfoto%20von%202019-01-17%2021-19-57

You can access this property now using its key: bpy.context.scene['Test'] should in this case yield My Nice Property. More clean is to use bpy.context.scene.get('Test'), as this will return None if the prop does not exist, instead of breaking your script.

Second, using a StringProperty from inside an operator instead:

There is only 2.7 docs available so far: https://docs.blender.org/api/blender2.7/bpy.types.StringProperty.html?highlight=stringproperty

However, difference is only in the syntax of the definition within Operators. With Scenes, the definition is identical. To show how to define it, I used the Operator Simple Python Template:

import bpy
from bpy.props import StringProperty


def main(context):
    for ob in context.scene.objects:
        print(ob)


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

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        main(context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.types.Scene.Test = StringProperty(default='My Nice Python Defined Prop')


def unregister():
    del bpy.types.Scene.Test
    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":
    register()

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

You can access this prop using bpy.context.scene.Test

In the code above, only three lines are needed for the prop. First, importing StringProperty from bpy.props (not bpy.types!!!) to access the namespace. Second, in the register() method, adding the property definition to the Scene type. And finally, in unregister(), deleting it again, to make sure when the Add-on is reloaded or unloaded, the definition is undone.

7 Likes

Worth noting that as soon as a property is attached to an ID datablock (a Scene is such an ID datablock), Blender saves it to the .blend file automatically.

Even after you " del bpy.types.Scene.Test" in the unregister call back?

Yes, this is necessary, as users could load a file with such properties stored, but forgot to enable the Add-on that created them. As soon as you re enable the Add-on, the data can be accessed again

Awesome, thanks for the reply

Another way I found to store information at the scene is this:

bpy.context.scene.collection["yourPropertyName"] = {} 
bpy.context.scene.collection["yourPropertyName"] = "hello world"

I think it’s the “Master Collection” and I think it’s the main collection that will always exist in blender in all scenes of blender 2.8 and can’t be removed by the user, so I thought it would be a good place to keep my information.

This is a possibility, yes. Any ID data type can store properties (as a rule of thumb, check the autocomplete of bpy.data.xxx to see which types support it). Also see https://docs.blender.org/api/blender_python_api_current/info_quickstart.html#custom-properties from the manual, where further examples are provided.

It makes more sense to store it on the scene itself rather than the master collection, unless it’s specifically related to collections.

bpy.context.scene["yourPropertyName"] = "hello world"
4 Likes

Is there a singleton object which the user can’t remove to store custom properties globally in 2.80? Scenes can be deleted and there can be many scenes. What some addons in the past did is to store properties in the default scene “Scene”, but it’s not reliable nor do I like the approach in general.

If we had an app.handler to monitor blender startup/shutdown events we could store that in addon data rather than relying on a global singleton. I’m always a fan of anything event-driven, personally… but that’s just me.

If it’s actually addon configuration fine, but what if it’s information related to a blend file and not an addon? Also, don’t get addon properties removed if you disable the addon (possibly restart Blender afterwards)?

Oh I see what you mean- dealing with more than one scene (or lack of a scene) in a single blend file

I am trying to store an array in an object.
I do the following:
bpy.context.active_object['my_test_array'] = ['a'] bpy.context.active_object['my_test_array'].append('b')

but once I query the array (bpy.context.active_object[‘my_test_array’]) after appending, it never seems to store the new data with append, is this normal or is it a bug?.

Thanks!.

If you look at your python console it’s probably throwing an exception, because active_object['my_test_array'] is not actually a list- it’s an IDProperty array, and there’s no .append() function for the IDProperty type.

The correct way to handle this would be to create a PropertyGroup.

1 Like

I would like to know if you think this is a good idea or if for some reason I shouldn’t use it like this:

###################
# __init__.py file:
###################
  
class Lists:
  def __init__(self, name='List', data=[]):
    self.name = name
    self.data = data

def register():
    bpy.types.Scene.test_list = Lists('Test')


def unregister():
    del bpy.types.Scene.test_list
    

#########
# Usage #
#########
bpy.context.scene.test_list.name
bpy.context.scene.test_list.data
bpy.context.scene.test_list.data.append(1)
bpy.context.scene.test_list.data

Ok, it seems that closing and opening blender loses the data saved in the list, but for temporary things it would be ok.