diff --git a/CHANGELOG.md b/CHANGELOG.md index 1789138..481e787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +1.8.1 + +- fix: Gp clipboard paste `Paste layers` don't skip empty frames anymore 1.8.0 diff --git a/OP_copy_paste.py b/OP_copy_paste.py index a4db3a6..cc167b8 100644 --- a/OP_copy_paste.py +++ b/OP_copy_paste.py @@ -17,7 +17,7 @@ bl_info = { "name": "GP clipboard", "description": "Copy/Cut/Paste Grease Pencil strokes to/from OS clipboard across layers and blends", "author": "Samuel Bernou", - "version": (1, 3, 2), + "version": (1, 3, 3), "blender": (2, 83, 0), "location": "View3D > Toolbar > Gpencil > GP clipboard", "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 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() 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" 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(stroke_list) + # print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds') return stroke_list 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 if no layer specified, active layer is used 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 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 target_frame = False 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: - 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) ''' for s in stroke_data: add_stroke(s, target_frame) ''' - print(len(stroke_list), 'strokes pasted') + + # print(len(stroke_list), 'strokes pasted') ### OPERATORS @@ -493,8 +494,8 @@ class GPCLIP_OT_paste_strokes(bpy.types.Operator): class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): bl_idname = "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_label = "GP Copy Multi Strokes" + bl_description = "Copy multiple layers>frames>strokes from selected layers to str in paperclip" bl_options = {"REGISTER"} #copy = bpy.props.BoolProperty(default=True) @@ -513,12 +514,12 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator): #ct = check_pressure() 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: 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"} - if not bake_moves:# copy only drawed frames as is. + if not bake_moves: # copy only drawed frames as is. for l in layerpool: if not l.frames: 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): bl_idname = "gp.paste_multi_strokes" - bl_label = "GP paste multi strokes" - bl_description = "Paste multiple layers>frames>strokes from paperclip" + bl_label = "GP Paste Multi Strokes" + bl_description = "Paste multiple layers>frames>strokes from paperclip on active layer" bl_options = {"REGISTER"} #copy = bpy.props.BoolProperty(default=True) @@ -614,8 +615,8 @@ class GPCLIP_OT_paste_multi_strokes(bpy.types.Operator): if not layer: layer = gpl.new(layname) 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 - add_multiple_strokes(fstrokes, use_current_frame=False)#create a new frame at each encoutered + 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 occurence print('total_time', time() - t0) @@ -640,13 +641,13 @@ class GPCLIP_PT_clipboard_ui(bpy.types.Panel): col = layout.column(align=True) row = col.row(align=True) - row.operator('gp.copy_strokes', text='Copy strokes', icon='COPYDOWN') - row.operator('gp.cut_strokes', text='Cut strokes', icon='PASTEFLIPUP') - col.operator('gp.paste_strokes', text='Paste strokes', icon='PASTEDOWN') + row.operator('gp.copy_strokes', text='Copy Strokes', icon='COPYDOWN') + row.operator('gp.cut_strokes', text='Cut Strokes', icon='PASTEFLIPUP') + col.operator('gp.paste_strokes', text='Paste Strokes', icon='PASTEDOWN') # layout.separator() col = layout.column(align=True) - 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.copy_multi_strokes', text='Copy Layers', icon='COPYDOWN') + col.operator('gp.paste_multi_strokes', text='Paste Layers', icon='PASTEDOWN') ###---TEST zone diff --git a/UI_tools.py b/UI_tools.py index 2552868..4dda1fd 100644 --- a/UI_tools.py +++ b/UI_tools.py @@ -572,65 +572,8 @@ def asset_browser_ui(self, context): -classes = ( -GPTB_PT_sidebar_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): +# Put back pop-over UI for Grease Pencil stroke interpolation tools native pop hover panel from 2.92 +class GPTB_PT_tools_grease_pencil_interpolate(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' bl_label = "Interpolate" @@ -680,4 +623,75 @@ class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): sub.prop(settings, "amplitude") 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 """ diff --git a/__init__.py b/__init__.py index c508db3..e3283c4 100755 --- a/__init__.py +++ b/__init__.py @@ -15,7 +15,7 @@ bl_info = { "name": "GP toolbox", "description": "Tool set for Grease Pencil in animation production", "author": "Samuel Bernou, Christophe Seux", -"version": (1, 8, 0), +"version": (1, 8, 1), "blender": (2, 91, 0), "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "warning": "",