[Addon] replace driver targets

(dirty-stick) #1

This addon is used to replace driver variable target ids.

http://wiki.blender.org/uploads/f/f2/Addon_RDT13.PNG

For example, if you want to replace the armature a mesh uses, this can be used to replace driver target ids the mesh uses, Armature to Armature.001, or any other target id types.

Download: http://wiki.blender.org/uploads/9/95/Addon_RDT_13.zip

#2

For a long time I have been wishing something like this! :):cool:

Although it’s in a wip state, it’s already very useful when you have to reassign driver variables to many shapekeys all at a time, since it can be a really painful and time consuming task.

I tested it with object IDs and it works very well (replaced 197 driver targets in a few seconds).

Thank you very much, dirty-stick!!

paolo

#3

I just realized that the addon works over the whole scene, unlike the early drafts of it that you posted on my thread which operated on the active object.
I may be wrong but sometimes it could be an unwanted behavior, and if you are not aware of it it can be source of problems, maybe you could set it as an option, or make it work only on the selected objects or the active one.

paolo

(dirty-stick) #4

K. Ill probably add an option.

#5

Great!
Actually an option would have the further advantage to be self explanatory, making clearer on which objects the addon operates.

Thank you,
paolo

(dirty-stick) #6

Thanks for testing, I added the option.
The addon should be kind of stable now.

#7

Hi dirty-stick,

you not only added the Active Object option, you even added all available Types of drivers, that’s awesome!

I don’t know your planning, but I could call it finished and AFAIK it’s now fully functional, I have tested only objects and keys for the moment and it works great.
Also the GUI seems to me good implemented and clear.

I would really like that this addon can have the deserved visibility and appreciation, because I’m sure that sooner or later many users will have to face certain situations, and if unaware of its existence and its practicality, will suffer and curse a lot.

Thank you very, very much!

paolo

#8

It was a while since last time I had to fix lots of drivers all at once over an appended figure, and this addon proved to be very useful once again.

I want to say thanks a lot dirty-stick, for this invaluable tool.:cool:

EDIT: 2.74.4 now, it works flawlessly.

paolo

(JuhaW) #9

This is usefull. Why not ‘E’’ key for picking ‘replace’ and ‘with’ objects do not work as usually ?

(Tokamak) #10

@dirty-stick I updated this script for 2.8. Would you mind me posting it here?

(draguu) #11

please do… it would be really helpful

(Tokamak) #12

Well, I haven’t gotten a response, and it is GLP code, so I may as well post it. I hope that’s within social norms here.

All I did was update it for 2.80, so any existing bugs should still be there, and I don’t think I created any new ones. While I’m not taking on the mantle of properly supporting it or anything, if I did mess up the update somehow, please tell me.

I’ve been using this new version in my workflow, and haven’t found anything wrong with it yet, at least.

# ##### 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.
#
#  Code by Koilz @ http://wiki.blender.org/ 2014
#  Edited slightly for compatibility with Blender 2.80 by Stellarator 2019 
#
# ##### END GPL LICENSE BLOCK #####

# Key Error for red driver target id's.

# ----------------------------------------------------------------
# Header

bl_info = {
    "name": "Replace Driver Targets",
    "author": "koilz",
    "version": (1, 4),
    "blender": (2, 80, 0),
    "location": "3D View > Object Mode > Tools > RDT",
    "description": "Replace driver variable target id's.",
    "warning": "",
    "wiki_url": "http://blenderartists.org/forum/showthread.php?356354-wip-Addon-replace-driver-targets",
    "category": "Rigging"}

import bpy

DD = ['actions',
      'armatures',
      'brushes',
      'cameras',
      'curves',
      'fonts',
      'grease_pencils',
      'groups',
      'images',
      'shape_keys',
      'lamps',
      'libraries',
      'linestyles',
      'lattices',
      'materials',
      'metaballs',
      'meshes',
      'node_groups',
      'objects',
      'particles',
      'scenes',
      'screens',
      'speakers',
      'sounds',
      'texts',
      'textures',
      'worlds',
      'window_managers']

IDT = [('ACTION', 'Action', 'Action', 'ACTION', 0),
       ('ARMATURE', 'Armature', 'Armature', 'ARMATURE_DATA', 1),
       ('BRUSH', 'Brush', 'Brush', 'BRUSH_DATA', 2),
       ('CAMERA', 'Camera', 'Camera', 'CAMERA_DATA', 3),
       ('CURVE', 'Curve', 'Curve', 'CURVE_DATA', 4),
       ('FONT', 'Font', 'Font', 'FILE_FONT', 5),
       ('GREASEPENCIL', 'Grease Pencil', 'Grease Pencil', 'GREASEPENCIL', 6),
       ('GROUP', 'Group', 'Group', 'GROUP', 7),
       ('IMAGE', 'Image', 'Image', 'FILE_IMAGE', 8),
       ('KEY', 'Key', 'Key', 'SHAPEKEY_DATA', 9),
       ('LAMP', 'Lamp', 'Lamp', 'LAMP_DATA', 10),
       ('LIBRARY', 'Library', 'Library', 'LIBRARY_DATA_DIRECT', 11),
       ('LINESTYLE', 'Line Style', 'Line Style', 'LINE_DATA', 12),
       ('LATTICE', 'Lattice', 'Lattice', 'LATTICE_DATA', 13),
       ('MATERIAL', 'Material', 'Material', 'MATERIAL_DATA', 14),
       ('META', 'Meta', 'Meta', 'META_DATA', 15),
       ('MESH', 'Mesh', 'Mesh', 'MESH_DATA', 16),
       ('NODETREE', 'Node Tree', 'Node Tree', 'NODETREE', 17),
       ('OBJECT', 'Object', 'Object', 'OBJECT_DATA', 18),
       ('PARTICLE', 'Particle', 'Particle', 'PARTICLE_DATA', 19),
       ('SCENE', 'Scene', 'Scene', 'SCENE_DATA', 20),
       ('SCREEN', 'Screen', 'Screen', 'SPLITSCREEN', 21),
       ('SPEAKER', 'Speaker', 'Speaker', 'SPEAKER', 22),
       ('SOUND', 'Sound', 'Sound', 'SOUND', 23),
       ('TEXT', 'Text', 'Text', 'TEXT', 24),
       ('TEXTURE', 'Texture', 'Texture', 'TEXTURE_DATA', 25),
       ('WORLD', 'World', 'World', 'WORLD_DATA', 26),
       ('WINDOWMANAGER', 'Window Manager', 'Window Manager', 'DOT', 27)]

LMT = [('ASC', 'Active Scene', 'Active Scene'),
       ('AOB', 'Active Object', 'Active Object')]

# Driver Tools PropertyGroup
class DT_props(bpy.types.PropertyGroup):
    limit: bpy.props.EnumProperty(items=LMT, name="Limit", description="Limit operator to active Scene or Object.", default='ASC')
    didtr: bpy.props.EnumProperty(items=IDT, name="Driver Target ID Type", default='OBJECT')
    didr: bpy.props.StringProperty(name="Driver Target ID Replace", description="Driver Target ID Replace", default='')
    didw: bpy.props.StringProperty(name="Driver Target ID With", description="Driver Target ID With", default='')

# ----------------------------------------------------------------

# ----------------------------------------------------------------
# Replace Driver Targets

def op_rdt(self, context):

    OK = True

    # ----------------------------------------------------------------
    # animation data

    AD_dir = []
    if context.scene.DT.limit == 'ASC':                                # compile animation_data list based on active scene
        if hasattr(context.scene, 'animation_data'):                   # ad check
            AD_dir.append(context.scene.animation_data)                # ad scene
        for ob in context.scene.objects:                               # loop obs
            #print(ob)                                                 # debug
            if hasattr(ob, 'animation_data'):                          # ad check
                AD_dir.append(ob.animation_data)                       # ad object
            if hasattr(ob.data, 'animation_data'):                     # ad check
                AD_dir.append(ob.data.animation_data)                  # ad data
            if hasattr(ob.data, 'shape_keys'):                         # shape_keys check
                if hasattr(ob.data.shape_keys, 'animation_data'):      # ad check
                    AD_dir.append(ob.data.shape_keys.animation_data)   # ad shape_keys
            if hasattr(ob.data, 'materials'):                          # material check
                for m in ob.data.materials:                            # loop mats
                    if hasattr(m, 'animation_data'):                   # ad check
                        AD_dir.append(m.animation_data)                # ad material
    else:                                                          # compile animation_data list based on active object
        ob = context.active_object                                 # active_object
        if hasattr(ob, 'animation_data'):                          # ad check
            AD_dir.append(ob.animation_data)                       # ad object
        if hasattr(ob.data, 'animation_data'):                     # ad check
            AD_dir.append(ob.data.animation_data)                  # ad data
        if hasattr(ob.data, 'shape_keys'):                         # shape_keys check
            if hasattr(ob.data.shape_keys, 'animation_data'):      # ad check
                AD_dir.append(ob.data.shape_keys.animation_data)   # ad shape_keys
        if hasattr(ob.data, 'materials'):                          # material check
            for m in ob.data.materials:                            # loop mats
                if hasattr(m, 'animation_data'):                   # ad check
                    AD_dir.append(m.animation_data)                # ad material

    # ----------------------------------------------------------------

    # ----------------------------------------------------------------
    # set driver target id based on type

    didtr = context.scene.DT.didtr

    didr = None
    didw = None

    DDD = [bpy.data.actions,
           bpy.data.armatures,
           bpy.data.brushes,
           bpy.data.cameras,
           bpy.data.curves,
           bpy.data.fonts,
           bpy.data.grease_pencils,
           bpy.data.collections,
           bpy.data.images,
           bpy.data.shape_keys,
           bpy.data.lights,
           bpy.data.libraries,
           bpy.data.linestyles,
           bpy.data.lattices,
           bpy.data.materials,
           bpy.data.metaballs,
           bpy.data.meshes,
           bpy.data.node_groups,
           bpy.data.objects,
           bpy.data.particles,
           bpy.data.scenes,
           bpy.data.screens,
           bpy.data.speakers,
           bpy.data.sounds,
           bpy.data.texts,
           bpy.data.textures,
           bpy.data.worlds,
           bpy.data.window_managers]

    for i in range(28):
        if didtr == IDT[i][0]:
            if context.scene.DT.didr != '':
                didr = DDD[i][context.scene.DT.didr]
            if context.scene.DT.didw != '':
                didw = DDD[i][context.scene.DT.didw]

    # ----------------------------------------------------------------

    # ----------------------------------------------------------------
    # replace driver targets

    if OK:

        dvc = 1      # driver variable continue
        dvr = 0      # driver variable targets replaced
        dvs = 0      # driver variable targets counted

        if dvc:
            for ad in AD_dir:
                if hasattr(ad, 'drivers'):
                    for d in ad.drivers:
                        for dv in d.driver.variables:
                            for dt in dv.targets:
                                dvs+=1                                       # targets processed count
                                if dt.id_type == didtr and dt.id == didr:    # compare IDT and ID
                                    dt.id = didw                             # replace ID
                                    dvr+=1                                   # targets replaced count
                                    print("")
                                    print("Replaced Driver Target: "+str(d.id_data))
                                    print(str(d.data_path)+": "+str(d.array_index))

            print("")
            self.report({"INFO"},"Replaced "+str(dvr)+" of "+str(dvs)+" driver targets. See console for more info.")

    # ----------------------------------------------------------------

class OT_RDT(bpy.types.Operator):
    """Replace Driver Target ID's"""
    bl_idname = "scene.replace_driver_targets"
    bl_label = "Replace Driver Targets"
    bl_options = {'REGISTER', 'UNDO'}
    #bl_options = {'REGISTER', 'UNDO', 'PRESET'}

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        op_rdt(self, context)
        return {'FINISHED'}

# ----------------------------------------------------------------

# ----------------------------------------------------------------
# Panel

class PT_RDT(bpy.types.Panel):

    bl_label = "Replace Driver Targets"
    bl_idname = "VIEW3D_PT_RDT"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Tools"
    bl_context = "objectmode"

    @classmethod
    def poll(cls, context):
        return (context.active_object and (context.mode == 'OBJECT'))

    def draw(self, context):
        layout = self.layout

        # operator + settings
        row = layout.row(align=True)
        col = row.column(align=True)
        col.row(align=True).prop(context.scene.DT, 'limit', expand=True)
        col.operator('scene.replace_driver_targets')

        # id type
        row = layout.row(align=True)
        col = row.column(align=True)
        col.label(text='ID Type:')
        col.prop(context.scene.DT, 'didtr', text='')

        # replace
        row = layout.row(align=True)
        col = row.column(align=True)
        col.label(text='Replace:')
        for i in range(28):
            if context.scene.DT.didtr == IDT[i][0]:
                col.prop_search(context.scene.DT, 'didr', bpy.data, DD[i], text='')

        # with
        row = layout.row(align=True)
        col = row.column(align=True)
        col.label(text='With:')
        for i in range(28):
            if context.scene.DT.didtr == IDT[i][0]:
                col.prop_search(context.scene.DT, 'didw', bpy.data, DD[i], text='')

# ----------------------------------------------------------------

# ----------------------------------------------------------------
# Register


classes = (
    DT_props,
    OT_RDT,
    PT_RDT
)


def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
    bpy.types.Scene.DT = bpy.props.PointerProperty(type=DT_props)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.DT
#def register():
#    bpy.utils.register_class(DT_props)
#    bpy.types.Scene.DT = bpy.props.PointerProperty(type=DT_props)
#    bpy.utils.register_class(OT_RDT)
#    bpy.utils.register_class(PT_RDT)

#def unregister():
#    bpy.utils.unregister_class(PT_RDT)
#    bpy.utils.unregister_class(OT_RDT)
#    del bpy.types.Scene.DT
#    bpy.utils.unregister_class(DT_props)

if __name__ == "__main__":
    register()

# ----------------------------------------------------------------