10
$\begingroup$

How to show open dialog box that can navigate to blender datablocks. I can show open dialog that can select .blend files, but how to select specific datablock just as 'File->Link' or 'File->Append' does?

Below is the code I am using ( copied from template ):

def read_some_data( context, filepath ):
  print("running read_some_data...")
  print( filepath )
  return {'FINISHED'}

class ImportSomeData( Operator, ImportHelper ):
    bl_idname = "import_test.some_data"
    bpy.ops.import_test.some_data is constructed
    bl_label = "Import Some Data"

    filename_ext = ".blend"

    filter_glob = StringProperty(
        default="*.blend",
        options={ 'HIDDEN' },
        )

def execute(self, context):
    return read_some_data(context, self.filepath)

Thanks!

$\endgroup$
2
  • $\begingroup$ The question I would ask in response to this is why you would want to do this, if the feature already exists? I presume you want to access data blocks from code, rather than a visual interface, then? $\endgroup$
    – MaVCArt
    Nov 27, 2015 at 11:17
  • $\begingroup$ No @MaVCArt, if you have a question, ask it in a different thread lol :p Let programmers be programmers, that's why we have the internet in the first place ^_^. My boss said I couldn't reasonably render 11 billion points in a browser application. I didn't even have to prove him wrong! (see potree) Anyways, hovering over the append/link commands exposes the python code executed when you click them. I LOVE LOVE blender for this epic use of open source tooltips. Turns out your question has already been answered here: blender.stackexchange.com/questions/3 $\endgroup$ Feb 25, 2017 at 7:10

2 Answers 2

2
$\begingroup$

You need to use bpy.data.libraries. The class has a function load which read the datablock of a blend file.

Here an example of how to draw such dialog box to append either a group or an object.

class open_blend_file(bpy.types.Operator, ImportHelper):
    """Import a .blend file to append model from"""
    bl_idname = "blend.open"
    bl_label = "Import"
    bl_options = {'UNDO'}

    filter_glob: StringProperty(
        default="*.blend",
        options={'HIDDEN'},
    )

    group: BoolProperty()

    def get_lib_groups(self,context):
        l = [];
        if os.path.exists(self.filepath) and self.filepath.endswith('.blend'):
            with bpy.data.libraries.load(self.filepath) as (data_from, data_to):
                groups = data_from.groups;
                # objects = data_from.objects;
                for g in groups:
                    l.append((g,g,g));
        if len(l) == 0:
            l = [("None","None","None")];

        return l;

    def get_lib_objects(self,context):
        l = [];
        if os.path.exists(self.filepath) and self.filepath.endswith('.blend'):
            with bpy.data.libraries.load(self.filepath) as (data_from, data_to):
                # groups = data_from.groups;
                objects = data_from.objects;
                for o in objects:
                    l.append((o, o, o));
        if len(l) == 0:
            l = [("None","None","None")];

        return l;

    lib_groups: EnumProperty(       
        name = "List of groups in the .blend file",
        description = "List of groups in the .blend file",
        items = get_lib_groups
    );

    lib_objects: EnumProperty(
        name = "List of objects in the .blend file",
        description = "List of objects in the .blend file",
        items = get_lib_objects
    );


    def draw(self, context):
        scene = bpy.context.scene;
        props = scene.property;
        layout = self.layout;

        row = layout.row();
        row.prop(props,'import_from', expand = True)     

        if props.import_from =='Groups':
            if len(self.lib_groups)>0:       
                row = layout.row();
                row.prop(self,'lib_groups', text = "Groups", icon = "GROUP")     
        else:
            if len(self.lib_objects)>0:       
                row = layout.row();
                row.prop(self,'lib_objects', text = "Objects", icon = "OBJECT_DATA")

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}
    
    def execute(self, context):
        if self.group :
            name = self.lib_groups;
        else:
            name = self.lib_objects;

        if self.group:
            blend_file = self.filepath + "/Group/"
            bpy.ops.wm.append(directory=blend_file, link=False, filename=name, autoselect = True);

            group = bpy.data.groups.get(name); 
            if group is not None:
                bpy.ops.object.empty_add(type='PLAIN_AXES', location = (0,0,0));
                obj = get_active_object();
                obj.name = group.name;

                for o in group.objects:
                    o.parent = obj;

                group.objects.link(obj);


        else:
            blend_file = self.filepath + "/Object/"
            bpy.ops.wm.append(directory=blend_file, link=False, filename=name, autoselect = True);

        return {'FINISHED'}
$\endgroup$
2
  • $\begingroup$ What does scene.property look like? I get the error: AttributeError: 'Scene' object has no attribute 'property' $\endgroup$
    – dval
    Feb 24, 2019 at 9:17
  • $\begingroup$ Ahh, got it. 'propterty' can be a PropertyGroup with a StringProperty 'import_from'. $\endgroup$
    – dval
    Feb 24, 2019 at 10:40
1
$\begingroup$

It seems there is now way to bring this dialog box that Append and Link are using. I've wrote some custom append operator by reworking the code from the previous answer, hope it will be useful.

import bpy
import os

bl_info = {
    "name": "Blender Custom Append",
    "author": "@Andrej730",
    "version": (1, 0),
    "blender": (2, 90, 0),
    "location": "",
    "description": "",
    "warning": "",
    "category": ""
}

def append_data_block(filepath, data_block_type, name, link=False, relative=False):
    with bpy.data.libraries.load(filepath, link=link, relative=relative) as (data_from, data_to):
        if name not in getattr(data_from, data_block_type):
            return {
                "data_block": None, 
                "msg": f"Data-block {data_block_type}/{name} not found in {filepath}"
            }
        getattr(data_to, data_block_type).append(name)
    return {
        "data_block": getattr(data_to, data_block_type)[0],
        "msg": ""
    }

class CustomBlendAppend(bpy.types.Operator):
    """Import a .blend file to append model from"""
    bl_idname = "blend.custom_append"
    bl_label = "Custom Blend Append"
    bl_options = {'UNDO'}

    filepath: bpy.props.StringProperty(
        name="File Path", description="Filepath used to import from", 
        maxlen=1024, default="", subtype="FILE_PATH"
    )

    filter_glob: bpy.props.StringProperty(
        default="*.blend",
        options={'HIDDEN'},
    )

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {"RUNNING_MODAL"}

    def get_lib_groups(self, context):
        l = [("0", "", "")]
        if os.path.exists(self.filepath) and self.filepath.endswith('.blend'):
            with bpy.data.libraries.load(self.filepath) as (data_from, data_to):
                for data_block_type in dir(data_from):
                    data = getattr(data_from, data_block_type)
                    if data:
                        item = (data_block_type,) * 3
                        l.append(item)
        return l

    def get_lib_objects(self, context):
        l = [("", "", "")]
        if self.data_block_type != '0' and os.path.exists(self.filepath) and self.filepath.endswith('.blend'):
            with bpy.data.libraries.load(self.filepath) as (data_from, data_to):
                objects = getattr(data_from, self.data_block_type);
                for o in objects:
                    l.append((o, o, o))
        return l

    data_block_type: bpy.props.EnumProperty(       
        name = "Data Block Type",
        description = "List of data blocks in the .blend file",
        items=get_lib_groups,
    )

    data_block: bpy.props.EnumProperty(
        name = "List of objects in the .blend file",
        description = "List of objects in the .blend file",
        items=get_lib_objects,
    )

    def draw(self, context):
        layout = self.layout
        layout.label(text="Data Block Type")
        layout.prop(self, "data_block_type", text = "", icon = "GROUP")
        layout.label(text="Data Block")
        layout.prop(self,'data_block', text = "")     

    def execute(self, context):
        if self.data_block_type == "0":
            self.report({"ERROR"}, "Select a data block type")
            return {"CANCELLED"}

        if self.data_block == "":
            self.report({"ERROR"}, "Select a data block")
            return {"CANCELLED"}
        
        if not os.path.exists(self.filepath):
            self.report({"ERROR"}, f"File not found:\n'{self.filepath}'")
            return {"CANCELLED"}
        
        db = append_data_block(self.filepath, self.data_block_type, self.data_block)
        if not db['data_block']:
            self.report({"ERROR"}, db["msg"])
            return {"CANCELLED"}
        
        props = context.scene.AppendedDatablockProps
        props.data_block_type = self.data_block_type
        props.data_block_name = db['data_block'].name
        props.blend_filepath = self.filepath
        print(db['data_block'], props.data_block_type, props.data_block_name, props.blend_filepath)

        return {"FINISHED"}
    

class AppendedDatablockProps(bpy.types.PropertyGroup):
    data_block_type: bpy.props.StringProperty(name="Data Block Type", default="")
    data_block_name: bpy.props.StringProperty(name="Data Block Name", default="")
    blend_filepath: bpy.props.StringProperty(name="Blend Filepth", default="")

def register():
    bpy.utils.register_class(CustomBlendAppend)
    bpy.utils.register_class(AppendedDatablockProps)
    bpy.types.Scene.AppendedDatablockProps = bpy.props.PointerProperty(type=AppendedDatablockProps)

def unregister():
    bpy.utils.unregister_class(CustomBlendAppend)
    bpy.utils.unregister_class(AppendedDatablockProps)
    del bpy.types.Scene.AppendedDatablockProps

if __name__ == "__main__":
    register()
```
$\endgroup$

You must log in to answer this question.

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