1
$\begingroup$

For a Blender add-on I am trying to create an UI that let's the user

  • choose from a list of existing instances of a PropertyGroup stored in a CollectionProperty
  • create new instances of that PropertyGroup and name them
  • possibly delete instances

This is much like the normal chooser for ID types that you get with the template_ID() function, but for a non-ID type.

example of template_ID() chooser

First I was thinking if it would be possible to derive an ID-type myself, but I found no information on that online, so I doubt that it is even possible. Note that my items cannot really be represented by any ID-type already present in Blender.

Things I've already tried:

  • template_search() gives me a nice way to look up existing properties, but because my items' name properties are not necessarily unique, it is not really an option, because I can't get the original item back from the name string.
  • prop() with a dynamically generated EnumProperty sort of works. However there is no way to rename the item without a secondary name input box.

I was wondering if somebody has already created a similar kind of input for another Blender add-on or even as a Python module that I could use for my purposes.

$\endgroup$
  • 1
    $\begingroup$ You can use an UIList, see: Create an interface which is similar to the material list box $\endgroup$ – p2or Aug 18 at 12:45
  • $\begingroup$ That might be an option, but only if it can be made more compact, as it has to fit inside a node. Would it be possible to display an UIList inside a modal dialogue / popup that can be opened with a button? (I am not too familiar with the correct terms in Blender UI world) I'm ok with not having search functionality. $\endgroup$ – geloescht Aug 18 at 15:59
  • $\begingroup$ As you can see, UIList has a compact mode which should also work as part of an invoke_props_dialog. $\endgroup$ – p2or Aug 18 at 17:10
  • $\begingroup$ Thank you, I implemented a solution based on invoke_search_popup() which I wouldn't have found without your link. I am going to post an answer to my own question once I am confident that my code works as intended. $\endgroup$ – geloescht Aug 18 at 21:30
1
$\begingroup$

It is possible to create a group of inputs that behaves very similar to the normal chooser. This is the code to draw the widgets:

def draw_buttons(self, context, layout):
    group = layout.row(align=True)
    choose_props = group.operator('example.choose_item', text="", icon='PRESET')
    if self.item_set:
        group.prop(self.get_item(), "name", text="", expand=True)
        clear_props = group.operator('example.clear_item', icon='X', text="")
        clear_props.node_tree = self.id_data.name
        clear_props.node = self.name
    else:
        new_props = group.operator('example.new_item', icon='ADD', text='New')
        new_props.node_tree = self.id_data.name
        new_props.node = self.name
    choose_props.node_tree = self.id_data.name
    choose_props.name = self.name

The choose operator itself uses context.window_manager.invoke_search_popup() to open a search box of available items. Here is example code for the operators:

items = [('one', 'Any', "", 'PRESET', 1), ('two', 'PropertyGroup', "", 'PRESET', 2), ('three', 'type', "", 'PRESET', 3)]

class ChooseItemOperator(bpy.types.Operator):
    bl_idname = "example.choose_item"
    bl_label = "Choose item"
    bl_options = {'INTERNAL'}
    bl_property = "enum"

    def get_enum_options(self, context):
        global items
        return items

    enum: EnumProperty(items=get_enum_options, name="Items")
    node_tree: StringProperty()
    node: StringProperty()

    def execute(self, context):
        tree = bpy.data.node_groups[self.node_tree]
        node = tree.nodes[self.node]
        node.item_set = true
        node.set_item(self.enum)
        return {"FINISHED"}

    def invoke(self, context, event):
        context.window_manager.invoke_search_popup(self)
        return {"FINISHED"}

class NewItemOperator(bpy.types.Operator):
    bl_idname = "example.new_item"
    bl_label = "New Item"
    bl_options = {'INTERNAL'}

    node_tree: StringProperty()
    node: StringProperty()

    def execute(self, context):
        global items
        tree = bpy.data.node_groups[self.node_tree]
        node = tree.nodes[self.node]
        node.item_set = True
        newitem = ('four', 'type', "", 'PRESET', 4)
        items.append(newitem)
        node.set_item(newitem)
        return {'FINISHED'}

class ClearItemOperator(bpy.types.Operator):
    bl_idname = "example.clear_item"
    bl_label = "Clear Item"
    bl_options = {'INTERNAL'}

    node_tree: StringProperty()
    node: StringProperty()

    def execute(self, context):
        tree = bpy.data.node_groups[self.node_tree]
        node = tree.nodes[self.node]
        node.item_set = False
        return {'FINISHED'}

Of course this is just example code and adding new items should be smarter than just adding items to a global list, but this is the general idea.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.