3
$\begingroup$

For an import/export plugin I'd like to create a long list of objects to select from. This is fairly easy to do in a panel with a template list, but it's not quite what I want - it's too small and ever-present, I'd prefer a nice, big popup window/dialog box which can be invoked from a button and disappears after an item has been selected.

I've tried a few options which I've summarised in the hacky code below:

class ObjectRefItem(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Object Name", default="Unknown")
    ref = bpy.props.IntProperty(name="Ref", default=22, subtype='UNSIGNED')

class ExamplePanel(Panel):
    bl_idname = "SCENE_PT_Example"
    bl_label = "Example"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout
        scn = bpy.context.scene

        row = layout.row()
        row.operator("custom.example_action", text="DIALOG").action = 'DIALOG'
        row.operator("custom.example_action", text="POPUP").action = 'POPUP'
        row.operator("custom.example_action", text="SEARCH").action = 'SEARCH'

def item_getter(self, context):
    numItems= 1000
    for i in range(numItems):
        yield (str(i), "Object %i" % i, "")

class ExampleDialog(bpy.types.Operator):
    bl_idname = "object.dialog_operator"
    bl_label = "Simple Dialog Operator"

    def execute(self, context):
        scn = bpy.context.scene
        index = scn.custom_index
        message = '%s (index %d) selected' % (scn.custom[index].name, index)
        self.report({'INFO'}, message)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self, width=800, height=800)

    def draw(self, context):
        layout = self.layout
        scn = bpy.context.scene

        col = layout.column()
        col.template_list("UI_UL_list", "example_dialog", scn, "custom", scn, "custom_index", rows=4)

class ExamplePopup(bpy.types.Operator):
    bl_idname = "object.popup_operator"
    bl_label = "Simple Popup Operator"
    bl_options = {'REGISTER', 'UNDO'}

    my_enum = bpy.props.EnumProperty(items=item_getter)

    def execute(self, context):
        message = '%s selected' % self.my_enum
        self.report({'INFO'}, message)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_popup(self, event)

class ExampleSearch(bpy.types.Operator):
    bl_idname = "object.search_operator"
    bl_label = "Simple Search Operator"
    bl_property = "my_enum"

    my_enum = bpy.props.EnumProperty(items=item_getter)

    def execute(self, context):
        message = '%s selected' % self.my_enum
        self.report({'INFO'}, message)
        return {'FINISHED'}

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

class ExampleActions(Operator):
    bl_idname = "custom.example_action"
    bl_label = "Example Action"

    action = bpy.props.EnumProperty(
        items=(
            ('DIALOG', "Dialog", ""),
            ('POPUP', "Pop-up", ""),
            ('SEARCH', "Search", "")
        )
    )

    def invoke(self, context, event):

        scn = context.scene
        wm = context.window_manager

        if self.action == 'DIALOG':
            scn.custom.clear()
            for index, name, _ in item_getter(self, context):
                item = scn.custom.add()
                item.ref = int(index)
                item.name = name
            bpy.ops.object.dialog_operator('INVOKE_DEFAULT')            

        elif self.action == 'POPUP':
            bpy.ops.object.popup_operator('INVOKE_DEFAULT')

        elif self.action == 'SEARCH':
            bpy.ops.object.search_operator('INVOKE_DEFAULT')

        return {"FINISHED"}

def register():
    bpy.utils.register_class(ExamplePanel)
    bpy.utils.register_class(ExampleDialog)
    bpy.utils.register_class(ExamplePopup)
    bpy.utils.register_class(ExampleSearch)
    bpy.utils.register_class(ExampleActions)
    bpy.utils.register_class(ObjectRefItem)
    bpy.types.Scene.custom = CollectionProperty(type=ObjectRefItem)
    bpy.types.Scene.custom_index = IntProperty()

def unregister():
    bpy.utils.unregister_class(ExamplePanel)
    bpy.utils.unregister_class(ExampleDialog)
    bpy.utils.unregister_class(ExamplePopup)
    bpy.utils.unregister_class(ExampleSearch)
    bpy.utils.unregister_class(ExampleActions)
    bpy.utils.unregister_class(ObjectRefItem)
    del bpy.types.Scene.custom
    del bpy.types.Scene.custom_index

if __name__ == "__main__":
    register()

The dialog is kind of close to what I want, but the scrollbar and filtering options are broken. The popop shows a grid with a subset of the options, AFAICT there's no way to scroll and if I add too many items to it Blender crashes. The search box is OK but again too small.

Something like the file selector, but selecting from a hierarchy of lists instead of directories would be ideal. Is there any way I can improve on the dialog, popup or search above?

$\endgroup$

Your Answer

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

Browse other questions tagged or ask your own question.