Animation manager improve animated hint

2.3.1

- changed: Animation manager show icons when animation is enabled: fully, partially or not at all.
gpv2
pullusb 2023-03-09 17:53:40 +01:00
parent 7e92ae182e
commit 798afbe82a
4 changed files with 162 additions and 21 deletions

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
2.3.1
- changed: Animation manager show hints when animation is enabled: fully, partially or not at all.
2.3.0 2.3.0
- added: Animation manager buttons are colored red when objects have disabled animation - added: Animation manager buttons are colored red when objects have disabled animation

View File

@ -1,5 +1,8 @@
# from . import addon_updater_ops # from . import addon_updater_ops
from .utils import get_addon_prefs, all_anim_enabled, all_object_modifier_enabled from .utils import (get_addon_prefs,
anim_status,
gp_modifier_status,
)
import bpy import bpy
from pathlib import Path from pathlib import Path
from bpy.types import Panel from bpy.types import Panel
@ -179,48 +182,69 @@ class GPTB_PT_anim_manager(Panel):
# def draw_header(self,context): # def draw_header(self,context):
# self.layout.prop(context.scene.camera.data, "show_background_images", text="") # self.layout.prop(context.scene.camera.data, "show_background_images", text="")
def get_object_by_types(self, context) -> dict:
# import time
# t0 = time.perf_counter()
# objs = [o for o in context.scene.objects if o.type not in ('GPENCIL', 'CAMERA')]
# gps = [o for o in context.scene.objects if o.type == 'GPENCIL']
# cams = [o for o in context.scene.objects if o.type == 'CAMERA']
objs = []
gps = []
cams = []
for o in context.scene.objects:
if o.type not in ('GPENCIL', 'CAMERA'):
objs.append(o)
elif o.type == 'GPENCIL':
gps.append(o)
elif o.type == 'CAMERA':
cams.append(o)
# print(f'{time.perf_counter() - t0:.8f}s')
return {'OBJECT': objs, 'GPENCIL': gps, 'CAMERA': cams}
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.use_property_split = True layout.use_property_split = True
col = layout.column() col = layout.column()
## Animation enable disable anim (shift click to select) OP_helpers.GPTB_OT_toggle_mute_animation ## Animation enable disable anim (shift click to select) OP_helpers.GPTB_OT_toggle_mute_animation
# import time obj_types = self.get_object_by_types(context)
# t0 = time.time()
scn = context.scene
objs = [o for o in scn.objects if o.type not in ('GPENCIL', 'CAMERA')] # show_alert
gps = [o for o in scn.objects if o.type == 'GPENCIL'] # show_alert
cams = [o for o in scn.objects if o.type == 'CAMERA'] # show_alert
col.operator('gp.list_disabled_anims') col.operator('gp.list_disabled_anims')
## Show Enable / Disable ## Show Enable / Disable anims
for cat, cat_type, tgt in [('Obj anims:', 'OBJECT', objs), ('Cam anims:', 'CAMERA', cams), ('Gp anims:', 'GPENCIL', gps)]: for cat, cat_type in [('Obj anims:', 'OBJECT'), ('Cam anims:', 'CAMERA'), ('Gp anims:', 'GPENCIL')]:
on_icon, off_icon = anim_status(obj_types[cat_type])
subcol = col.column() subcol = col.column()
subcol.alert = not all_anim_enabled(tgt) # show_alert # subcol.alert = off_icon == 'LAYER_ACTIVE' # Turn red
row = subcol.row(align=True) row = subcol.row(align=True)
row.label(text=cat) row.label(text=cat)
ops = row.operator('gp.toggle_mute_animation', text = 'ON')
ops = row.operator('gp.toggle_mute_animation', text='ON', icon=on_icon)
ops.mode = cat_type ops.mode = cat_type
ops.mute = False ops.mute = False
ops = row.operator('gp.toggle_mute_animation', text = 'OFF') ops = row.operator('gp.toggle_mute_animation', text='OFF', icon=off_icon)
ops.mode = cat_type ops.mode = cat_type
ops.mute = True ops.mute = True
## GP modifiers ## GP modifiers
subcol = col.column() subcol = col.column()
subcol.alert = not all_object_modifier_enabled(gps) # show_alert
row = subcol.row(align=True) row = subcol.row(align=True)
row.label(text='Gp modifiers:') row.label(text='Gp modifiers:')
row.operator('gp.toggle_hide_gp_modifier', text = 'ON').show = True on_icon, off_icon = gp_modifier_status(obj_types['GPENCIL'])
row.operator('gp.toggle_hide_gp_modifier', text = 'OFF').show = False # subcol.alert = off_icon == 'LAYER_ACTIVE' # Turn red
row.operator('gp.toggle_hide_gp_modifier', text='ON', icon=on_icon).show = True
row.operator('gp.toggle_hide_gp_modifier', text='OFF', icon=off_icon).show = False
## Follow curve path ## Follow curve path
col = col.column() col = col.column()
row = col.row(align=True) row = col.row(align=True)
# row.operator('object.create_follow_path_curve', text='Create Curve', icon='CURVE_BEZCURVE')
if context.object: if context.object:
if context.object.type == 'CURVE' and context.mode in ('OBJECT', 'EDIT_CURVE'): if context.object.type == 'CURVE' and context.mode in ('OBJECT', 'EDIT_CURVE'):
@ -248,7 +272,6 @@ class GPTB_PT_anim_manager(Panel):
text, icon = ('Cursor Follow On', 'PIVOT_CURSOR') if context.scene.gptoolprops.cursor_follow else ('Cursor Follow Off', 'CURSOR') text, icon = ('Cursor Follow On', 'PIVOT_CURSOR') if context.scene.gptoolprops.cursor_follow else ('Cursor Follow Off', 'CURSOR')
col.prop(context.scene.gptoolprops, 'cursor_follow', text=text, icon=icon) col.prop(context.scene.gptoolprops, 'cursor_follow', text=text, icon=icon)
# print(f'{time.time() - t0:.6f}s')
class GPTB_PT_toolbox_playblast(Panel): class GPTB_PT_toolbox_playblast(Panel):
bl_label = "Playblast" bl_label = "Playblast"

View File

@ -4,7 +4,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": (2, 3, 0), "version": (2, 3, 1),
"blender": (3, 0, 0), "blender": (3, 0, 0),
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
"warning": "", "warning": "",

116
utils.py
View File

@ -1119,6 +1119,7 @@ def go_edit_mode(ob, context=None):
bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.object.mode_set(mode='EDIT', toggle=False)
## Not used anymore
def all_anim_enabled(objects) -> bool: def all_anim_enabled(objects) -> bool:
'''return False if at least one fcurve/group has disabled animation in passed object anim''' '''return False if at least one fcurve/group has disabled animation in passed object anim'''
for o in objects: for o in objects:
@ -1146,6 +1147,7 @@ def all_anim_enabled(objects) -> bool:
return True return True
## Not used anymore
def all_object_modifier_enabled(objects) -> bool: def all_object_modifier_enabled(objects) -> bool:
'''Return False if one modifier of one object has GP modifier disabled in viewport but enabled in render''' '''Return False if one modifier of one object has GP modifier disabled in viewport but enabled in render'''
for o in objects: for o in objects:
@ -1158,5 +1160,117 @@ def all_object_modifier_enabled(objects) -> bool:
## return False when viewport but no render ? ## return False when viewport but no render ?
# if not m.show_render and m.show_viewport: # if not m.show_render and m.show_viewport:
# return False # return False
return True return True
## Only used in anim_status in 'per GP object' mode
def has_fully_enabled_anim(o):
if o.animation_data and o.animation_data.action:
## Skip hided object ? (missmatch)
# if o.hide_get():
# continue
## Check if groups are muted
for grp in o.animation_data.action.groups:
if grp.mute:
return False
## Check if fcurves are muted
for fcu in o.animation_data.action.fcurves:
if fcu.mute:
return False
if o.type in ('GPENCIL', 'CAMERA'):
if o.data.animation_data and o.data.animation_data.action:
## Check if object data attributes fcurves are muted
for fcu in o.animation_data.action.fcurves:
if fcu.mute:
return False
return True
def anim_status(objects) -> tuple((str, str)):
'''Return a tutple of icon string status in ('ALL_ON', 'MIXED', 'ALL_OFF', 'NONE')'''
on_count = off_count = count = 0
for o in objects:
## Skip object with no animation
if not (o.animation_data and o.animation_data.action) and not (o.data.animation_data and o.data.animation_data.action):
continue
## Also skip hidden objects
if o.hide_get() and o.hide_render:
continue
### Per Object
# count += 1
# if has_fully_enabled_anim(o):
# on_count += 1
# else:
# off_count += 1
### Consider All channels individually
for grp in o.animation_data.action.groups:
## Check if groups are muted
if grp.mute:
off_count += 1
else:
on_count += 1
count += 1
for fcu in o.animation_data.action.fcurves:
## Check if fcurves are muted
if fcu.mute:
off_count += 1
else:
on_count += 1
count += 1
if o.type in ('GPENCIL', 'CAMERA'):
if o.data.animation_data and o.data.animation_data.action:
## Check if object data attributes fcurves are muted
for fcu in o.animation_data.action.fcurves:
if fcu.mute:
off_count += 1
else:
on_count += 1
count += 1
if not on_count and not off_count:
return ('BLANK1', 'BLANK1') # 'NONE'
elif on_count == count:
return ('LAYER_ACTIVE', 'BLANK1') # 'ALL_ON'
elif off_count == count:
return ('BLANK1', 'LAYER_ACTIVE') # 'ALL_OFF'
else:
return ('LAYER_USED', 'LAYER_USED') # 'MIXED'
def gp_modifier_status(objects) -> tuple((str, str)):
'''return icons on/off tuple'''
on_count = off_count = count = 0
for o in objects:
if o.type != 'GPENCIL':
continue
## Skip hided object
if o.hide_get() and o.hide_render:
continue
for m in o.grease_pencil_modifiers:
if m.show_render and not m.show_viewport:
off_count += 1
else:
on_count += 1
count += 1
## return False when viewport but no render ?
# if not m.show_render and m.show_viewport:
# return False
if not on_count and not off_count:
return ('BLANK1', 'BLANK1') # 'NONE'
elif on_count == count:
return ('LAYER_ACTIVE', 'BLANK1') # 'ALL_ON'
elif off_count == count:
return ('BLANK1', 'LAYER_ACTIVE') # 'ALL_OFF'
else:
return ('LAYER_USED', 'LAYER_USED')