AnimAll: extend the update function to account for new attribute API
[blender-addons.git] / animation_animall / __init__.py
blob09d4f155a4ac9b2caafe9ecd2a81230df4c02480
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, 10, 0),
9 "blender": (4, 0, 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
25 import re
27 # Property Definitions
28 class AnimallProperties(bpy.types.PropertyGroup):
29 key_selected: BoolProperty(
30 name="Key Selected Only",
31 description="Insert keyframes only on selected elements",
32 default=False)
34 # Generic attributes
35 key_point_location: BoolProperty(
36 name="Location",
37 description="Insert keyframes on point locations",
38 default=False)
39 key_shape_key: BoolProperty(
40 name="Shape Key",
41 description="Insert keyframes on active Shape Key layer",
42 default=False)
43 key_material_index: BoolProperty(
44 name="Material Index",
45 description="Insert keyframes on face material indices",
46 default=False)
48 # Mesh attributes
49 key_vertex_bevel: BoolProperty(
50 name="Vertex Bevel",
51 description="Insert keyframes on vertex bevel weight",
52 default=False)
54 key_vertex_crease: BoolProperty(
55 name="Vertex Crease",
56 description="Insert keyframes on vertex crease weight",
57 default=False)
59 key_vertex_group: BoolProperty(
60 name="Vertex Group",
61 description="Insert keyframes on active vertex group values",
62 default=False)
64 key_edge_bevel: BoolProperty(
65 name="Edge Bevel",
66 description="Insert keyframes on edge bevel weight",
67 default=False)
68 key_edge_crease: BoolProperty(
69 name="Edge Crease",
70 description="Insert keyframes on edge creases",
71 default=False)
73 key_active_attribute: BoolProperty(
74 name="Active Attribute",
75 description="Insert keyframes on active attribute values",
76 default=False)
77 key_uvs: BoolProperty(
78 name="UV Map",
79 description="Insert keyframes on active UV coordinates",
80 default=False)
82 # Curve and surface attributes
83 key_radius: BoolProperty(
84 name="Radius",
85 description="Insert keyframes on point radius (Shrink/Fatten)",
86 default=False)
87 key_tilt: BoolProperty(
88 name="Tilt",
89 description="Insert keyframes on point tilt",
90 default=False)
93 # Utility functions
95 def refresh_ui_keyframes():
96 try:
97 for area in bpy.context.screen.areas:
98 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
99 area.tag_redraw()
100 except:
101 pass
104 def insert_key(data, key, group=None):
105 try:
106 if group is not None:
107 data.keyframe_insert(key, group=group)
108 else:
109 data.keyframe_insert(key)
110 except:
111 pass
114 def delete_key(data, key):
115 try:
116 data.keyframe_delete(key)
117 except:
118 pass
121 def get_attribute(data, name, type=None, domain=None):
122 if name in data.attributes:
123 return data.attributes[name]
124 if type is not None and domain is not None:
125 return data.attributes.new(name, type, domain)
128 def get_attribute_paths(data, attribute, key_selected):
129 # Cannot animate string attributes?
130 if attribute.data_type == 'STRING':
131 yield ("", "")
133 if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}:
134 attribute_key = "value"
135 elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}:
136 attribute_key = "color"
137 elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}:
138 attribute_key = "vector"
140 if attribute.domain == 'POINT':
141 group = data_("Vertex %s")
142 elif attribute.domain == 'EDGE':
143 group = data_("Edge %s")
144 elif attribute.domain == 'FACE':
145 group = data_("Face %s")
146 elif attribute.domain == 'CORNER':
147 group = data_("Loop %s")
149 for e_i, _attribute_data in enumerate(attribute.data):
150 if (not key_selected
151 or attribute.domain == 'POINT' and data.vertices[e_i].select
152 or attribute.domain == 'EDGE' and data.edges[e_i].select
153 or attribute.domain == 'FACE' and data.polygons[e_i].select
154 or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)):
155 yield (f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}', group % e_i)
158 def insert_attribute_key(data, attribute, key_selected):
159 for path, group in get_attribute_paths(data, attribute, key_selected):
160 if path:
161 insert_key(data, path, group=group)
164 def delete_attribute_key(data, attribute, key_selected):
165 for path, group in get_attribute_paths(data, attribute, key_selected):
166 if path:
167 delete_key(data, path)
170 def is_selected_vert_loop(data, loop_i):
171 """Get selection status of vertex corresponding to a loop"""
172 vertex_index = data.loops[loop_i].vertex_index
173 return data.vertices[vertex_index].select
176 # GUI (Panel)
178 class VIEW3D_PT_animall(Panel):
179 bl_space_type = 'VIEW_3D'
180 bl_region_type = 'UI'
181 bl_category = "Animation"
182 bl_label = ''
184 @classmethod
185 def poll(self, context):
186 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
188 def draw_header(self, context):
190 layout = self.layout
191 row = layout.row()
192 row.label (text = 'AnimAll', icon = 'ARMATURE_DATA')
194 def draw(self, context):
195 obj = context.active_object
196 animall_properties = context.scene.animall_properties
198 layout = self.layout
200 layout.label(text='Key:')
202 layout.use_property_split = True
203 layout.use_property_decorate = False
205 if obj.type == 'LATTICE':
206 col = layout.column(heading="Points", align=True)
207 col.prop(animall_properties, "key_point_location")
209 col = layout.column(heading="Others", align=True)
210 col.prop(animall_properties, "key_shape_key")
212 elif obj.type == 'MESH':
213 col = layout.column(heading="Points", align=True)
214 col.prop(animall_properties, "key_point_location")
215 col.prop(animall_properties, "key_vertex_bevel", text="Bevel")
216 col.prop(animall_properties, "key_vertex_crease", text="Crease")
217 col.prop(animall_properties, "key_vertex_group")
219 col = layout.column(heading="Edges", align=True)
220 col.prop(animall_properties, "key_edge_bevel", text="Bevel")
221 col.prop(animall_properties, "key_edge_crease", text="Crease")
223 col = layout.column(heading="Faces", align=True)
224 col.prop(animall_properties, "key_material_index")
226 col = layout.column(heading="Others", align=True)
227 col.prop(animall_properties, "key_active_attribute")
228 col.prop(animall_properties, "key_uvs")
229 col.prop(animall_properties, "key_shape_key")
231 # Vertex group update operator
232 if (obj.data.animation_data is not None
233 and obj.data.animation_data.action is not None):
234 for fcurve in context.active_object.data.animation_data.action.fcurves:
235 if bpy.ops.anim.update_attribute_animation_animall.poll():
236 col = layout.column(align=True)
237 col.label(text="Object includes old-style attributes. Consider updating them.", icon="ERROR")
238 col.operator("anim.update_attribute_animation_animall", icon="FILE_REFRESH")
239 break
241 elif obj.type in {'CURVE', 'SURFACE'}:
242 col = layout.column(align=True)
243 col = layout.column(heading="Points", align=True)
244 col.prop(animall_properties, "key_point_location")
245 col.prop(animall_properties, "key_radius")
246 col.prop(animall_properties, "key_tilt")
248 col = layout.column(heading="Splines", align=True)
249 col.prop(animall_properties, "key_material_index")
251 col = layout.column(heading="Others", align=True)
252 col.prop(animall_properties, "key_shape_key")
254 if animall_properties.key_shape_key:
255 shape_key = obj.active_shape_key
256 shape_key_index = obj.active_shape_key_index
258 if shape_key_index > 0:
259 col = layout.column(align=True)
260 row = col.row(align=True)
261 row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA")
262 row.prop(obj, "show_only_shape_key", text="")
263 if shape_key.value < 1:
264 col.label(text=iface_('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO")
265 elif shape_key is not None:
266 col = layout.column(align=True)
267 col.label(text="Cannot key on Basis Shape", icon="ERROR")
268 else:
269 col = layout.column(align=True)
270 col.label(text="No active Shape Key", icon="ERROR")
272 if animall_properties.key_point_location:
273 col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO")
275 layout.use_property_split = False
276 layout.separator()
277 row = layout.row()
278 row.prop(animall_properties, "key_selected")
280 row = layout.row(align=True)
281 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
282 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
283 row = layout.row()
284 row.operator("anim.clear_animation_animall", icon="CANCEL")
287 class ANIM_OT_insert_keyframe_animall(Operator):
288 bl_label = "Insert Key"
289 bl_idname = "anim.insert_keyframe_animall"
290 bl_description = "Insert a Keyframe"
291 bl_options = {'REGISTER', 'UNDO'}
293 def execute(self, context):
294 animall_properties = context.scene.animall_properties
296 if context.mode == 'OBJECT':
297 objects = context.selected_objects
298 else:
299 objects = context.objects_in_mode_unique_data[:]
301 mode = context.object.mode
303 # Separate loop for lattices, curves and surfaces, since keyframe insertion
304 # has to happen in Edit Mode, otherwise points move back upon mode switch...
305 # (except for curve shape keys)
306 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
307 data = obj.data
309 if obj.type == 'LATTICE':
310 if animall_properties.key_shape_key:
311 if obj.active_shape_key_index > 0:
312 sk_name = obj.active_shape_key.name
313 for p_i, point in enumerate(obj.active_shape_key.data):
314 if not animall_properties.key_selected or data.points[p_i].select:
315 insert_key(point, 'co', group=data_("%s Point %s") % (sk_name, p_i))
317 if animall_properties.key_point_location:
318 for p_i, point in enumerate(data.points):
319 if not animall_properties.key_selected or point.select:
320 insert_key(point, 'co_deform', group=data_("Point %s") % p_i)
322 else:
323 if animall_properties.key_material_index:
324 for s_i, spline in enumerate(data.splines):
325 if (not animall_properties.key_selected
326 or any(point.select for point in spline.points)
327 or any(point.select_control_point for point in spline.bezier_points)):
328 insert_key(spline, 'material_index', group=data_("Spline %s") % s_i)
330 for s_i, spline in enumerate(data.splines):
331 if spline.type == 'BEZIER':
332 for v_i, CV in enumerate(spline.bezier_points):
333 if (not animall_properties.key_selected
334 or CV.select_control_point
335 or CV.select_left_handle
336 or CV.select_right_handle):
337 if animall_properties.key_point_location:
338 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
339 insert_key(CV, 'handle_left', group=data_("Spline %s CV %s") % (s_i, v_i))
340 insert_key(CV, 'handle_right', group=data_("Spline %s CV %s") % (s_i, v_i))
342 if animall_properties.key_radius:
343 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
345 if animall_properties.key_tilt:
346 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
348 elif spline.type in ('POLY', 'NURBS'):
349 for v_i, CV in enumerate(spline.points):
350 if not animall_properties.key_selected or CV.select:
351 if animall_properties.key_point_location:
352 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
354 if animall_properties.key_radius:
355 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
357 if animall_properties.key_tilt:
358 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
360 bpy.ops.object.mode_set(mode='OBJECT')
362 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
363 data = obj.data
364 if obj.type == 'MESH':
365 if animall_properties.key_point_location:
366 for v_i, vert in enumerate(data.vertices):
367 if not animall_properties.key_selected or vert.select:
368 insert_key(vert, 'co', group=data_("Vertex %s") % v_i)
370 if animall_properties.key_vertex_bevel:
371 attribute = get_attribute(data, "bevel_weight_vert", 'FLOAT', 'POINT')
372 insert_attribute_key(data, attribute, animall_properties.key_selected)
374 if animall_properties.key_vertex_crease:
375 attribute = get_attribute(data, "crease_vert", 'FLOAT', 'POINT')
376 insert_attribute_key(data, attribute, animall_properties.key_selected)
378 if animall_properties.key_vertex_group:
379 for v_i, vert in enumerate(data.vertices):
380 if not animall_properties.key_selected or vert.select:
381 for group in vert.groups:
382 insert_key(group, 'weight', group=data_("Vertex %s") % v_i)
384 if animall_properties.key_edge_bevel:
385 attribute = get_attribute(data, "bevel_weight_edge", 'FLOAT', 'EDGE')
386 insert_attribute_key(data, attribute, animall_properties.key_selected)
388 if animall_properties.key_edge_crease:
389 attribute = get_attribute(data, "crease_edge", 'FLOAT', 'EDGE')
390 insert_attribute_key(data, attribute, animall_properties.key_selected)
392 if animall_properties.key_material_index:
393 for p_i, polygon in enumerate(data.polygons):
394 if not animall_properties.key_selected or polygon.select:
395 insert_key(polygon, 'material_index', group=data_("Face %s") % p_i)
397 if animall_properties.key_active_attribute:
398 if data.attributes.active is not None:
399 for path, group in get_attribute_paths(
400 data, data.attributes.active,
401 animall_properties.key_selected):
402 if path:
403 insert_key(data, path, group=group)
405 if animall_properties.key_uvs:
406 if data.uv_layers.active is not None:
407 for uv_i, uv in enumerate(data.uv_layers.active.data):
408 if not animall_properties.key_selected or uv.select:
409 insert_key(uv, 'uv', group=data_("UV layer %s") % uv_i)
411 if animall_properties.key_shape_key:
412 if obj.active_shape_key_index > 0:
413 sk_name = obj.active_shape_key.name
414 for v_i, vert in enumerate(obj.active_shape_key.data):
415 if not animall_properties.key_selected or data.vertices[v_i].select:
416 insert_key(vert, 'co', group=data_("%s Vertex %s") % (sk_name, v_i))
418 elif obj.type in {'CURVE', 'SURFACE'}:
419 # Shape key keys have to be inserted in object mode for curves...
420 if animall_properties.key_shape_key:
421 sk_name = obj.active_shape_key.name
422 global_spline_index = 0 # numbering for shape keys, which have flattened indices
423 for s_i, spline in enumerate(data.splines):
424 if spline.type == 'BEZIER':
425 for v_i, CV in enumerate(spline.bezier_points):
426 if (not animall_properties.key_selected
427 or CV.select_control_point
428 or CV.select_left_handle
429 or CV.select_right_handle):
430 if obj.active_shape_key_index > 0:
431 CV = obj.active_shape_key.data[global_spline_index]
432 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
433 insert_key(CV, 'handle_left', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
434 insert_key(CV, 'handle_right', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
435 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
436 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
437 global_spline_index += 1
439 elif spline.type in ('POLY', 'NURBS'):
440 for v_i, CV in enumerate(spline.points):
441 if not animall_properties.key_selected or CV.select:
442 if obj.active_shape_key_index > 0:
443 CV = obj.active_shape_key.data[global_spline_index]
444 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
445 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
446 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
447 global_spline_index += 1
449 bpy.ops.object.mode_set(mode=mode)
450 refresh_ui_keyframes()
452 return {'FINISHED'}
455 class ANIM_OT_delete_keyframe_animall(Operator):
456 bl_label = "Delete Key"
457 bl_idname = "anim.delete_keyframe_animall"
458 bl_description = "Delete a Keyframe"
459 bl_options = {'REGISTER', 'UNDO'}
461 def execute(self, context):
462 animall_properties = context.scene.animall_properties
464 if context.mode == 'OBJECT':
465 objects = context.selected_objects
466 else:
467 objects = context.objects_in_mode_unique_data[:]
469 mode = context.object.mode
471 for obj in objects:
472 data = obj.data
473 if obj.type == 'MESH':
474 bpy.ops.object.mode_set(mode='OBJECT')
476 if animall_properties.key_point_location:
477 for vert in data.vertices:
478 if not animall_properties.key_selected or vert.select:
479 delete_key(vert, 'co')
481 if animall_properties.key_vertex_bevel:
482 attribute = get_attribute(data, "bevel_weight_vert", 'FLOAT', 'POINT')
483 if attribute is not None:
484 delete_attribute_key(data, attribute, animall_properties.key_selected)
486 if animall_properties.key_vertex_crease:
487 attribute = get_attribute(data, "crease_vert", 'FLOAT', 'POINT')
488 if attribute is not None:
489 delete_attribute_key(data, attribute, animall_properties.key_selected)
491 if animall_properties.key_vertex_group:
492 for vert in data.vertices:
493 if not animall_properties.key_selected or vert.select:
494 for group in vert.groups:
495 delete_key(group, 'weight')
497 if animall_properties.key_edge_bevel:
498 attribute = get_attribute(data, "bevel_weight_edge", 'FLOAT', 'EDGE')
499 if attribute is not None:
500 delete_attribute_key(data, attribute, animall_properties.key_selected)
502 if animall_properties.key_edge_crease:
503 attribute = get_attribute(data, "crease_edge", 'FLOAT', 'EDGE')
504 if attribute is not None:
505 delete_attribute_key(data, attribute, animall_properties.key_selected)
507 if animall_properties.key_material_index:
508 for p_i, polygon in enumerate(data.polygons):
509 if not animall_properties.key_selected or polygon.select:
510 delete_key(polygon, 'material_index')
512 if animall_properties.key_shape_key:
513 if obj.active_shape_key:
514 for v_i, vert in enumerate(obj.active_shape_key.data):
515 if not animall_properties.key_selected or data.vertices[v_i].select:
516 delete_key(vert, 'co')
518 if animall_properties.key_uvs:
519 if data.uv_layers.active is not None:
520 for uv in data.uv_layers.active.data:
521 if not animall_properties.key_selected or uv.select:
522 delete_key(uv, 'uv')
524 if animall_properties.key_active_attribute:
525 if data.attributes.active is not None:
526 for path, _group in get_attribute_paths(
527 data, data.attributes.active,
528 animall_properties.key_selected):
529 if path:
530 delete_key(data, path)
532 bpy.ops.object.mode_set(mode=mode)
534 elif obj.type == 'LATTICE':
535 if animall_properties.key_shape_key:
536 if obj.active_shape_key:
537 for point in obj.active_shape_key.data:
538 delete_key(point, 'co')
540 if animall_properties.key_point_location:
541 for point in data.points:
542 if not animall_properties.key_selected or point.select:
543 delete_key(point, 'co_deform')
545 elif obj.type in {'CURVE', 'SURFACE'}:
546 # Run this outside the splines loop (only once)
547 if animall_properties.key_shape_key:
548 if obj.active_shape_key_index > 0:
549 for CV in obj.active_shape_key.data:
550 delete_key(CV, 'co')
551 delete_key(CV, 'handle_left')
552 delete_key(CV, 'handle_right')
554 for spline in data.splines:
555 if spline.type == 'BEZIER':
556 for CV in spline.bezier_points:
557 if (not animall_properties.key_selected
558 or CV.select_control_point
559 or CV.select_left_handle
560 or CV.select_right_handle):
561 if animall_properties.key_point_location:
562 delete_key(CV, 'co')
563 delete_key(CV, 'handle_left')
564 delete_key(CV, 'handle_right')
565 if animall_properties.key_radius:
566 delete_key(CV, 'radius')
567 if animall_properties.key_tilt:
568 delete_key(CV, 'tilt')
570 elif spline.type in ('POLY', 'NURBS'):
571 for CV in spline.points:
572 if not animall_properties.key_selected or CV.select:
573 if animall_properties.key_point_location:
574 delete_key(CV, 'co')
575 if animall_properties.key_radius:
576 delete_key(CV, 'radius')
577 if animall_properties.key_tilt:
578 delete_key(CV, 'tilt')
580 refresh_ui_keyframes()
582 return {'FINISHED'}
585 class ANIM_OT_clear_animation_animall(Operator):
586 bl_label = "Clear Animation"
587 bl_idname = "anim.clear_animation_animall"
588 bl_description = ("Delete all keyframes for this object\n"
589 "If in a specific case it doesn't work\n"
590 "try to delete the keys manually")
591 bl_options = {'REGISTER', 'UNDO'}
593 def invoke(self, context, event):
594 wm = context.window_manager
595 return wm.invoke_confirm(self, event)
597 def execute(self, context):
598 if context.mode == 'OBJECT':
599 objects = context.selected_objects
600 else:
601 objects = context.objects_in_mode_unique_data
603 for obj in objects:
604 try:
605 data = obj.data
606 data.animation_data_clear()
607 except:
608 self.report({'WARNING'}, "Clear Animation could not be performed")
609 return {'CANCELLED'}
611 refresh_ui_keyframes()
613 return {'FINISHED'}
616 class ANIM_OT_update_attribute_animation_animall(Operator):
617 bl_label = "Update Attribute Animation"
618 bl_idname = "anim.update_attribute_animation_animall"
619 bl_description = "Update attributes from the old format"
620 bl_options = {'REGISTER', 'UNDO'}
622 path_re = re.compile(r"^vertex_colors|(vertices|edges)\[([0-9]+)\]\.(bevel_weight|crease)")
623 attribute_map = {
624 ("vertices", "bevel_weight"): ("bevel_weight_vert", "FLOAT", "POINT"),
625 ("edges", "bevel_weight"): ("bevel_weight_edge", "FLOAT", "POINT"),
626 ("vertices", "crease"): ("crease_vert", "FLOAT", "EDGE"),
627 ("edges", "crease"): ("crease_edge", "FLOAT", "EDGE"),
630 @classmethod
631 def poll(self, context):
632 if (context.active_object is None
633 or context.active_object.type != 'MESH'
634 or context.active_object.data.animation_data is None
635 or context.active_object.data.animation_data.action is None):
636 return False
637 for fcurve in context.active_object.data.animation_data.action.fcurves:
638 if self.path_re.match(fcurve.data_path):
639 return True
641 def execute(self, context):
642 for fcurve in context.active_object.data.animation_data.action.fcurves:
643 if fcurve.data_path.startswith("vertex_colors"):
644 # Update pre-3.3 vertex colors
645 fcurve.data_path = fcurve.data_path.replace("vertex_colors", "attributes")
646 else:
647 # Update pre-4.0 attributes
648 match = self.path_re.match(fcurve.data_path)
649 if match is None:
650 continue
651 domain, index, src_attribute = match.groups()
652 attribute, type, domain = self.attribute_map[(domain, src_attribute)]
653 get_attribute(context.active_object.data, attribute, type, domain)
654 fcurve.data_path = f'attributes["{attribute}"].data[{index}].value'
655 return {'FINISHED'}
657 # Add-ons Preferences Update Panel
659 # Define Panel classes for updating
660 panels = [VIEW3D_PT_animall]
663 def update_panel(self, context):
664 message = "AnimAll: Updating Panel locations has failed"
665 try:
666 for panel in panels:
667 if "bl_rna" in panel.__dict__:
668 bpy.utils.unregister_class(panel)
670 for panel in panels:
671 panel.bl_category = context.preferences.addons[__name__].preferences.category
672 bpy.utils.register_class(panel)
674 except Exception as e:
675 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
676 pass
679 class AnimallAddonPreferences(AddonPreferences):
680 # this must match the addon name, use '__package__'
681 # when defining this in a submodule of a python package.
682 bl_idname = __name__
684 category: StringProperty(
685 name="Tab Category",
686 description="Choose a name for the category of the panel",
687 default="Animation",
688 update=update_panel
691 def draw(self, context):
692 layout = self.layout
693 row = layout.row()
694 col = row.column()
696 col.label(text="Tab Category:")
697 col.prop(self, "category", text="")
699 register_classes, unregister_classes = bpy.utils.register_classes_factory(
700 (AnimallProperties, VIEW3D_PT_animall, ANIM_OT_insert_keyframe_animall,
701 ANIM_OT_delete_keyframe_animall, ANIM_OT_clear_animation_animall,
702 ANIM_OT_update_attribute_animation_animall, AnimallAddonPreferences))
704 def register():
705 register_classes()
706 bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
707 update_panel(None, bpy.context)
708 bpy.app.translations.register(__name__, translations.translations_dict)
710 def unregister():
711 bpy.app.translations.unregister(__name__)
712 del bpy.types.Scene.animall_properties
713 unregister_classes()
715 if __name__ == "__main__":
716 register()