0
$\begingroup$

I've been strugling with this issue for several days now...

I would like to link a collection to an external file, then make a library override.

When I use the 'import_asset' only one time, it's working perfectly. But I want to use it in a loop. Problems starts to occur. I assume it's because of the bpy.app.timers.register(lambda: override_object(override, ob)) call that will directly return None and will launch an other thread. The main issue is without this I'm not able to call the Outliner operator.

I was thinking of finding a way to wait for the timer to finish its job before continue the iteration in the loop but I didn't find my way yet.

Should I use a custom operator for this?

Thanks a lot for your help!

Here is the full code:

import bpy
import os


def import_asset(filepath='', col_name='', asset_type=''):
    '''
        Will import asset and link it to specified asset
        type collection.

        Args:
            filepath (str): Filepath of asset blend file.
            col_name (str): Collection name where asset is.
            asset_type (str): Asset type folder to parent asset to.
    '''
    for var in [filepath, col_name, asset_type]:
        assert isinstance(var, str)

    layer_collection = view_layers_dict().get(asset_type)
    if not layer_collection:
        msg = 'Collection {asset_type} not fond!'
        raise Exception(msg)

    bpy.context.view_layer.active_layer_collection = layer_collection
    
    blender_filepath = os.path.join(filepath, 'Collection')
    return_value = bpy.ops.wm.link(filepath=blender_filepath, 
                                   directory=blender_filepath,  
                                   filename=col_name, 
                                   link=True)

    return_value = next(iter(return_value))
    if not return_value == 'FINISHED':
        msg = 'Link for collection failed!'
        raise Exception(msg)

    if not bpy.data.collections.get(col_name):
        msg = 'Link for collection failed!'
        raise Exception(msg)

    make_library_override(col_name)
    

def make_library_override(object_name):
    bpy.ops.object.select_all(action='DESELECT')
    ob = bpy.data.objects[object_name]
    ob.select_set(True)
    for window in bpy.context.window_manager.windows:
        screen = window.screen
        for area in screen.areas:
            if area.type == 'OUTLINER':
                override = bpy.context.copy()
                override["area"] = area
                bpy.app.timers.register(lambda: override_object(override, ob))
                break 


def override_object(override, ob):
    if ob in set(bpy.data.objects):
        bpy.ops.outliner.liboverride_operation(
            override, 'INVOKE_DEFAULT', 
            type='OVERRIDE_LIBRARY_CREATE_HIERARCHY', 
            selection_set='SELECTED_AND_CONTENT')
        bpy.ops.object.select_all(action='DESELECT')

        return None


def get_all_children(col):
    yield col
    for child in col.children:
        yield from get_all_children(child)


def view_layers_dict():
    layer_dict = {}

    view_layer = bpy.context.view_layer.layer_collection

    all_layers = get_all_children(view_layer)

    [layer_dict.update({l.name: l}) for l in all_layers]

    return layer_dict
$\endgroup$

1 Answer 1

1
$\begingroup$

I ended up by doing this instead:

obj = bpy.data.objects.get(obj_name)
arg_dict = {'active_object': obj}
with bpy.context.temp_override(**arg_dict):
   bpy.ops.object.make_override_library()
$\endgroup$

You must log in to answer this question.

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