Skip to content

Instantly share code, notes, and snippets.

@Pakmanv
Created May 2, 2025 18:13
Show Gist options
  • Select an option

  • Save Pakmanv/908877a876eb09c26af93d932a8cf375 to your computer and use it in GitHub Desktop.

Select an option

Save Pakmanv/908877a876eb09c26af93d932a8cf375 to your computer and use it in GitHub Desktop.
Matching Renamer 1.0
import maya.cmds as cmds
from PySide2 import QtWidgets, QtCore
import time
import json
import os
import getpass
import re
from collections import deque
class ValidatedInputDialog(QtWidgets.QDialog):
def __init__(self, title, label, default_text="", parent=None):
super(ValidatedInputDialog, self).__init__(parent)
self.setWindowTitle(title)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.label = QtWidgets.QLabel(label)
self.input = QtWidgets.QLineEdit(default_text)
self.feedback = QtWidgets.QLabel("")
self.feedback.setStyleSheet("color: red;")
self.buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
self.ok_button = self.buttons.button(QtWidgets.QDialogButtonBox.Ok)
self.ok_button.setEnabled(False)
self.layout.addWidget(self.label)
self.layout.addWidget(self.input)
self.layout.addWidget(self.feedback)
self.layout.addWidget(self.buttons)
self.input.textChanged.connect(self.validate_input)
self.buttons.accepted.connect(self.accept)
self.buttons.rejected.connect(self.reject)
self.valid = False
def validate_input(self):
text = self.input.text()
names = [name.strip() for name in text.split(',') if name.strip()] if ',' in text else [text.strip()]
all_valid = True
error_messages = []
for name in names:
if not name or name.isspace():
error_messages.append("Empty name not allowed")
all_valid = False
elif not name[0].isalpha():
error_messages.append(f"'{name}' must start with a letter")
all_valid = False
elif not re.match(r'^[a-zA-Z][a-zA-Z0-9_]*$', name):
error_messages.append(f"'{name}' contains invalid characters")
all_valid = False
if not names:
error_messages.append("No valid names entered")
all_valid = False
self.valid = all_valid
self.ok_button.setEnabled(all_valid)
if error_messages:
self.feedback.setText("; ".join(error_messages))
self.feedback.setStyleSheet("color: red;")
else:
self.feedback.setText("Valid name(s)")
self.feedback.setStyleSheet("color: #CCCCCC;")
def get_text(self):
return self.input.text()
class MatchingRenamerUI(QtWidgets.QWidget):
def __init__(self):
super(MatchingRenamerUI, self).__init__()
self.setWindowTitle("Matching Renamer VV 1.0")
# Set the window to stay on top by default
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.selection_data = []
self.script_job_id = None
self.undo_stack = deque(maxlen=50)
self.redo_stack = deque(maxlen=50)
self.drag_enabled = False
self.is_renaming = False
self.always_on_top = True # Track the "stay on top" state
self.resize(600, 500)
self.user = getpass.getuser()
self.base_path = f"C:/Users/{self.user}/Documents/vvtoolbag/setlistmanager"
if not os.path.exists(self.base_path):
os.makedirs(self.base_path)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.menu_bar = QtWidgets.QMenuBar()
self.actions_menu = QtWidgets.QMenu("Actions", self)
self.menu_bar.addMenu(self.actions_menu)
self.save_action = QtWidgets.QAction("Save List", self)
self.load_action = QtWidgets.QAction("Load List", self)
self.undo_action = QtWidgets.QAction("Undo", self)
self.redo_action = QtWidgets.QAction("Redo", self)
self.toggle_on_top_action = QtWidgets.QAction("Disable Always On Top", self) # New action
self.actions_menu.addAction(self.save_action)
self.actions_menu.addAction(self.load_action)
self.actions_menu.addSeparator()
self.actions_menu.addAction(self.undo_action)
self.actions_menu.addAction(self.redo_action)
self.actions_menu.addSeparator()
self.actions_menu.addAction(self.toggle_on_top_action)
self.save_action.triggered.connect(self.save_list)
self.load_action.triggered.connect(self.load_list)
self.undo_action.triggered.connect(self.undo)
self.redo_action.triggered.connect(self.redo)
self.toggle_on_top_action.triggered.connect(self.toggle_always_on_top) # Connect new action
self.layout.addWidget(self.menu_bar)
self.sort_search_layout = QtWidgets.QHBoxLayout()
self.sort_label = QtWidgets.QLabel("Sort by:")
self.sort_combo = QtWidgets.QComboBox()
self.sort_combo.addItems(["Selection Order", "Time", "Alphabetical A-Z", "Alphabetical Z-A"])
self.search_label = QtWidgets.QLabel("Search:")
self.search_input = QtWidgets.QLineEdit()
self.search_input.setPlaceholderText("Enter search term (e.g., TN_)")
self.search_input.textChanged.connect(self.update_list)
self.sort_search_layout.addWidget(self.sort_label)
self.sort_search_layout.addWidget(self.sort_combo)
self.sort_search_layout.addWidget(self.search_label)
self.sort_search_layout.addWidget(self.search_input)
self.sort_search_layout.addStretch()
self.list_container = QtWidgets.QWidget()
self.list_container_layout = QtWidgets.QHBoxLayout()
self.list_container.setLayout(self.list_container_layout)
self.name_list_container = QtWidgets.QWidget()
self.name_list_layout = QtWidgets.QVBoxLayout()
self.name_list_container.setLayout(self.name_list_layout)
self.name_list_header = QtWidgets.QHBoxLayout()
self.name_list_label = QtWidgets.QLabel("Name List")
self.name_list_count_label = QtWidgets.QLabel("List Objects: 0")
self.name_selection_feedback_label = QtWidgets.QLabel("All/0")
self.name_selection_feedback_label.setAlignment(QtCore.Qt.AlignRight)
self.name_list_header.addWidget(self.name_list_label)
self.name_list_header.addStretch()
self.name_list_header.addWidget(self.name_list_count_label)
self.name_list_header.addWidget(self.name_selection_feedback_label)
self.name_list_widget = QtWidgets.QListWidget()
self.name_list_widget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.name_list_widget.itemDoubleClicked.connect(self.rename_item)
self.name_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.name_list_widget.customContextMenuRequested.connect(self.show_name_list_context_menu)
self.name_list_widget.itemSelectionChanged.connect(self.update_selection_count)
self.rename_order_layout = QtWidgets.QHBoxLayout()
self.rename_order_label = QtWidgets.QLabel("Rename by:")
self.rename_order_combo = QtWidgets.QComboBox()
self.rename_order_combo.addItems(["List Order", "Selection Order"])
self.rename_order_combo.setCurrentText("List Order")
self.rename_order_layout.addWidget(self.rename_order_label)
self.rename_order_layout.addWidget(self.rename_order_combo)
self.rename_order_layout.addStretch()
self.name_list_layout.addLayout(self.name_list_header)
self.name_list_layout.addWidget(self.name_list_widget)
self.name_list_layout.addLayout(self.rename_order_layout)
self.scene_list_container = QtWidgets.QWidget()
self.scene_list_layout = QtWidgets.QVBoxLayout()
self.scene_list_container.setLayout(self.scene_list_layout)
self.scene_list_header = QtWidgets.QHBoxLayout()
self.scene_list_label = QtWidgets.QLabel("Scene List")
self.scene_selection_count_label = QtWidgets.QLabel("Scene Selection: <span style='color: #FFFFFF;'>0</span>")
self.scene_selection_count_label.setAlignment(QtCore.Qt.AlignRight)
self.scene_list_header.addWidget(self.scene_list_label)
self.scene_list_header.addStretch()
self.scene_list_header.addWidget(self.scene_selection_count_label)
self.scene_list_widget = QtWidgets.QListWidget()
self.scene_list_widget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.scene_list_widget.setDragEnabled(False)
self.scene_list_widget.setAcceptDrops(False)
self.scene_list_widget.setDragDropMode(QtWidgets.QAbstractItemView.NoDragDrop)
self.scene_list_widget.itemDoubleClicked.connect(self.rename_scene_item)
self.scene_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.scene_list_widget.customContextMenuRequested.connect(self.show_scene_list_context_menu)
self.scene_list_layout.addLayout(self.scene_list_header)
self.scene_list_layout.addWidget(self.scene_list_widget)
self.list_container_layout.addWidget(self.name_list_container, stretch=3)
self.list_container_layout.addWidget(self.scene_list_container, stretch=2)
self.feedback_layout = QtWidgets.QHBoxLayout()
self.feedback_label = QtWidgets.QLabel("Ready")
self.feedback_label.setStyleSheet("color: white;")
self.feedback_layout.addWidget(self.feedback_label)
self.feedback_layout.addStretch()
self.layout.addLayout(self.sort_search_layout)
self.layout.addWidget(self.list_container)
self.layout.addLayout(self.feedback_layout)
self.button_layout = QtWidgets.QHBoxLayout()
self.load_button = QtWidgets.QPushButton("Load Objects as Name")
self.load_button.clicked.connect(self.load_objects)
self.load_button.setStyleSheet("background-color: #C1E1C1; color: #000000;")
self.rename_button = QtWidgets.QPushButton("Rename Selections")
self.rename_button.clicked.connect(self.rename_selection)
self.rename_button.setStyleSheet("background-color: #A7C7E7; color: #000000;")
self.button_layout.addWidget(self.load_button)
self.button_layout.addWidget(self.rename_button)
self.layout.addLayout(self.button_layout)
self.footer_label = QtWidgets.QLabel("Matching Renamer VV 1.0 - Tool made by Vypac Voeur")
self.footer_label.setStyleSheet("font-size: 10px; color: #AAAAAA;")
self.layout.addWidget(self.footer_label, alignment=QtCore.Qt.AlignLeft)
self.sort_combo.currentIndexChanged.connect(self.update_list)
self.update_selection_count()
self.setup_selection_monitor()
def toggle_always_on_top(self):
"""Toggle the 'Always On Top' behavior of the window."""
self.always_on_top = not self.always_on_top
if self.always_on_top:
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.toggle_on_top_action.setText("Disable Always On Top")
self.feedback_label.setText("Always On Top enabled")
else:
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)
self.toggle_on_top_action.setText("Enable Always On Top")
self.feedback_label.setText("Always On Top disabled")
self.feedback_label.setStyleSheet("color: white;")
self.show() # Re-show the window to apply the new flags
def setup_selection_monitor(self):
if self.script_job_id is not None:
try:
cmds.scriptJob(kill=self.script_job_id)
except:
pass
self.script_job_id = None
self.script_job_id = cmds.scriptJob(
event=["SelectionChanged", self.update_selection_count],
protected=True
)
def closeEvent(self, event):
if self.script_job_id is not None:
try:
cmds.scriptJob(kill=self.script_job_id)
except:
pass
self.script_job_id = None
super(MatchingRenamerUI, self).closeEvent(event)
def save_state(self, action_type, data_before, data_after):
self.undo_stack.append({
"action": action_type,
"before": data_before,
"after": data_after
})
self.redo_stack.clear()
self.feedback_label.setText(f"{action_type} performed")
self.feedback_label.setStyleSheet("color: white;")
def undo(self):
if not self.undo_stack:
self.feedback_label.setText("Nothing to undo")
self.feedback_label.setStyleSheet("color: red;")
return
state = self.undo_stack.pop()
self.redo_stack.append({
"action": state["action"],
"before": state["after"],
"after": state["before"]
})
self.selection_data = state["before"]
self.update_list()
self.feedback_label.setText(f"Undid {state['action']}")
self.feedback_label.setStyleSheet("color: white;")
def redo(self):
if not self.redo_stack:
self.feedback_label.setText("Nothing to redo")
self.feedback_label.setStyleSheet("color: red;")
return
state = self.redo_stack.pop()
self.undo_stack.append({
"action": state["action"],
"before": state["after"],
"after": state["before"]
})
self.selection_data = state["before"]
self.update_list()
self.feedback_label.setText(f"Redid {state['action']}")
self.feedback_label.setStyleSheet("color: white;")
def show_name_list_context_menu(self, position):
menu = QtWidgets.QMenu()
add_action = menu.addAction("Add New Entry")
menu.addSeparator()
rename_action = menu.addAction("Rename")
delete_action = menu.addAction("Delete Selected")
clear_action = menu.addAction("Clear List")
action = menu.exec_(self.name_list_widget.mapToGlobal(position))
if action == clear_action:
self.clear_list()
elif action == add_action:
self.add_new_entry()
elif action == delete_action:
self.delete_selected_entries()
elif action == rename_action:
selected_items = self.name_list_widget.selectedItems()
if len(selected_items) == 1:
self.rename_item(selected_items[0])
else:
self.feedback_label.setText("Select exactly one item to rename")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("Select exactly one item to rename")
def show_scene_list_context_menu(self, position):
menu = QtWidgets.QMenu()
rename_action = menu.addAction("Rename")
remove_action = menu.addAction("Remove Selected")
menu.addSeparator()
toggle_drag_action = menu.addAction("Enable Drag-and-Drop" if not self.drag_enabled else "Disable Drag-and-Drop")
action = menu.exec_(self.scene_list_widget.mapToGlobal(position))
if action == rename_action:
selected_items = self.scene_list_widget.selectedItems()
if len(selected_items) == 1:
self.rename_scene_item(selected_items[0])
else:
self.feedback_label.setText("Select exactly one item to rename")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("Select exactly one item to rename")
elif action == remove_action:
self.remove_selected_scene_items()
elif action == toggle_drag_action:
self.toggle_drag_drop()
def toggle_drag_drop(self):
self.drag_enabled = not self.drag_enabled
if self.drag_enabled:
self.scene_list_widget.setDragEnabled(True)
self.scene_list_widget.setAcceptDrops(True)
self.scene_list_widget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
self.feedback_label.setText("Drag-and-Drop enabled")
else:
self.scene_list_widget.setDragEnabled(False)
self.scene_list_widget.setAcceptDrops(False)
self.scene_list_widget.setDragDropMode(QtWidgets.QAbstractItemView.NoDragDrop)
self.feedback_label.setText("Drag-and-Drop disabled")
self.feedback_label.setStyleSheet("color: white;")
def remove_selected_scene_items(self):
selected_items = self.scene_list_widget.selectedItems()
if not selected_items:
self.feedback_label.setText("No items selected to remove")
self.feedback_label.setStyleSheet("color: red;")
return
selected_names = [item.text() for item in selected_items]
selected_scene_objects = cmds.ls(selection=True, long=True)
objects_to_deselect = []
for obj in selected_scene_objects:
short_name = obj.split('|')[-1].split(':')[-1]
if short_name in selected_names:
objects_to_deselect.append(obj)
if objects_to_deselect:
cmds.select(objects_to_deselect, deselect=True)
self.feedback_label.setText(f"Removed {len(objects_to_deselect)} item(s) from scene selection")
self.feedback_label.setStyleSheet("color: white;")
else:
self.feedback_label.setText("No matching objects found in scene selection")
self.feedback_label.setStyleSheet("color: red;")
self.update_selection_count()
def is_valid_name(self, name):
if not name or name.isspace():
return False
if not name[0].isalpha():
return False
if not re.match(r'^[a-zA-Z][a-zA-Z0-9_]*$', name):
return False
return True
def get_unique_name(self, name, existing_names):
if name not in existing_names:
return name
base_name = name
counter = 1
while f"{base_name}_{counter}" in existing_names:
counter += 1
return f"{base_name}_{counter}"
def get_unique_scene_name(self, name, current_obj=None):
if current_obj and name == current_obj.split('|')[-1].split(':')[-1]:
return name
if not cmds.objExists(name):
return name
base_name = name
counter = 1
while True:
test_name = f"{base_name}_{counter}"
if not cmds.objExists(test_name):
return test_name
if current_obj and test_name == current_obj.split('|')[-1].split(':')[-1]:
return test_name
counter += 1
def add_new_entry(self):
data_before = self.selection_data[:]
dialog = ValidatedInputDialog(
title="Add New Entry",
label="Enter name(s) (comma-separated, e.g., apple,pear,chair):",
parent=self
)
if dialog.exec_() == QtWidgets.QDialog.Accepted and dialog.valid:
new_names = dialog.get_text()
name_list = [name.strip() for name in new_names.split(',') if name.strip()]
existing_names = [item["name"] for item in self.selection_data]
added_count = 0
for name in name_list:
if self.is_valid_name(name):
unique_name = self.get_unique_name(name, existing_names)
self.selection_data.append({"name": unique_name, "time": time.time()})
existing_names.append(unique_name)
added_count += 1
if added_count > 0:
self.save_state("Add Entries", data_before, self.selection_data[:])
self.update_list()
self.feedback_label.setText(f"Added {added_count} new entry(ies)")
self.feedback_label.setStyleSheet("color: white;")
print(f"Added {added_count} new entry(ies)")
else:
self.feedback_label.setText("No valid names entered")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("No valid names entered")
def delete_selected_entries(self):
selected_items = self.name_list_widget.selectedItems()
selected_names = [item.text() for item in selected_items]
if not selected_names:
self.feedback_label.setText("No items selected to delete")
self.feedback_label.setStyleSheet("color: red;")
return
data_before = self.selection_data[:]
self.selection_data = [item for item in self.selection_data
if item["name"] not in selected_names]
self.save_state("Delete Entries", data_before, self.selection_data[:])
self.update_list()
self.feedback_label.setText(f"Deleted {len(selected_names)} selected entries")
self.feedback_label.setStyleSheet("color: white;")
def clear_list(self):
if not self.selection_data:
self.feedback_label.setText("List is already empty")
self.feedback_label.setStyleSheet("color: red;")
return
data_before = self.selection_data[:]
self.selection_data = []
self.save_state("Clear List", data_before, self.selection_data[:])
self.update_list()
self.feedback_label.setText("Cleared list")
self.feedback_label.setStyleSheet("color: white;")
def get_selected_sets(self):
selected = cmds.ls(selection=True, long=False)
current_time = time.time()
new_selections = []
existing_names = [item["name"] for item in self.selection_data]
for sel in selected:
if sel:
short_name = sel.split('|')[-1].split(':')[-1]
if self.is_valid_name(short_name):
unique_name = self.get_unique_name(short_name, existing_names)
new_selections.append({"name": unique_name, "time": current_time})
existing_names.append(unique_name)
else:
self.feedback_label.setText(f"Skipped invalid object name: {short_name}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Skipped invalid object name: {short_name}")
return new_selections
def load_objects(self):
data_before = self.selection_data[:]
new_selections = self.get_selected_sets()
self.selection_data.extend(new_selections)
self.save_state("Load Objects", data_before, self.selection_data[:])
self.update_list()
self.feedback_label.setText(f"Loaded {len(new_selections)} object(s)")
self.feedback_label.setStyleSheet("color: white;")
def update_list(self):
self.name_list_widget.clear()
search_term = self.search_input.text().lower()
if search_term:
filtered_data = [item for item in self.selection_data if search_term in item["name"].lower()]
else:
filtered_data = self.selection_data
sort_mode = self.sort_combo.currentText()
if sort_mode == "Selection Order":
sorted_data = filtered_data
elif sort_mode == "Time":
sorted_data = sorted(filtered_data, key=lambda x: x["time"], reverse=True)
elif sort_mode == "Alphabetical A-Z":
sorted_data = sorted(filtered_data, key=lambda x: x["name"].lower())
else:
sorted_data = sorted(filtered_data, key=lambda x: x["name"].lower(), reverse=True)
for item in sorted_data:
self.name_list_widget.addItem(item["name"])
self.name_list_count_label.setText(f"List Objects: {len(filtered_data)}")
self.update_selection_count()
def rename_item(self, item):
current_name = item.text()
row = self.name_list_widget.row(item)
dialog = ValidatedInputDialog(
title="Rename List Item",
label="Enter new name (must start with a letter, contain only letters, numbers, underscores):",
default_text=current_name,
parent=self
)
if dialog.exec_() == QtWidgets.QDialog.Accepted and dialog.valid:
new_name = dialog.get_text()
existing_names = [d["name"] for d in self.selection_data if d["name"] != current_name]
unique_name = self.get_unique_name(new_name, existing_names)
data_before = self.selection_data[:]
if row < len(self.selection_data):
self.selection_data[row]["name"] = unique_name
self.selection_data[row]["time"] = time.time()
self.feedback_label.setText(f"Updated list item to {unique_name}")
self.feedback_label.setStyleSheet("color: white;")
print(f"Updated list item to {unique_name}")
else:
self.feedback_label.setText("Error: Invalid row index")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("Error: Invalid row index")
self.save_state("Rename Item", data_before, self.selection_data[:])
self.update_list()
def rename_scene_item(self, item):
current_name = item.text()
selected = cmds.ls(selection=True, long=True)
matching_objects = [obj for obj in selected if obj.split('|')[-1].split(':')[-1] == current_name]
if not matching_objects:
self.feedback_label.setText(f"Object {current_name} not found in selection")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Object {current_name} not found in selection")
return
if len(matching_objects) > 1:
self.feedback_label.setText(f"Multiple objects with name {current_name} found; select one")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Multiple objects with name {current_name} found; select one")
return
obj_long_name = matching_objects[0]
dialog = ValidatedInputDialog(
title="Rename Scene Object",
label="Enter new name (must start with a letter, contain only letters, numbers, underscores):",
default_text=current_name,
parent=self
)
if dialog.exec_() == QtWidgets.QDialog.Accepted and dialog.valid:
new_name = dialog.get_text()
unique_name = self.get_unique_scene_name(new_name, current_obj=obj_long_name)
try:
cmds.rename(obj_long_name, unique_name)
self.feedback_label.setText(f"Renamed {current_name} to {unique_name} in scene")
self.feedback_label.setStyleSheet("color: white;")
print(f"Renamed {current_name} to {unique_name} in scene")
self.update_selection_count()
except Exception as e:
self.feedback_label.setText(f"Failed to rename {current_name}: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Failed to rename {current_name}: {str(e)}")
def save_list(self):
file_path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save List",
self.base_path, "JSON Files (*.json)")
if file_path:
try:
with open(file_path, 'w') as f:
json.dump(self.selection_data, f, indent=4)
self.feedback_label.setText("List saved successfully")
self.feedback_label.setStyleSheet("color: white;")
print("List saved successfully")
except Exception as e:
self.feedback_label.setText(f"Failed to save: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Failed to save: {str(e)}")
def load_list(self):
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Load List",
self.base_path, "JSON Files (*.json)")
if file_path:
try:
with open(file_path, 'r') as f:
loaded_data = json.load(f)
valid_data = []
existing_names = [item["name"] for item in self.selection_data]
for item in loaded_data:
if self.is_valid_name(item.get("name", "")):
unique_name = self.get_unique_name(item["name"], existing_names)
valid_data.append({"name": unique_name, "time": item.get("time", time.time())})
existing_names.append(unique_name)
if valid_data:
data_before = self.selection_data[:]
self.selection_data.extend(valid_data)
self.save_state("Load List", data_before, self.selection_data[:])
self.update_list()
self.feedback_label.setText(f"Loaded {len(valid_data)} valid name(s) successfully")
self.feedback_label.setStyleSheet("color: white;")
print(f"Loaded {len(valid_data)} valid name(s) successfully")
else:
self.feedback_label.setText("No valid names found in the file")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("No valid names found in the file")
except Exception as e:
self.feedback_label.setText(f"Failed to load: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Failed to load: {str(e)}")
def update_selection_count(self):
selected = cmds.ls(selection=True, long=False)
self.scene_selection_count_label.setText(f"Scene Selection: <span style='color: #FFFFFF;'>{len(selected)}</span>")
self.scene_list_widget.clear()
for sel in selected:
short_name = sel.split('|')[-1].split(':')[-1]
self.scene_list_widget.addItem(short_name)
selected_items = self.name_list_widget.selectedItems()
scene_count = len(selected)
selected_count = len(selected_items)
if selected_count == 0:
feedback_text = f"All/{scene_count}"
else:
feedback_text = f"{selected_count}/{scene_count}"
color = "#00FF00" if (selected_count == scene_count and scene_count > 0) or (selected_count == 0 and scene_count == len(self.selection_data) and scene_count > 0) else "#CCCCCC"
self.name_selection_feedback_label.setText(f"<span style='color: {color};'>{feedback_text}</span>")
def rename_selection(self):
if self.is_renaming:
return
self.is_renaming = True
try:
def perform_rename():
selected = cmds.ls(selection=True, long=True)
if not selected:
self.feedback_label.setText("No objects selected in the scene")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("No objects selected in the scene")
return 0, []
rename_mode = self.rename_order_combo.currentText()
renamed_count = 0
failed_names = []
if rename_mode == "Selection Order":
selected_items = self.name_list_widget.selectedItems()
selected_names = [item.text() for item in selected_items]
if not selected_names:
self.feedback_label.setText("No names selected in Name List for Selection Order")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning("No names selected in Name List for Selection Order")
return 0, []
if len(selected_names) != len(selected):
self.feedback_label.setText(f"Selection Order requires {len(selected)} names selected, got {len(selected_names)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Selection Order requires {len(selected)} names selected, got {len(selected_names)}")
return 0, []
try:
for i, obj in enumerate(selected):
new_name = selected_names[i]
current_short_name = obj.split('|')[-1].split(':')[-1]
if new_name == current_short_name:
renamed_count += 1
continue
if self.is_valid_name(new_name):
unique_name = self.get_unique_scene_name(new_name, current_obj=obj)
try:
cmds.rename(obj, unique_name)
renamed_count += 1
print(f"Renamed {obj} to {unique_name}")
except Exception as e:
failed_names.append(f"{new_name}: {str(e)}")
else:
failed_names.append(f"{new_name}: Invalid name")
except Exception as e:
self.feedback_label.setText(f"Unexpected error during rename: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Unexpected error during rename: {str(e)}")
return 0, []
else: # List Order
selected_items = self.name_list_widget.selectedItems()
selected_names = [item.text() for item in selected_items]
if selected_names: # Use selected items if any
if len(selected_names) != len(selected):
self.feedback_label.setText(f"List Order with selected names requires {len(selected)} names selected, got {len(selected_names)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"List Order with selected names requires {len(selected)} names selected, got {len(selected_names)}")
return 0, []
try:
for i, obj in enumerate(selected):
new_name = selected_names[i]
current_short_name = obj.split('|')[-1].split(':')[-1]
if new_name == current_short_name:
renamed_count += 1
continue
if self.is_valid_name(new_name):
unique_name = self.get_unique_scene_name(new_name, current_obj=obj)
try:
cmds.rename(obj, unique_name)
renamed_count += 1
print(f"Renamed {obj} to {unique_name}")
except Exception as e:
failed_names.append(f"{new_name}: {str(e)}")
else:
failed_names.append(f"{new_name}: Invalid name")
except Exception as e:
self.feedback_label.setText(f"Unexpected error during rename: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Unexpected error during rename: {str(e)}")
return 0, []
else: # Use full list if no items selected
if len(selected) > len(self.selection_data):
self.feedback_label.setText(f"List Order requires scene selection ({len(selected)}) <= Name List count ({len(self.selection_data)})")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"List Order requires scene selection ({len(selected)}) <= Name List count ({len(self.selection_data)})")
return 0, []
search_term = self.search_input.text().lower()
if search_term:
filtered_data = [item for item in self.selection_data if search_term in item["name"].lower()]
else:
filtered_data = self.selection_data
sort_mode = self.sort_combo.currentText()
if sort_mode == "Selection Order":
sorted_data = filtered_data
elif sort_mode == "Time":
sorted_data = sorted(filtered_data, key=lambda x: x["time"], reverse=True)
elif sort_mode == "Alphabetical A-Z":
sorted_data = sorted(filtered_data, key=lambda x: x["name"].lower())
else:
sorted_data = sorted(filtered_data, key=lambda x: x["name"].lower(), reverse=True)
if len(selected) > len(sorted_data):
self.feedback_label.setText(f"Not enough names in filtered/sorted list ({len(sorted_data)}) for {len(selected)} objects")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Not enough names in filtered/sorted list ({len(sorted_data)}) for {len(selected)} objects")
return 0, []
try:
for i, obj in enumerate(selected):
new_name = sorted_data[i]["name"]
current_short_name = obj.split('|')[-1].split(':')[-1]
if new_name == current_short_name:
renamed_count += 1
continue
if self.is_valid_name(new_name):
unique_name = self.get_unique_scene_name(new_name, current_obj=obj)
try:
cmds.rename(obj, unique_name)
renamed_count += 1
print(f"Renamed {obj} to {unique_name}")
except Exception as e:
failed_names.append(f"{new_name}: {str(e)}")
else:
failed_names.append(f"{new_name}: Invalid name")
except Exception as e:
self.feedback_label.setText(f"Unexpected error during rename: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Unexpected error during rename: {str(e)}")
return 0, []
return renamed_count, failed_names
# Perform first rename
renamed_count, failed_names = perform_rename()
if renamed_count == 0 and failed_names:
return # Exit if first rename failed
# If in Selection Order, perform second rename immediately
if self.rename_order_combo.currentText() == "Selection Order":
renamed_count2, failed_names2 = perform_rename()
renamed_count += renamed_count2
failed_names.extend(failed_names2)
self.update_selection_count()
# Compute adjusted count
divisor = 2 if self.rename_order_combo.currentText() == "Selection Order" else 1
adjusted_count = renamed_count // divisor
expected_count = len(cmds.ls(selection=True, long=True)) * divisor
if renamed_count == expected_count:
self.feedback_label.setText(f"Renamed {adjusted_count} object(s) successfully")
self.feedback_label.setStyleSheet("color: white;")
print(f"Renamed {adjusted_count} object(s) successfully")
else:
self.feedback_label.setText(f"Renamed {adjusted_count} object(s). Failed: {', '.join(failed_names)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Renamed {adjusted_count} object(s). Failed: {', '.join(failed_names)}")
except Exception as e:
self.feedback_label.setText(f"Unexpected error in rename_selection: {str(e)}")
self.feedback_label.setStyleSheet("color: red;")
cmds.warning(f"Unexpected error in rename_selection: {str(e)}")
finally:
self.is_renaming = False
def show_ui():
global matching_renamer_ui
try:
if matching_renamer_ui.script_job_id is not None:
cmds.scriptJob(kill=matching_renamer_ui.script_job_id)
matching_renamer_ui.close()
matching_renamer_ui.deleteLater()
except:
pass
matching_renamer_ui = MatchingRenamerUI()
matching_renamer_ui.show()
if __name__ == "__main__":
show_ui()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment