From aa2e8a2a0ab183f18847d5eb96e2aabf370f5261 Mon Sep 17 00:00:00 2001 From: Rainer Trummer Date: Fri, 14 Dec 2018 13:54:34 +0100 Subject: Port 'Edit Linked Library' addon to Blender 2.8 Differential Revision: https://developer.blender.org/D4070 --- object_edit_linked.py | 153 ++++++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 66 deletions(-) diff --git a/object_edit_linked.py b/object_edit_linked.py index 28a50096..0077fb50 100644 --- a/object_edit_linked.py +++ b/object_edit_linked.py @@ -19,21 +19,24 @@ bl_info = { "name": "Edit Linked Library", - "author": "Jason van Gumster (Fweeb), Bassam Kurdali, Pablo Vazquez", - "version": (0, 8, 1), - "blender": (2, 74, 0), - "location": "View3D > Toolshelf > Edit Linked Library", + "author": "Jason van Gumster (Fweeb), Bassam Kurdali, Pablo Vazquez, Rainer Trummer", + "version": (0, 9, 1), + "blender": (2, 80, 0), + "location": "File > External Data > Edit Linked Library", "description": "Allows editing of objects linked from a .blend library.", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Object/Edit_Linked_Library", "category": "Object", } - import bpy -from bpy.app.handlers import persistent +import logging import os +from bpy.app.handlers import persistent + +logger = logging.getLogger('object_edit_linked') + settings = { "original_file": "", "linked_file": "", @@ -42,15 +45,15 @@ settings = { @persistent -def linked_file_check(context): +def linked_file_check(context: bpy.context): if settings["linked_file"] != "": if os.path.samefile(settings["linked_file"], bpy.data.filepath): - print("Editing a linked library.") + logger.info("Editing a linked library.") bpy.ops.object.select_all(action='DESELECT') for ob_name in settings["linked_objects"]: - bpy.data.objects[ob_name].select = True # XXX Assumes selected object is in the active scene + bpy.data.objects[ob_name].select_set(True) # XXX Assumes selected object is in the active scene if len(settings["linked_objects"]) == 1: - bpy.context.scene.objects.active = bpy.data.objects[settings["linked_objects"][0]] + context.view_layer.objects.active = bpy.data.objects[settings["linked_objects"][0]] else: # For some reason, the linked editing session ended # (failed to find a file or opened a different file @@ -59,32 +62,30 @@ def linked_file_check(context): settings["linked_file"] = "" -class EditLinked(bpy.types.Operator): +class OBJECT_OT_EditLinked(bpy.types.Operator): """Edit Linked Library""" bl_idname = "object.edit_linked" bl_label = "Edit Linked Library" - use_autosave = bpy.props.BoolProperty( + use_autosave: bpy.props.BoolProperty( name="Autosave", description="Save the current file before opening the linked library", default=True) - use_instance = bpy.props.BoolProperty( + use_instance: bpy.props.BoolProperty( name="New Blender Instance", description="Open in a new Blender instance", default=False) @classmethod - def poll(cls, context): + def poll(cls, context: bpy.context): return settings["original_file"] == "" and context.active_object is not None and ( (context.active_object.instance_collection and - context.active_object.instance_collection.library is not None) or + context.active_object.instance_collection.library is not None) or (context.active_object.proxy and - context.active_object.proxy.library is not None) or - context.active_object.library is not None) - #return context.active_object is not None + context.active_object.proxy.library is not None) or + context.active_object.library is not None) - def execute(self, context): - #print(bpy.context.active_object.library) + def execute(self, context: bpy.context): target = context.active_object if target.instance_collection and target.instance_collection.library: @@ -99,7 +100,7 @@ class EditLinked(bpy.types.Operator): settings["linked_objects"].append(target.name) if targetpath: - print(target.name + " is linked to " + targetpath) + logger.debug(target.name + " is linked to " + targetpath) if self.use_autosave: if not bpy.data.filepath: @@ -116,35 +117,35 @@ class EditLinked(bpy.types.Operator): try: subprocess.Popen([bpy.app.binary_path, settings["linked_file"]]) except: - print("Error on the new Blender instance") + logger.error("Error on the new Blender instance") import traceback - traceback.print_exc() + logger.error(traceback.print_exc()) else: bpy.ops.wm.open_mainfile(filepath=settings["linked_file"]) - print("Opened linked file!") + logger.info("Opened linked file!") else: self.report({'WARNING'}, target.name + " is not linked") - print(target.name + " is not linked") + logger.warning(target.name + " is not linked") return {'FINISHED'} -class ReturnToOriginal(bpy.types.Operator): +class WM_OT_ReturnToOriginal(bpy.types.Operator): """Load the original file""" bl_idname = "wm.return_to_original" bl_label = "Return to Original File" - use_autosave = bpy.props.BoolProperty( + use_autosave: bpy.props.BoolProperty( name="Autosave", description="Save the current file before opening original file", default=True) @classmethod - def poll(cls, context): + def poll(cls, context: bpy.context): return (settings["original_file"] != "") - def execute(self, context): + def execute(self, context: bpy.context): if self.use_autosave: bpy.ops.wm.save_mainfile() @@ -152,25 +153,29 @@ class ReturnToOriginal(bpy.types.Operator): settings["original_file"] = "" settings["linked_objects"] = [] - print("Back to the original!") + logger.info("Back to the original!") return {'FINISHED'} -# UI -# TODO:Add operators to the File menu? -# Hide the entire panel for non-linked objects? -class PanelLinkedEdit(bpy.types.Panel): +class VIEW3D_PT_PanelLinkedEdit(bpy.types.Panel): bl_label = "Edit Linked Library" bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_category = "Relations" - bl_context = "objectmode" + bl_region_type = 'UI' + bl_category = "View" + bl_context = 'objectmode' @classmethod - def poll(cls, context): + def poll(cls, context: bpy.context): return (context.active_object is not None) or (settings["original_file"] != "") - def draw(self, context): + def draw_common(self, scene, layout, props): + props.use_autosave = scene.use_autosave + props.use_instance = scene.use_instance + + layout.prop(scene, "use_autosave") + layout.prop(scene, "use_instance") + + def draw(self, context: bpy.context): layout = self.layout scene = context.scene icon = "OUTLINER_DATA_" + context.active_object.type @@ -184,7 +189,7 @@ class PanelLinkedEdit(bpy.types.Panel): if settings["original_file"] == "" and ( (target and - target.library is not None) or + target.library is not None) or context.active_object.library is not None): if (target is not None): @@ -193,18 +198,15 @@ class PanelLinkedEdit(bpy.types.Panel): else: props = layout.operator("object.edit_linked", icon="LINK_BLEND", text="Edit Library: %s" % context.active_object.name) - props.use_autosave = scene.use_autosave - props.use_instance = scene.use_instance - layout.prop(scene, "use_autosave") - layout.prop(scene, "use_instance") + self.draw_common(scene, layout, props) if (target is not None): layout.label(text="Path: %s" % - target.library.filepath) + target.library.filepath) else: layout.label(text="Path: %s" % - context.active_object.library.filepath) + context.active_object.library.filepath) elif settings["original_file"] != "": @@ -215,19 +217,17 @@ class PanelLinkedEdit(bpy.types.Panel): layout.separator() - #XXX - This is for nested linked assets... but it only works - # when launching a new Blender instance. Nested links don't - # currently work when using a single instance of Blender. + # XXX - This is for nested linked assets... but it only works + # when launching a new Blender instance. Nested links don't + # currently work when using a single instance of Blender. props = layout.operator("object.edit_linked", text="Edit Library: %s" % context.active_object.instance_collection.name, icon="LINK_BLEND") - props.use_autosave = scene.use_autosave - props.use_instance = scene.use_instance - layout.prop(scene, "use_autosave") - layout.prop(scene, "use_instance") + + self.draw_common(scene, layout, props) layout.label(text="Path: %s" % - context.active_object.instance_collection.library.filepath) + context.active_object.instance_collection.library.filepath) else: props = layout.operator("wm.return_to_original", icon="LOOP_BACK") @@ -237,31 +237,50 @@ class PanelLinkedEdit(bpy.types.Panel): else: layout.label(text="%s is not linked" % context.active_object.name, - icon=icon) + icon=icon) + + +class TOPBAR_MT_edit_linked_submenu(bpy.types.Menu): + bl_label = 'Edit Linked Library' + bl_idname = 'view3d.TOPBAR_MT_edit_linked_submenu' + + def draw(self, context): + self.layout.separator() + self.layout.operator(OBJECT_OT_EditLinked.bl_idname) + self.layout.operator(WM_OT_ReturnToOriginal.bl_idname) addon_keymaps = [] +classes = ( + OBJECT_OT_EditLinked, + WM_OT_ReturnToOriginal, + VIEW3D_PT_PanelLinkedEdit, + TOPBAR_MT_edit_linked_submenu + ) def register(): - bpy.app.handlers.load_post.append(linked_file_check) - bpy.utils.register_class(EditLinked) - bpy.utils.register_class(ReturnToOriginal) - bpy.utils.register_class(PanelLinkedEdit) + bpy.app.handlers.load_post.append(linked_file_check) + + for c in classes: + bpy.utils.register_class(c) - # Is there a better place to store this properties? bpy.types.Scene.use_autosave = bpy.props.BoolProperty( name="Autosave", description="Save the current file before opening a linked file", default=True) + bpy.types.Scene.use_instance = bpy.props.BoolProperty( name="New Blender Instance", description="Open in a new Blender instance", default=False) + # add the function to the file menu + bpy.types.TOPBAR_MT_file_external_data.append(TOPBAR_MT_edit_linked_submenu.draw) + # Keymapping (deactivated by default; activated when a library object is selected) - kc = bpy.context.window_manager.keyconfigs.addon - if kc: # don't register keymaps from command line + kc = bpy.context.window_manager.keyconfigs.addon + if kc: # don't register keymaps from command line km = kc.keymaps.new(name="3D View", space_type='VIEW_3D') kmi = km.keymap_items.new("object.edit_linked", 'NUMPAD_SLASH', 'PRESS', shift=True) kmi.active = True @@ -272,10 +291,9 @@ def register(): def unregister(): - bpy.utils.unregister_class(EditLinked) - bpy.utils.unregister_class(ReturnToOriginal) - bpy.utils.unregister_class(PanelLinkedEdit) - bpy.app.handlers.load_post.remove(linked_file_check) + + bpy.app.handlers.load_post.remove(linked_file_check) + bpy.types.TOPBAR_MT_file_external_data.remove(TOPBAR_MT_edit_linked_submenu) del bpy.types.Scene.use_autosave del bpy.types.Scene.use_instance @@ -285,6 +303,9 @@ def unregister(): km.keymap_items.remove(kmi) addon_keymaps.clear() + for c in reversed(classes): + bpy.utils.unregister_class(c) + if __name__ == "__main__": register() -- cgit v1.2.3