forked from bartvdbraak/blender
utility function for updating animation system values when RNA changes.
- any numnber of attributes are supported for renaming, eg: modifiers["Foo.Bar"].prop.bar - the path is resolved in blender so each attribute is type checked against the naming list. - inherited properties are supported by recursively checking parent classes names aganst the name list. - fcurves and drivers are currently supported.
This commit is contained in:
parent
cdd62d6150
commit
5e53012558
183
release/scripts/modules/animsys_refactor.py
Normal file
183
release/scripts/modules/animsys_refactor.py
Normal file
@ -0,0 +1,183 @@
|
||||
# ##### 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 LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
"""
|
||||
This module has utility functions for renaming
|
||||
rna values in fcurves and drivers.
|
||||
|
||||
The main function to use is: update_data_paths(...)
|
||||
"""
|
||||
|
||||
IS_TESTING = False
|
||||
|
||||
class DataPathBuilder(object):
|
||||
__slots__ = ("data_path", )
|
||||
""" Dummy class used to parse fcurve and driver data paths.
|
||||
"""
|
||||
def __init__(self, attrs):
|
||||
self.data_path = attrs
|
||||
|
||||
def __getattr__(self, attr):
|
||||
str_value = ".%s" % attr
|
||||
return DataPathBuilder(self.data_path + (str_value, ))
|
||||
|
||||
def __getitem__(self, key):
|
||||
str_value = '["%s"]' % key
|
||||
return DataPathBuilder(self.data_path + (str_value, ))
|
||||
|
||||
def resolve(self, real_base):
|
||||
""" Return (attribute, value) pairs.
|
||||
"""
|
||||
pairs = []
|
||||
base = real_base
|
||||
for item in self.data_path:
|
||||
if base is not Ellipsis:
|
||||
try:
|
||||
base = eval("base" + item)
|
||||
except:
|
||||
print("Failed to resolve data path:", self.data_path)
|
||||
base = Ellipsis
|
||||
|
||||
pairs.append((item, base))
|
||||
return pairs
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def id_iter():
|
||||
type_iter = type(bpy.data.objects)
|
||||
|
||||
for attr in dir(bpy.data):
|
||||
data_iter = getattr(bpy.data, attr, None)
|
||||
if type(data_iter) == type_iter:
|
||||
for id_data in data_iter:
|
||||
if id_data.library is None:
|
||||
yield id_data
|
||||
|
||||
|
||||
def anim_data_actions(anim_data):
|
||||
actions = []
|
||||
actions.append(anim_data.action)
|
||||
for track in anim_data.nla_tracks:
|
||||
for strip in track.strips:
|
||||
actions.append(strip.action)
|
||||
|
||||
# filter out None
|
||||
return [act for act in actions if act]
|
||||
|
||||
|
||||
def classes_recursive(base_type, clss=None):
|
||||
if clss is None:
|
||||
clss = [base_type]
|
||||
else:
|
||||
clss.append(base_type)
|
||||
|
||||
for base_type_iter in base_type.__bases__:
|
||||
if base_type_iter is not object:
|
||||
classes_recursive(base_type_iter, clss)
|
||||
|
||||
return clss
|
||||
|
||||
|
||||
def find_path_new(id_data, data_path, rna_update_dict):
|
||||
# ignore ID props for now
|
||||
if data_path.startswith("["):
|
||||
return data_path
|
||||
|
||||
# recursive path fixing, likely will be one in most cases.
|
||||
data_path_builder = eval("DataPathBuilder(tuple())." + data_path)
|
||||
data_resolve = data_path_builder.resolve(id_data)
|
||||
|
||||
path_new = [pair[0] for pair in data_resolve]
|
||||
|
||||
# print(data_resolve)
|
||||
data_base = id_data
|
||||
|
||||
for i, (attr, data) in enumerate(data_resolve):
|
||||
if data is Ellipsis:
|
||||
break
|
||||
|
||||
if attr.startswith("."):
|
||||
# try all classes
|
||||
for data_base_type in classes_recursive(type(data_base)):
|
||||
attr_new = rna_update_dict.get(data_base_type.__name__, {}).get(attr[1:])
|
||||
if attr_new:
|
||||
path_new[i] = "." + attr_new
|
||||
|
||||
# set this as the base for further properties
|
||||
data_base = data
|
||||
|
||||
data_path_new = "".join(path_new)[1:] # skip the first "."
|
||||
return data_path_new
|
||||
|
||||
|
||||
def update_data_paths(rna_update):
|
||||
''' rna_update triple [(class_name, from, to), ...]
|
||||
'''
|
||||
|
||||
# make a faster lookup dict
|
||||
rna_update_dict = {}
|
||||
for ren_class, ren_from, ren_to in rna_update:
|
||||
rna_update_dict.setdefault(ren_class, {})[ren_from] = ren_to
|
||||
|
||||
for id_data in id_iter():
|
||||
anim_data = getattr(id_data, "animation_data", None)
|
||||
if anim_data is None:
|
||||
continue
|
||||
|
||||
for fcurve in anim_data.drivers:
|
||||
for var in fcurve.driver.variables:
|
||||
if var.type == 'SINGLE_PROP':
|
||||
for tar in var.targets:
|
||||
id_data_other = tar.id
|
||||
data_path = tar.data_path
|
||||
|
||||
if id_data_other and data_path:
|
||||
data_path_new = find_path_new(id_data_other, data_path, rna_update_dict)
|
||||
# print(data_path_new)
|
||||
if data_path_new != data_path:
|
||||
if not IS_TESTING:
|
||||
tar.data_path = data_path_new
|
||||
print("driver (%s): %s -> %s" % (id_data_other.name, data_path, data_path_new))
|
||||
|
||||
|
||||
|
||||
for action in anim_data_actions(anim_data):
|
||||
for fcu in action.fcurves:
|
||||
data_path = fcu.data_path
|
||||
data_path_new = find_path_new(id_data, data_path, rna_update_dict)
|
||||
# print(data_path_new)
|
||||
if data_path_new != data_path:
|
||||
if not IS_TESTING:
|
||||
fcu.data_path = data_path_new
|
||||
print("fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Example, should be called externally
|
||||
# (class, from, to)
|
||||
replace_ls = [
|
||||
('AnimVizMotionPaths', 'after_current', 'frame_after'),
|
||||
('AnimVizMotionPaths', 'before_current', 'frame_before'),
|
||||
('AnimVizOnionSkinning', 'after_current', 'frame_after'),
|
||||
]
|
||||
|
||||
update_data_paths(replace_ls)
|
@ -150,7 +150,7 @@ static void graph_panel_view(const bContext *C, Panel *pa)
|
||||
uiItemR(row, &sceneptr, "frame_current", 0, "Cursor X", 0);
|
||||
uiItemEnumO(row, "GRAPH_OT_snap", "To Keys", 0, "type", GRAPHKEYS_SNAP_CFRA);
|
||||
row= uiLayoutSplit(subcol, 0.7, 1);
|
||||
uiItemR(row, &spaceptr, "cursor_value", 0, "Cursor Y", 0);
|
||||
uiItemR(row, &spaceptr, "cursor_position_y", 0, "Cursor Y", 0);
|
||||
uiItemEnumO(row, "GRAPH_OT_snap", "To Keys", 0, "type", GRAPHKEYS_SNAP_VALUE);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user