mirror of
https://github.com/blender/blender-addons.git
synced 2025-07-29 12:05:36 +00:00
Port 'Edit Linked Library' addon to Blender 2.8
Differential Revision: https://developer.blender.org/D4070
This commit is contained in:

committed by
Jacques Lucke

parent
96d48401f4
commit
aa2e8a2a0a
@ -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()
|
||||
|
Reference in New Issue
Block a user