From a493dbf16bcc9fdd3713cc28c176354ef8fb6555 Mon Sep 17 00:00:00 2001 From: Thomas Barlow Date: Fri, 27 Oct 2023 04:27:54 +0100 Subject: [PATCH 1/3] Update for removed Pose.bone_groups in Blender 4.0 --- tools/armature.py | 8 ++++++-- tools/common.py | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/armature.py b/tools/armature.py index a2e09ee9..ecba8a96 100644 --- a/tools/armature.py +++ b/tools/armature.py @@ -494,8 +494,12 @@ def execute(self, context): bpy.ops.mesh.reveal() # Remove Bone Groups - for group in armature.pose.bone_groups: - armature.pose.bone_groups.remove(group) + # Replaced in 4.0 with Bone Collections (Armature.collections), which also subsumed Armature.layers. Bone colors + # are now defined per-bone, Bone.color.palette and PoseBone.color.palette + if Common.version_3_6_or_older: + bone_groups = armature.pose.bone_groups + for group in bone_groups: + bone_groups.remove(group) # Bone constraints should be deleted # if context.scene.remove_constraints: diff --git a/tools/common.py b/tools/common.py index 069e8a13..c6c3741c 100644 --- a/tools/common.py +++ b/tools/common.py @@ -42,6 +42,9 @@ def version_2_93_or_older(): return bpy.app.version < (2, 90) +version_3_6_or_older = bpy.app.version < (4, 0) + + def get_objects(): return bpy.context.scene.objects if version_2_79_or_older() else bpy.context.view_layer.objects From b7f9d77823fa7c09f3c4447b213b5f1476360388 Mon Sep 17 00:00:00 2001 From: Thomas Barlow Date: Fri, 27 Oct 2023 05:11:32 +0100 Subject: [PATCH 2/3] Update for removed EditBone.layers in Blender 4.0 --- tools/armature.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/armature.py b/tools/armature.py index ecba8a96..66a3ef22 100644 --- a/tools/armature.py +++ b/tools/armature.py @@ -510,13 +510,35 @@ def execute(self, context): # Count steps for loading bar again and reset the layers steps += len(armature.data.edit_bones) + if Common.version_3_6_or_older: + def set_bone_visible(edit_bone): + edit_bone.layers[0] = True + else: + # Armature/Bone layers were replaced with Bone Collections in Blender 4.0. + bone_collections = armature.data.collections + if not bone_collections: + # All bones are visible when there are no bone collections, so nothing to do. + def set_bone_visible(_edit_bone): + pass + else: + # The default collection on new Armatures is called "Bones" and usually has all bones assigned to it. + default_collection_name = "Bones" + bone_collection = bone_collections.get(default_collection_name) + if bone_collection is None: + # The default "Bones" collection does not exist, create it. + bone_collection = bone_collections.new(default_collection_name) + # Ensure the collection is visible. + bone_collection.is_visible = True + + def set_bone_visible(edit_bone): + bone_collection.assign(edit_bone) for bone in armature.data.edit_bones: if bone.name in Bones.bone_list or bone.name.startswith(tuple(Bones.bone_list_with)): if bone.parent is not None: steps += 1 else: steps -= 1 - bone.layers[0] = True + set_bone_visible(bone) # Start loading bar current_step = 0 From b9e0e956d9c79d7925c32cc8c37182fdbd44ede2 Mon Sep 17 00:00:00 2001 From: Thomas Barlow Date: Wed, 15 Nov 2023 15:51:43 +0000 Subject: [PATCH 3/3] Update for removal of Operator context override arguments A new version-independent function for calling operators with a context override has been added to common.py. --- tools/armature_manual.py | 8 ++++---- tools/common.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/tools/armature_manual.py b/tools/armature_manual.py index e445bd5e..fa6ae544 100644 --- a/tools/armature_manual.py +++ b/tools/armature_manual.py @@ -388,7 +388,7 @@ def execute(self, context): # active object e.g., the user has multiple armatures opened in pose mode, but a different armature is currently # active. We can use an operator override to tell the operator to treat armature_obj as if it's the active # object even if it's not, skipping the need to actually set armature_obj as the active object. - bpy.ops.pose.armature_apply({'active_object': armature_obj}) + Common.op_override(bpy.ops.pose.armature_apply, {'active_object': armature_obj}) # Stop pose mode after operation bpy.ops.cats_manual.stop_pose_mode() @@ -411,14 +411,14 @@ def apply_armature_to_mesh_with_no_shape_keys(armature_obj, mesh_obj): # first and potentially having unexpected results. if bpy.app.version >= (2, 90, 0): # modifier_move_to_index was added in Blender 2.90 - bpy.ops.object.modifier_move_to_index(context_override, modifier=mod_name, index=0) + Common.op_override(bpy.ops.object.modifier_move_to_index, context_override, modifier=mod_name, index=0) else: # The newly created modifier will be at the bottom of the list armature_mod_index = len(mesh_obj.modifiers) - 1 # Move the modifier up until it's at the top of the list for _ in range(armature_mod_index): - bpy.ops.object.modifier_move_up(context_override, modifier=mod_name) - bpy.ops.object.modifier_apply(context_override, modifier=mod_name) + Common.op_override(bpy.ops.object.modifier_move_up, context_override, modifier=mod_name) + Common.op_override(bpy.ops.object.modifier_apply, context_override, modifier=mod_name) @staticmethod def apply_armature_to_mesh_with_shape_keys(armature_obj, mesh_obj, scene): diff --git a/tools/common.py b/tools/common.py index c6c3741c..78bbe151 100644 --- a/tools/common.py +++ b/tools/common.py @@ -11,6 +11,7 @@ from datetime import datetime from html.parser import HTMLParser from html.entities import name2codepoint +from typing import Optional, Set, Dict, Any from . import common as Common from . import supporter as Supporter @@ -2476,6 +2477,41 @@ def wrapped_items_func(self, context): return wrapped_items_func +if bpy.app.version >= (3, 2): + # Passing in context_override as a positional-only argument is deprecated as of Blender 3.2, replaced with + # Context.temp_override + def op_override(operator, context_override: dict[str, Any], context: Optional[bpy.types.Context] = None, + execution_context: Optional[str] = None, + undo: Optional[bool] = None, **operator_args) -> set[str]: + """Call an operator with a context override""" + args = [] + if execution_context is not None: + args.append(execution_context) + if undo is not None: + args.append(undo) + + if context is None: + context = bpy.context + with context.temp_override(**context_override): + return operator(*args, **operator_args) +else: + def op_override(operator, context_override: Dict[str, Any], context: Optional[bpy.types.Context] = None, + execution_context: Optional[str] = None, + undo: Optional[bool] = None, **operator_args) -> Set[str]: + """Call an operator with a context override""" + if context is not None: + context_base = context.copy() + context_base.update(context_override) + context_override = context_base + args = [context_override] + if execution_context is not None: + args.append(execution_context) + if undo is not None: + args.append(undo) + + return operator(*args, **operator_args) + + """ === THIS CODE COULD BE USEFUL === """ # def addvertex(meshname, shapekey_name):