diff options
author | Ines Almeida <britalmeida@gmail.com> | 2016-02-26 22:50:29 +0300 |
---|---|---|
committer | Ines Almeida <britalmeida@gmail.com> | 2016-03-03 04:08:21 +0300 |
commit | d7cd2f6dfe8c424a11bdd6f0a9060ad985c851c7 (patch) | |
tree | 5240d3927b8cec175d85eac39b3414ca77a914a6 | |
parent | d33b3e15ca6672490062774d18965af3d4968cf0 (diff) |
Refactored Selection Sets to have an interface similar to Bone Groups.
'Toggle' operator was converted into Select/Deselect, which adds and deletes from the selection, respectively.
Added Assign/Unassign to be able to edit a set.
Tweaked bl-info and properties descriptions and labels
Motionpath functions from the Gooseberry branch were removed
-rw-r--r-- | bone_selection_groups.py | 244 | ||||
-rw-r--r-- | bone_selection_sets.py | 315 |
2 files changed, 315 insertions, 244 deletions
diff --git a/bone_selection_groups.py b/bone_selection_groups.py deleted file mode 100644 index 1c00bb13..00000000 --- a/bone_selection_groups.py +++ /dev/null @@ -1,244 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ***** END GPL LICENCE BLOCK ***** - -bl_info = { - "name": "Bone Selection Groups", - "author": "Antony Riakiotakis", - "version": (1, 0, 2), - "blender": (2, 75, 0), - "location": "Properties > Object Buttons", - "description": "Operator and storage for restoration of bone selection state.", - "category": "Animation", -} - -import bpy -from bpy.types import ( - Operator, - Panel, - UIList, -) - -from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - CollectionProperty, - BoolVectorProperty, - FloatVectorProperty, -) - - -class POSE_PT_selection_sets(Panel): - bl_label = "Selection Sets" - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "data" - - @classmethod - def poll(cls, context): - return context.object.type == 'ARMATURE' - - def draw(self, context): - layout = self.layout - - armature = context.object - - # Rig type list - row = layout.row() - row.template_list( - "POSE_UL_selection_set", "", - armature, "selection_sets", - armature, "active_selection_set") - - col = row.column() - colsub = col.column(align=True) - colsub.operator("pose.selection_set_add", icon='ZOOMIN', text="") - colsub.operator("pose.selection_set_remove", icon='ZOOMOUT', text="") - - layout.operator("pose.selection_set_toggle") - - -class POSE_UL_selection_set(UIList): - def draw_item(self, context, layout, data, set, icon, active_data, active_propname, index): - layout.prop(set, "name", text="", emboss=False) - - -class SelectionEntry(bpy.types.PropertyGroup): - name = StringProperty(name="Bone Name") - - -class SelectionSet(bpy.types.PropertyGroup): - name = StringProperty(name="Set Name") - bone_ids = CollectionProperty(type=SelectionEntry) - - -class PluginOperator(Operator): - @classmethod - def poll(self, context): - return (context.object and - context.object.type == 'ARMATURE' and - context.mode == 'POSE') - - -class POSE_OT_selection_set_add(PluginOperator): - bl_idname = "pose.selection_set_add" - bl_label = "Add Selection Set" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - keep = False - armature = context.object - pose = armature.pose - - selection_set = armature.selection_sets.add() - selection_set.name = "SelectionSet.%d" % len(armature.selection_sets) - armature.active_selection_set = len(armature.selection_sets) - 1 - for bone in pose.bones: - if (bone.bone.select): - bone_id = selection_set.bone_ids.add() - bone_id.name = bone.name - keep = True - - if (not keep): - armature.selection_sets.remove(armature.active_selection_set) - numsets = len(armature.selection_sets) - if (armature.active_selection_set > (numsets - 1) and numsets > 0): - armature.active_selection_set = len(armature.selection_sets) - 1 - return {'CANCELLED'} - - return {'FINISHED'} - - -class POSE_OT_selection_set_remove(PluginOperator): - bl_idname = "pose.selection_set_remove" - bl_label = "Delete Selection Set" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - armature = context.object - - armature.selection_sets.remove(armature.active_selection_set) - numsets = len(armature.selection_sets) - if (armature.active_selection_set > (numsets - 1) and numsets > 0): - armature.active_selection_set = len(armature.selection_sets) - 1 - return {'FINISHED'} - - -class POSE_OT_selection_set_toggle(PluginOperator): - bl_idname = "pose.selection_set_toggle" - bl_label = "Toggle Selection Set" - bl_options = {'UNDO', 'REGISTER'} - - def execute(self, context): - armature = context.object - pose = armature.pose - - selection_set = armature.selection_sets[armature.active_selection_set] - for bone in pose.bones: - bone.bone.select = False - - for bone in selection_set.bone_ids: - pose.bones[bone.name].bone.select = True - - return {'FINISHED'} - - -class MotionPathsCopyStartFrame(Operator): - bl_idname = "anim.motionpaths_copy_scene_startframe" - bl_label = "Copy Scene Start Frame" - bl_options = {'REGISTER', 'UNDO'} - - armature_paths = BoolProperty() - - @classmethod - def poll(cls, context): - return (context.object) - - def execute(self, context): - avs = None - motionpath = None - ob = context.object - scene = context.scene - - if (self.armature_paths): - avs = ob.pose.animation_visualization - else: - avs = ob.animation_visualization - - preview = scene.use_preview_range - - if (avs): - motionpath = avs.motion_path - - if (motionpath): - if (preview): - motionpath.frame_start = scene.frame_preview_start - motionpath.frame_end = scene.frame_preview_end - else: - motionpath.frame_start = scene.frame_start - motionpath.frame_end = scene.frame_end - else: - return {'CANCELLED'} - - return {'FINISHED'} - - -classes = ( - POSE_PT_selection_sets, - POSE_UL_selection_set, - SelectionEntry, - SelectionSet, - POSE_OT_selection_set_add, - POSE_OT_selection_set_remove, - POSE_OT_selection_set_toggle, - MotionPathsCopyStartFrame -) - - -def copy_frames_armature(self, context): - layout = self.layout - layout.operator("anim.motionpaths_copy_scene_startframe").armature_paths = True - - -def copy_frames_object(self, context): - layout = self.layout - layout.operator("anim.motionpaths_copy_scene_startframe").armature_paths = False - - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Object.selection_sets = CollectionProperty(type=SelectionSet) - bpy.types.Object.active_selection_set = IntProperty() - bpy.types.DATA_PT_motion_paths.append(copy_frames_armature) - bpy.types.OBJECT_PT_motion_paths.append(copy_frames_object) - - -def unregister(): - for cls in classes: - bpy.utils.unregister_class(cls) - - del bpy.types.Object.selection_sets - del bpy.types.Object.active_selection_set - - -if __name__ == "__main__": - register() diff --git a/bone_selection_sets.py b/bone_selection_sets.py new file mode 100644 index 00000000..8bd4841e --- /dev/null +++ b/bone_selection_sets.py @@ -0,0 +1,315 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENCE BLOCK ***** + +bl_info = { + "name": "Bone Selection Sets", + "author": "Antony Riakiotakis, InĂªs Almeida", + "version": (2, 0, 0), + "blender": (2, 75, 0), + "location": "Properties > Object Data (Armature) > Selection Sets", + "description": "List of Bone sets for easy selection while animating", + "warning": "", + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" + "Scripts/Animation/SelectionSets", + "category": "Animation", +} + +import bpy +from bpy.types import ( + Operator, + Menu, + Panel, + UIList, + PropertyGroup, +) + +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + CollectionProperty, + BoolVectorProperty, + FloatVectorProperty, +) + +# Data Structure ############################################################## + +# Note: bones are stored by name, this means that if the bone is renamed, +# there can be problems. However, bone renaming is unlikely during animation +class SelectionEntry(PropertyGroup): + name = StringProperty(name="Bone Name") + + +class SelectionSet(PropertyGroup): + name = StringProperty(name="Set Name") + bone_ids = CollectionProperty(type=SelectionEntry) + + +# UI Panel w/ UIList ########################################################## + +class POSE_MT_selection_sets_specials(Menu): + bl_label = "Selection Sets Specials" + + def draw(self, context): + layout = self.layout + + # TODO + #layout.operator("pose.selection_sets_sort", icon='SORTALPHA', text="Sort by Name").sort_type = 'NAME' + + +class POSE_PT_selection_sets(Panel): + bl_label = "Selection Sets" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return (context.armature + and context.object + and context.object.type == 'ARMATURE' + and context.object.pose + ) + + def draw(self, context): + layout = self.layout + + ob = context.object + arm = context.object + + layout.enabled = (ob.proxy is None) + + row = layout.row() + + # UI list + rows = 4 #TODO if is being used, else 1 + row.template_list( + "POSE_UL_selection_set", "", + arm, "selection_sets", + arm, "active_selection_set", + rows=rows + ) + + # add/remove/specials UI list Menu + col = row.column(align=True) + col.operator("pose.selection_set_add", icon='ZOOMIN', text="") + col.operator("pose.selection_set_remove", icon='ZOOMOUT', text="") + # TODO specials like sorting + #col.menu("POSE_MT_selection_sets_specials", icon='DOWNARROW_HLT', text="") + + # TODO move up/down arrows + + # buttons + row = layout.row() + + sub = row.row(align=True) + sub.operator("pose.selection_set_assign", text="Assign") + sub.operator("pose.selection_set_unassign", text="Remove") + + sub = row.row(align=True) + sub.operator("pose.selection_set_select", text="Select") + sub.operator("pose.selection_set_deselect", text="Deselect") + + +class POSE_UL_selection_set(UIList): + def draw_item(self, context, layout, data, set, icon, active_data, active_propname, index): + layout.prop(set, "name", text="", emboss=False) + + +# Operators ################################################################### + +class PluginOperator(Operator): + @classmethod + def poll(self, context): + return (context.object and + context.object.type == 'ARMATURE' and + context.mode == 'POSE') + +class NeedSelSetPluginOperator(PluginOperator): + @classmethod + def poll(self, context): + if super().poll(context): + arm = context.object + return (arm.active_selection_set < len(arm.selection_sets)) + return False + + +class POSE_OT_selection_set_add(PluginOperator): + bl_idname = "pose.selection_set_add" + bl_label = "Create Selection Set" + bl_description = "Creates a new empty Selection Set" + bl_options = {'UNDO', 'REGISTER'} + + created_counter = 0 + + def execute(self, context): + arm = context.object + + selection_set = arm.selection_sets.add() + + selection_set.name = "SelectionSet" + if POSE_OT_selection_set_add.created_counter > 0: + selection_set.name += ".{:03d}".format(POSE_OT_selection_set_add.created_counter) + POSE_OT_selection_set_add.created_counter += 1 + + arm.active_selection_set = len(arm.selection_sets) - 1 + + return {'FINISHED'} + + +class POSE_OT_selection_set_remove(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_remove" + bl_label = "Delete Selection Set" + bl_description = "Delete a Selection Set" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + arm = context.object + + arm.selection_sets.remove(arm.active_selection_set) + numsets = len(arm.selection_sets) + if (arm.active_selection_set > (numsets - 1) and numsets > 0): + arm.active_selection_set = len(arm.selection_sets) - 1 + return {'FINISHED'} + + +class POSE_OT_selection_set_assign(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_assign" + bl_label = "Add Bones to Selection Set" + bl_description = "Add selected bones to Selection Set" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + arm = context.object + pose = arm.pose + + #if arm.active_selection_set <= 0: + # arm.selection_sets.add() + #TODO naming convention + # return {'FINISHED'} + + selection_set = arm.selection_sets[arm.active_selection_set] + for bone in pose.bones: + if bone.bone.select: + bone_id = selection_set.bone_ids.add() + bone_id.name = bone.name + + return {'FINISHED'} + + +class POSE_OT_selection_set_unassign(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_unassign" + bl_label = "Remove Bones from Selection Set" + bl_description = "Remove selected bones from Selection Set" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + arm = context.object + pose = arm.pose + + selection_set = arm.selection_sets[arm.active_selection_set] + for bone in pose.bones: + if bone.bone.select and bone.name in selection_set.bone_ids: + selection_set.bone_ids[bone.name].remove() + + return {'FINISHED'} + + +class POSE_OT_selection_set_select(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_select" + bl_label = "Select Selection Set" + bl_description = "Add Selection Set bones to current selection" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + arm = context.object + pose = arm.pose + + selection_set = arm.selection_sets[arm.active_selection_set] + for bone in pose.bones: + if bone.name in selection_set.bone_ids: + bone.bone.select = True + + return {'FINISHED'} + + +class POSE_OT_selection_set_deselect(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_deselect" + bl_label = "Deselect Selection Set" + bl_description = "Remove Selection Set bones from current selection" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + arm = context.object + pose = arm.pose + + selection_set = arm.selection_sets[arm.active_selection_set] + for bone in pose.bones: + if bone.name in selection_set.bone_ids: + bone.bone.select = False + + return {'FINISHED'} + + +# Registry #################################################################### + +classes = ( + POSE_MT_selection_sets_specials, + POSE_PT_selection_sets, + POSE_UL_selection_set, + SelectionEntry, + SelectionSet, + POSE_OT_selection_set_add, + POSE_OT_selection_set_remove, + POSE_OT_selection_set_assign, + POSE_OT_selection_set_unassign, + POSE_OT_selection_set_select, + POSE_OT_selection_set_deselect, +) + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Object.selection_sets = CollectionProperty( + type=SelectionSet, + name="Selection Sets", + description="List of groups of bones for easy selection" + ) + bpy.types.Object.active_selection_set = IntProperty( + name="Active Selection Set", + description="Index of the currently active selection set", + default=0 + ) + + bpy.types.DATA_PT_motion_paths.append(copy_frames_armature) + bpy.types.OBJECT_PT_motion_paths.append(copy_frames_object) + + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + + del bpy.types.Object.selection_sets + del bpy.types.Object.active_selection_set + + +if __name__ == "__main__": + register() |