AnimAll: set mode to Object when deleting keyframes for meshes
[blender-addons.git] / animation_animall / __init__.py
blob04d08d3f771d8adfd926cf4fc4c772604187e396
1 # SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "AnimAll",
7 "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)",
8 "version": (0, 9, 6),
9 "blender": (3, 3, 0),
10 "location": "3D View > Toolbox > Animation tab > AnimAll",
11 "description": "Allows animation of mesh, lattice, curve and surface data",
12 "warning": "",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html",
14 "category": "Animation",
17 import bpy
18 from bpy.types import (Operator, Panel, AddonPreferences)
19 from bpy.props import (BoolProperty, StringProperty)
20 from bpy.app.handlers import persistent
21 from bpy.app.translations import (pgettext_iface as iface_,
22 pgettext_data as data_)
23 from . import translations
26 # Property Definitions
27 class AnimallProperties(bpy.types.PropertyGroup):
28 key_selected: BoolProperty(
29 name="Key Selected Only",
30 description="Insert keyframes only on selected elements",
31 default=False)
33 # Generic attributes
34 key_point_location: BoolProperty(
35 name="Location",
36 description="Insert keyframes on point locations",
37 default=False)
38 key_shape_key: BoolProperty(
39 name="Shape Key",
40 description="Insert keyframes on active Shape Key layer",
41 default=False)
42 key_material_index: BoolProperty(
43 name="Material Index",
44 description="Insert keyframes on face material indices",
45 default=False)
47 # Mesh attributes
48 key_vertex_bevel: BoolProperty(
49 name="Vertex Bevel",
50 description="Insert keyframes on vertex bevel weight",
51 default=False)
52 # key_vertex_crease: BoolProperty(
53 # name="Vertex Crease",
54 # description="Insert keyframes on vertex crease weight",
55 # default=False)
56 key_vertex_group: BoolProperty(
57 name="Vertex Group",
58 description="Insert keyframes on active vertex group values",
59 default=False)
61 key_edge_bevel: BoolProperty(
62 name="Edge Bevel",
63 description="Insert keyframes on edge bevel weight",
64 default=False)
65 key_edge_crease: BoolProperty(
66 name="Edge Crease",
67 description="Insert keyframes on edge creases",
68 default=False)
70 key_attribute: BoolProperty(
71 name="Attribute",
72 description="Insert keyframes on active attribute values",
73 default=False)
74 key_uvs: BoolProperty(
75 name="UV Map",
76 description="Insert keyframes on active UV coordinates",
77 default=False)
79 # Curve and surface attributes
80 key_radius: BoolProperty(
81 name="Radius",
82 description="Insert keyframes on point radius (Shrink/Fatten)",
83 default=False)
84 key_tilt: BoolProperty(
85 name="Tilt",
86 description="Insert keyframes on point tilt",
87 default=False)
90 # Utility functions
92 def refresh_ui_keyframes():
93 try:
94 for area in bpy.context.screen.areas:
95 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
96 area.tag_redraw()
97 except:
98 pass
101 def insert_key(data, key, group=None):
102 try:
103 if group is not None:
104 data.keyframe_insert(key, group=group)
105 else:
106 data.keyframe_insert(key)
107 except:
108 pass
111 def delete_key(data, key):
112 try:
113 data.keyframe_delete(key)
114 except:
115 pass
118 def is_selected_vert_loop(data, loop_i):
119 """Get selection status of vertex corresponding to a loop"""
120 vertex_index = data.loops[loop_i].vertex_index
121 return data.vertices[vertex_index].select
124 # GUI (Panel)
126 class VIEW3D_PT_animall(Panel):
127 bl_space_type = 'VIEW_3D'
128 bl_region_type = 'UI'
129 bl_category = "Animation"
130 bl_label = ''
132 @classmethod
133 def poll(self, context):
134 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
136 def draw_header(self, context):
138 layout = self.layout
139 row = layout.row()
140 row.label (text = 'AnimAll', icon = 'ARMATURE_DATA')
142 def draw(self, context):
143 obj = context.active_object
144 animall_properties = context.scene.animall_properties
146 layout = self.layout
148 layout.label(text='Key:')
150 layout.use_property_split = True
151 layout.use_property_decorate = False
153 if obj.type == 'LATTICE':
154 col = layout.column(heading="Points", align=True)
155 col.prop(animall_properties, "key_point_location")
157 col = layout.column(heading="Others", align=True)
158 col.prop(animall_properties, "key_shape_key")
160 elif obj.type == 'MESH':
161 col = layout.column(heading="Points", align=True)
162 col.prop(animall_properties, "key_point_location")
163 col.prop(animall_properties, "key_vertex_bevel", text="Bevel")
164 col.prop(animall_properties, "key_vertex_group")
166 col = layout.column(heading="Edges", align=True)
167 col.prop(animall_properties, "key_edge_bevel", text="Bevel")
168 col.prop(animall_properties, "key_edge_crease", text="Crease")
170 col = layout.column(heading="Faces", align=True)
171 col.prop(animall_properties, "key_material_index")
173 col = layout.column(heading="Others", align=True)
174 col.prop(animall_properties, "key_attribute")
175 col.prop(animall_properties, "key_uvs")
176 col.prop(animall_properties, "key_shape_key")
178 # Vertex group update operator
179 if (obj.data.animation_data is not None
180 and obj.data.animation_data.action is not None):
181 for fcurve in context.active_object.data.animation_data.action.fcurves:
182 if fcurve.data_path.startswith("vertex_colors"):
183 col = layout.column(align=True)
184 col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR")
185 col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH")
186 break
188 elif obj.type in {'CURVE', 'SURFACE'}:
189 col = layout.column(align=True)
190 col = layout.column(heading="Points", align=True)
191 col.prop(animall_properties, "key_point_location")
192 col.prop(animall_properties, "key_radius")
193 col.prop(animall_properties, "key_tilt")
195 col = layout.column(heading="Splines", align=True)
196 col.prop(animall_properties, "key_material_index")
198 col = layout.column(heading="Others", align=True)
199 col.prop(animall_properties, "key_shape_key")
201 if animall_properties.key_shape_key:
202 shape_key = obj.active_shape_key
203 shape_key_index = obj.active_shape_key_index
205 if shape_key_index > 0:
206 col = layout.column(align=True)
207 row = col.row(align=True)
208 row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA")
209 row.prop(obj, "show_only_shape_key", text="")
210 if shape_key.value < 1:
211 col.label(text=iface_('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO")
212 elif shape_key is not None:
213 col = layout.column(align=True)
214 col.label(text="Cannot key on Basis Shape", icon="ERROR")
215 else:
216 col = layout.column(align=True)
217 col.label(text="No active Shape Key", icon="ERROR")
219 if animall_properties.key_point_location:
220 col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO")
222 layout.use_property_split = False
223 layout.separator()
224 row = layout.row()
225 row.prop(animall_properties, "key_selected")
227 row = layout.row(align=True)
228 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
229 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
230 row = layout.row()
231 row.operator("anim.clear_animation_animall", icon="CANCEL")
234 class ANIM_OT_insert_keyframe_animall(Operator):
235 bl_label = "Insert Key"
236 bl_idname = "anim.insert_keyframe_animall"
237 bl_description = "Insert a Keyframe"
238 bl_options = {'REGISTER', 'UNDO'}
240 def execute(self, context):
241 animall_properties = context.scene.animall_properties
243 if context.mode == 'OBJECT':
244 objects = context.selected_objects
245 else:
246 objects = context.objects_in_mode_unique_data[:]
248 mode = context.object.mode
250 # Separate loop for lattices, curves and surfaces, since keyframe insertion
251 # has to happen in Edit Mode, otherwise points move back upon mode switch...
252 # (except for curve shape keys)
253 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
254 data = obj.data
256 if obj.type == 'LATTICE':
257 if animall_properties.key_shape_key:
258 if obj.active_shape_key_index > 0:
259 sk_name = obj.active_shape_key.name
260 for p_i, point in enumerate(obj.active_shape_key.data):
261 if not animall_properties.key_selected or data.points[p_i].select:
262 insert_key(point, 'co', group=data_("%s Point %s") % (sk_name, p_i))
264 if animall_properties.key_point_location:
265 for p_i, point in enumerate(data.points):
266 if not animall_properties.key_selected or point.select:
267 insert_key(point, 'co_deform', group=data_("Point %s") % p_i)
269 else:
270 if animall_properties.key_material_index:
271 for s_i, spline in enumerate(data.splines):
272 if (not animall_properties.key_selected
273 or any(point.select for point in spline.points)
274 or any(point.select_control_point for point in spline.bezier_points)):
275 insert_key(spline, 'material_index', group=data_("Spline %s") % s_i)
277 for s_i, spline in enumerate(data.splines):
278 if spline.type == 'BEZIER':
279 for v_i, CV in enumerate(spline.bezier_points):
280 if (not animall_properties.key_selected
281 or CV.select_control_point
282 or CV.select_left_handle
283 or CV.select_right_handle):
284 if animall_properties.key_point_location:
285 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
286 insert_key(CV, 'handle_left', group=data_("Spline %s CV %s") % (s_i, v_i))
287 insert_key(CV, 'handle_right', group=data_("Spline %s CV %s") % (s_i, v_i))
289 if animall_properties.key_radius:
290 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
292 if animall_properties.key_tilt:
293 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
295 elif spline.type in ('POLY', 'NURBS'):
296 for v_i, CV in enumerate(spline.points):
297 if not animall_properties.key_selected or CV.select:
298 if animall_properties.key_point_location:
299 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
301 if animall_properties.key_radius:
302 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
304 if animall_properties.key_tilt:
305 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
307 bpy.ops.object.mode_set(mode='OBJECT')
309 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
310 data = obj.data
311 if obj.type == 'MESH':
312 if animall_properties.key_point_location:
313 for v_i, vert in enumerate(data.vertices):
314 if not animall_properties.key_selected or vert.select:
315 insert_key(vert, 'co', group=data_("Vertex %s") % v_i)
317 if animall_properties.key_vertex_bevel:
318 for v_i, vert in enumerate(data.vertices):
319 if not animall_properties.key_selected or vert.select:
320 insert_key(vert, 'bevel_weight', group=data_("Vertex %s") % v_i)
321 # if animall_properties.key_vertex_crease:
322 # for v_i, vert in enumerate(data.vertices):
323 # if not animall_properties.key_selected or vert.select:
324 # insert_key(vert, 'crease', group=data_("Vertex %s") % v_i)
326 if animall_properties.key_vertex_group:
327 for v_i, vert in enumerate(data.vertices):
328 if not animall_properties.key_selected or vert.select:
329 for group in vert.groups:
330 insert_key(group, 'weight', group=data_("Vertex %s") % v_i)
332 if animall_properties.key_edge_bevel:
333 for e_i, edge in enumerate(data.edges):
334 if not animall_properties.key_selected or edge.select:
335 insert_key(edge, 'bevel_weight', group=data_("Edge %s") % e_i)
337 if animall_properties.key_edge_crease:
338 for e_i, edge in enumerate(data.edges):
339 if not animall_properties.key_selected or edge.select:
340 insert_key(edge, 'crease', group=data_("Edge %s") % e_i)
342 if animall_properties.key_material_index:
343 for p_i, polygon in enumerate(data.polygons):
344 if not animall_properties.key_selected or polygon.select:
345 insert_key(polygon, 'material_index', group=data_("Face %s") % p_i)
347 if animall_properties.key_attribute:
348 if data.attributes.active is not None:
349 attribute = data.attributes.active
350 if attribute.data_type != 'STRING':
351 # Cannot animate string attributes?
352 if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}:
353 attribute_key = "value"
354 elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}:
355 attribute_key = "color"
356 elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}:
357 attribute_key = "vector"
359 if attribute.domain == 'POINT':
360 group = data_("Vertex %s")
361 elif attribute.domain == 'EDGE':
362 group = data_("Edge %s")
363 elif attribute.domain == 'FACE':
364 group = data_("Face %s")
365 elif attribute.domain == 'CORNER':
366 group = data_("Loop %s")
368 for e_i, _attribute_data in enumerate(attribute.data):
369 if (not animall_properties.key_selected
370 or attribute.domain == 'POINT' and data.vertices[e_i].select
371 or attribute.domain == 'EDGE' and data.edges[e_i].select
372 or attribute.domain == 'FACE' and data.polygons[e_i].select
373 or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)):
374 insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}',
375 group=group % e_i)
377 if animall_properties.key_uvs:
378 if data.uv_layers.active is not None:
379 for uv_i, uv in enumerate(data.uv_layers.active.data):
380 if not animall_properties.key_selected or uv.select:
381 insert_key(uv, 'uv', group=data_("UV layer %s") % uv_i)
383 if animall_properties.key_shape_key:
384 if obj.active_shape_key_index > 0:
385 sk_name = obj.active_shape_key.name
386 for v_i, vert in enumerate(obj.active_shape_key.data):
387 if not animall_properties.key_selected or data.vertices[v_i].select:
388 insert_key(vert, 'co', group=data_("%s Vertex %s") % (sk_name, v_i))
390 elif obj.type in {'CURVE', 'SURFACE'}:
391 # Shape key keys have to be inserted in object mode for curves...
392 if animall_properties.key_shape_key:
393 sk_name = obj.active_shape_key.name
394 global_spline_index = 0 # numbering for shape keys, which have flattened indices
395 for s_i, spline in enumerate(data.splines):
396 if spline.type == 'BEZIER':
397 for v_i, CV in enumerate(spline.bezier_points):
398 if (not animall_properties.key_selected
399 or CV.select_control_point
400 or CV.select_left_handle
401 or CV.select_right_handle):
402 if obj.active_shape_key_index > 0:
403 CV = obj.active_shape_key.data[global_spline_index]
404 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
405 insert_key(CV, 'handle_left', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
406 insert_key(CV, 'handle_right', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
407 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
408 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
409 global_spline_index += 1
411 elif spline.type in ('POLY', 'NURBS'):
412 for v_i, CV in enumerate(spline.points):
413 if not animall_properties.key_selected or CV.select:
414 if obj.active_shape_key_index > 0:
415 CV = obj.active_shape_key.data[global_spline_index]
416 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
417 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
418 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
419 global_spline_index += 1
421 bpy.ops.object.mode_set(mode=mode)
422 refresh_ui_keyframes()
424 return {'FINISHED'}
427 class ANIM_OT_delete_keyframe_animall(Operator):
428 bl_label = "Delete Key"
429 bl_idname = "anim.delete_keyframe_animall"
430 bl_description = "Delete a Keyframe"
431 bl_options = {'REGISTER', 'UNDO'}
433 def execute(self, context):
434 animall_properties = context.scene.animall_properties
436 if context.mode == 'OBJECT':
437 objects = context.selected_objects
438 else:
439 objects = context.objects_in_mode_unique_data[:]
441 mode = context.object.mode
443 for obj in objects:
444 data = obj.data
445 if obj.type == 'MESH':
446 bpy.ops.object.mode_set(mode='OBJECT')
448 if animall_properties.key_point_location:
449 for vert in data.vertices:
450 if not animall_properties.key_selected or vert.select:
451 delete_key(vert, 'co')
453 if animall_properties.key_vertex_bevel:
454 for vert in data.vertices:
455 if not animall_properties.key_selected or vert.select:
456 delete_key(vert, 'bevel_weight')
458 if animall_properties.key_vertex_group:
459 for vert in data.vertices:
460 if not animall_properties.key_selected or vert.select:
461 for group in vert.groups:
462 delete_key(group, 'weight')
464 # if animall_properties.key_vertex_crease:
465 # for vert in data.vertices:
466 # if not animall_properties.key_selected or vert.select:
467 # delete_key(vert, 'crease')
469 if animall_properties.key_edge_bevel:
470 for edge in data.edges:
471 if not animall_properties.key_selected or edge.select:
472 delete_key(edge, 'bevel_weight')
474 if animall_properties.key_edge_crease:
475 for edge in data.edges:
476 if not animall_properties.key_selected or vert.select:
477 delete_key(edge, 'crease')
479 if animall_properties.key_shape_key:
480 if obj.active_shape_key:
481 for v_i, vert in enumerate(obj.active_shape_key.data):
482 if not animall_properties.key_selected or data.vertices[v_i].select:
483 delete_key(vert, 'co')
485 if animall_properties.key_uvs:
486 if data.uv_layers.active is not None:
487 for uv in data.uv_layers.active.data:
488 if not animall_properties.key_selected or uv.select:
489 delete_key(uv, 'uv')
491 if animall_properties.key_attribute:
492 if data.attributes.active is not None:
493 attribute = data.attributes.active
494 if attribute.data_type != 'STRING':
495 # Cannot animate string attributes?
496 if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}:
497 attribute_key = "value"
498 elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}:
499 attribute_key = "color"
500 elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}:
501 attribute_key = "vector"
503 for e_i, _attribute_data in enumerate(attribute.data):
504 if (not animall_properties.key_selected
505 or attribute.domain == 'POINT' and data.vertices[e_i].select
506 or attribute.domain == 'EDGE' and data.edges[e_i].select
507 or attribute.domain == 'FACE' and data.polygons[e_i].select
508 or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)):
509 delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}')
511 bpy.ops.object.mode_set(mode=mode)
513 elif obj.type == 'LATTICE':
514 if animall_properties.key_shape_key:
515 if obj.active_shape_key:
516 for point in obj.active_shape_key.data:
517 delete_key(point, 'co')
519 if animall_properties.key_point_location:
520 for point in data.points:
521 if not animall_properties.key_selected or point.select:
522 delete_key(point, 'co_deform')
524 elif obj.type in {'CURVE', 'SURFACE'}:
525 # Run this outside the splines loop (only once)
526 if animall_properties.key_shape_key:
527 if obj.active_shape_key_index > 0:
528 for CV in obj.active_shape_key.data:
529 delete_key(CV, 'co')
530 delete_key(CV, 'handle_left')
531 delete_key(CV, 'handle_right')
533 for spline in data.splines:
534 if spline.type == 'BEZIER':
535 for CV in spline.bezier_points:
536 if (not animall_properties.key_selected
537 or CV.select_control_point
538 or CV.select_left_handle
539 or CV.select_right_handle):
540 if animall_properties.key_point_location:
541 delete_key(CV, 'co')
542 delete_key(CV, 'handle_left')
543 delete_key(CV, 'handle_right')
544 if animall_properties.key_radius:
545 delete_key(CV, 'radius')
546 if animall_properties.key_tilt:
547 delete_key(CV, 'tilt')
549 elif spline.type in ('POLY', 'NURBS'):
550 for CV in spline.points:
551 if not animall_properties.key_selected or CV.select:
552 if animall_properties.key_point_location:
553 delete_key(CV, 'co')
554 if animall_properties.key_radius:
555 delete_key(CV, 'radius')
556 if animall_properties.key_tilt:
557 delete_key(CV, 'tilt')
559 refresh_ui_keyframes()
561 return {'FINISHED'}
564 class ANIM_OT_clear_animation_animall(Operator):
565 bl_label = "Clear Animation"
566 bl_idname = "anim.clear_animation_animall"
567 bl_description = ("Delete all keyframes for this object\n"
568 "If in a specific case it doesn't work\n"
569 "try to delete the keys manually")
570 bl_options = {'REGISTER', 'UNDO'}
572 def invoke(self, context, event):
573 wm = context.window_manager
574 return wm.invoke_confirm(self, event)
576 def execute(self, context):
577 if context.mode == 'OBJECT':
578 objects = context.selected_objects
579 else:
580 objects = context.objects_in_mode_unique_data
582 for obj in objects:
583 try:
584 data = obj.data
585 data.animation_data_clear()
586 except:
587 self.report({'WARNING'}, "Clear Animation could not be performed")
588 return {'CANCELLED'}
590 refresh_ui_keyframes()
592 return {'FINISHED'}
595 class ANIM_OT_update_vertex_color_animation_animall(Operator):
596 bl_label = "Update Vertex Color Animation"
597 bl_idname = "anim.update_vertex_color_animation_animall"
598 bl_description = "Update old vertex color channel formats from pre-3.3 versions"
599 bl_options = {'REGISTER', 'UNDO'}
601 @classmethod
602 def poll(self, context):
603 if (context.active_object is None
604 or context.active_object.type != 'MESH'
605 or context.active_object.data.animation_data is None
606 or context.active_object.data.animation_data.action is None):
607 return False
608 for fcurve in context.active_object.data.animation_data.action.fcurves:
609 if fcurve.data_path.startswith("vertex_colors"):
610 return True
612 def execute(self, context):
613 for fcurve in context.active_object.data.animation_data.action.fcurves:
614 if fcurve.data_path.startswith("vertex_colors"):
615 fcurve.data_path = fcurve.data_path.replace("vertex_colors", "attributes")
616 return {'FINISHED'}
618 # Add-ons Preferences Update Panel
620 # Define Panel classes for updating
621 panels = [
622 VIEW3D_PT_animall
626 def update_panel(self, context):
627 message = "AnimAll: Updating Panel locations has failed"
628 try:
629 for panel in panels:
630 if "bl_rna" in panel.__dict__:
631 bpy.utils.unregister_class(panel)
633 for panel in panels:
634 panel.bl_category = context.preferences.addons[__name__].preferences.category
635 bpy.utils.register_class(panel)
637 except Exception as e:
638 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
639 pass
642 class AnimallAddonPreferences(AddonPreferences):
643 # this must match the addon name, use '__package__'
644 # when defining this in a submodule of a python package.
645 bl_idname = __name__
647 category: StringProperty(
648 name="Tab Category",
649 description="Choose a name for the category of the panel",
650 default="Animation",
651 update=update_panel
654 def draw(self, context):
655 layout = self.layout
656 row = layout.row()
657 col = row.column()
659 col.label(text="Tab Category:")
660 col.prop(self, "category", text="")
662 register_classes, unregister_classes = bpy.utils.register_classes_factory(
663 (AnimallProperties, VIEW3D_PT_animall, ANIM_OT_insert_keyframe_animall,
664 ANIM_OT_delete_keyframe_animall, ANIM_OT_clear_animation_animall,
665 ANIM_OT_update_vertex_color_animation_animall, AnimallAddonPreferences))
667 def register():
668 register_classes()
669 bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
670 update_panel(None, bpy.context)
671 bpy.app.translations.register(__name__, translations.translations_dict)
673 def unregister():
674 bpy.app.translations.unregister(__name__)
675 del bpy.types.Scene.animall_properties
676 unregister_classes()
678 if __name__ == "__main__":
679 register()