feat: duplicate send keys
1.3.0: - feat: new duplicate send to layer feaure - `ctrl + shift + D` in GP dopesheetgpv2
parent
517eceab76
commit
6cf22b81e8
|
@ -1,6 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
1.3.0:
|
||||
|
||||
- feat: new duplicate send to layer feaure - `ctrl + shift + D` in GP dopesheet
|
||||
|
||||
1.2.2:
|
||||
|
||||
- fix: realign anim return error
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
|
||||
def get_layer_list(self, context):
|
||||
'''return (identifier, name, description) of enum content'''
|
||||
return [(l.info, l.info, '') for l in context.object.data.layers if l != context.object.data.layers.active]
|
||||
# try:
|
||||
# except:
|
||||
# return [("", "", "")]
|
||||
# return [(i, basename(i), "") for i in blends]
|
||||
# return [(i.path, basename(i.path), "") for i in self.blends]
|
||||
|
||||
class GPTB_OT_duplicate_send_to_layer(Operator) :
|
||||
bl_idname = "gp.duplicate_send_to_layer"
|
||||
bl_label = 'Duplicate and send to layer'
|
||||
# important to have the updated enum here as bl_property
|
||||
bl_property = "layers_enum"
|
||||
|
||||
|
||||
layers_enum : bpy.props.EnumProperty(
|
||||
name="Duplicate to layers",
|
||||
description="Duplicate selected keys in active layer and send them to choosen layer",
|
||||
items=get_layer_list
|
||||
)
|
||||
|
||||
delete_source : bpy.props.BoolProperty(default=False, options={'SKIP_SAVE'})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL' and context.space_data.ui_mode == 'GPENCIL'
|
||||
|
||||
# history : bpy.props.StringProperty(default='', options={'SKIP_SAVE'}) # need to have a variable to store (to get it in self)
|
||||
|
||||
def execute(self, context):
|
||||
target_layer = self.layers_enum
|
||||
if not target_layer:
|
||||
self.report({'WARNING'}, 'Target layer not specified')
|
||||
return {'CANCELLED'}
|
||||
|
||||
gpl = context.object.data.layers
|
||||
target_layer = gpl.get(target_layer)
|
||||
|
||||
act_layer = gpl.active
|
||||
selected_frames = [f for f in act_layer.frames if f.select]
|
||||
|
||||
act_frame_num = [f.frame_number for f in act_layer.frames if f.select]
|
||||
|
||||
to_replace = [f for f in target_layer.frames if f.frame_number in act_frame_num]
|
||||
|
||||
replaced = len(to_replace)
|
||||
|
||||
## remove overlapping frames
|
||||
for f in reversed(to_replace):
|
||||
target_layer.frames.remove(f)
|
||||
|
||||
## copy original frames
|
||||
for f in selected_frames:
|
||||
target_layer.frames.copy(f)
|
||||
sent = len(selected_frames)
|
||||
|
||||
## delete original frames as an option
|
||||
if self.delete_source:
|
||||
for f in reversed(selected_frames):
|
||||
act_layer.frames.remove(f)
|
||||
|
||||
mess = f'{sent} keys copied'
|
||||
if replaced:
|
||||
mess += f' ({replaced} replaced)'
|
||||
|
||||
# context.view_layer.update()
|
||||
# bpy.ops.gpencil.editmode_toggle()
|
||||
|
||||
mod = context.mode
|
||||
bpy.ops.gpencil.editmode_toggle()
|
||||
bpy.ops.object.mode_set(mode=mod)
|
||||
|
||||
self.report({'INFO'}, mess)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
gp = context.object.data
|
||||
if not len(gp.layers):
|
||||
self.report({'WARNING'}, 'No layers on current GP object')
|
||||
return {'CANCELLED'}
|
||||
|
||||
active = gp.layers.active
|
||||
|
||||
if not active:
|
||||
self.report({'WARNING'}, 'No active layer to take keys from')
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.selected_frames = [f for f in active.frames if f.select]
|
||||
|
||||
if not self.selected_frames:
|
||||
self.report({'WARNING'}, 'No selected keys in active layer')
|
||||
return {'CANCELLED'}
|
||||
|
||||
wm = context.window_manager
|
||||
wm.invoke_search_popup(self) # can't specify size... width=500, height=600
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
addon_keymaps = []
|
||||
def register_keymaps():
|
||||
# pref = get_addon_prefs()
|
||||
# if not pref.kfj_use_shortcut:
|
||||
# return
|
||||
addon = bpy.context.window_manager.keyconfigs.addon
|
||||
# km = addon.keymaps.new(name = "Screen", space_type = "EMPTY")
|
||||
km = addon.keymaps.new(name = "Dopesheet", space_type = "DOPESHEET_EDITOR")
|
||||
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='D', value="PRESS", alt=False, ctrl=True, shift=True, any=False)
|
||||
addon_keymaps.append((km,kmi))
|
||||
kmi = km.keymap_items.new('gp.duplicate_send_to_layer', type='X', value="PRESS", alt=False, ctrl=True, shift=True, any=False)
|
||||
kmi.properties.delete_source = True
|
||||
addon_keymaps.append((km,kmi))
|
||||
|
||||
|
||||
def unregister_keymaps():
|
||||
for km, kmi in addon_keymaps:
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
|
||||
classes = (
|
||||
GPTB_OT_duplicate_send_to_layer,
|
||||
)
|
||||
|
||||
def register():
|
||||
if not bpy.app.background:
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
register_keymaps()
|
||||
|
||||
def unregister():
|
||||
if not bpy.app.background:
|
||||
unregister_keymaps()
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -15,7 +15,7 @@ bl_info = {
|
|||
"name": "GP toolbox",
|
||||
"description": "Set of tools for Grease Pencil in animation production",
|
||||
"author": "Samuel Bernou",
|
||||
"version": (1, 2, 2),
|
||||
"version": (1, 3, 0),
|
||||
"blender": (2, 91, 0),
|
||||
"location": "sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||
"warning": "",
|
||||
|
@ -46,6 +46,7 @@ from . import OP_render
|
|||
from . import OP_copy_paste
|
||||
from . import OP_realign
|
||||
from . import OP_depth_move
|
||||
from . import OP_key_duplicate_send
|
||||
from . import keymaps
|
||||
|
||||
from .OP_pseudo_tint import GPT_OT_auto_tint_gp_layers
|
||||
|
@ -446,6 +447,7 @@ def register():
|
|||
OP_copy_paste.register()
|
||||
OP_realign.register()
|
||||
OP_depth_move.register()
|
||||
OP_key_duplicate_send.register()
|
||||
UI_tools.register()
|
||||
keymaps.register()
|
||||
bpy.types.Scene.gptoolprops = bpy.props.PointerProperty(type = GP_PG_ToolsSettings)
|
||||
|
@ -467,6 +469,7 @@ def unregister():
|
|||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
UI_tools.unregister()
|
||||
OP_key_duplicate_send.unregister()
|
||||
OP_depth_move.unregister()
|
||||
OP_realign.unregister()
|
||||
OP_copy_paste.unregister()
|
||||
|
|
Loading…
Reference in New Issue