2
$\begingroup$

I'm making an add-on and would like to have properties for selecting ID-blocks and data paths, similar to those in KeyingSetPath. Here's how I'm declaring them:

item: PointerProperty(name="Data",
                      type=bpy.types.ID,
                      description="Data with an animatable property")
item_type: EnumProperty(name="Data type", items=id_type_items, default="OBJECT")
data_path: StringProperty(name="Data path",
                          description="RNA path of property to check")

Here's how I'm making the UI (referencing properties_scene.py):

flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=True)

layout.use_property_split = True
layout.use_property_decorate = False

col = flow.column()
col.template_any_ID(myobject, "item", "item_type", text="Target ID-Block")
col.separator()
col.template_path_builder(myobject, "data_path", myobject.item)

However, the resulting search box is always disabled/grayed-out. As far as I can tell the only difference between the property that I declared and KeyingSetPath.id is that mine is created dynamically (i.e., in a Python script). What else could I be doing wrong?

Strangely, if I change the property type to a specific ID subclass, like Object, it works. The documentation suggests this might be intentional, but I can't find the code that enforces it:

type (class) – A subclass of bpy.types.PropertyGroup or bpy.types.ID.

If it is intentional, what is the reason for the limitation, and why is KeyingSetPath.id exempt?

Update 1: KeyingSetPath.id does have "editable" and "type" functions set, to dynamically determine those things, and I can't set those functions from Python, but it seems like it should still work correctly without them. I don't need the type of the data block so I tried just using template_ID instead of template_any_ID, but then the field disappears entirely!

Update 2: I found this answer which makes their own ID type selector and search box but does so by creating a separate property for every single ID data type. It would still be nice to know why template_any_ID doesn't work.

Update 3: I figured out that it is indeed the "type" function that makes it work for KeyingSetPath.id. ui_but_add_search calls RNA_property_pointer_type to get the pointed type, which calls type_fn, and then search_id_collection which looks for a collection that holds that type. I'll see if I can make a workaround or patch.

$\endgroup$
5
  • $\begingroup$ Hello ! Yes, I believe the difference is the AnyType pointer defined in keying set is coded in C and it's not possible to create such pointer in Python. I suspect under the hood it acts like the monstrosity I created in the answer you linked at the end of your question. Would need to parse the source code to be sure though. $\endgroup$
    – Gorgious
    May 4, 2023 at 5:48
  • $\begingroup$ KeyingSetPath.id is defined directly as a property in the C code, not a getter function. You can check this with bpy.types.KeyingSetPath.bl_rna.properties['id']. It's declared in rna_animation.c rna_def_keyingset_path, and the line RNA_def_property_struct_type(prop, "ID"); seems to suggest that it's a pointer property with type ID, just like the one I defined. $\endgroup$
    – impiaaa
    May 5, 2023 at 4:44
  • $\begingroup$ Except you defined it using the python API, not in C code. Which is not supported $\endgroup$
    – Gorgious
    May 5, 2023 at 21:23
  • $\begingroup$ Yes. I'd like to know why it's not supported. $\endgroup$
    – impiaaa
    May 6, 2023 at 3:58
  • $\begingroup$ This is not really a question we can answer on this site though. The reason is the dev team either chose to or didn't have enough ressources to implement this feature. This is unfortunately not the right site for a feature request, you can try to use the official channel to ask this question blender.community/c/rightclickselect $\endgroup$
    – Gorgious
    May 6, 2023 at 8:47

1 Answer 1

2
$\begingroup$

UILayout.template_any_ID calls UILayout.prop which for pointer properties calls UILayout.prop_search. That first first checks if it's provided the object and property name of a collection to search through, and if not, uses the appropriate main collection for the pointed type. There is no collection of ID though, so this fails for our case. But, we can shortcut UILayout.template_any_ID and UILayout.prop and call UILayout.prop_search directly, with our own search collection. With that, here's a version of template_any_ID that calls UILayout.prop_search directly (and also uses the property name instead of "ID-Block:" for the label):

id_type_to_collection_name = {
    'ACTION': 'actions',
    'ARMATURE': 'armatures',
    ...
    'WORKSPACE': 'workspaces',
    'WORLD': 'worlds'
}

def template_any_ID(layout: bpy.types.UILayout, data: typing.Any, property: str, type_property: str, text: str='', text_ctxt: str='', translate: bool=True) -> None:
    split = layout.split(factor=0.33)

    # FIRST PART
    row = split.row()

    # Label - either use the provided text, or will become "ID-Block:"
    if text != '':
        row.label(text=text, text_ctxt=text_ctxt, translate=translate)
    elif data.bl_rna.properties[property].name != '':
        row.label(text=data.bl_rna.properties[property].name, text_ctxt=text_ctxt, translate=translate)
    else:
        row.label(text="ID-Block:")

    # SECOND PART
    row = split.row(align=True)

    # ID-Type Selector - just have a menu of icons

    # HACK: special group just for the enum,
    # otherwise we get ugly layout with text included too...
    sub = row.row(align=True)
    sub.alignment = 'LEFT'

    sub.prop(data, type_property, icon_only=True)

    # ID-Block Selector - just use pointer widget...

    # HACK: special group to counteract the effects of the previous enum,
    # which now pushes everything too far right.
    sub = row.row(align=True)
    sub.alignment = 'EXPAND'

    type_name = getattr(data, type_property)
    if type_name in id_type_to_collection_name:
        icon = data.bl_rna.properties[type_property].enum_items[type_name].icon
        sub.prop_search(data, property, bpy.data, id_type_to_collection_name[type_name], text='', icon=icon)
```
$\endgroup$
2
  • 1
    $\begingroup$ Hey it seems like a nice workaround ! Would you mind adding a snippet for how it should be used (called) in a script ? $\endgroup$
    – Gorgious
    May 19, 2023 at 10:34
  • $\begingroup$ It's just the same as template_any_ID, which I already used in my question. So instead of col.template_any_ID(myobject, "item", "item_type") it would be template_any_ID(col, myobject, "item", "item_type"). $\endgroup$
    – impiaaa
    May 20, 2023 at 15:24

You must log in to answer this question.

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