Fix #4 paste layers skipping empty frames

1.8.1

- fix: Gp clipboard paste `Paste layers` don't skip empty frames anymore
gpv2
Pullusb 2021-12-17 23:26:48 +01:00
parent 6c19fa54af
commit 3c7477c442
4 changed files with 108 additions and 90 deletions

View File

@ -1,5 +1,8 @@
# Changelog # Changelog
1.8.1
- fix: Gp clipboard paste `Paste layers` don't skip empty frames anymore
1.8.0 1.8.0

View File

@ -17,7 +17,7 @@ bl_info = {
"name": "GP clipboard", "name": "GP clipboard",
"description": "Copy/Cut/Paste Grease Pencil strokes to/from OS clipboard across layers and blends", "description": "Copy/Cut/Paste Grease Pencil strokes to/from OS clipboard across layers and blends",
"author": "Samuel Bernou", "author": "Samuel Bernou",
"version": (1, 3, 2), "version": (1, 3, 3),
"blender": (2, 83, 0), "blender": (2, 83, 0),
"location": "View3D > Toolbar > Gpencil > GP clipboard", "location": "View3D > Toolbar > Gpencil > GP clipboard",
"warning": "", "warning": "",
@ -280,7 +280,6 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None):
''' '''
copy all stroke, not affected by selection on active frame copy all stroke, not affected by selection on active frame
layers can be None, a single layer object or list of layer object as filter layers can be None, a single layer object or list of layer object as filter
if keep_empty is False the frame is deleted when all strokes are cutted
''' '''
t0 = time() t0 = time()
scene = bpy.context.scene scene = bpy.context.scene
@ -312,8 +311,7 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None):
# send index of all points to get the whole stroke with "range" # send index of all points to get the whole stroke with "range"
stroke_list.append( dump_gp_stroke_range(s, [i for i in range(len(s.points))], l, obj) ) stroke_list.append( dump_gp_stroke_range(s, [i for i in range(len(s.points))], l, obj) )
print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds') # print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds')
#print(stroke_list)
return stroke_list return stroke_list
def add_stroke(s, frame, layer, obj, select=False): def add_stroke(s, frame, layer, obj, select=False):
@ -368,7 +366,8 @@ def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select
add a list of strokes to active frame of given layer add a list of strokes to active frame of given layer
if no layer specified, active layer is used if no layer specified, active layer is used
if use_current_frame is True, a new frame will be created only if needed if use_current_frame is True, a new frame will be created only if needed
if select is True, newly added strokes are selected if select is True, newly added strokes are set selected
if stroke list is empty create an empty frame at current frame
''' '''
scene = bpy.context.scene scene = bpy.context.scene
obj = bpy.context.object obj = bpy.context.object
@ -382,24 +381,26 @@ def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select
fnum = scene.frame_current fnum = scene.frame_current
target_frame = False target_frame = False
act = layer.active_frame act = layer.active_frame
## set frame if needed
if act:
if use_current_frame or act.frame_number == fnum:
#work on current frame if exists
# use current frame anyway if one key exist at this scene.frame
target_frame = act
if not target_frame:
#no active frame
#or active exists but not aligned scene.current with use_current_frame disabled
target_frame = layer.frames.new(fnum)
for s in stroke_list: for s in stroke_list:
if act:
if use_current_frame or act.frame_number == fnum:
#work on current frame if exists
# use current frame anyway if one key exist at this scene.frame
target_frame = act
if not target_frame:
#no active frame
#or active exists but not aligned scene.current with use_current_frame disabled
target_frame = layer.frames.new(fnum)
add_stroke(s, target_frame, layer, obj, select=select) add_stroke(s, target_frame, layer, obj, select=select)
''' '''
for s in stroke_data: for s in stroke_data:
add_stroke(s, target_frame) add_stroke(s, target_frame)
''' '''
print(len(stroke_list), 'strokes pasted')
# print(len(stroke_list), 'strokes pasted')
### OPERATORS ### OPERATORS
@ -493,8 +494,8 @@ class GPCLIP_OT_paste_strokes(bpy.types.Operator):
class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
bl_idname = "gp.copy_multi_strokes" bl_idname = "gp.copy_multi_strokes"
bl_label = "GP Copy multi strokes" bl_label = "GP Copy Multi Strokes"
bl_description = "Copy multiple layers>frames>strokes (unlocked and unhided ones) to str in paperclip" bl_description = "Copy multiple layers>frames>strokes from selected layers to str in paperclip"
bl_options = {"REGISTER"} bl_options = {"REGISTER"}
#copy = bpy.props.BoolProperty(default=True) #copy = bpy.props.BoolProperty(default=True)
@ -513,12 +514,12 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
#ct = check_pressure() #ct = check_pressure()
layerdic = {} layerdic = {}
layerpool = [l for l in gpl if not l.hide and l.select]# and not l.lock layerpool = [l for l in gpl if not l.hide and l.select] # and not l.lock
if not layerpool: if not layerpool:
self.report({'ERROR'}, 'No layers selected in GP dopesheet (needs to be visible and selected to be copied)\nHint: Changing active layer reset selection to active only') self.report({'ERROR'}, 'No layers selected in GP dopesheet (needs to be visible and selected to be copied)\nHint: Changing active layer reset selection to active only')
return {"CANCELLED"} return {"CANCELLED"}
if not bake_moves:# copy only drawed frames as is. if not bake_moves: # copy only drawed frames as is.
for l in layerpool: for l in layerpool:
if not l.frames: if not l.frames:
continue# skip empty layers continue# skip empty layers
@ -579,8 +580,8 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
class GPCLIP_OT_paste_multi_strokes(bpy.types.Operator): class GPCLIP_OT_paste_multi_strokes(bpy.types.Operator):
bl_idname = "gp.paste_multi_strokes" bl_idname = "gp.paste_multi_strokes"
bl_label = "GP paste multi strokes" bl_label = "GP Paste Multi Strokes"
bl_description = "Paste multiple layers>frames>strokes from paperclip" bl_description = "Paste multiple layers>frames>strokes from paperclip on active layer"
bl_options = {"REGISTER"} bl_options = {"REGISTER"}
#copy = bpy.props.BoolProperty(default=True) #copy = bpy.props.BoolProperty(default=True)
@ -614,8 +615,8 @@ class GPCLIP_OT_paste_multi_strokes(bpy.types.Operator):
if not layer: if not layer:
layer = gpl.new(layname) layer = gpl.new(layname)
for fnum, fstrokes in allframes.items(): for fnum, fstrokes in allframes.items():
context.scene.frame_set(int(fnum))#use matrix of this frame for copying (maybe just evaluate depsgraph for object context.scene.frame_set(int(fnum)) # use matrix of this frame for copying (maybe just evaluate depsgraph for object
add_multiple_strokes(fstrokes, use_current_frame=False)#create a new frame at each encoutered add_multiple_strokes(fstrokes, use_current_frame=False) # create a new frame at each encoutered occurence
print('total_time', time() - t0) print('total_time', time() - t0)
@ -640,13 +641,13 @@ class GPCLIP_PT_clipboard_ui(bpy.types.Panel):
col = layout.column(align=True) col = layout.column(align=True)
row = col.row(align=True) row = col.row(align=True)
row.operator('gp.copy_strokes', text='Copy strokes', icon='COPYDOWN') row.operator('gp.copy_strokes', text='Copy Strokes', icon='COPYDOWN')
row.operator('gp.cut_strokes', text='Cut strokes', icon='PASTEFLIPUP') row.operator('gp.cut_strokes', text='Cut Strokes', icon='PASTEFLIPUP')
col.operator('gp.paste_strokes', text='Paste strokes', icon='PASTEDOWN') col.operator('gp.paste_strokes', text='Paste Strokes', icon='PASTEDOWN')
# layout.separator() # layout.separator()
col = layout.column(align=True) col = layout.column(align=True)
col.operator('gp.copy_multi_strokes', text='Copy layers', icon='COPYDOWN') col.operator('gp.copy_multi_strokes', text='Copy Layers', icon='COPYDOWN')
col.operator('gp.paste_multi_strokes', text='Paste layers', icon='PASTEDOWN') col.operator('gp.paste_multi_strokes', text='Paste Layers', icon='PASTEDOWN')
###---TEST zone ###---TEST zone

View File

@ -572,65 +572,8 @@ def asset_browser_ui(self, context):
classes = ( # Put back pop-over UI for Grease Pencil stroke interpolation tools native pop hover panel from 2.92
GPTB_PT_sidebar_panel, class GPTB_PT_tools_grease_pencil_interpolate(Panel):
GPTB_PT_checker,
GPTB_PT_anim_manager,
GPTB_PT_color,
GPTB_PT_tint_layers,
GPTB_PT_toolbox_playblast,
# palettes linker
GPTB_PT_palettes_linker_main_ui, # main panel
GPTB_PT_palettes_list_popup, # popup (dummy region)
GPTB_PT_palettes_path_ui, # subpanels
GPTB_PT_palettes_list_ui, # subpanels
# GPTB_PT_extra,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
# if bpy.app.version >= (3,0,0):
# bpy.types.ASSETBROWSER_PT_metadata.append(asset_browser_ui)
def unregister():
bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
# if bpy.app.version >= (3,0,0):
# bpy.types.ASSETBROWSER_PT_metadata.remove(asset_browser_ui)
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
"""
## direct panel def append (no submenu with arrow)
## need to use append and remove in register/unregister
# bpy.types.DATA_PT_gpencil_layers.append(UI_tools.GPdata_toolbox_panel)
# bpy.types.DATA_PT_gpencil_layers.remove(UI_tools.GPdata_toolbox_panel)
def GPdata_toolbox_panel(self, context):
layout = self.layout
layout.use_property_split = True
settings = context.scene.gptoolprops
col = layout.column(align = True)
col.prop(settings, 'autotint_offset')
col.operator("gp.auto_tint_gp_layers", icon = "COLOR").reset = False
col.operator("gp.auto_tint_gp_layers", text = "Reset tint", icon = "COLOR").reset = True
"""
"""
Put back UI for interpolate
# Grease Pencil stroke interpolation tools native pop hover panel from 2.92
class VIEW3D_PT_tools_grease_pencil_interpolate(Panel):
bl_space_type = 'VIEW_3D' bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER' bl_region_type = 'HEADER'
bl_label = "Interpolate" bl_label = "Interpolate"
@ -680,4 +623,75 @@ class VIEW3D_PT_tools_grease_pencil_interpolate(Panel):
sub.prop(settings, "amplitude") sub.prop(settings, "amplitude")
sub.prop(settings, "period") sub.prop(settings, "period")
def interpolate_header_ui(self, context):
layout = self.layout
obj = context.active_object
if obj and obj.type == 'GPENCIL' and context.gpencil_data:
gpd = context.gpencil_data
else:
return
if gpd.use_stroke_edit_mode or gpd.is_stroke_paint_mode:
row = layout.row(align=True)
row.popover(
panel="GPTB_PT_tools_grease_pencil_interpolate",
text="Interpolate",
)
classes = (
GPTB_PT_sidebar_panel,
GPTB_PT_checker,
GPTB_PT_anim_manager,
GPTB_PT_color,
GPTB_PT_tint_layers,
GPTB_PT_toolbox_playblast,
# GPTB_PT_tools_grease_pencil_interpolate, # WIP
# palettes linker
GPTB_PT_palettes_linker_main_ui, # main panel
GPTB_PT_palettes_list_popup, # popup (dummy region)
GPTB_PT_palettes_path_ui, # subpanels
GPTB_PT_palettes_list_ui, # subpanels
# GPTB_PT_extra,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
# bpy.types.VIEW3D_HT_header.append(interpolate_header_ui) # WIP
# if bpy.app.version >= (3,0,0):
# bpy.types.ASSETBROWSER_PT_metadata.append(asset_browser_ui)
def unregister():
# bpy.types.VIEW3D_HT_header.remove(interpolate_header_ui) # WIP
bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
# if bpy.app.version >= (3,0,0):
# bpy.types.ASSETBROWSER_PT_metadata.remove(asset_browser_ui)
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
"""
## direct panel def append (no submenu with arrow)
## need to use append and remove in register/unregister
# bpy.types.DATA_PT_gpencil_layers.append(UI_tools.GPdata_toolbox_panel)
# bpy.types.DATA_PT_gpencil_layers.remove(UI_tools.GPdata_toolbox_panel)
def GPdata_toolbox_panel(self, context):
layout = self.layout
layout.use_property_split = True
settings = context.scene.gptoolprops
col = layout.column(align = True)
col.prop(settings, 'autotint_offset')
col.operator("gp.auto_tint_gp_layers", icon = "COLOR").reset = False
col.operator("gp.auto_tint_gp_layers", text = "Reset tint", icon = "COLOR").reset = True
""" """

View File

@ -15,7 +15,7 @@ bl_info = {
"name": "GP toolbox", "name": "GP toolbox",
"description": "Tool set for Grease Pencil in animation production", "description": "Tool set for Grease Pencil in animation production",
"author": "Samuel Bernou, Christophe Seux", "author": "Samuel Bernou, Christophe Seux",
"version": (1, 8, 0), "version": (1, 8, 1),
"blender": (2, 91, 0), "blender": (2, 91, 0),
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
"warning": "", "warning": "",