Assigning value to PointerProperty? [resolved]

Edit for brevity and clarity. The short short version is that I can use a PointerProperty and set it via a UI panel, but actually trying to set the value in a python operator suddenly makes the UI panel reading the value throw errors.

I’ve searched around and found a couple of references to this, but I’ll be damned if I can make heads or tails of them, and most are many years outdated anyway, so I figure I should ask here.

In a personal addon I am working on, I’m using a PointerProperty to specify a target for an operation, instead of just a text field with the name. Among other things, this lets me use the eyedropper function to select an object.

Setting values / selecting objects in the UI works fine, both with the dropdown list, and the eyedropper. But when an operator changes the value (in this example, I have a button that sets the variable to point to the active object), suddenly row.prop() stops working.

Specifically, the code that displays the field produces the error:
rna_uiItemR: property not found: TestProps.propertyDisplayTarget

This despite the fact that other calls like
rna_uiItemR: property not found: TestProps.propertyDisplayTarget
work just fine.

Here is my code, stripped down to the bare minimum:

import bpy

class TestProps(bpy.types.PropertyGroup):
    propertyDisplayTarget: bpy.props.PointerProperty(name = "Object", type = bpy.types.Object)
    propertyDisplayUseActiveObject: bpy.props.BoolProperty(name = "Use Active")

class TEST_OT_setPropertyDisplayTargetToActive(bpy.types.Operator):
    """Set property display target object to active"""
    bl_idname = "object.setpropertydisplaytargettoactive"
    bl_label = "Set to active"
    bl_context = "object"
    
    @classmethod
    def poll(cls, context):
        if bpy.context.active_object:
            return True
        else:
            return False

    def execute(self, context):
        TestProps.propertyDisplayTarget = bpy.context.active_object.id_data
        print("#"*7, "set ", TestProps.propertyDisplayTarget)
        return {'FINISHED'}

class TEST_PT_propertiesPanel(bpy.types.Panel):
    """Shows properties of specified object"""
    bl_label = "Object/Bone Properties"
    bl_idname = "SCENE_PT_properties"
    bl_space_type = 'VIEW_3D'
    bl_category = 'Testing'
    bl_region_type = "UI"

    @classmethod
    def poll(cls, context):
        return True

    def draw(self, context):
        layout = self.layout
        scene = bpy.context.scene
        row = layout.row()
        row.label(text = "Test panel")
        row = layout.row()
        if scene.TestProps:
            row = layout.row()
            row.label(text = "Target name is " + scene.TestProps.propertyDisplayTarget.name)
        row = layout.row()        
        row.prop(scene.TestProps, "propertyDisplayTarget")
        row = layout.row()
        row.operator('object.setpropertydisplaytargettoactive')

classes = (TestProps,
            TEST_OT_setPropertyDisplayTargetToActive,
            TEST_PT_propertiesPanel,
            TestProps
            )

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
        bpy.types.Scene.TestProps = bpy.props.PointerProperty(type=TestProps)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
        del bpy.types.Scene.TestProps
        
if __name__ == "__main__":
    register()
    

It works fine until I use the “set to active” button. Then it stops working and spams my console with the error stated above.

Also, I can still access the PointerProperty from the console just fine, it is ONLY the prop field in the UI panel that breaks.

Can anyone explain this to me? Thanks!

Hi,
I’m still learning python Blender but I can see somes mistakes in the code :

  • classes contains 2 TestProps
  • in register and unregister function, you don’t need to include bpy.types.Scene.TestProps in the for loop, you just have to call it once
  • in the panel, scene.TestProps.propertyDisplayTarget contains no object at the beginning so the if statement should be

if scene.TestProps.DisplayTarget:

  • in the poll function of the TEST_OT_setPropertyDisplayTargetToActive class, just need this line

return context.active_object != None

  • in the execute function of the TEST_OT_setProperty … class use context.scene.TestProps. The error you mention is here.

So here your code modified:

> import bpy
> 
> class TestProps(bpy.types.PropertyGroup):
>     propertyDisplayTarget: bpy.props.PointerProperty(name = "Object", type = bpy.types.Object)
>     propertyDisplayUseActiveObject: bpy.props.BoolProperty(name = "Use Active")
> 
> class TEST_OT_setPropertyDisplayTargetToActive(bpy.types.Operator):
>     """Set property display target object to active"""
>     bl_idname = "object.setpropertydisplaytargettoactive"
>     bl_label = "Set to active"
>     bl_context = "object"
>     
>     @classmethod
>     def poll(cls, context):
>         return bpy.context.active_object != None
>       
>     def execute(self, context):
>         TestProps = context.scene.TestProps
>         TestProps.propertyDisplayTarget.select_set(state = True)
>         bpy.context.view_layer.objects.active = TestProps.propertyDisplayTarget
>         print("#"*7, "set ", TestProps.propertyDisplayTarget)
>         return {'FINISHED'}
> 
> class TEST_PT_propertiesPanel(bpy.types.Panel):
>     """Shows properties of specified object"""
>     bl_label = "Object/Bone Properties"
>     bl_idname = "SCENE_PT_properties"
>     bl_space_type = 'VIEW_3D'
>     bl_category = 'Testing'
>     bl_region_type = "UI"
> 
>     def draw(self, context):
>         layout = self.layout
>         scene = bpy.context.scene
>         obj = context.active_object
>         row = layout.row()
>         row.label(text = "Test panel")
>         row = layout.row()
>         row = layout.row()
>         if scene.TestProps.propertyDisplayTarget:
>             row.label(text = "Target name is " + scene.TestProps.propertyDisplayTarget.name)
>         else:
>             row.label(text = "Target name is ")
>         row = layout.row()        
>         row.prop(scene.TestProps, "propertyDisplayTarget")
>         row = layout.row()
>         row.operator('object.setpropertydisplaytargettoactive')
> 
> classes = (TestProps,
>             TEST_OT_setPropertyDisplayTargetToActive,
>             TEST_PT_propertiesPanel
>             )
> 
> def register():
>     from bpy.utils import register_class
>     for cls in classes:
>         register_class(cls)
>     bpy.types.Scene.TestProps = bpy.props.PointerProperty(type=TestProps)
> 
> def unregister():
>     from bpy.utils import unregister_class
>     for cls in reversed(classes):
>         unregister_class(cls)
>     del bpy.types.Scene.TestProps
>         
> if __name__ == "__main__":
>     register()

Thank you for pointing out those errors. That makes sense, and is a bit embarrassing. I appreciate it! However, the fixed code doesn’t actually do what I am trying to do. Fortunately, fixing the other errors fixes the issue! So I was focusing on the one line that threw the error instead of the rest of the code. Derp.

Again, thanks for your help! :smiley:

So, if anyone else is searching around about the same thing and finds this thread, I’ll leave some extra information.

I think I may have incorrectly explained what I am trying to accomplish, due to bad phrasing. The idea is not to read object propertyDisplayTarget points to, and make the referenced object active.

The thing I am trying to do is to read bpy.context.active_object, and change the value of propertyDisplayTarget to whatever object is active when the operator is executed.

Deleting two lines from and adding one to the fixed code accomplishes what I meant to do.

import bpy

class TestProps(bpy.types.PropertyGroup):
    propertyDisplayTarget: bpy.props.PointerProperty(name = "Object", type = bpy.types.Object)
    propertyDisplayUseActiveObject: bpy.props.BoolProperty(name = "Use Active")

class TEST_OT_setPropertyDisplayTargetToActive(bpy.types.Operator):
    """Set property display target object to active"""
    bl_idname = "object.setpropertydisplaytargettoactive"
    bl_label = "Set to active"
    bl_context = "object"
    
    @classmethod
    def poll(cls, context):
        return bpy.context.active_object != None
      
    def execute(self, context):
        TestProps = context.scene.TestProps
        TestProps.propertyDisplayTarget = bpy.context.active_object
        print("#"*7, "set to", TestProps.propertyDisplayTarget) #for testing purposes
        return {'FINISHED'}

class TEST_PT_propertiesPanel(bpy.types.Panel):
    """Shows properties of specified object"""
    bl_label = "Object/Bone Properties"
    bl_idname = "SCENE_PT_properties"
    bl_space_type = 'VIEW_3D'
    bl_category = 'Testing'
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        scene = bpy.context.scene
        obj = context.active_object
        row = layout.row()
        row.label(text = "Test panel")
        row = layout.row()
        if scene.TestProps.propertyDisplayTarget:
            row.label(text = "Target name is " + scene.TestProps.propertyDisplayTarget.name)
        else:
            row.label(text = "Target name is ")
        row = layout.row()        
        row.prop(scene.TestProps, "propertyDisplayTarget")
        row = layout.row()
        row.operator('object.setpropertydisplaytargettoactive')

classes = (TestProps,
            TEST_OT_setPropertyDisplayTargetToActive,
            TEST_PT_propertiesPanel
            )

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
    bpy.types.Scene.TestProps = bpy.props.PointerProperty(type=TestProps)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.TestProps
        
if __name__ == "__main__":
    register()