Created
May 2, 2025 20:09
-
-
Save Pakmanv/93e4a2ec10c38c6b3e7efa0a18aa430e to your computer and use it in GitHub Desktop.
Cluster Namer VV 1.0
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 pymel.core as pm | |
| class VVCLrenamer(object): | |
| def __init__(self): | |
| self.window = "VVCLrenamer" | |
| self.current_mesh = None | |
| self.clusters = [] | |
| self.cluster_colors = {} # Store color status per cluster | |
| self.multi_select_enabled = False # Track multi-selection state | |
| self.init_ui() | |
| self.load_preferences() | |
| def init_ui(self): | |
| """Initialize a popup window with default Maya colors and tooltips.""" | |
| if pm.window(self.window, exists=True): | |
| pm.deleteUI(self.window) | |
| self.window = pm.window(self.window, title="Cluster Namer VV 1.0", width=300, height=550) | |
| main_layout = pm.columnLayout(adjustableColumn=True, rowSpacing=5) | |
| pm.text(label="Mesh:") | |
| self.mesh_field = pm.textField(editable=True, annotation="Enter or select a mesh") | |
| pm.textField(self.mesh_field, edit=True, enterCommand=self.set_mesh) | |
| pm.button(label="Load Mesh", command=self.set_mesh, backgroundColor=[0.7, 0.8, 0.9], | |
| annotation="Load selected or entered mesh") # Pastel blue | |
| pm.rowLayout(numberOfColumns=2, adjustableColumn=2) | |
| pm.text(label="Sort By: ") | |
| self.sort_menu = pm.optionMenuGrp(changeCommand=self.update_cluster_list, | |
| annotation="Sort clusters by different criteria") | |
| pm.menuItem(label="Alphabetical A") | |
| pm.menuItem(label="Alphabetical Z") | |
| pm.menuItem(label="Creation Order") | |
| pm.menuItem(label="Vertex Influence") | |
| pm.setParent("..") | |
| self.cluster_label = pm.text(label="Clusters (0):") | |
| self.cluster_list = pm.textScrollList(allowMultiSelection=False, height=200, | |
| annotation="List of clusters; double-click to rename") | |
| pm.textScrollList(self.cluster_list, edit=True, selectCommand=self.select_cluster, | |
| doubleClickCommand=self.edit_cluster_name) | |
| self.popup_menu = pm.popupMenu(parent=self.cluster_list) | |
| pm.menuItem(label="Select Deformer Node", command=self.select_deformer_node, parent=self.popup_menu) | |
| pm.menuItem(label="X", command=lambda x: self.set_cluster_color("green"), parent=self.popup_menu) | |
| pm.menuItem(label="O", command=lambda x: self.set_cluster_color("red"), parent=self.popup_menu) | |
| pm.menuItem(label="Set Default", command=lambda x: self.set_cluster_color("default"), parent=self.popup_menu) | |
| self.multi_select_item = pm.menuItem(label="Toggle Multi Selection", command=self.toggle_multi_selection, | |
| parent=self.popup_menu) | |
| pm.menuItem(label="Rename All Clusters", command=self.rename_all_clusters, parent=self.popup_menu) | |
| pm.button(label="Zero Transforms", command=self.zero_transforms, | |
| annotation="Reset transforms of all cluster handles") # Default Maya color | |
| # Renaming fields and buttons | |
| pm.columnLayout(adjustableColumn=True, rowSpacing=3) | |
| self.search_field = pm.textField(placeholderText="Search", width=290, annotation="Text to search for") | |
| self.replace_field = pm.textField(placeholderText="Replace", width=290, annotation="Text to replace with") | |
| pm.button(label="Search Replace", command=self.search_replace, | |
| annotation="Replace text in selected cluster names") # Default Maya color | |
| self.prefix_field = pm.textField(placeholderText="Prefix", annotation="Prefix to add") | |
| pm.rowLayout(numberOfColumns=3, adjustableColumn=1) | |
| pm.button(label="Add Prefix", command=self.add_prefix, annotation="Add prefix to selected clusters") | |
| pm.text(label=" ") # Spacer | |
| self.prefix_after = pm.checkBox(label="After Existing Prefix", value=False, | |
| annotation="Add prefix after lt_/rt_ if present") | |
| pm.setParent("..") | |
| self.suffix_field = pm.textField(placeholderText="Suffix", annotation="Suffix to add") | |
| pm.button(label="Add Suffix", command=self.add_suffix, | |
| annotation="Add suffix to selected clusters") # Default Maya color | |
| pm.rowLayout(numberOfColumns=4, adjustableColumn=1) | |
| pm.button(label="Append Number", command=self.append_number, backgroundColor=[0.8, 0.8, 0.9], | |
| annotation="Append numbered suffix (e.g., _001)") # Pastel blue | |
| pm.text(label=" ") # Spacer | |
| self.number_before_suffix = pm.checkBox(label="Before Suffix", value=False, | |
| annotation="Place number before existing suffix") | |
| self.uppercase_rt_lt = pm.checkBox(label="RT/LT", value=False, | |
| annotation="Use uppercase RT/LT in names") | |
| pm.setParent("..") | |
| pm.rowLayout(numberOfColumns=4, adjustableColumn=1) | |
| pm.button(label="Auto Prefix Lt/Rt", command=self.auto_prefix, backgroundColor=[0.8, 0.9, 0.8], | |
| annotation="Add lt_/rt_ or LT_/RT_ prefix") # Pastel green | |
| pm.text(label=" ") # Spacer | |
| self.side_prefix = pm.optionMenu(changeCommand=lambda x: None, annotation="Choose side prefix") | |
| pm.menuItem(label="lt_") | |
| pm.menuItem(label="rt_") | |
| self.case_prefix = pm.optionMenu(changeCommand=lambda x: None, annotation="Choose case for prefix") | |
| pm.menuItem(label="lt_/rt_") | |
| pm.menuItem(label="LT_/RT_") | |
| pm.setParent("..") | |
| pm.setParent("..") # End renaming columnLayout | |
| pm.button(label="Close", command=lambda x: pm.deleteUI(self.window), annotation="Close the tool") | |
| pm.showWindow(self.window) | |
| def load_preferences(self): | |
| """Load saved preferences using optionVar without triggering update_cluster_list.""" | |
| if pm.optionVar(exists="VVCLrenamer_sort_mode"): | |
| sort_mode = pm.optionVar(query="VVCLrenamer_sort_mode") | |
| pm.optionMenuGrp(self.sort_menu, edit=True, value=sort_mode) | |
| if pm.optionVar(exists="VVCLrenamer_multi_select"): | |
| self.multi_select_enabled = pm.optionVar(query="VVCLrenamer_multi_select") | |
| pm.textScrollList(self.cluster_list, edit=True, allowMultiSelection=self.multi_select_enabled) | |
| pm.menuItem(self.multi_select_item, edit=True, | |
| label="Toggle Multi Selection (%s)" % ("On" if self.multi_select_enabled else "Off")) | |
| # Do not call update_cluster_list here to avoid premature execution | |
| def save_preferences(self): | |
| """Save preferences using optionVar.""" | |
| sort_mode = pm.optionMenuGrp(self.sort_menu, query=True, value=True) | |
| pm.optionVar(stringValue=("VVCLrenamer_sort_mode", sort_mode)) | |
| pm.optionVar(intValue=("VVCLrenamer_multi_select", int(self.multi_select_enabled))) | |
| def set_mesh(self, *args): | |
| """Set the mesh from selection or text field, displaying short name.""" | |
| print("set_mesh: args=%s" % str(args)) | |
| mesh_name = pm.textField(self.mesh_field, query=True, text=True).strip() | |
| print("set_mesh: mesh_name from text field=%s" % mesh_name) | |
| if not mesh_name: | |
| selected = cmds.ls(selection=True) | |
| print("set_mesh: selected=%s" % selected) | |
| if not selected: | |
| cmds.warning("No object selected or entered.") | |
| return | |
| mesh_name = selected[0] | |
| print("set_mesh: final mesh_name=%s" % mesh_name) | |
| if not cmds.objExists(mesh_name): | |
| cmds.warning("Object %s does not exist in the scene." % mesh_name) | |
| return | |
| mesh = self.get_mesh_from_selection(mesh_name) | |
| print("set_mesh: mesh from get_mesh_from_selection=%s" % mesh) | |
| if mesh: | |
| self.current_mesh = mesh | |
| short_name = mesh.split("|")[-1] | |
| pm.textField(self.mesh_field, edit=True, text=short_name) | |
| self.update_cluster_list() | |
| else: | |
| cmds.warning("%s is not a valid mesh (no mesh shape found)." % mesh_name) | |
| def get_mesh_from_selection(self, obj): | |
| """Get the mesh transform from a transform or shape node.""" | |
| print("get_mesh_from_selection: obj=%s" % obj) | |
| if not obj or not cmds.objExists(obj): | |
| print("get_mesh_from_selection: Invalid or non-existent obj: %s" % str(obj)) | |
| return None | |
| try: | |
| if cmds.nodeType(obj) == "transform": | |
| shapes = cmds.listRelatives(obj, shapes=True, type="mesh", fullPath=True) | |
| print("get_mesh_from_selection: transform shapes=%s" % shapes) | |
| if shapes: | |
| return obj | |
| elif cmds.nodeType(obj) == "mesh": | |
| parents = cmds.listRelatives(obj, parent=True, type="transform", fullPath=True) | |
| print("get_mesh_from_selection: shape parents=%s" % parents) | |
| if parents: | |
| return parents[0] | |
| transforms = cmds.listRelatives(obj, parent=True, type="transform", fullPath=True) or [] | |
| for transform in transforms: | |
| shapes = cmds.listRelatives(transform, shapes=True, type="mesh", fullPath=True) | |
| print("get_mesh_from_selection: related transform shapes=%s" % shapes) | |
| if shapes: | |
| return transform | |
| print("get_mesh_from_selection: No valid mesh transform found for %s" % obj) | |
| except Exception as e: | |
| print("get_mesh_from_selection: Error: %s" % str(e)) | |
| return None | |
| def get_vertex_influence_count(self, cluster, mesh_shape): | |
| """Count the number of vertices influenced by the cluster.""" | |
| try: | |
| weights = cmds.getAttr(cluster + ".weightList[0].weights[*]") | |
| if weights: | |
| non_zero = sum(1 for w in weights if w > 0.0) | |
| print("get_vertex_influence_count: %s influences %d vertices" % (cluster, non_zero)) | |
| return non_zero | |
| return 0 | |
| except Exception as e: | |
| print("get_vertex_influence_count: Error for %s: %s" % (cluster, str(e))) | |
| return 0 | |
| def get_cluster_handle(self, cluster): | |
| """Find the handle associated with a cluster deformer.""" | |
| print("get_cluster_handle: cluster=%s" % cluster) | |
| handle = None | |
| potential_handle = cluster + "Handle" | |
| if cmds.objExists(potential_handle) and cmds.nodeType(potential_handle) == "transform": | |
| handle = [potential_handle] | |
| print("get_cluster_handle: name-based handle=%s" % handle) | |
| if not handle: | |
| handle = cmds.listConnections(cluster + ".matrix", source=False, destination=True, type="transform") | |
| print("get_cluster_handle: .matrix handle=%s" % handle) | |
| if not handle: | |
| history = cmds.listHistory(cluster, future=True) | |
| cluster_handles = [node for node in history if cmds.nodeType(node) == "clusterHandle"] | |
| if cluster_handles: | |
| handle = cmds.listRelatives(cluster_handles[0], parent=True, type="transform") | |
| print("get_cluster_handle: history handle=%s" % handle) | |
| return handle[0] if handle and cmds.objExists(handle[0]) else None | |
| def toggle_multi_selection(self, *args): | |
| """Toggle multi-selection in the cluster list.""" | |
| self.multi_select_enabled = not self.multi_select_enabled | |
| print("toggle_multi_selection: %s" % ("enabled" if self.multi_select_enabled else "disabled")) | |
| pm.textScrollList(self.cluster_list, edit=True, allowMultiSelection=self.multi_select_enabled) | |
| pm.menuItem(self.multi_select_item, edit=True, | |
| label="Toggle Multi Selection (%s)" % ("On" if self.multi_select_enabled else "Off")) | |
| self.save_preferences() | |
| def set_cluster_color(self, color): | |
| """Set the color indicator for selected cluster(s).""" | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| print("set_cluster_color: selected=%s to %s" % (selected, color)) | |
| for item in selected: | |
| cluster = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(cluster): | |
| continue | |
| self.cluster_colors[cluster] = color | |
| self.update_cluster_list() | |
| def update_cluster_list(self, *args): | |
| """Populate the cluster deformer list with sorting and indicators.""" | |
| print("update_cluster_list: called with args=%s" % str(args)) | |
| try: | |
| pm.textScrollList(self.cluster_list, edit=True, removeAll=True) | |
| self.clusters = [] | |
| if not self.current_mesh or not cmds.objExists(self.current_mesh): | |
| print("update_cluster_list: No current mesh or mesh does not exist") | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| if cmds.nodeType(self.current_mesh) != "transform": | |
| print("update_cluster_list: %s is not a transform node" % self.current_mesh) | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| shapes = cmds.listRelatives(self.current_mesh, shapes=True, type="mesh", fullPath=True) | |
| if not shapes: | |
| print("update_cluster_list: No mesh shapes found for %s" % self.current_mesh) | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| mesh_shape = shapes[0] | |
| print("update_cluster_list: mesh_shape=%s" % mesh_shape) | |
| try: | |
| deformers = cmds.findDeformers(mesh_shape) or [] | |
| except Exception as e: | |
| print("update_cluster_list: Failed to find deformers for %s: %s" % (mesh_shape, str(e))) | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| print("update_cluster_list: deformers=%s" % deformers) | |
| if not deformers: | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| for deformer in deformers: | |
| if cmds.nodeType(deformer) == "cluster": | |
| print("update_cluster_list: cluster=%s" % deformer) | |
| self.clusters.append(deformer) | |
| if not self.clusters: | |
| print("update_cluster_list: No clusters found") | |
| pm.text(self.cluster_label, edit=True, label="Clusters (0):") | |
| return | |
| pm.text(self.cluster_label, edit=True, label="Clusters (%d):" % len(self.clusters)) | |
| sort_mode = pm.optionMenuGrp(self.sort_menu, query=True, value=True) | |
| sorted_clusters = self.clusters[:] | |
| if sort_mode == "Alphabetical A": | |
| sorted_clusters.sort(key=lambda x: x.lower()) | |
| print("update_cluster_list: Sorted A to Z: %s" % sorted_clusters) | |
| elif sort_mode == "Alphabetical Z": | |
| sorted_clusters.sort(key=lambda x: x.lower(), reverse=True) | |
| print("update_cluster_list: Sorted Z to A: %s" % sorted_clusters) | |
| elif sort_mode == "Vertex Influence": | |
| sorted_clusters.sort(key=lambda x: self.get_vertex_influence_count(x, mesh_shape), reverse=True) | |
| print("update_cluster_list: Sorted by vertex influence: %s" % sorted_clusters) | |
| print("update_cluster_list: final clusters=%s" % sorted_clusters) | |
| for cluster in sorted_clusters: | |
| display_text = cluster | |
| if cluster in self.cluster_colors and self.cluster_colors[cluster]: | |
| color = self.cluster_colors[cluster] | |
| prefix = "X" if color == "green" else "O" if color == "red" else "" | |
| display_text = "{} {}".format(prefix, cluster) if prefix else cluster | |
| pm.textScrollList(self.cluster_list, edit=True, append=display_text) | |
| self.save_preferences() | |
| except Exception as e: | |
| print("update_cluster_list: Failed to update list: %s" % str(e)) | |
| cmds.warning("Failed to update cluster list: %s" % str(e)) | |
| def select_cluster(self, *args): | |
| """Select the cluster handle(s) in the viewport.""" | |
| print("select_cluster: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| print("select_cluster: No cluster selected") | |
| return | |
| handles = [] | |
| for item in selected: | |
| cluster = item.split(" ")[-1] if " " in item else item | |
| print("select_cluster: cluster=%s" % cluster) | |
| if not cmds.objExists(cluster): | |
| cmds.warning("Cluster %s does not exist." % cluster) | |
| continue | |
| handle = self.get_cluster_handle(cluster) | |
| if handle: | |
| handles.append(handle) | |
| print("select_cluster: Highlighted handle %s" % handle) | |
| else: | |
| cmds.warning("No handle found for cluster %s." % cluster) | |
| if handles: | |
| cmds.select(handles, replace=True) | |
| else: | |
| cmds.warning("No valid handles found for selection.") | |
| def select_deformer_node(self, *args): | |
| """Select the deformer node(s) directly.""" | |
| print("select_deformer_node: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| print("select_deformer_node: No cluster selected") | |
| return | |
| clusters = [] | |
| for item in selected: | |
| cluster = item.split(" ")[-1] if " " in item else item | |
| print("select_deformer_node: cluster=%s" % cluster) | |
| if cmds.objExists(cluster): | |
| clusters.append(cluster) | |
| print("select_deformer_node: Selected deformer %s" % cluster) | |
| else: | |
| cmds.warning("Cluster %s does not exist." % cluster) | |
| if clusters: | |
| cmds.select(clusters, replace=True) | |
| else: | |
| cmds.warning("No valid clusters found for selection.") | |
| def edit_cluster_name(self, *args): | |
| """Rename a single cluster node on double-click.""" | |
| print("edit_cluster_name: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) | |
| if not selected or len(selected) > 1: | |
| cmds.warning("Select exactly one cluster to rename.") | |
| return | |
| old_name = selected[0].split(" ")[-1] if " " in selected[0] else selected[0] | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| return | |
| result = pm.promptDialog( | |
| title="Rename Cluster", | |
| message="Enter new name for %s:" % old_name, | |
| text=old_name, | |
| button=["OK", "Cancel"], | |
| defaultButton="OK", | |
| cancelButton="Cancel", | |
| dismissString="Cancel" | |
| ) | |
| if result == "OK": | |
| new_name = pm.promptDialog(query=True, text=True).strip() | |
| if new_name and new_name != old_name: | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| return | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("edit_cluster_name: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("edit_cluster_name: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("edit_cluster_name: Failed: %s" % str(e)) | |
| cmds.warning("Failed to rename cluster: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def zero_transforms(self, *args): | |
| """Reset translate and rotate to 0 and scale to 1 for all cluster handles.""" | |
| print("zero_transforms: args=%s" % str(args)) | |
| if not self.clusters: | |
| cmds.warning("No clusters available to zero transforms.") | |
| return | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for cluster in self.clusters: | |
| if not cmds.objExists(cluster): | |
| cmds.warning("Cluster %s does not exist." % cluster) | |
| continue | |
| handle = self.get_cluster_handle(cluster) | |
| if handle: | |
| try: | |
| cmds.setAttr(handle + ".translateX", 0) | |
| cmds.setAttr(handle + ".translateY", 0) | |
| cmds.setAttr(handle + ".translateZ", 0) | |
| cmds.setAttr(handle + ".rotateX", 0) | |
| cmds.setAttr(handle + ".rotateY", 0) | |
| cmds.setAttr(handle + ".rotateZ", 0) | |
| cmds.setAttr(handle + ".scaleX", 1) | |
| cmds.setAttr(handle + ".scaleY", 1) | |
| cmds.setAttr(handle + ".scaleZ", 1) | |
| print("zero_transforms: Reset transforms for %s" % handle) | |
| except Exception as e: | |
| cmds.warning("Failed to zero transforms for %s: %s" % (handle, str(e))) | |
| else: | |
| cmds.warning("No handle found for cluster %s." % cluster) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def search_replace(self, *args): | |
| """Search and replace in selected cluster names.""" | |
| print("search_replace: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| cmds.warning("No clusters selected.") | |
| return | |
| search = pm.textField(self.search_field, query=True, text=True).strip() | |
| replace = pm.textField(self.replace_field, query=True, text=True).strip() | |
| if not search: | |
| cmds.warning("Search string cannot be empty.") | |
| return | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for item in selected: | |
| old_name = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| continue | |
| new_name = old_name.replace(search, replace) | |
| if new_name == old_name: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("search_replace: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("search_replace: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("search_replace: Failed: %s" % str(e)) | |
| cmds.warning("Failed to search/replace: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def add_prefix(self, *args): | |
| """Add a prefix to selected cluster(s).""" | |
| print("add_prefix: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| cmds.warning("No clusters selected.") | |
| return | |
| prefix = pm.textField(self.prefix_field, query=True, text=True).strip() | |
| after_prefix = pm.checkBox(self.prefix_after, query=True, value=True) | |
| if not prefix: | |
| cmds.warning("Prefix cannot be empty.") | |
| return | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for item in selected: | |
| old_name = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| continue | |
| new_name = old_name | |
| if after_prefix: | |
| for existing in ["lt_", "rt_", "LT_", "RT_"]: | |
| if old_name.startswith(existing): | |
| new_name = existing + prefix + old_name[len(existing):] | |
| break | |
| else: | |
| new_name = prefix + old_name | |
| else: | |
| new_name = prefix + old_name | |
| if new_name == old_name: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("add_prefix: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("add_prefix: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("add_prefix: Failed: %s" % str(e)) | |
| cmds.warning("Failed to add prefix: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def add_suffix(self, *args): | |
| """Add a suffix to selected cluster(s).""" | |
| print("add_suffix: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| cmds.warning("No clusters selected.") | |
| return | |
| suffix = pm.textField(self.suffix_field, query=True, text=True).strip() | |
| if not suffix: | |
| cmds.warning("Suffix cannot be empty.") | |
| return | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for item in selected: | |
| old_name = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| continue | |
| new_name = old_name + suffix | |
| if new_name == old_name: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("add_suffix: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("add_suffix: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("add_suffix: Failed: %s" % str(e)) | |
| cmds.warning("Failed to add suffix: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def append_number(self, *args): | |
| """Append numbered suffix to selected clusters.""" | |
| print("append_number: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| cmds.warning("No clusters selected.") | |
| return | |
| before_suffix = pm.checkBox(self.number_before_suffix, query=True, value=True) | |
| uppercase = pm.checkBox(self.uppercase_rt_lt, query=True, value=True) | |
| print("append_number: before_suffix=%s, uppercase=%s" % (before_suffix, uppercase)) | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for i, item in enumerate(selected, 1): | |
| old_name = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| continue | |
| new_name = old_name | |
| suffix = "_{:03d}".format(i) | |
| if before_suffix: | |
| parts = old_name.rsplit("_", 1) | |
| if len(parts) > 1 and parts[1].lower() in ["grp", "rig", "geo", "cluster"]: | |
| new_name = parts[0] + suffix + "_" + parts[1] | |
| else: | |
| new_name = old_name + suffix | |
| else: | |
| new_name = old_name + suffix | |
| if new_name == old_name: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("append_number: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("append_number: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("append_number: Failed: %s" % str(e)) | |
| cmds.warning("Failed to append number: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def auto_prefix(self, *args): | |
| """Add lt_/rt_ or LT_/RT_ prefix based on dropdowns.""" | |
| print("auto_prefix: args=%s" % str(args)) | |
| selected = pm.textScrollList(self.cluster_list, query=True, selectItem=True) or [] | |
| if not selected: | |
| cmds.warning("No clusters selected.") | |
| return | |
| side = pm.optionMenu(self.side_prefix, query=True, value=True) | |
| case = pm.optionMenu(self.case_prefix, query=True, value=True) | |
| print("auto_prefix: side=%s, case=%s" % (side, case)) | |
| prefix = side.upper() if case == "LT_/RT_" else side | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for item in selected: | |
| old_name = item.split(" ")[-1] if " " in item else item | |
| if not cmds.objExists(old_name): | |
| cmds.warning("Cluster %s does not exist." % old_name) | |
| continue | |
| new_name = prefix + old_name | |
| if new_name == old_name: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(old_name) | |
| print("auto_prefix: Renaming deformer %s to %s" % (old_name, new_name)) | |
| new_name = cmds.rename(old_name, new_name) | |
| if handle: | |
| print("auto_prefix: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if old_name in self.cluster_colors: | |
| color = self.cluster_colors.pop(old_name) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("auto_prefix: Failed: %s" % str(e)) | |
| cmds.warning("Failed to add prefix: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def rename_all_clusters(self, *args): | |
| """Rename all clusters with a prefix, suffix, or numbering.""" | |
| print("rename_all_clusters: args=%s" % str(args)) | |
| if not self.clusters: | |
| cmds.warning("No clusters available to rename.") | |
| return | |
| result = pm.promptDialog( | |
| title="Rename All Clusters", | |
| message="Enter prefix (optional), suffix (optional), or leave blank for numbering:", | |
| text="", | |
| button=["OK", "Cancel"], | |
| defaultButton="OK", | |
| cancelButton="Cancel", | |
| dismissString="Cancel" | |
| ) | |
| if result == "OK": | |
| input_text = pm.promptDialog(query=True, text=True).strip() | |
| prefix = "" | |
| suffix = "" | |
| if input_text: | |
| parts = input_text.split("*") | |
| prefix = parts[0] if len(parts) > 0 else "" | |
| suffix = parts[1] if len(parts) > 1 else "" | |
| cmds.undoInfo(openChunk=True) | |
| try: | |
| for i, cluster in enumerate(self.clusters, 1): | |
| if not cmds.objExists(cluster): | |
| cmds.warning("Cluster %s does not exist." % cluster) | |
| continue | |
| new_name = cluster | |
| if prefix or suffix: | |
| new_name = prefix + cluster + suffix | |
| else: | |
| new_name = cluster + "_{:03d}".format(i) | |
| if new_name == cluster: | |
| continue | |
| if cmds.objExists(new_name): | |
| cmds.warning("Name %s already exists." % new_name) | |
| continue | |
| handle_new_name = new_name | |
| handle = self.get_cluster_handle(cluster) | |
| print("rename_all_clusters: Renaming deformer %s to %s" % (cluster, new_name)) | |
| new_name = cmds.rename(cluster, new_name) | |
| if handle: | |
| print("rename_all_clusters: Renaming handle %s to %s" % (handle, handle_new_name)) | |
| cmds.rename(handle, handle_new_name) | |
| if cluster in self.cluster_colors: | |
| color = self.cluster_colors.pop(cluster) | |
| self.cluster_colors[new_name] = color | |
| self.update_cluster_list() | |
| except Exception as e: | |
| print("rename_all_clusters: Failed: %s" % str(e)) | |
| cmds.warning("Failed to rename clusters: %s" % str(e)) | |
| finally: | |
| cmds.undoInfo(closeChunk=True) | |
| def run_tool(): | |
| """Launch the tool.""" | |
| VVCLrenamer() | |
| if __name__ == "__main__": | |
| run_tool() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment