bone_widget/operators.py

792 lines
23 KiB
Python
Raw Normal View History

2022-10-28 23:23:06 +02:00
import bpy
from bpy.types import Operator
from bpy.props import BoolProperty, IntProperty, FloatProperty, FloatVectorProperty, \
StringProperty
from bone_widget import ctx
from .transform_utils import transform_matrix, apply_mat_to_verts, get_bone_size_factor, \
get_bone_matrix
from .icon_utils import render_widget
2023-01-18 16:02:42 +01:00
from .shape_utils import get_clean_shape, symmetrize_bone_shape, link_to_col, get_bone, custom_shape_matrix
2022-10-28 23:23:06 +02:00
import re
from pathlib import Path
import os
from tempfile import gettempdir
from mathutils import Matrix
import json
2023-01-18 16:02:42 +01:00
class BW_OT_copy_widgets(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.copy_widgets'
bl_label = "Copy Widgets"
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
from tempfile import gettempdir
blend_file = Path(gettempdir())/'bone_widgets.blend'
shapes = []
ob = context.object
if ob.mode == 'POSE':
bones = ctx.selected_bones
else:
bones = ctx.bones
for b in bones:
if b.custom_shape:
s = b.custom_shape.copy()
s.data = s.data.copy()
if not b.use_custom_shape_bone_size:
mat = transform_matrix(scale=(1/b.bone.length,)*3)
s.data.transform(mat)
#s.data.transform(s.matrix_world.inverted())
#s.matrix_world = Matrix()
s['.bw_bone'] = b.name
if bpy.app.version_string < '3.0.0':
s['.bw_custom_shape_scale'] = b.custom_shape_scale
else:
s['.bw_custom_shape_translation'] = b.custom_shape_translation
s['.bw_custom_shape_rotation_euler'] = b.custom_shape_rotation_euler
s['.bw_custom_shape_scale_xyz'] = b.custom_shape_scale_xyz
s['.bw_custom_shape_transform'] = b.custom_shape_transform
s['.bw_use_custom_shape_bone_size'] = b.use_custom_shape_bone_size
#s['.bw_size_factor'] = get_bone_size_factor(b, s, b.use_custom_shape_bone_size)
shapes.append(s)
bpy.data.libraries.write(str(blend_file), set(shapes))
for s in shapes:
data = s.data
data_type = s.type
bpy.data.objects.remove(s)
if data_type == 'MESH':
bpy.data.meshes.remove(data)
elif data_type == 'CURVE':
bpy.data.curves.remove(data)
# for b in ctx.bones:
# if b.custom_shape:
# for k in b.custom_shape.keys():
# if k.startswith('.bw'):
# del b.custom_shape[k]
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_paste_widgets(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.paste_widgets'
bl_label = "Paste Widgets"
path: StringProperty(subtype='FILE_PATH')
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
from tempfile import gettempdir
rig = context.object
#for b in rig.pose.bones:
# if b.custom_shape:
# bpy.data.objects.remove(b.custom_shape)
#b.custom_shape = get_clean_shape(b, b.custom_shape, separate=b.custom_shape.users>2)
if self.path:
blend_file = Path(self.path)
else:
blend_file = Path(gettempdir())/'bone_widgets.blend'
with bpy.data.libraries.load(str(blend_file), link=False) as (data_src, data_dst):
data_dst.objects = data_src.objects
shapes = data_src.objects
#old_shapes = set()
for s in shapes:
bone = rig.pose.bones.get(s['.bw_bone'])
if not bone:
print('No bone found for', s)
continue
if bpy.app.version_string < '3.0.0':
bone.custom_shape_scale = s['.bw_custom_shape_scale']
else:
bone.custom_shape_scale_xyz = s['.bw_custom_shape_scale_xyz']
link_to_col(s, ctx.widget_col)
#if bone.custom_shape:
#size_factor = max(s.dimensions)#get_bone_size_factor(bone, s, relative=False)
#size_factor /= s['.bw_size_factor']
#size_factor = s['.bw_size_factor']
#mat = transform_matrix(scale=(size_factor,)*3)
#s.data.transform(Matrix.Scale(1/size_factor, 4))
#s.scale = 1,1,1
bone.custom_shape = get_clean_shape(bone, s, col=ctx.widget_col, prefix=ctx.prefs.prefix,
separate=False, apply_transforms=False)
if bpy.app.version_string < '3.0.0':
#bone.custom_shape_scale = s['.bw_custom_shape_scale']
bone.custom_shape_scale = 1
else:
bone.custom_shape_translation = s['.bw_custom_shape_translation']
bone.custom_shape_rotation_euler = s['.bw_custom_shape_rotation_euler']
#bone.custom_shape_scale_xyz = s['.bw_custom_shape_scale_xyz']
bone.custom_shape_scale_xyz = 1,1,1
#use_custom_shape_bone_size
bone.custom_shape_transform = s['.bw_custom_shape_transform']
bone.use_custom_shape_bone_size = True#s['.bw_use_custom_shape_bone_size']
bone.bone.show_wire = not bool(s.data.polygons)
#mat = transform_matrix(scale=(s['.bw_size_factor'],)*3)
#s.data.transform(mat)
for b in rig.pose.bones:
if b.custom_shape:
for k in list(b.custom_shape.keys()):
if k.startswith('.bw'):
del b.custom_shape[k]
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_remove_unused_shape(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.remove_unused_shape'
bl_label = "Remove Unused Shape"
def execute(self, context):
objects = list(ctx.widget_col.all_objects)
for ob in objects:
if not get_bone(ob) and ob in bpy.data.objects[:]:
bpy.data.objects.remove(ob)
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_auto_color(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.auto_color'
bl_label = "Auto Color"
bl_options = {'REGISTER', 'UNDO'}
def is_bone_protected(self, bone):
rig = bone.id_data
return any([i==j==True for i, j in zip(rig.data.layers_protected, bone.bone.layers)])
def execute(self, context):
ob = context.object
bones = context.selected_pose_bones or ob.pose.bones
for b in bones:
if self.is_bone_protected(b):
continue
for group in ob.pose.bone_groups:
if any(i in b.name.lower() for i in group.name.lower().split(' ')):
b.bone_group = group
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_load_default_color(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.load_default_color'
bl_label = "Load Default Color"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
select_color: FloatVectorProperty(name='Color', subtype='COLOR', default=[0.0, 1.0, 1.0])
active_color: FloatVectorProperty(name='Color', subtype='COLOR', default=[1.0, 1.0, 1.0])
2022-10-28 23:23:06 +02:00
def execute(self, context):
ob = context.object
colors = {
2023-01-18 16:02:42 +01:00
'Black': [0, 0, 0],
'Yellow': [1, 1, 0],
'Red': [0, 0.035, 0.95],
'Blue': [1, 0, 0],
2023-05-22 12:05:49 +02:00
'Green': [0.733333, 0.937255, 0.356863],
2023-01-18 16:02:42 +01:00
'Pink': [1, 0.1, 0.85],
'Purple': [0.67, 0, 0.87],
'Brown': [0.75, 0.65, 0],
'Orange': [1, 0.6, 0],
2022-10-28 23:23:06 +02:00
}
for k, v in colors.items():
bone_group = ob.pose.bone_groups.get(k)
if not bone_group:
bone_group = ob.pose.bone_groups.new(name=k)
bone_group.color_set = 'CUSTOM'
bone_group.colors.normal = v
bone_group.colors.select = self.select_color
bone_group.colors.active = self.active_color
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_copy_bone_groups(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.copy_bone_groups'
bl_label = "Copy Bone Groups"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(self, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
print('Copy Bone Group')
ob = context.object
bone_groups = {}
for bg in ob.pose.bone_groups:
bone_groups[bg.name] = {
'colors': [bg.colors.normal[:], bg.colors.select[:], bg.colors.active[:]],
2023-01-18 16:02:42 +01:00
'bones': [b.name for b in ob.pose.bones if b.bone_group == bg]
2022-10-28 23:23:06 +02:00
}
blend_file = Path(gettempdir()) / 'bw_copy_bone_groups.json'
blend_file.write_text(json.dumps(bone_groups))
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_paste_bone_groups(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.paste_bone_groups'
bl_label = "Paste Bone Groups"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
clear: BoolProperty(default=False)
2022-10-28 23:23:06 +02:00
@classmethod
def poll(self, context):
return context.object and context.object.type == 'ARMATURE'
def execute(self, context):
print('Paste Bone Group')
ob = context.object
blend_file = Path(gettempdir()) / 'bw_copy_bone_groups.json'
bone_groups = json.loads(blend_file.read_text())
if self.clear:
for bg in reversed(ob.pose.bone_groups):
ob.pose.bone_groups.remove(bg)
for bg_name, bg_data in bone_groups.items():
bg = ob.pose.bone_groups.get(bg_name)
if not bg:
bg = ob.pose.bone_groups.new(name=bg_name)
bg.color_set = 'CUSTOM'
bg.colors.normal,bg.colors.select, bg.colors.active = bg_data['colors']
for b in bg_data['bones']:
bone = ob.pose.bones.get(b)
if bone:
bone.bone_group = bg
return {'FINISHED'}
2023-01-24 22:54:40 +01:00
2023-01-18 16:02:42 +01:00
class BW_OT_add_folder(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.add_folder'
bl_label = "Add Folder"
def execute(self, context):
folder = ctx.prefs.folders.add()
folder.path = ''
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_remove_folder(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.remove_folder'
bl_label = "Remove Folder"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
index: IntProperty()
2022-10-28 23:23:06 +02:00
def execute(self, context):
ctx.prefs.folders.remove(self.index)
if self.index == 0:
ctx.prefs.folder_enum = str(ctx.default_folder_path)
#update_folder_items()
bpy.context.area.tag_redraw()
return {'FINISHED'}
2023-01-18 16:02:42 +01:00
class BW_OT_refresh_folders(Operator):
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.refresh_folders'
bl_label = "Refresh"
def execute(self, context):
for f in ctx.folders:
f.widgets.clear()
f.load_widgets()
return {'FINISHED'}
class BW_OT_rename_folder(Operator):
bl_idname = 'bonewidget.rename_folder'
bl_label = "Rename Folder"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
name: StringProperty(name='Name')
2022-10-28 23:23:06 +02:00
@classmethod
def poll(cls, context):
return ctx.active_folder
def execute(self, context):
folder = ctx.active_folder
folder.rename(self.name)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
self.name = Path(ctx.prefs.folder_enum).name
return wm.invoke_props_dialog(self)
2023-01-22 00:35:21 +01:00
# class BW_OT_show_preferences(Operator):
# """Display the preferences to the tab panel"""
# bl_idname = 'bonewidget.show_preferences'
# bl_label = "Show Preferences"
# bl_options = {'REGISTER'}
2022-10-28 23:23:06 +02:00
2023-01-22 00:35:21 +01:00
# def execute(self, context):
# #ctx.init_transforms()
# return {'FINISHED'}
2022-10-28 23:23:06 +02:00
class BW_OT_transform_widget(Operator):
2023-01-18 16:02:42 +01:00
"""Transform the Rotation Location or Scale of the selected shapes"""
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.transform_widget'
bl_label = "Transfom"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
symmetrize: BoolProperty(name='Symmetrize', default=True)
2022-10-28 23:23:06 +02:00
@classmethod
def poll(cls, context):
return context.object and context.object.mode == 'POSE'
def execute(self, context):
ctx.init_transforms()
return {'FINISHED'}
class BW_OT_create_widget(Operator):
2023-01-18 16:02:42 +01:00
"""Create the widget shape and assign it to the bone"""
2022-10-28 23:23:06 +02:00
bl_idname = 'bonewidget.create_widget'
bl_label = "Create Widget"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
symmetrize: BoolProperty(name='Symmetrize', default=True)
@classmethod
def poll(cls, context):
2023-02-06 15:10:00 +01:00
return ctx.selected_bones and ctx.active_widget
2022-10-28 23:23:06 +02:00
def execute(self, context):
folder = ctx.active_folder
blend = folder.get_widget_path(folder.active_widget)
with bpy.data.libraries.load(str(blend), link=False) as (data_src, data_dst):
data_dst.objects = data_src.objects
shape = data_src.objects[0]
for bone in ctx.selected_bones:
2023-01-18 16:02:42 +01:00
#shape_copy = shape.copy()
2022-10-28 23:23:06 +02:00
if bpy.app.version_string < '3.0.0':
bone.custom_shape_scale = 1.0
else:
bone.custom_shape_scale_xyz = [1.0]*3 # for blender 3.0
bone.bone.show_wire = not bool(shape.data.polygons)
#if bone.custom_shape and bone.custom_shape.users == 2:
# bpy.data.objects.remove(bone.custom_shape)
#copy_shape = shape.copy()
#copy_shape.data = copy_shape.data.copy()
2023-01-18 16:02:42 +01:00
bone.custom_shape = get_clean_shape(bone, shape, col=ctx.widget_col, prefix=ctx.prefs.prefix)
2022-10-28 23:23:06 +02:00
if self.symmetrize:
symmetrize_bone_shape(bone, prefix=ctx.prefs.prefix)
bpy.data.objects.remove(shape)
ctx.init_transforms()
return {'FINISHED'}
class BW_OT_match_transform(Operator):
bl_idname = 'bonewidget.match_transform'
bl_label = "Match Transforms"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
relative: BoolProperty(default=False)
2022-10-28 23:23:06 +02:00
def execute(self, context):
for bone in ctx.selected_bones:
shape = bone.custom_shape
if not shape:
continue
size_factor = get_bone_size_factor(bone, shape, self.relative)
mat = transform_matrix(scale=(size_factor,)*3)
shape.data.transform(mat)
#apply_mat_to_verts(shape.data, mat)
ctx.init_transforms()
return {'FINISHED'}
class BW_OT_edit_widget(Operator):
bl_idname = 'bonewidget.edit_widget'
bl_label = "Edit Widget"
bl_options = {'REGISTER', 'UNDO'}
2023-01-24 22:54:40 +01:00
clean: BoolProperty(name='Clean', default=True)
2022-10-28 23:23:06 +02:00
@classmethod
def poll(cls, context):
return ctx.bone and ctx.bone.custom_shape
def execute(self, context):
#ctx._rig = ctx.rig
widgets = ctx.selected_widgets
2023-01-24 22:54:40 +01:00
bones = ctx.selected_bones
2022-10-28 23:23:06 +02:00
rig = ctx.rig
2023-01-24 22:54:40 +01:00
prefs = ctx.prefs
if self.clean:
bpy.ops.bonewidget.clean_widget()
2022-10-28 23:23:06 +02:00
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
rig.hide_set(True)
ctx.widget_col.hide_viewport = False
ctx.widget_layer_col.exclude = False
ctx.widget_layer_col.hide_viewport = False
2023-01-24 22:54:40 +01:00
for bone in bones:
if not bone.custom_shape:
continue
#link_to_col(w, ctx.widget_col)
bone.custom_shape.select_set(True)
context.view_layer.objects.active = bone.custom_shape
2022-10-28 23:23:06 +02:00
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
class BW_OT_return_to_rig(Operator):
bl_idname = 'bonewidget.return_to_rig'
bl_label = "Return to rig"
bl_options = {'REGISTER', 'UNDO'}
2023-01-24 22:54:40 +01:00
clean: BoolProperty(name='Clean', default=True)
2022-10-28 23:23:06 +02:00
def invoke(self, context, event):
2023-01-24 22:54:40 +01:00
self.symmetrize = ctx.prefs.auto_symmetrize
2022-10-28 23:23:06 +02:00
return self.execute(context)
def execute(self, context):
rig = ctx.rig
#print('Rig', rig)
bone = ctx.bone
bones = ctx.selected_bones
ctx.widget_col.hide_viewport = False
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
rig.hide_set(False)
ctx.widget_col.hide_viewport = True
context.view_layer.objects.active = rig
bpy.ops.object.mode_set(mode='POSE')
rig.data.bones.active = bone.bone
for b in rig.pose.bones:
b.bone.select = b in bones+[bone]
2023-01-24 22:54:40 +01:00
if self.clean:
bpy.ops.bonewidget.clean_widget()
2022-10-28 23:23:06 +02:00
return {'FINISHED'}
'''
class BW_OT_symmetrize_widget(Operator):
bl_idname = 'bonewidget.symmetrize_widget'
bl_label = "Symmetrize"
bl_options = {'REGISTER', 'UNDO'}
2023-01-18 16:02:42 +01:00
match_transform: BoolProperty(True)
2022-10-28 23:23:06 +02:00
def get_name_side(self, name, fallback=None):
if name.lower().endswith('.l'):
return 'LEFT'
elif name.lower().endswith('.r'):
return 'RIGHT'
return fallback
2023-01-18 16:02:42 +01:00
def mirror_name(self, name):
2022-10-28 23:23:06 +02:00
mirror = None
match = {
2023-01-18 16:02:42 +01:00
'R': 'L',
'r': 'l',
'L': 'R',
'l': 'r',
2022-10-28 23:23:06 +02:00
}
separator = ['.', '_']
if name.startswith(tuple(match.keys())):
2023-01-18 16:02:42 +01:00
if name[1] in separator:
2022-10-28 23:23:06 +02:00
mirror = match[name[0]] + name[1:]
if name.endswith(tuple(match.keys())):
2023-01-18 16:02:42 +01:00
if name[-2] in separator:
2022-10-28 23:23:06 +02:00
mirror = name[:-1] + match[name[-1]]
return mirror
def execute(self, context):
bones = ctx.selected_bones
if ctx.bone:
active_side = self.get_name_side(ctx.bone.name, 'LEFT')
if active_side:
bones = [b for b in ctx.selected_bones if self.get_name_side(b) == active_side]
for bone in bones:
flip_name = self.mirror_name(bone.name)
flip_bone = ctx.rig.pose.bones.get(flip_name)
if flip_bone:
shape = flip_bone.custom_shape
if shape.users <= 2:
bpy.data.objects.remove(shape)
shape = bone.custom_shape.copy()
if shape
flip_bone.custom_shape = shape
shape.matrix_world = get_bone_matrix(bone)
shape.data.transform(transform_matrix(scale=(-1, 1, 1)))
ctx.rename_shape(shape, flip_bone)
return {'FINISHED'}
'''
class BW_OT_add_widget(Operator):
bl_idname = 'bonewidget.add_widget'
bl_label = "Add Widget"
bl_options = {'REGISTER', 'UNDO'}
2023-02-06 15:10:00 +01:00
replace: BoolProperty(default=False, name='Replace')
2022-10-28 23:23:06 +02:00
@classmethod
def poll(cls, context):
return ctx.widget
2023-02-06 15:10:00 +01:00
def invoke(self, context, event):
if event.ctrl:
self.replace = True
return self.execute(context)
2022-10-28 23:23:06 +02:00
def execute(self, context):
2023-02-06 15:10:00 +01:00
folder = ctx.active_folder
2022-10-28 23:23:06 +02:00
shape = ctx.widget
bone = ctx.bone
2023-02-06 15:10:00 +01:00
if self.replace and ctx.active_widget:
name = ctx.active_widget.name
else: #Find a unique name
name = folder.get_widget_display_name(bone.name if bone else shape.name)
2022-10-28 23:23:06 +02:00
2023-02-06 15:10:00 +01:00
i = 0
org_name = name
while name in folder.widgets:
name = f'{org_name} {i:02d}'
i += 1
2022-10-28 23:23:06 +02:00
widget_path = folder.get_widget_path(name)
icon_path = folder.get_icon_path(name)
widget_path.parent.mkdir(exist_ok=True, parents=True)
bpy.data.libraries.write(str(widget_path), {shape})
2023-01-18 16:02:42 +01:00
# Copy the shape to apply the bone matrix to the mesh
shape_copy = shape.copy()
shape_copy.data = shape_copy.data.copy()
2023-02-06 15:10:00 +01:00
if bone:
shape_copy.matrix_world = get_bone_matrix(bone)
2023-01-18 16:02:42 +01:00
2023-02-06 15:10:00 +01:00
mat = custom_shape_matrix(bone)
shape_copy.data.transform(mat)
2023-01-18 16:02:42 +01:00
render_widget(shape_copy, icon_path)
folder.add_widget(name)
2022-10-28 23:23:06 +02:00
2023-01-18 16:02:42 +01:00
bpy.data.objects.remove(shape_copy)
2022-10-28 23:23:06 +02:00
bpy.context.area.tag_redraw()
return {'FINISHED'}
class BW_OT_remove_widget(Operator):
bl_idname = 'bonewidget.remove_widget'
bl_label = "Remove Widget"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return ctx.active_widget
def execute(self, context):
folder = ctx.active_folder
widget = ctx.active_widget
folder.remove_widget(widget)
return {'FINISHED'}
class BW_OT_clean_widget(Operator):
bl_idname = 'bonewidget.clean_widget'
bl_label = "Clean"
bl_options = {'REGISTER', 'UNDO'}
all: BoolProperty(name='All', default=False)
2023-01-18 16:02:42 +01:00
symmetrize: BoolProperty(name='Symmetrize', default=True)
2022-10-28 23:23:06 +02:00
@classmethod
def poll(cls, context):
return context.object and context.object.mode == 'POSE'
def invoke(self, context, event):
2023-01-24 22:54:40 +01:00
self.symmetrize = ctx.prefs.auto_symmetrize
2022-10-28 23:23:06 +02:00
return self.execute(context)
def execute(self, context):
scene = context.scene
prefs = ctx.prefs
if self.all:
bones = ctx.bones
else:
bones = ctx.selected_bones
for bone in bones:
shape = bone.custom_shape
if not shape:
continue
2023-01-24 22:54:40 +01:00
bone.custom_shape = get_clean_shape(bone, shape, separate=prefs.auto_separate,
rename=prefs.auto_rename, col=ctx.widget_col, prefix=prefs.prefix, match=prefs.auto_match_transform)
2022-10-28 23:23:06 +02:00
if self.symmetrize:
symmetrize_bone_shape(bone, prefix=ctx.prefs.prefix)
if shape in bpy.data.objects[:] and shape.users <= 1:
bpy.data.objects.remove(shape)
return {'FINISHED'}
classes = (
BW_OT_remove_unused_shape,
BW_OT_auto_color,
BW_OT_load_default_color,
BW_OT_add_folder,
BW_OT_remove_folder,
BW_OT_refresh_folders,
BW_OT_rename_folder,
BW_OT_transform_widget,
BW_OT_match_transform,
BW_OT_create_widget,
BW_OT_edit_widget,
BW_OT_return_to_rig,
#BW_OT_symmetrize_widget,
BW_OT_add_widget,
BW_OT_remove_widget,
BW_OT_clean_widget,
2023-01-22 00:35:21 +01:00
# BW_OT_show_preferences,
2022-10-28 23:23:06 +02:00
BW_OT_copy_widgets,
BW_OT_paste_widgets,
BW_OT_copy_bone_groups,
BW_OT_paste_bone_groups
)
def bw_bone_group_menu(self, context):
layout = self.layout
layout.operator("bonewidget.load_default_color", icon='IMPORT')
layout.operator("bonewidget.auto_color", icon='COLOR')
layout.operator("bonewidget.copy_bone_groups", icon='COPYDOWN')
layout.operator("bonewidget.paste_bone_groups", icon='PASTEDOWN')
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.DATA_MT_bone_group_context_menu.prepend(bw_bone_group_menu)
def unregister():
bpy.types.DATA_MT_bone_group_context_menu.remove(bw_bone_group_menu)
for cls in reversed(classes):
bpy.utils.unregister_class(cls)