From 7e92ae182e6962369c7f3e236efbeffec8b2cf59 Mon Sep 17 00:00:00 2001 From: pullusb Date: Thu, 9 Mar 2023 14:52:58 +0100 Subject: [PATCH] Anim manager alert when anim off and affect groups 2.3.0 - added: Animation manager buttons are colored red when objects have disabled animation - fixed: Animation manager not enabling/disabling Action Groups - fixed: Animation manager `List Disabled Anims` list groups as well --- CHANGELOG.md | 6 +++++ OP_helpers.py | 11 +++++++++- UI_tools.py | 61 +++++++++++++++++++++++---------------------------- __init__.py | 2 +- utils.py | 47 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d9e32..6077a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +2.3.0 + +- added: Animation manager buttons are colored red when objects have disabled animation +- fixed: Animation manager not enabling/disabling Action Groups +- fixed: Animation manager `List Disabled Anims` list groups as well + 2.2.3 - fixed: Type error on realign ops diff --git a/OP_helpers.py b/OP_helpers.py index b9e5913..a72dc97 100644 --- a/OP_helpers.py +++ b/OP_helpers.py @@ -415,6 +415,8 @@ class GPTB_OT_toggle_mute_animation(bpy.types.Operator): print(i, fcu.data_path, fcu.array_index) # fcu.group don't have mute attribute in api. fcu.mute = self.mute + for g in act.groups: + g.mute = self.mute def execute(self, context): if self.selection: @@ -452,7 +454,9 @@ class GPTB_OT_toggle_mute_animation(bpy.types.Operator): class GPTB_OT_toggle_hide_gp_modifier(bpy.types.Operator): bl_idname = "gp.toggle_hide_gp_modifier" bl_label = "Toggle Modifier Hide" - bl_description = "Show/Hide gp objects modifier (only touch modifier that are showed in render)\n(shift+clic to affect selection only)" + bl_description = "Show/Hide viewport on GP objects modifier\ + \nOnly touch modifier that are showed in render\ + \nShift + click to affect selection only" bl_options = {"REGISTER"} show : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'}) @@ -521,6 +525,11 @@ class GPTB_OT_list_disabled_anims(bpy.types.Operator): act = o.animation_data.action if not act: continue + + for g in act.groups: + if g.mute: + li.append(f'{o.name} - group: {g.name}') + for i, fcu in enumerate(act.fcurves): # print(i, fcu.data_path, fcu.array_index) if fcu.mute: diff --git a/UI_tools.py b/UI_tools.py index 426979b..119dd48 100644 --- a/UI_tools.py +++ b/UI_tools.py @@ -1,9 +1,10 @@ # from . import addon_updater_ops -from .utils import get_addon_prefs +from .utils import get_addon_prefs, all_anim_enabled, all_object_modifier_enabled import bpy from pathlib import Path from bpy.types import Panel + ## UI in properties ### dataprop_panel not used --> transferred to sidebar @@ -183,49 +184,41 @@ class GPTB_PT_anim_manager(Panel): layout.use_property_split = True col = layout.column() ## Animation enable disable anim (shift click to select) OP_helpers.GPTB_OT_toggle_mute_animation + + # import time + # 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') - ## Objs - row = col.row(align=True) - row.label(text='Obj anims:') - ops = row.operator('gp.toggle_mute_animation', text = 'ON') - ops.mode = 'OBJECT' - ops.mute = False - - ops = row.operator('gp.toggle_mute_animation', text = 'OFF') - ops.mode = 'OBJECT' - ops.mute = True - - ## Cams - row = col.row(align=True) - row.label(text='Cam anims:') - ops = row.operator('gp.toggle_mute_animation', text = 'ON') - ops.mode = 'CAMERA' - ops.mute = False - ops = row.operator('gp.toggle_mute_animation', text = 'OFF') - ops.mode = 'CAMERA' - ops.mute = True - - ## GPs - row = col.row(align=True) - row.label(text='Gp anims:') - ops = row.operator('gp.toggle_mute_animation', text = 'ON') - ops.mode = 'GPENCIL' - ops.mute = False - - ops = row.operator('gp.toggle_mute_animation', text = 'OFF') - ops.mode = 'GPENCIL' - ops.mute = True + ## Show Enable / Disable + for cat, cat_type, tgt in [('Obj anims:', 'OBJECT', objs), ('Cam anims:', 'CAMERA', cams), ('Gp anims:', 'GPENCIL', gps)]: + subcol = col.column() + subcol.alert = not all_anim_enabled(tgt) # show_alert + row = subcol.row(align=True) + row.label(text=cat) + ops = row.operator('gp.toggle_mute_animation', text = 'ON') + ops.mode = cat_type + ops.mute = False + ops = row.operator('gp.toggle_mute_animation', text = 'OFF') + ops.mode = cat_type + ops.mute = True ## GP modifiers - row = col.row(align=True) + subcol = col.column() + subcol.alert = not all_object_modifier_enabled(gps) # show_alert + row = subcol.row(align=True) row.label(text='Gp modifiers:') row.operator('gp.toggle_hide_gp_modifier', text = 'ON').show = True row.operator('gp.toggle_hide_gp_modifier', text = 'OFF').show = False ## Follow curve path + col = col.column() row = col.row(align=True) # row.operator('object.create_follow_path_curve', text='Create Curve', icon='CURVE_BEZCURVE') @@ -254,6 +247,8 @@ class GPTB_PT_anim_manager(Panel): col.use_property_split = False 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) + + # print(f'{time.time() - t0:.6f}s') class GPTB_PT_toolbox_playblast(Panel): bl_label = "Playblast" diff --git a/__init__.py b/__init__.py index e905823..88fba66 100755 --- a/__init__.py +++ b/__init__.py @@ -4,7 +4,7 @@ bl_info = { "name": "GP toolbox", "description": "Tool set for Grease Pencil in animation production", "author": "Samuel Bernou, Christophe Seux", -"version": (2, 2, 3), +"version": (2, 3, 0), "blender": (3, 0, 0), "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "warning": "", diff --git a/utils.py b/utils.py index 31e65b2..87414cf 100644 --- a/utils.py +++ b/utils.py @@ -1104,6 +1104,11 @@ def create_follow_path_constraint(ob, curve, follow_curve=False, use_fixed_locat # const.use_curve_follow = True # return curve, const + +# ----------------- +### Object +# ----------------- + def go_edit_mode(ob, context=None): '''set mode to object, set passed obhject as active and go Edit''' @@ -1113,3 +1118,45 @@ def go_edit_mode(ob, context=None): context.view_layer.objects.active = ob bpy.ops.object.mode_set(mode='EDIT', toggle=False) + +def all_anim_enabled(objects) -> bool: + '''return False if at least one fcurve/group has disabled animation in passed object anim''' + for o in objects: + if o.animation_data and o.animation_data.action: + ## Skip hided object ? + # 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 all_object_modifier_enabled(objects) -> bool: + '''Return False if one modifier of one object has GP modifier disabled in viewport but enabled in render''' + for o in objects: + if o.type != 'GPENCIL': + continue + for m in o.grease_pencil_modifiers: + if m.show_render and not m.show_viewport: + return False + + ## return False when viewport but no render ? + # if not m.show_render and m.show_viewport: + # return False + + return True \ No newline at end of file