Created
May 2, 2025 17:59
-
-
Save Pakmanv/04e282d163d29b6d6c7545c02a179eba to your computer and use it in GitHub Desktop.
Recall Components VV 1.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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