Skip to content

Instantly share code, notes, and snippets.

@Pakmanv
Created May 2, 2025 17:59
Show Gist options
  • Select an option

  • Save Pakmanv/04e282d163d29b6d6c7545c02a179eba to your computer and use it in GitHub Desktop.

Select an option

Save Pakmanv/04e282d163d29b6d6c7545c02a179eba to your computer and use it in GitHub Desktop.
Recall Components VV 1.8
import maya.cmds as cmds
import maya.mel as mel
class ComponentSelectionUI(object):
def __init__(self):
self.window = "RecallComponentsWindow"
self.selection_data = {} # Store selections: {custom_name: {'obj': obj_name, 'vertices': [], 'edges': [], 'faces': [], 'objects': []}}
self.selection_list = None
self.recall_on_selections_button = None
self.select_common_objects_menu_item = None # Store menu item for enabling/disabling
self.version = "Recall Components VV 1.8"
def create_ui(self):
# Delete window if it already exists
if cmds.window(self.window, exists=True):
cmds.deleteUI(self.window, window=True)
# Create the window
self.window = cmds.window(self.window, title=self.version, widthHeight=(350, 400))
# Create layout
cmds.columnLayout(adjustableColumn=True, rowSpacing=5)
# Create list to display stored selections (enable multi-selection)
self.selection_list = cmds.textScrollList(
numberOfRows=10,
allowMultiSelection=True,
height=250,
selectCommand=self.update_button_state
)
# Right-click context menu
self.popup_menu = cmds.popupMenu(parent=self.selection_list)
cmds.menuItem(label="Delete Selected", command=self.delete_selected_items)
self.select_common_objects_menu_item = cmds.menuItem(
label="Select Common Objects",
command=self.select_common_objects
)
cmds.menuItem(label="Refresh", command=self.refresh_list)
cmds.menuItem(label="Rename", command=self.rename_selection)
# Buttons
cmds.button(label="Add to List", command=self.add_to_list, height=30)
cmds.button(label="Recall Selection", command=self.recall_selection, height=30)
self.recall_on_selections_button = cmds.button(
label="Recall on Selection(s)",
command=self.recall_on_selections,
height=30,
enable=False # Initially disabled until valid selection
)
# Show window
cmds.showWindow(self.window)
def update_button_state(self, *args):
# Get selected items
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True) or []
# Enable "Recall on Selection(s)" only if all selected items are component-based
all_components = True
for item in selected_items:
custom_name = item.split(":")[0]
if custom_name in self.selection_data and self.selection_data[custom_name]['objects']:
all_components = False
break
cmds.button(self.recall_on_selections_button, edit=True, enable=bool(selected_items and all_components))
# Enable "Select Common Objects" if any selected item is object-based
enable_common_objects = self.is_object_selection()
if self.select_common_objects_menu_item:
cmds.menuItem(self.select_common_objects_menu_item, edit=True, enable=enable_common_objects)
def is_object_selection(self, *args):
# Enable "Select Common Objects" if any selected item is object-based
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True) or []
for item in selected_items:
custom_name = item.split(":")[0]
if custom_name in self.selection_data and self.selection_data[custom_name]['objects']:
return True
return False
def add_to_list(self, *args):
# Get selected objects
selected = cmds.ls(selection=True, objectsOnly=True)
if not selected:
cmds.warning("Please select at least one object or components.")
return
obj = selected[0] # Use the first selected object as reference
# Check if components are selected
selected_components = cmds.filterExpand(sm=(31, 32, 34), expand=True) # 31=vertices, 32=edges, 34=faces
is_component_selection = bool(selected_components)
# Prompt for a custom name, pre-filled with object name
default_name = f"{obj}_Selection"
result = cmds.promptDialog(
title="Name Selection",
message="Enter a name for this selection:",
text=default_name,
button=["OK", "Cancel"],
defaultButton="OK",
cancelButton="Cancel",
dismissString="Cancel"
)
if result != "OK":
return
custom_name = cmds.promptDialog(query=True, text=True)
if not custom_name:
cmds.warning("Please provide a valid name.")
return
# Check for duplicate name
if custom_name in self.selection_data:
cmds.warning(f"Name '{custom_name}' already exists. Please choose a unique name.")
return
# Initialize storage for this selection
self.selection_data[custom_name] = {'obj': obj, 'vertices': [], 'edges': [], 'faces': [], 'objects': []}
if is_component_selection:
# Categorize components
for comp in selected_components:
if '.vtx[' in comp:
self.selection_data[custom_name]['vertices'].append(comp)
elif '.e[' in comp:
self.selection_data[custom_name]['edges'].append(comp)
elif '.f[' in comp:
self.selection_data[custom_name]['faces'].append(comp)
else:
# Store objects as a selection set
self.selection_data[custom_name]['objects'] = selected
# Update the list
self.update_selection_list()
def update_selection_list(self):
# Clear the list
cmds.textScrollList(self.selection_list, edit=True, removeAll=True)
# Populate the list with stored selections
for custom_name in sorted(self.selection_data.keys()):
vtx_count = len(self.selection_data[custom_name]['vertices'])
edge_count = len(self.selection_data[custom_name]['edges'])
face_count = len(self.selection_data[custom_name]['faces'])
obj_count = len(self.selection_data[custom_name]['objects'])
if vtx_count or edge_count or face_count:
list_item = f"{custom_name}: {vtx_count} verts, {edge_count} edges, {face_count} faces"
elif obj_count:
list_item = f"{custom_name}: {obj_count} object(s)"
else:
continue # Skip empty entries
cmds.textScrollList(self.selection_list, edit=True, append=list_item)
def delete_selected_items(self, *args):
# Get selected items from the list
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True)
if not selected_items:
cmds.warning("Please select an item from the list to delete.")
return
# Extract custom names and delete from selection_data
for item in selected_items:
custom_name = item.split(":")[0]
if custom_name in self.selection_data:
del self.selection_data[custom_name]
# Update the list
self.update_selection_list()
self.update_button_state()
def refresh_list(self, *args):
# Remove entries for non-existent objects
to_remove = []
for custom_name in self.selection_data:
obj = self.selection_data[custom_name]['obj']
objects = self.selection_data[custom_name]['objects']
# Check if the reference object exists
if not cmds.objExists(obj):
to_remove.append(custom_name)
continue
# Check if all stored objects exist
if objects and not all(cmds.objExists(o) for o in objects):
self.selection_data[custom_name]['objects'] = [o for o in objects if cmds.objExists(o)]
if not self.selection_data[custom_name]['objects'] and not any(
self.selection_data[custom_name][key] for key in ['vertices', 'edges', 'faces']
):
to_remove.append(custom_name)
for custom_name in to_remove:
del self.selection_data[custom_name]
# Update the list
self.update_selection_list()
self.update_button_state()
if to_remove:
cmds.warning(f"Removed {len(to_remove)} invalid entries from the list.")
def rename_selection(self, *args):
# Get selected items from the list
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True)
if not selected_items:
cmds.warning("Please select an item to rename.")
return
if len(selected_items) > 1:
cmds.warning("Please select only one item to rename.")
return
# Get the custom name
old_name = selected_items[0].split(":")[0]
if old_name not in self.selection_data:
cmds.warning(f"Selection '{old_name}' not found in stored data.")
return
# Prompt for a new name
result = cmds.promptDialog(
title="Rename Selection",
message="Enter a new name:",
text=old_name,
button=["OK", "Cancel"],
defaultButton="OK",
cancelButton="Cancel",
dismissString="Cancel"
)
if result != "OK":
return
new_name = cmds.promptDialog(query=True, text=True)
if not new_name:
cmds.warning("Please provide a valid name.")
return
# Check for duplicate name
if new_name in self.selection_data and new_name != old_name:
cmds.warning(f"Name '{new_name}' already exists. Please choose a unique name.")
return
# Update the name
self.selection_data[new_name] = self.selection_data.pop(old_name)
self.update_selection_list()
self.update_button_state()
def select_common_objects(self, *args):
# Get the selected items from the list
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True)
if not selected_items:
cmds.warning("Please select at least one item from the list.")
return
# Use the first selected item
custom_name = selected_items[0].split(":")[0]
if custom_name not in self.selection_data:
cmds.warning(f"Selection '{custom_name}' not found in stored data.")
return
# Get the original object's vertex count
original_obj = self.selection_data[custom_name]['obj']
if not cmds.objExists(original_obj):
cmds.warning(f"Original object '{original_obj}' no longer exists.")
return
shape = cmds.listRelatives(original_obj, shapes=True, fullPath=True)
if not shape or not cmds.objectType(shape[0], isType='mesh'):
cmds.warning(f"{original_obj} is not a valid mesh.")
return
vertex_count = cmds.polyEvaluate(original_obj, vertex=True)
# Find all mesh objects in the scene with the same vertex count
all_meshes = cmds.ls(type='mesh', long=True)
matching_objects = []
for mesh in all_meshes:
transform = cmds.listRelatives(mesh, parent=True, fullPath=True)
if transform and cmds.polyEvaluate(transform[0], vertex=True) == vertex_count:
matching_objects.append(transform[0])
if not matching_objects:
cmds.warning(f"No objects found with {vertex_count} vertices.")
return
# Select the matching objects
try:
cmds.select(matching_objects, replace=True)
print(f"Selected {len(matching_objects)} objects with {vertex_count} vertices: {matching_objects}")
except Exception as e:
cmds.warning(f"Failed to select common objects: {str(e)}")
def recall_selection(self, *args):
# Get the selected items from the list
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True)
if not selected_items:
cmds.warning("Please select at least one item from the list.")
return
# Collect components and objects from all selected items
stored_components = []
stored_objects = []
for item in selected_items:
custom_name = item.split(":")[0]
if custom_name not in self.selection_data:
cmds.warning(f"Selection '{custom_name}' not found in stored data. Skipping.")
continue
# Add components for this selection
for comp_type in ['vertices', 'edges', 'faces']:
stored_components.extend(self.selection_data[custom_name][comp_type])
# Add objects for this selection
stored_objects.extend(self.selection_data[custom_name]['objects'])
if not (stored_components or stored_objects):
cmds.warning("No valid components or objects found in the selected items.")
return
# Select the components and objects
try:
cmds.select(clear=True)
if stored_components:
cmds.select(stored_components, add=True)
cmds.selectMode(component=True)
cmds.selectType(vertex=True, edge=True, facet=True)
if stored_objects:
cmds.select(stored_objects, add=True)
if not stored_components: # Only set to object mode if no components
cmds.selectMode(object=True)
print(f"Recalled selection: {stored_components + stored_objects}")
except Exception as e:
cmds.warning(f"Failed to recall selection: {str(e)}")
return
def recall_on_selections(self, *args):
# Get the selected items from the list
selected_items = cmds.textScrollList(self.selection_list, query=True, selectItem=True)
if not selected_items:
cmds.warning("Please select at least one item from the list.")
return
# Get the currently selected objects
current_selected = cmds.ls(selection=True, objectsOnly=True)
if not current_selected:
cmds.warning("Please select at least one target object.")
return
# Clear any existing selection to avoid conflicts
cmds.select(clear=True)
# Process each selected object
valid_components_selected = False
for current_obj in current_selected:
# Ensure the current object is a mesh
shape = cmds.listRelatives(current_obj, shapes=True, fullPath=True)
if not shape or not cmds.objectType(shape[0], isType='mesh'):
cmds.warning(f"{current_obj} is not a valid mesh. Skipping.")
continue
# Select the current object to set the context
cmds.select(current_obj, add=True)
# Collect and map components from all selected items
new_components = []
for item in selected_items:
custom_name = item.split(":")[0]
if custom_name not in self.selection_data:
cmds.warning(f"Selection '{custom_name}' not found in stored data. Skipping.")
continue
# Skip object-only selections for component mapping
if self.selection_data[custom_name]['objects'] and not any(
self.selection_data[custom_name][key] for key in ['vertices', 'edges', 'faces']
):
continue
original_obj = self.selection_data[custom_name]['obj']
for comp_type in ['vertices', 'edges', 'faces']:
for comp in self.selection_data[custom_name][comp_type]:
# Extract the component index (e.g., 'vtx[5]' from 'pCube1.vtx[5]')
comp_index = comp.split('.', 1)[1] # Get 'vtx[5]', 'f[3]', etc.
# Construct new component for current object
new_comp = f"{current_obj}.{comp_index}"
# Validate component exists on current object
if cmds.objExists(new_comp):
new_components.append(new_comp)
else:
cmds.warning(f"Component {new_comp} does not exist on {current_obj}. Skipping.")
if new_components:
try:
cmds.select(new_components, add=True) # Add to selection to support multiple objects
print(f"Recalled components on {current_obj}: {new_components}")
valid_components_selected = True
except Exception as e:
cmds.warning(f"Failed to recall components on {current_obj}: {str(e)}")
# Set selection mode to component and enable appropriate types
if valid_components_selected:
cmds.selectMode(component=True)
cmds.selectType(vertex=True, edge=True, facet=True)
else:
cmds.warning("No valid components could be mapped to any selected objects. Check topology compatibility.")
cmds.select(clear=True) # Clear selection if nothing is valid
def show(self):
self.create_ui()
# Run the UI
if __name__ == "__main__":
ui = ComponentSelectionUI()
ui.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment