Skip to content

Instantly share code, notes, and snippets.

@benmorgantd
Last active July 11, 2024 06:43
Show Gist options
  • Save benmorgantd/c3a5d0719b62fcd75aecdd210ab6054b to your computer and use it in GitHub Desktop.
Save benmorgantd/c3a5d0719b62fcd75aecdd210ab6054b to your computer and use it in GitHub Desktop.
Scene cleanup tools including naming, labeling, and history/node deletion.
# our standard imports when using pySide
import maya.OpenMayaUI as omui
import maya.cmds as cmds
import maya.mel as mel
from PySide import QtCore
from PySide import QtGui
from shiboken import wrapInstance
# convenience function to get the main window. Part of the basic pySide Gui framework.
def maya_main_window():
main_window_ptr = omui.MQtUtil.mainWindow()
return wrapInstance(long(main_window_ptr), QtGui.QWidget)
# Cleanup toolbox that includes renaming functions, relabeling functions, and History/Node Cleanup
class CleanupToolsUI(QtGui.QDialog):
def __init__(self, parent=maya_main_window()):
# init the QDialog class to that the child inherits its properties
super(CleanupToolsUI, self).__init__(parent)
# name the window
self.setWindowTitle("bm_cleanupTools")
# make the window a "tool" in Maya's eyes so that it stays on top when you click off
self.setWindowFlags(QtCore.Qt.Tool)
# Makes the object get deleted from memory, not just hidden, when it is closed.
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
# call our create_layout function here in the init
self.create_layout()
# establish our connections
self.create_connections()
def create_layout(self):
# place to put layout code
# formatting for labels
self.labelFont = QtGui.QFont()
self.labelFont.setBold(1)
self.labelFont.setPixelSize(12)
# History Deleting tools
self.delLbl = QtGui.QLabel()
self.delLbl.setFont(self.labelFont)
self.delLbl.setAlignment(QtCore.Qt.AlignVCenter)
self.delLbl.setText(" Delete:")
self.delLbl.setStyleSheet("QLabel {color: #b7b7b7;}")
self.delHistBtn = QtGui.QPushButton("History")
# change our button's text and background color
self.delHistBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
self.delHistBtn.setToolTip("Delete All By Type: History")
self.delNonDefHistBtn = QtGui.QPushButton("Non-Deformer History")
self.delNonDefHistBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
self.delNonDefHistBtn.setToolTip("Delete All By Type: Non-Deformer History")
self.delUnusedNodesBtn = QtGui.QPushButton("Unused Nodes")
self.delUnusedNodesBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
self.delUnusedNodesBtn.setToolTip("Delete Unused Nodes")
# relabel tools
self.relabelLbl = QtGui.QLabel()
self.relabelLbl.setFont(self.labelFont)
self.relabelLbl.setText("Relabel Tools: ")
self.relabelLbl.setStyleSheet("QLabel {color: #b7b7b7;}")
self.relabelBtn = QtGui.QPushButton("Relabel")
self.relabelBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
# radio buttons to determine whether the label is a prefix or suffix. It seems like they automatically
# act as a group.
self.suffixRadio = QtGui.QRadioButton("Suffix")
self.prefixRadio = QtGui.QRadioButton("Prefix")
self.prefixRadio.setChecked(1)
# a dropdown menu with common prefix/suffix choices
self.labelBox = QtGui.QComboBox()
self.labelBox.addItem("BIND")
self.labelBox.addItem("DRIVE")
self.labelBox.addItem("FK")
self.labelBox.addItem("GRP")
self.labelBox.addItem("GEO")
self.labelBox.addItem("IK")
self.labelBox.addItem("JNT")
self.labelBox.addItem("R")
self.labelBox.addItem("L")
# add a check box that determines if the label is replaced or appended
self.labelCheckBox = QtGui.QCheckBox("Append Label")
self.labelCheckBox.setChecked(0)
self.labelCheckBox.setToolTip("Tip: Append the new label instead of replacing the old one.\n"
" Ex: L_head_GEO")
# renamer tools
self.renamerLbl = QtGui.QLabel()
self.renamerLbl.setFont(self.labelFont)
self.renamerLbl.setText("Renamer Tools: ")
self.renamerLbl.setStyleSheet("QLabel {color: #b7b7b7;}")
# QLineEdit is a good object type for standard text input
self.renamerTxt = QtGui.QLineEdit()
self.renamerTxt.setPlaceholderText("Rename")
self.renamerTxt.setToolTip("Tip: Use # to insert a number inside the string.\n"
" - Supports up to ####")
self.renamerBtn = QtGui.QPushButton("Rename")
self.renamerBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
# selection tools
self.selectionTxt = QtGui.QLineEdit()
self.selectionTxt.setToolTip("Tip: Use * to select all objects containing that string.\n"
" Ex: *_GEO will select all nodes ending in _GEO")
self.selectionTxt.setPlaceholderText("Select")
self.selectionBtn = QtGui.QPushButton("Select")
self.selectionBtn.setStyleSheet("QPushButton {background-color: #828282; color: black;}")
# create a layout
main_layout = QtGui.QGridLayout()
# change our margins
main_layout.setContentsMargins(2, 2, 2, 2)
# fix margin spacing
main_layout.setSpacing(5)
main_layout.setColumnMinimumWidth(0, 50)
# add our items to our layout
# delete history tools
main_layout.addWidget(self.delLbl, 0, 0)
main_layout.addWidget(self.delHistBtn, 0, 1)
main_layout.addWidget(self.delNonDefHistBtn, 0, 2)
main_layout.addWidget(self.delUnusedNodesBtn, 0, 3)
# relabel tools
main_layout.addWidget(self.relabelBtn, 2, 3, 1, 1)
main_layout.addWidget(self.suffixRadio, 2, 1)
main_layout.addWidget(self.labelBox, 2, 2)
main_layout.addWidget(self.labelCheckBox, 3, 3)
# renamer tools
# have our text widget span across two columns (row,column,rowSpan,columnSpan)
main_layout.addWidget(self.renamerTxt, 4, 0, 1, 3)
main_layout.addWidget(self.renamerBtn, 4, 3)
main_layout.addWidget(self.selectionBtn, 5, 3, 1, 1)
main_layout.addWidget(self.selectionTxt, 5, 0, 1, 3)
# set column stretching
main_layout.setColumnStretch(0, 1)
main_layout.setColumnStretch(1, 1)
main_layout.setColumnStretch(2, 1)
main_layout.setColumnStretch(3, 1)
main_layout.setColumnStretch(4, 1)
# fit window to contents
self.resize(self.minimumSizeHint())
# set our layout
self.setLayout(main_layout)
def create_connections(self):
# establish our button connections
self.delHistBtn.clicked.connect(self.delHist)
self.delNonDefHistBtn.clicked.connect(self.delNonDefHist)
self.delUnusedNodesBtn.clicked.connect(self.delUnusedNodes)
# we can use the returnPressed attribute on our text to run a function
self.renamerTxt.returnPressed.connect(self.renamerTxtFctn)
self.renamerBtn.clicked.connect(self.renamerTxtFctn)
self.relabelBtn.clicked.connect(self.relabelFctn)
self.selectionBtn.clicked.connect(self.selectionTxtFctn)
self.selectionTxt.returnPressed.connect(self.selectionTxtFctn)
# our functions for the button conections. If we just put the cmd in the
# clicked.connect function, it would call it when we open the Ui
def delHist(self):
mel.eval("DeleteAllHistory")
print "Cleanup Helper -- Delete All History"
def delNonDefHist(self):
mel.eval("BakeAllNonDefHistory")
print "Cleanup Helper -- Delete All Non-Deformer History"
def delUnusedNodes(self):
mel.eval('hyperShadePanelMenuCommand("hyperShadePanel1","deleteUnusedNodes")')
print "Cleanup Helper -- Delete Unused Nodes"
def renamerTxtFctn(self):
sel = cmds.ls(sl=1)
newName = self.renamerTxt.text().replace(" ", "_")
newName = newName.replace("'", "")
if "#" in newName:
j = newName.index("#")
k = newName.count("#")
newName = newName.split("#")
i = 1
if len(sel) > 0 and len(newName) > 0:
for obj in sel:
if j == 0:
# maya doesn't allow you to lead an object name with a number
cmds.rename(obj, newName[1:])
else:
if k == 1:
cmds.rename(obj, newName[0] + "%01d" % i + newName[1])
if k == 2:
cmds.rename(obj, newName[0] + "%02d" % i + newName[1])
if k == 3:
cmds.rename(obj, newName[0] + "%03d" % i + newName[1])
if k == 4:
cmds.rename(obj, newName[0] + "%04d" % i + newName[1])
i += 1
print "Cleanup Helper -- Rename Object"
def relabelFctn(self):
sel = cmds.ls(sl=1)
newLabel = self.labelBox.currentText()
if self.suffixRadio.isChecked():
position = "suffix"
else:
position = "prefix"
# returns a list containing all of the labels in the labelBox
labels = [self.labelBox.itemText(i) for i in range(self.labelBox.count())]
if len(sel) > 0:
for obj in cmds.ls(sl=1):
objName = obj
# strip off any old labels
if self.labelCheckBox.isChecked() == 0:
for label in labels:
objName = objName.replace("_" + label, "")
objName = objName.replace(label + "_", "")
if position == "prefix":
cmds.rename(obj, newLabel + "_" + objName)
else:
cmds.rename(obj, objName + "_" + newLabel)
print "Cleanup Helper -- Relabel Object"
def selectionTxtFctn(self):
sceneObjects = cmds.ls(type="transform")
selectionQuery = self.selectionTxt.text()
if "*" in selectionQuery:
if len(selectionQuery) == 1:
# if all the user entered was *, return
return
i = selectionQuery.index("*")
if i == 0:
for obj in sceneObjects:
if selectionQuery[1:-1] in obj:
cmds.select(obj, add=1)
else:
for obj in sceneObjects:
if selectionQuery[0:i] in obj and selectionQuery[i + 1:-2] in obj:
cmds.select(obj, add=1)
else:
cmds.select(selectionQuery, add=1)
def selectionTxtToolTipFctn(self):
QtGui.QToolTip.showText(self.selectionTxt.mapToGlobal(QtCore.QPoint()), "Test Tool Tip")
def showUI():
global ui
try:
ui.close()
except:
pass
ui = CleanupToolsUI()
# useful line of code that gets rid of lots of errors during testing
ui.setAttribute(QtCore.Qt.WA_DeleteOnClose)
ui.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment