keyframe jump autobind

0.9.3:

- feat: keyframe jump keys are now auto-binded
- UI: added keyframe jump customisation in addon pref
- code: split keyframe jump in a separate file with his new key updater
gpv2
Pullusb 2021-01-19 01:11:18 +01:00
parent 3bb7cd5690
commit 6774484226
5 changed files with 282 additions and 82 deletions

View File

@ -32,82 +32,6 @@ class GPTB_OT_flipx_view(bpy.types.Operator):
context.scene.camera.scale.x *= -1 context.scene.camera.scale.x *= -1
return {"FINISHED"} return {"FINISHED"}
class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
bl_idname = "screen.gp_keyframe_jump"
bl_label = "Jump to GPencil keyframe"
bl_description = "Jump to prev/next keyframe on active and selected layers of active grease pencil object"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
next : bpy.props.BoolProperty(
name="Next GP keyframe", description="Go to next active GP keyframe", default=True)
target : bpy.props.EnumProperty(
name="Target layer", description="Choose wich layer to evaluate for keyframe change", default='ACTIVE',# options={'ANIMATABLE'}, update=None, get=None, set=None,
items=(
('ACTIVE', 'Active and selected', 'jump in keyframes of active and other selected layers ', 0),
('VISIBLE', 'Visibles layers', 'jump in keyframes of visibles layers', 1),
('ACCESSIBLE', 'Visible and unlocked layers', 'jump in keyframe of all layers', 2),
))
#(key, label, descr, id[, icon])
def execute(self, context):
if not context.object.data.layers.active:
self.report({'ERROR'}, 'No active layer on current GPencil object')
return {"CANCELLED"}
layer = []
if self.target == 'ACTIVE':
gpl = [l for l in context.object.data.layers if l.select and not l.hide]
if not context.object.data.layers.active in gpl:
gpl.append(context.object.data.layers.active)
elif self.target == 'VISIBLE':
gpl = [l for l in context.object.data.layers if not l.hide]
elif self.target == 'ACCESSIBLE':
gpl = [l for l in context.object.data.layers if not l.hide and not l.lock]
current = context.scene.frame_current
p = n = None
mins = []
maxs = []
for l in gpl:
for f in l.frames:
if f.frame_number < current:
p = f.frame_number
if f.frame_number > current:
n = f.frame_number
break
mins.append(p)
maxs.append(n)
p = n = None
mins = [i for i in mins if i is not None]
maxs = [i for i in maxs if i is not None]
if mins:
p = max(mins)
if maxs:
n = min(maxs)
if self.next and n is not None:
context.scene.frame_set(n)
elif not self.next and p is not None:
context.scene.frame_set(p)
else:
self.report({'INFO'}, 'No keyframe in this direction')
return {"CANCELLED"}
return {"FINISHED"}
class GPTB_OT_rename_data_from_obj(bpy.types.Operator): class GPTB_OT_rename_data_from_obj(bpy.types.Operator):
bl_idname = "gp.rename_data_from_obj" bl_idname = "gp.rename_data_from_obj"
bl_label = "Rename GP from object" bl_label = "Rename GP from object"
@ -528,7 +452,6 @@ class GPTB_OT_overlay_presets(bpy.types.Operator):
classes = ( classes = (
GPTB_OT_copy_text, GPTB_OT_copy_text,
GPTB_OT_flipx_view, GPTB_OT_flipx_view,
GPTB_OT_jump_gp_keyframe,
GPTB_OT_rename_data_from_obj, GPTB_OT_rename_data_from_obj,
GPTB_OT_draw_cam, GPTB_OT_draw_cam,
GPTB_OT_set_view_as_cam, GPTB_OT_set_view_as_cam,

177
OP_keyframe_jump.py Normal file
View File

@ -0,0 +1,177 @@
import bpy
from .utils import get_addon_prefs
class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
bl_idname = "screen.gp_keyframe_jump"
bl_label = "Jump to GPencil keyframe"
bl_description = "Jump to prev/next keyframe on active and selected layers of active grease pencil object"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
next : bpy.props.BoolProperty(
name="Next GP keyframe", description="Go to next active GP keyframe", default=True)
target : bpy.props.EnumProperty(
name="Target layer", description="Choose wich layer to evaluate for keyframe change", default='ACTIVE',# options={'ANIMATABLE'}, update=None, get=None, set=None,
items=(
('ACTIVE', 'Active and selected', 'jump in keyframes of active and other selected layers ', 0),
('VISIBLE', 'Visibles layers', 'jump in keyframes of visibles layers', 1),
('ACCESSIBLE', 'Visible and unlocked layers', 'jump in keyframe of all layers', 2),
))
#(key, label, descr, id[, icon])
def execute(self, context):
if not context.object.data.layers.active:
self.report({'ERROR'}, 'No active layer on current GPencil object')
return {"CANCELLED"}
if self.target == 'ACTIVE':
gpl = [l for l in context.object.data.layers if l.select and not l.hide]
if not context.object.data.layers.active in gpl:
gpl.append(context.object.data.layers.active)
elif self.target == 'VISIBLE':
gpl = [l for l in context.object.data.layers if not l.hide]
elif self.target == 'ACCESSIBLE':
gpl = [l for l in context.object.data.layers if not l.hide and not l.lock]
current = context.scene.frame_current
p = n = None
mins = []
maxs = []
for l in gpl:
for f in l.frames:
if f.frame_number < current:
p = f.frame_number
if f.frame_number > current:
n = f.frame_number
break
mins.append(p)
maxs.append(n)
p = n = None
mins = [i for i in mins if i is not None]
maxs = [i for i in maxs if i is not None]
if mins:
p = max(mins)
if maxs:
n = min(maxs)
if self.next and n is not None:
context.scene.frame_set(n)
elif not self.next and p is not None:
context.scene.frame_set(p)
else:
self.report({'INFO'}, 'No keyframe in this direction')
return {"CANCELLED"}
return {"FINISHED"}
class KFJ_OT_rebinder(bpy.types.Operator):
bl_idname = "prefs.shortcut_rebinder"
bl_label = "rebind keyframe jump shortcut"
bl_options = {'REGISTER', 'INTERNAL'}
s_keycode: bpy.props.StringProperty()
s_ctrl: bpy.props.StringProperty()
s_shift: bpy.props.StringProperty()
s_alt: bpy.props.StringProperty()
def invoke(self, context, event):
self.prefs = get_addon_prefs()
self.init_value = getattr(self.prefs, self.s_keycode)
setattr(self.prefs, self.s_keycode, '')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
exclude_keys = {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE',
'TIMER_REPORT', 'ESC', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}
exclude_in = ('SHIFT', 'CTRL', 'ALT')
if event.type == 'ESC':
setattr(self.prefs, self.s_keycode, self.init_value)
return {'CANCELLED'}
if event.type not in exclude_keys and not any(x in event.type for x in exclude_in):
print('key:', event.type, 'value:', event.value)
if event.value == 'PRESS':
self.report({'INFO'}, event.type)
setattr(self.prefs, self.s_shift, event.shift)
setattr(self.prefs, self.s_alt, event.alt)
setattr(self.prefs, self.s_ctrl, event.ctrl)
setattr(self.prefs, self.s_keycode, event.type)
# -# maybe add an autorebind trigger at keycode propertie level
# -# now dependent of register function within the scope of the file...
# auto_rebind()
unregister_keymaps()
register_keymaps()
context.area.tag_redraw()# seems to do nothing, have to move the mouse to update
return {'FINISHED'}
return {"RUNNING_MODAL"}
""" class KFJ_OT_rebind(bpy.types.Operator):
bl_idname = "prefs.rebind_kfj_shortcut"
bl_label = "rebind keyframe jump shortcut"
bl_options = {'REGISTER', 'INTERNAL'}
def execute(self, context):
unregister_keymaps()
register_keymaps()
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")
kmi = km.keymap_items.new('screen.gp_keyframe_jump', type=pref.kfj_next_keycode, value="PRESS", alt=pref.kfj_next_alt, ctrl=pref.kfj_next_ctrl, shift=pref.kfj_next_shift, any=False)
kmi.properties.next = True
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new('screen.gp_keyframe_jump', type=pref.kfj_prev_keycode, value="PRESS", alt=pref.kfj_prev_alt, ctrl=pref.kfj_prev_ctrl, shift=pref.kfj_prev_shift, any=False)
kmi.properties.next = False
addon_keymaps.append((km, kmi))
def unregister_keymaps():
# print('UNBIND CANVAS ROTATE KEYMAPS')#Dbg
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
# del addon_keymaps[:]
classes = (
KFJ_OT_rebinder,
GPTB_OT_jump_gp_keyframe,
)
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)

View File

@ -29,6 +29,12 @@ Set path to the palette folder (there is a json palette IO but you an also put a
Note about palette : For now the importer is not working with linked palette as it's not easy for animator (there are properties of the material you cannot access and the link grey-out fade the real color in UIlist preview) Note about palette : For now the importer is not working with linked palette as it's not easy for animator (there are properties of the material you cannot access and the link grey-out fade the real color in UIlist preview)
### Passive action
Add an "on save" Handler that trigger relative remap of all path.
### function
- Mirror flip : If in cam view flip the camera X scale value (you can see and draw mnirrored to see problems) - Mirror flip : If in cam view flip the camera X scale value (you can see and draw mnirrored to see problems)
<!-- - Overlay toggle : (toggle pref and overlay) --> <!-- - Overlay toggle : (toggle pref and overlay) -->
@ -43,9 +49,10 @@ Note about palette : For now the importer is not working with linked palette as
- can auto launch and/or auto open folder at finished (option in addon preferences) - can auto launch and/or auto open folder at finished (option in addon preferences)
- jump to GP keyframe operator - Jump to GP keyframe :
- you need to set two keymap shortcut in _windows_ or _screen(global)_ with indentifier `screen.gp_keyframe_jump` - Choose key to Auto bind in addon prefs (since 0.9.3).
> Manual setup: Add two keymap shortcut in _windows_ or _screen(global)_ with indentifier `screen.gp_keyframe_jump`, one should have `next` toggled off to jump back
- Rotate canvas with a clic + modifier combo (default is `ctrl + alt + MID-mouse`), can be change in addon preferences. - Rotate canvas with a clic + modifier combo (default is `ctrl + alt + MID-mouse`), can be change in addon preferences.
@ -96,10 +103,17 @@ Panel in sidebar : 3D view > sidebar 'N' > Gpencil
## Changelog: ## Changelog:
0.9.3:
- feat: keyframe jump keys are now auto-binded
- UI: added keyframe jump customisation in addon pref
- code: split keyframe jump in a separate file with his new key updater
0.9.2: 0.9.2:
- doc: Correct download link (important, bugged the addon install) + update - doc: Correct download link (important, bugged the addon install) + update
- updater: remove updater temp file, reset minimum version - code: added tracker url
- updater: remove updater temp file, reset minimum version, turn off verbose mode
0.9.1: 0.9.1:

View File

@ -27,7 +27,9 @@ Expose les options suivantes:
**Edit line opacity** - Il est pratique de pouvoir cacher l'edit line pour avoir un meilleur aperçu du rendu lors du sculpt par exemple. C'est une option lié au layer. Ce slider appelle une fonction pour affecter tout les layers de tout les objets au lieu de se cantonner au layer courant. **Edit line opacity** - Il est pratique de pouvoir cacher l'edit line pour avoir un meilleur aperçu du rendu lors du sculpt par exemple. C'est une option lié au layer. Ce slider appelle une fonction pour affecter tout les layers de tout les objets au lieu de se cantonner au layer courant.
### Action passive
- Ajoute un handler qui déclenche un remmapage des chemins en relatif à chaque sauvegarde.
### Tools principaux d'anim: ### Tools principaux d'anim:
@ -39,7 +41,7 @@ L'action cam (peut-être renommé en follow_cam ?) fonctionne sur le même princ
**Box deform** (`Ctrl+T`) - Déformation 4 coins (Déjà dans _Grease pencil tools_ donc a potentiellement retirer pour éviter de possible confits/redondances, mais d'un autre côté la version intégrée peut être customisée et corriger d'éventuel souci sans attendre les updates de la version native...) **Box deform** (`Ctrl+T`) - Déformation 4 coins (Déjà dans _Grease pencil tools_ donc a potentiellement retirer pour éviter de possible confits/redondances, mais d'un autre côté la version intégrée peut être customisée et corriger d'éventuel souci sans attendre les updates de la version native...)
**GP keyframe jump** (raccourci a ajouter manuellement, tant qu'on a pas une keymap anim un minimum standard) - Essentiel ! Permet d'ajouter une ou plusieurs paire de raccourcis pour aller de keyframe en keyframe (filtrage du saut personnalisable). Le raccourci doit appeler l'operateur `screen.gp_keyframe_jump`, (en ajouter un second avec le `next` décoché pour faire un saut arrière) **GP keyframe jump** (auto bind et personnalisable dans les addon prefs depuis 0.9.3) - Essentiel ! Permet d'ajouter une ou plusieurs paire de raccourcis pour aller de keyframe en keyframe (filtrage du saut personnalisable). Lance l'operateur `screen.gp_keyframe_jump`, (si ajout manuel, rajouter une seconde keymap avec la propriété `next` décoché pour faire un saut arrière)
**Breakdown en mode objet** (`Shift+E`) - Breakdown en pourcentage entre les deux keyframes (pose une clé si lauto-key est actif et utilise le keying set si actif). Même comportement et raccourci que le breakdown de bones en pose mode (juste qu'il n'existait bizzarement pas en objet) **Breakdown en mode objet** (`Shift+E`) - Breakdown en pourcentage entre les deux keyframes (pose une clé si lauto-key est actif et utilise le keying set si actif). Même comportement et raccourci que le breakdown de bones en pose mode (juste qu'il n'existait bizzarement pas en objet)

View File

@ -15,7 +15,7 @@ bl_info = {
"name": "gp toolbox", "name": "gp toolbox",
"description": "Set of tools for Grease Pencil in animation production", "description": "Set of tools for Grease Pencil in animation production",
"author": "Samuel Bernou", "author": "Samuel Bernou",
"version": (0, 9, 2), "version": (0, 9, 3),
"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": "",
@ -39,6 +39,7 @@ from . import OP_canvas_rotate
from . import OP_playblast_bg from . import OP_playblast_bg
from . import OP_playblast from . import OP_playblast
from . import OP_helpers from . import OP_helpers
from . import OP_keyframe_jump
from . import OP_box_deform from . import OP_box_deform
from . import OP_cursor_snap_canvas from . import OP_cursor_snap_canvas
from . import OP_palettes from . import OP_palettes
@ -239,6 +240,52 @@ class GPTB_prefs(bpy.types.AddonPreferences):
max=10000 max=10000
) )
## KF jumper
kfj_use_shortcut: BoolProperty(
name = "Use Keyframe Jump Shortcut",
description = "Auto bind shotcut for keyframe jump (else you can bien manually using 'screen.gp_keyframe_jump' id_name)",
default = True)
kfj_prev_keycode : StringProperty(
name="Jump Prev Shortcut",
description="Shortcut to trigger previous keyframe jump",
default="F5")
kfj_prev_shift: BoolProperty(
name = "Shift",
description = "add shift",
default = False)
kfj_prev_alt: BoolProperty(
name = "Alt",
description = "add alt",
default = False)
kfj_prev_ctrl: BoolProperty(
name = "combine with ctrl",
description = "add ctrl",
default = False)
kfj_next_keycode : StringProperty(
name="Jump Next Shortcut",
description="Shortcut to trigger keyframe jump",
default="F6")
kfj_next_shift: BoolProperty(
name = "Shift",
description = "add shift",
default = False)
kfj_next_alt: BoolProperty(
name = "Alt",
description = "add alt",
default = False)
kfj_next_ctrl: BoolProperty(
name = "combine with ctrl",
description = "add ctrl",
default = False)
## Temp cutter ## Temp cutter
# temp_cutter_use_shortcut: BoolProperty( # temp_cutter_use_shortcut: BoolProperty(
# name = "Use temp cutter Shortcut", # name = "Use temp cutter Shortcut",
@ -285,6 +332,41 @@ class GPTB_prefs(bpy.types.AddonPreferences):
box.prop(self, 'playblast_auto_play') box.prop(self, 'playblast_auto_play')
box.prop(self, 'playblast_auto_open_folder') box.prop(self, 'playblast_auto_open_folder')
# box.separator()## Keyframe jumper
box = layout.box()
box.label(text='Keyframe Jump option:')
box.prop(self, "kfj_use_shortcut", text='Bind shortcuts')
if self.kfj_use_shortcut:
prompt = '[TYPE SHORTCUT TO USE (can be with modifiers)]'
if self.kfj_prev_keycode:
mods = '+'.join([m for m, b in [('Ctrl', self.kfj_prev_ctrl), ('Shift', self.kfj_prev_shift), ('Alt', self.kfj_prev_alt)] if b])
text = f'{mods}+{self.kfj_prev_keycode}' if mods else self.kfj_prev_keycode
text = f'Jump Keyframe Prev: {text} (Click to change)'
else:
text = prompt
ops = box.operator('prefs.shortcut_rebinder', text=text, icon='FILE_REFRESH')
ops.s_keycode = 'kfj_prev_keycode'
ops.s_ctrl = 'kfj_prev_ctrl'
ops.s_shift = 'kfj_prev_shift'
ops.s_alt = 'kfj_prev_alt'
if self.kfj_next_keycode:
mods = '+'.join([m for m, b in [('Ctrl', self.kfj_next_ctrl), ('Shift', self.kfj_next_shift), ('Alt', self.kfj_next_alt)] if b])
text = f'{mods}+{self.kfj_next_keycode}' if mods else self.kfj_next_keycode
text = f'Jump Keyframe Next: {text} (Click to change)'
else:
text = prompt
ops = box.operator('prefs.shortcut_rebinder', text=text, icon='FILE_REFRESH')
ops.s_keycode = 'kfj_next_keycode'
ops.s_ctrl = 'kfj_next_ctrl'
ops.s_shift = 'kfj_next_shift'
ops.s_alt = 'kfj_next_alt'
else:
box.label(text="No Jump hotkey auto set. Following operators needs to be set manually", icon="ERROR")
box.label(text="screen.gp_keyframe_jump - preferably in 'screen' category to jump from any editor")
# box.separator()## Canvas # box.separator()## Canvas
box = layout.box() box = layout.box()
box.label(text='Canvas rotate options:') box.label(text='Canvas rotate options:')
@ -386,6 +468,7 @@ def register():
bpy.utils.register_class(cls) bpy.utils.register_class(cls)
OP_box_deform.register() OP_box_deform.register()
OP_helpers.register() OP_helpers.register()
OP_keyframe_jump.register()
OP_file_checker.register() OP_file_checker.register()
OP_breakdowner.register() OP_breakdowner.register()
OP_temp_cutter.register() OP_temp_cutter.register()
@ -418,6 +501,7 @@ def unregister():
OP_palettes.unregister() OP_palettes.unregister()
OP_file_checker.unregister() OP_file_checker.unregister()
OP_helpers.unregister() OP_helpers.unregister()
OP_keyframe_jump.unregister()
OP_breakdowner.unregister() OP_breakdowner.unregister()
OP_temp_cutter.unregister() OP_temp_cutter.unregister()
GP_colorize.unregister()## GP_guided_colorize. GP_colorize.unregister()## GP_guided_colorize.