Blender
评价数不足
Mute shape keys automatically
由 _MaZ_TeR_ 制作
With this, your shape keys will automatically mute when they are not in use. However, they require custom properties to drive the main value of the shape keys, which the first part of the script will do. You can then use the custom properties to adjust the shape keys.

If you use a UI manager, you can add those custom properties there and adjust the shape keys far more easily, especially if that UI allows you to categorize your properties.

Credits to ChatGPT and MS Co-Pilot. I'm not sure if they stole I mean copied the scripts from somewhere else, but either way, made this thread so I can find it more easily in the future, since they finally fixed ChatGPT without it giving some total bs answers and the code actually works now.
   
奖励
收藏
已收藏
取消收藏
This script will create, from a custom range (currently set to index 97-199), custom properties with the exact same name as the shape keys within that range, to the Object Properties tab.
import bpy # Function to add custom properties to the object properties tab def add_custom_properties(shape_key_name, default_value, min_value, max_value): # Get the selected object obj = bpy.context.active_object if obj is None: print("No active object selected.") return # Helper function to add a custom property with min and max def add_property(obj, name, value, min_value=None, max_value=None): if name not in obj: # Add the property if it doesn't already exist obj[name] = value prop = obj.id_properties_ui(name) if min_value is not None and max_value is not None: prop.update(min=min_value, max=max_value, default=value) else: prop.update(min=value, max=value, default=value) else: # Update the property if it already exists prop = obj.id_properties_ui(name) if min_value is not None and max_value is not None: prop.update(min=min_value, max=max_value, default=value) else: prop.update(min=value, max=value, default=value) # Add custom properties with min and max set to the specified values add_property(obj, shape_key_name, default_value, min_value, max_value) print(f"Custom property '{shape_key_name}' added to {obj.name}'s object properties tab.") print(f"Default value: {default_value}, Min value: {min_value}, Max value: {max_value}") # Example usage start_index = 97 end_index = 199 # Iterate over the shape keys within the specified range for i, shape_key in enumerate(bpy.context.active_object.data.shape_keys.key_blocks): if start_index <= i <= end_index: # Add custom property for each shape key within the range shape_key_name = shape_key.name add_custom_properties(shape_key_name, 1.0, 0.0, 1.0)

This script will add, within the specified range, mute buttons to the shape keys that reference the previously created custom properties with the exact same name as the shape key.

import bpy # Function to add a driver to a shape key's mute property def add_driver_to_shape_key(shape_key, shape_key_name, driver_expression, driver_variable_name, driver_object, driver_property_path): # Add a driver to the shape key's mute property driver = shape_key.driver_add('mute').driver driver.type = 'SCRIPTED' driver.expression = driver_expression # Add a variable to the driver var = driver.variables.new() var.name = driver_variable_name var.targets[0].id_type = 'OBJECT' var.targets[0].id = driver_object var.targets[0].data_path = f'["{driver_property_path}"]' print(f"Driver added to {shape_key_name}'s mute property with expression '{driver_expression}'.") # Get the selected object obj = bpy.context.active_object # Ensure there is an active object selected if obj is None: print("No active object selected.") else: # Ensure the object has shape keys if obj.data.shape_keys: # Define the range of shape keys to consider start_index = 97 end_index = 198 # Iterate over the shape keys in the specified range for i, shape_key in enumerate(obj.data.shape_keys.key_blocks): if start_index <= i <= end_index: # Add a driver to the mute property of each shape key within the range shape_key_name = shape_key.name add_driver_to_shape_key(shape_key, shape_key_name, "abs(a)<0.0001", "a", obj, shape_key_name) else: print(f"The selected object '{obj.name}' has no shape keys.")

Now when you adjust the factor of the respective shape keys, the shape keys will turn either off or on depending if the value is above 0.

You can now link the custom properties to any UI you want and keep the auto mute function.
Here's some extra scripts.

This one will delete the drivers from the values of the shape keys within the given custom index range.

import bpy # Function to remove drivers from the value section of shape keys def remove_drivers_from_shape_keys(obj, start_index, end_index): # Ensure the object has shape keys if obj.data.shape_keys: # Iterate over the shape keys in the specified range for i, shape_key in enumerate(obj.data.shape_keys.key_blocks): if start_index <= i <= end_index: # Check if the shape key has a driver on its value property if shape_key.driver_remove('value'): print(f"Driver removed from {shape_key.name}'s value property.") # Get the selected object obj = bpy.context.active_object # Ensure there is an active object selected if obj is None: print("No active object selected.") else: # Define the range of shape keys to consider start_index = 166 end_index = 197 # Remove drivers from the value section of shape keys within the specified range remove_drivers_from_shape_keys(obj, start_index, end_index)

This one will add drivers to the values of the shape keys within the custom index range, that will reference the custom properties you created previously, which links the two together.

import bpy # Function to add a driver to the value section of shape keys def add_driver_to_shape_key_value(obj, start_index, end_index): # Ensure the object has shape keys if obj.data.shape_keys: # Iterate over the shape keys in the specified range for i, shape_key in enumerate(obj.data.shape_keys.key_blocks): if start_index <= i <= end_index: # Add a driver to the shape key's value property driver = shape_key.driver_add('value').driver driver.type = 'SCRIPTED' driver.expression = "a" # Add a variable to the driver var = driver.variables.new() var.name = "a" var.targets[0].id_type = 'OBJECT' var.targets[0].id = obj var.targets[0].data_path = f'["{shape_key.name}"]' print(f"Driver added to {shape_key.name}'s value property with expression 'a'.") # Get the selected object obj = bpy.context.active_object # Ensure there is an active object selected if obj is None: print("No active object selected.") else: # Define the range of shape keys to consider start_index = 96 end_index = 102 # Add drivers to the value section of shape keys within the specified range add_driver_to_shape_key_value(obj, start_index, end_index)

This script will, within a custom index range, use the "Copy as new driver" function and paste them to the custom properties that share the same name as the shape keys within the index range.

import bpy def copy_as_new_driver_to_custom_props(obj, shape_key_range): if not obj.data.shape_keys: print("Object has no shape keys") return shape_keys = obj.data.shape_keys.key_blocks custom_props = obj.keys() for i in range(shape_key_range[0], shape_key_range[1] + 1): if i >= len(shape_keys): print(f"Shape key index {i} out of range") continue shape_key = shape_keys
shape_key_name = shape_key.name

if shape_key_name in custom_props:
# Remove existing drivers on the custom property
prop_path = f'["{shape_key_name}"]'
if obj.animation_data and obj.animation_data.drivers:
drivers = obj.animation_data.drivers
for driver in drivers:
if driver.data_path == prop_path:
obj.driver_remove(prop_path)
break

# Create new driver on custom property
prop_fcurve = obj.driver_add(prop_path)
prop_driver = prop_fcurve.driver

# Set up the driver to use the shape key value with averaged value type
var = prop_driver.variables.new()
var.name = "var"
var.type = 'SINGLE_PROP'
target = var.targets[0]
target.id_type = 'KEY'
target.id = obj.data.shape_keys
target.data_path = f'key_blocks["{shape_key_name}"].value'

prop_driver.type = 'AVERAGE'
prop_driver.expression = "var"

print(f"Copied driver from shape key '{shape_key_name}' to custom property '{shape_key_name}' as averaged value type")
else:
print(f"No matching custom property found for shape key '{shape_key_name}'")

# Set the object and the shape key range
selected_obj = bpy.context.object
shape_key_range = (132, 145)

copy_as_new_driver_to_custom_props(selected_obj, shape_key_range)[/code]
Here is some a bit unrelated stuff.

If you have two custom properties driving, say a shape key, but want only either one affecting it at the same time, add this to the expression:

max(varA, varB) - min(0, varA)

Then obviously create the variables A and B with the links to the custom properties.

If you want three custom properties, where only one can be active at one time, make it like this:

max(varA, max(varB, varC) - min(0, varB)) - min(0, varA)

If you want four properties doing the same thing, here:

max(a, max(b, max(c, d) - min(0, c)) - min(0, b)) - min(0, a)

Finally here is the same for 5 variables:

max(a, max(b, max(c, max(d, e) - min(0, d)) - min(0, c)) - min(0, b)) - min(0, a)

And 6:

max(a, max(b, max(c, max(d, max(e, f) - min(0, e)) - min(0, d)) - min(0, c)) - min(0, b)) - min(0, a)

This script will, within a custom index range, rename the shape keys' drivers' variables' target property names from and to what you want to give it:
import bpy # Ensure an object is selected obj = bpy.context.object if obj is None: raise Exception("No object selected") # Ensure the object has shape keys if obj.data.shape_keys is None: raise Exception("Selected object has no shape keys") # Access the shape keys data block shape_keys = obj.data.shape_keys # Loop through the shape keys within the specified range for i in range(6, 61): if i >= len(shape_keys.key_blocks): continue shape_key = shape_keys.key_blocks[] (add i inside the [] to the left and delete this one) # Access drivers for shape keys' values and mute properties if shape_keys.animation_data and shape_keys.animation_data.drivers: for driver in shape_keys.animation_data.drivers: if driver.data_path == f'key_blocks["{shape_key.name}"].value' or driver.data_path == f'key_blocks["{shape_key.name}"].mute': for var in driver.driver.variables: for target in var.targets: if target.id and target.id.name == "OriginalName": target.id = bpy.data.objects["NewName"] print("Drivers updated successfully.")

This script will, with the selected object, delete all custom properties from the object properties tab, except ones that you will specify:
import bpy # Get the selected object obj = bpy.context.object if obj is not None: # List of keywords to exclude from deletion exclude_keywords = ["Cube1", "Cube2"] # Get all custom properties custom_properties = obj.keys() # Collect properties to delete properties_to_delete = [] for prop in custom_properties: # Check if the property name contains any of the exclude keywords if not any(keyword in prop for keyword in exclude_keywords): # Add the property to the list to delete properties_to_delete.append(prop) # Delete collected properties for prop in properties_to_delete: del obj[prop] print("Custom properties deleted, except for those containing 'Daz'.") else: print("No object selected.")


This script will, with the selected object, delete all custom properties from the object DATA properties tab, except ones that you will specify:
import bpy # Get the selected object obj = bpy.context.object if obj is not None: # Get the object data obj_data = obj.data if obj_data is not None: # List of keywords to exclude from deletion exclude_keywords = ["Cube1", "Cube2"] # Get all custom properties custom_properties = obj_data.keys() # Collect properties to delete properties_to_delete = [] for prop in custom_properties: # Check if the property name contains any of the exclude keywords if not any(keyword in prop for keyword in exclude_keywords): # Add the property to the list to delete properties_to_delete.append(prop) # Delete collected properties for prop in properties_to_delete: del obj_data[prop] print("Custom properties deleted from Object Data, except for those containing 'Daz'.") else: print("No object data found.") else: print("No object selected.")

This script will, within an index range of the selected object's shape keys, make it so that the expression is called "a", variable targets a custom named object property and assumes the path is the same as the shape key (you've created custom properties to that object with the same name as the shape key.)

import bpy # Get all selected objects selected_objects = bpy.context.selected_objects # Define the index range for the shape keys (inclusive) start_index = 1 end_index = None # Set to None to use the last shape key index for each object # Iterate over each selected object for obj in selected_objects: # Ensure the object has shape keys if not obj.data.shape_keys: continue # Skip objects without shape keys # Determine the end index if not set if end_index is None: end_index = len(obj.data.shape_keys.key_blocks) - 1 # Iterate through each shape key within the specified index range for i, key in enumerate(obj.data.shape_keys.key_blocks): if i < 23 or i > end_index: continue # Skip shape keys outside the specified range # Get the driver for the current shape key's value, creating it if necessary driver = key.driver_add("value").driver # Set the driver expression to "a" driver.expression = "a" # Remove all existing variables while driver.variables: driver.variables.remove(driver.variables[0]) # Add the new variable "a" if it doesn't already exist var = driver.variables.new() var.name = "a" var.type = 'SINGLE_PROP' # Set the target for the variable target = var.targets[0] target.id_type = 'OBJECT' target.id = bpy.data.objects["Cube"] target.data_path = f'["{key.name}"]' print("Drivers and variables updated successfully for all selected objects.")