12
$\begingroup$

I am looking for class types of blender property objects.

I am looping over some properties in a scene and want to operate on them when they are bpy.types.CollectionProperty.

My custom property is for example stored in bpy.data.scene[0].my_custom_collection_prop

Here is some pseudo code:

for key,value in bpy.data.scene[0].items():
    prop = getattr(bpy.data.scene[0],key)
    if type(prop) == bpy.types.CollectionProperty:
        do something

type(prop) gives me <class 'bpy_prop_collection_idprop'> which is not bpy.types.CollectionProperty. So I don't know how to compare both.


Here is an example showing whats intended, it runs but doesn't find the type:

import bpy
from bpy.props import StringProperty, CollectionProperty

class MyCollectionProperty(bpy.types.PropertyGroup):
    name = StringProperty(default="")


bpy.utils.register_class(MyCollectionProperty)
bpy.types.Scene.my_collection_prop = CollectionProperty(
        type=MyCollectionProperty)

scene = bpy.data.scenes[0]
for attr in dir(scene):
    prop = getattr(scene, attr, None)
    if prop is not None:
        if type(prop) == bpy.types.CollectionProperty:
            print(prop)
$\endgroup$

2 Answers 2

11
$\begingroup$

ID.items() is going to give you custom-properties, which is lower level and bypasses properties created by bpy.props, I suspect you want to iterate over attributes, instead of custom-properties.

You can do this instead:

scene = bpy.data.scenes[0]
for attr in dir(scene):
    value = getattr(scene, attr)
    if type(value) == bpy.types.CollectionProperty:
        print("something", attr, value)

The example above uses Python's dir and getattr functions, you can also use a Blender specific method of introspecting properties.

scene = bpy.data.scene[0]
for attr in scene.bl_rna.properties.keys():
    value = getattr(scene, attr)
    if type(value) == bpy.types.CollectionProperty:
        print("something", attr, value)

This limits the properties to Blender's data-api, and wont include functions for example.


Here is a working example based on the example in your question:

import bpy
from bpy.props import StringProperty, CollectionProperty

class MyCollectionProperty(bpy.types.PropertyGroup):
    name = StringProperty(default="")


bpy.utils.register_class(MyCollectionProperty)
bpy.types.Scene.my_collection_prop = CollectionProperty(
        type=MyCollectionProperty)

scene = bpy.data.scenes[0]
for attr in dir(scene):
    prop = getattr(scene, attr, None)
    if isinstance(prop, bpy.types.bpy_prop_collection):
        if bpy.types.Scene.bl_rna.properties[attr].fixed_type == MyCollectionProperty.bl_rna:
            print(prop, "found")

This used Blender specific data-api introspection (bl_rna).

Note: You may want to use isinstance.

$\endgroup$
3
  • $\begingroup$ it seems type(value) == bpy.types.CollectionPropeHrrty is not the same. Here is an example file: dropbox.com/s/2ulhscx54oti1bq/property_type.blend?dl=0 $\endgroup$
    – ndee
    Commented Jun 8, 2016 at 13:18
  • $\begingroup$ You should writing scene = bpy.data.scene[0] with an s to scenes if you expect sth run ! $\endgroup$
    – Marco_105
    Commented May 24, 2018 at 8:24
  • $\begingroup$ Corrected the error. $\endgroup$
    – ideasman42
    Commented May 26, 2018 at 7:27
1
$\begingroup$

Another solution which doesn't rely on knowing the collection items' type beforehand.

Get the property special dictionary construct by accessing the holder's (bpy_struct.id_data) custom properties (barely documented bl_rna.properties), and get the property by name using bpy_struct.path_from_id.

import bpy
from bpy.props import StringProperty, CollectionProperty

class MyCollectionProperty(bpy.types.PropertyGroup):
    name = StringProperty(default="")

bpy.utils.register_class(MyCollectionProperty)
bpy.types.Scene.my_collection_prop = CollectionProperty(type=MyCollectionProperty)

def get_class(collection_prop: bpy.types.CollectionProperty) -> type:
    prop = collection_prop.id_data.bl_rna.properties.get(collection_prop.path_from_id())
    return type(prop.fixed_type) if prop is not None else None

assert MyCollectionProperty == get_class(bpy.data.scenes[0].my_collection_prop)

Note this will only work if the bpy.props.CollectionProperty is a direct member of a bpy.types.ID instance. In the case where a collection property is held in a property group you can use something like :

def get_class(collection_prop: bpy.types.CollectionProperty) -> type:
    holder_path = ".".join(repr(collection_prop).split(".")[:-1])  # Get the path to the prop and remove the last element
    holder = eval(holder_path)
    prop_name = repr(collection_prop).split(".")[-1]
    return type(holder.bl_rna.properties.get(prop_name).fixed_type)

It's a bit more hacky but more extendable.

$\endgroup$
2
  • $\begingroup$ Thank you very much for your answer! :) I have a small tweak so we avoid a loop. It seems like .id_data.bl_rna.properties behaves like a dict and the following works too: return type(collection_prop.id_data.bl_rna.properties[collection_prop.path_from_id()].fixed_type) (you might want to have intermediate variables for readability) $\endgroup$
    – AlexLoss
    Commented Jul 4, 2022 at 16:56
  • $\begingroup$ @AlexLoss Oh right that makes sense ! Thank you I'll fix it. $\endgroup$
    – Gorgious
    Commented Jul 4, 2022 at 20:45

You must log in to answer this question.

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