So, I am having an issue when it comes to linking objects from from another blend file. If the object is a parent to other objects, it does not include its sibblings along with the selected object. I thought maybe I could create a feature that would organize the data into a hierarchical structure, giving more freedom to link all siblings that belong to the selected object. Now, I see that you can do library linking with python, using bpy.data.libraries.load, but it does not include any method calls to open the file browser. I tried making something up, basing it off of the dxf import using context.window_manage.fileselect_add, but my script crashes Blender. Here is the script
In spite of this, I tried looking through the api, but the only thing I could find was for the template image operator, but I do not see an operator to open the browser for library linking. Is there way that this can be done?
I’ve included the ImportHelper as it makes the filebrowser update when a file is changed.
It lists the objects in the blend file selected
It imports the objects into the blend file if the button is pressed and prints their parent to the console.
It doesn’t link the objects into the current scene, I assume your new_scene prop has something to do with how this will work.
import bpy
from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty
from bpy.types import Operator
import os
class LINK_OT_blend(Operator, ImportHelper) :
bl_idname = "link.blend_data"
bl_description = 'link blend library data'
bl_label = "Link Blend"
#bl_space_type = "PROPERTIES"
#bl_region_type = "WINDOW"
#bl_options = {'UNDO'}
filepath = StringProperty(
name="File Path",
description="Filepath used for importing the file",
maxlen=1024,
subtype='FILE_PATH',
)
filter_blender = BoolProperty(default=True)
new_scene = BoolProperty(name="Replace scene", description="Replace scene")
def invoke(self, context, event):
print("-----------------------")
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def draw(self, context) :
layout = self.layout
row = layout.row()
row.prop(self, "filepath")
if not os.path.exists(self.filepath):
return
with bpy.data.libraries.load(self.filepath) as (data_from, data_to):
if data_from.objects:
row = layout.row()
row.label("OBJECTS")
for obj in data_from.objects:
row = layout.row()
op = row.label(obj)
row = layout.row()
row.prop(self, 'new_scene')
def execute(self, context) :
#context.window_manager.fileselect_add(self)
print(self.filepath)
with bpy.data.libraries.load(self.filepath, link=True) as (data_from, data_to):
data_to.objects = data_from.objects
for obj in data_to.objects:
if obj is not None:
print(obj.name, end="")
if obj.parent is not None:
print(" parent:", obj.parent.name)
else:
print()
return {'FINISHED'}
def register() :
bpy.utils.register_class(LINK_OT_blend)
if __name__ == "__main__":
register()
bpy.ops.link.blend_data('INVOKE_DEFAULT')
Hi batFinger, I am sorry that I missed your reply. From looking at the code, I think this is what I need. However, why is there a draw function inside the class operator. I have never seen this being used before. Is this how the file browser is drawn?
draw() functions in operators aren’t unusual, you can override the default drawing code for the redo panel in here (if bl_options = {‘REGISTER’, ‘UNDO’}).
In a file selector modal operator, it is used to the draw the panel at the bottom of the left sidebar.
In modal operators like props_dialog it is used to the draw the popup content (lots of restrictions in popups however, they don’t properly refresh).
I’m pretty sure your original code craashes 'cause you add a modal operation in the execute() function
context.window_manager.fileselect_add(self)
You’re supposed to add it in the invoke() method. execute() is called on completion (in case of file selector, confirmation to import/export). It won’t work however, if there’s no invoke(), 'cause Blender will try to call execute() directly instead.