Custom pointer property is read only?

Pretty sure i’m doing something wrong here, seems a bit strange to have a custom property default to read only (what would be the point in creating it if you can never set it to something other than the default?)

here’s a bare bones test script that can be run in a scene with a default cube:

import bpy
from bpy.props import CollectionProperty, PointerProperty, StringProperty

class CUSTOM_test(bpy.types.PropertyGroup):
    my_attr: StringProperty()
    
bpy.utils.register_class(CUSTOM_test)
bpy.types.Scene.my_test = CollectionProperty(type=CUSTOM_test)
bpy.types.Object.my_pointer = PointerProperty(type=CUSTOM_test)

bpy.context.scene.my_test.add()

# AttributeError:
bpy.context.active_object.my_pointer = bpy.context.scene.my_test[0]

which results in the following exception:
AttributeError: bpy_struct: attribute "my_pointer" from "Object" is read-only

So what’s the solution here? I have a custom PropertyGroup stored on the Scene that I need to be able to keep track of on individual objects.

nb: cross-posted on devtalk

1 Like

It’s read-only in the context you’re accessing it I would assume… When are you trying to set the pointer?

it’s read only any time I try to set it, regardless. but for the sake of example, the script in the original post is just a fire and forget script that can be run at any time and it happens then.

Oh right I see. Yes when you declare a pointer property to a PropertyGroup you are actually creating a reference to that prop group. So your my_pointer will have a .my_attr property. You can’t change the base reference by assignment.

If you want to reference a specific collection, you need to use an int on the object and store the collections index or use named keys.

So you could change the above code to:

bpy.types.Object.my_pointer = IntProperty()
bpy.context.scene.my_test.add()
idx = len(bpy.context.scene.my_test) - 1

bpy.context.active_object.my_pointer = idx

# Reference collection via
bpy.context.scene.my_test[bpy.context.active_object.my_pointer]

interesting… seems semantically incorrect to have a pointer to a custom type that doesn’t actually point to a reference of said type. that’s the opposite of how a PointerProperty to a built-in type works, eg: bpy.types.Material, etc. In your example, if the index changes, the references are still pointing to the old index, which is precisely why you’d want a pointer to it.

Oh well, I guess that’s just how things work and I’ll work around it. thanks!

Yes, its not very intuitive. Especially since the pointer property normally works as you would expect…

Basically you’re creating a read-only pointer to set instance of the property group assigned for that object. I think the issue is that there isn’t another way to assign the group and in most cases the current behaviour is probably what is desired.

Met this issue too. Solution by @netherby works but it involves some more management to make sure either not to mess with my_test order of elements or to add additional checks to make sure is up to date - for example after removing item from collection all indices that were used in other objects has to be updated.

Isn’t it just a bug after all?