457 lines
12 KiB
Python
457 lines
12 KiB
Python
"""
|
|
|
|
module docstring here
|
|
"""
|
|
|
|
from PySide2 import QtCore, QtWidgets, QtGui
|
|
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
|
|
from maya import cmds
|
|
from . import checker
|
|
from . import framelayout
|
|
|
|
try:
|
|
from importlib import reload
|
|
except ImportError:
|
|
# if python2, use build-in reload
|
|
pass
|
|
|
|
reload(framelayout)
|
|
reload(checker)
|
|
|
|
|
|
class Separator(QtWidgets.QWidget):
|
|
|
|
def __init__(self, category="", checkers=None):
|
|
super(Separator, self).__init__()
|
|
|
|
self.checkerWidgets = checkers
|
|
self.category = category
|
|
|
|
self.checkbox = QtWidgets.QCheckBox()
|
|
self.checkbox.setChecked(True)
|
|
self.checkbox.stateChanged.connect(self.checkboxToggle)
|
|
|
|
line = QtWidgets.QFrame()
|
|
line.setFrameShape(QtWidgets.QFrame.HLine)
|
|
line.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
|
|
QtWidgets.QSizePolicy.Expanding)
|
|
label = QtWidgets.QLabel(" " + category)
|
|
font = QtGui.QFont()
|
|
font.setItalic(True)
|
|
font.setCapitalization(QtGui.QFont.AllUppercase)
|
|
font.setBold(True)
|
|
label.setFont(font)
|
|
|
|
layout = QtWidgets.QHBoxLayout()
|
|
layout.addWidget(self.checkbox)
|
|
layout.addWidget(line)
|
|
layout.addWidget(label)
|
|
# layout.setSpacing(0)
|
|
layout.setContentsMargins(2, 2, 2, 2)
|
|
self.setLayout(layout)
|
|
|
|
def checkboxToggle(self, *args):
|
|
state = args[0]
|
|
for w in self.checkerWidgets:
|
|
if self.category == w.checker.category:
|
|
if state == 2:
|
|
w.setEnabled(True)
|
|
else:
|
|
w.setEnabled(False)
|
|
|
|
|
|
class CheckerWidget(QtWidgets.QWidget):
|
|
|
|
def __init__(self, chk, parent, settings=None):
|
|
# type: (checker.BaseChecker)
|
|
super(CheckerWidget, self).__init__()
|
|
|
|
self.checker = chk
|
|
self.settings = settings
|
|
self.createUI()
|
|
|
|
def createUI(self):
|
|
layout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight)
|
|
layout.setContentsMargins(2, 2, 2, 2)
|
|
layout.setSpacing(2)
|
|
|
|
self.frame = framelayout.FrameLayout(self.checker.name)
|
|
|
|
self.checkbox = QtWidgets.QCheckBox()
|
|
self.checkbox.stateChanged.connect(self.toggleEnable)
|
|
|
|
self.checkButton = QtWidgets.QPushButton("Check")
|
|
# self.checkButton.setSizePolicy(
|
|
# QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
|
|
self.checkButton.clicked.connect(self.check)
|
|
self.fixButton = QtWidgets.QPushButton("Fix")
|
|
self.fixButton.clicked.connect(self.fix)
|
|
if self.checker.isFixable is not True:
|
|
self.fixButton.setEnabled(False)
|
|
self.selectAllButton = QtWidgets.QPushButton('Select')
|
|
self.selectAllButton.clicked.connect(self.selectAll)
|
|
|
|
buttonLayout = QtWidgets.QHBoxLayout()
|
|
buttonLayout.addWidget(self.checkButton)
|
|
buttonLayout.addWidget(self.fixButton)
|
|
buttonLayout.addWidget(self.selectAllButton)
|
|
|
|
self.errorList = QtWidgets.QListWidget()
|
|
self.errorList.itemClicked.connect(self.errorSelected)
|
|
|
|
self.frame.addWidget(self.errorList)
|
|
self.frame.addLayout(buttonLayout)
|
|
|
|
layout.addWidget(self.checkbox, alignment=QtCore.Qt.AlignTop)
|
|
layout.addWidget(self.frame)
|
|
|
|
self.setLayout(layout)
|
|
|
|
if self.checker.isEnabled:
|
|
self.setEnabled(True)
|
|
else:
|
|
self.setEnabled(False)
|
|
|
|
def setEnabled(self, state):
|
|
if state is True:
|
|
self.checkbox.setChecked(True)
|
|
self.frame.setEnabled(True)
|
|
self.checker.isEnabled = True
|
|
else:
|
|
self.checkbox.setChecked(False)
|
|
self.frame.setEnabled(False)
|
|
self.frame.collapse()
|
|
self.checker.isEnabled = False
|
|
|
|
def check(self, path=None, dummy=None):
|
|
if not self.checker.isEnabled:
|
|
return
|
|
|
|
root = self.parent().parent().parent().parent().rootLE.text()
|
|
if root != "":
|
|
path = root
|
|
|
|
if path is None:
|
|
sel = cmds.ls(sl=True, fl=True, long=True)
|
|
if not sel:
|
|
cmds.warning("Nothing is selected")
|
|
return
|
|
path = sel[0]
|
|
|
|
self.doCheck(path)
|
|
|
|
def toggleEnable(self, *args):
|
|
state = args[0]
|
|
|
|
if state == 2:
|
|
self.setEnabled(True)
|
|
else:
|
|
self.setEnabled(False)
|
|
|
|
def doCheck(self, obj):
|
|
|
|
# Clear list items
|
|
self.errorList.clear()
|
|
|
|
errs = self.checker.checkIt(obj, self.settings)
|
|
|
|
if errs:
|
|
for err in errs:
|
|
self.errorList.addItem(err)
|
|
if self.checker.isWarning:
|
|
self.frame.setStatusIcon("warning")
|
|
else:
|
|
self.frame.setStatusIcon("bad")
|
|
else:
|
|
self.frame.setStatusIcon("good")
|
|
|
|
def fix(self):
|
|
if not self.checker.isEnabled:
|
|
return
|
|
|
|
self.checker.fixIt()
|
|
|
|
# Re-check
|
|
self.check()
|
|
|
|
def errorSelected(self, *args):
|
|
"""
|
|
Select error components
|
|
|
|
"""
|
|
|
|
err = args[0]
|
|
if err.components is None:
|
|
cmds.select(err.longName, r=True)
|
|
else:
|
|
cmds.select(err.components, r=True)
|
|
|
|
def selectAll(self):
|
|
errs = [i.longName for i in self.checker.errors]
|
|
cmds.select(errs, r=True)
|
|
|
|
|
|
class Settings(QtWidgets.QWidget):
|
|
|
|
def __init__(self, parent=None):
|
|
super(Settings, self).__init__(parent)
|
|
|
|
self.createUI()
|
|
|
|
def createUI(self):
|
|
|
|
self.maxFaceArea = QtWidgets.QLineEdit("0.000001")
|
|
|
|
layout = QtWidgets.QGridLayout()
|
|
layout.setAlignment(QtCore.Qt.AlignTop)
|
|
layout.addWidget(QtWidgets.QLabel("Max face area"), 0, 0)
|
|
layout.addWidget(self.maxFaceArea, 0, 1)
|
|
self.setLayout(layout)
|
|
|
|
def getSettings(self):
|
|
|
|
data = {
|
|
"maxFaceArea": float(self.maxFaceArea.text())
|
|
}
|
|
|
|
return data
|
|
|
|
|
|
class ModelSanityChecker(QtWidgets.QWidget):
|
|
""" Main sanity checker class """
|
|
|
|
def __init__(self, settings=None, parent=None):
|
|
super(ModelSanityChecker, self).__init__(parent)
|
|
|
|
checkerObjs = [i() for i in checker.CHECKERS]
|
|
checkerObjs.sort()
|
|
self.checkerWidgets = [CheckerWidget(i, self, settings) for i in checkerObjs]
|
|
self.separators = []
|
|
|
|
self.createUI()
|
|
|
|
def createUI(self):
|
|
"""
|
|
GUI method
|
|
|
|
"""
|
|
|
|
mainLayout = QtWidgets.QVBoxLayout()
|
|
mainLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
scroll = QtWidgets.QScrollArea()
|
|
scroll.setWidgetResizable(1)
|
|
|
|
scrollLayout = QtWidgets.QVBoxLayout()
|
|
|
|
currentCategory = self.checkerWidgets[0].checker.category
|
|
|
|
sep = Separator(currentCategory, self.checkerWidgets)
|
|
self.separators.append(sep)
|
|
scrollLayout.addWidget(sep)
|
|
|
|
for widget in self.checkerWidgets:
|
|
if currentCategory != widget.checker.category:
|
|
cat = widget.checker.category
|
|
currentCategory = cat
|
|
sep = Separator(cat, self.checkerWidgets)
|
|
self.separators.append(sep)
|
|
scrollLayout.addWidget(sep)
|
|
scrollLayout.addWidget(widget)
|
|
|
|
content = QtWidgets.QWidget()
|
|
content.setLayout(scrollLayout)
|
|
|
|
scroll.setWidget(content)
|
|
|
|
self.rootLE = QtWidgets.QLineEdit()
|
|
setButton = QtWidgets.QPushButton("Set Selected")
|
|
setButton.clicked.connect(self.setSelected)
|
|
|
|
checkboxAll = QtWidgets.QCheckBox("Select All")
|
|
checkboxAll.setChecked(True)
|
|
checkboxAll.stateChanged.connect(self.selectAllToggle)
|
|
|
|
checkAllButton = QtWidgets.QPushButton("Check All")
|
|
checkAllButton.clicked.connect(self.checkAll)
|
|
|
|
fixAllButton = QtWidgets.QPushButton("Fix All")
|
|
fixAllButton.clicked.connect(self.fixAll)
|
|
|
|
rootLayout = QtWidgets.QHBoxLayout()
|
|
rootLayout.addWidget(self.rootLE)
|
|
rootLayout.addWidget(setButton)
|
|
|
|
mainLayout.addLayout(rootLayout)
|
|
mainLayout.addWidget(checkboxAll)
|
|
mainLayout.addWidget(scroll)
|
|
mainLayout.addWidget(checkAllButton)
|
|
mainLayout.addWidget(fixAllButton)
|
|
|
|
self.setLayout(mainLayout)
|
|
|
|
def selectAllToggle(self, *args):
|
|
state = args[0]
|
|
|
|
for s in self.separators:
|
|
if state == 2:
|
|
s.checkbox.setChecked(True)
|
|
else:
|
|
s.checkbox.setChecked(False)
|
|
|
|
def setSelected(self):
|
|
sel = cmds.ls(sl=True, fl=True, long=True)
|
|
if sel:
|
|
root = sel[0]
|
|
self.rootLE.setText(root)
|
|
|
|
def checkAll(self):
|
|
"""
|
|
Check all
|
|
|
|
"""
|
|
|
|
node = self.rootLE.text()
|
|
|
|
progDialog = QtWidgets.QProgressDialog(
|
|
"Now Checking...",
|
|
"Cancel",
|
|
0,
|
|
len(self.checkerWidgets),
|
|
self)
|
|
progDialog.setWindowTitle("Building library")
|
|
# progDialog.setWindowModality(QtCore.Qt.WindowModal)
|
|
progDialog.show()
|
|
|
|
for num, widget in enumerate(self.checkerWidgets):
|
|
checkerName = widget.checker.name
|
|
if widget.checker.isEnabled:
|
|
print("Running {} checker".format(checkerName))
|
|
if node == "":
|
|
widget.check()
|
|
else:
|
|
widget.check(node)
|
|
else:
|
|
print("{} checker is disabled. Skipped".format(checkerName))
|
|
|
|
progDialog.setValue(num+1)
|
|
progDialog.setLabel(QtWidgets.QLabel(
|
|
r'Now checking "{}"'.format(widget.checker.name)))
|
|
QtCore.QCoreApplication.processEvents()
|
|
|
|
progDialog.close()
|
|
|
|
def fixAll(self):
|
|
"""
|
|
Fix all
|
|
|
|
"""
|
|
|
|
for widget in self.checkerWidgets:
|
|
widget.fix()
|
|
|
|
|
|
class CentralWidget(QtWidgets.QWidget):
|
|
""" Central widget """
|
|
|
|
def __init__(self, parent=None):
|
|
""" Init """
|
|
|
|
super(CentralWidget, self).__init__(parent)
|
|
|
|
self.createUI()
|
|
self.layoutUI()
|
|
|
|
def createUI(self):
|
|
""" Crete widgets """
|
|
|
|
settings = Settings(self)
|
|
checker = ModelSanityChecker(settings, self)
|
|
|
|
self.tabWidget = QtWidgets.QTabWidget()
|
|
self.tabWidget.addTab(checker, "SanityChecker")
|
|
self.tabWidget.addTab(settings, "Settings")
|
|
|
|
def layoutUI(self):
|
|
""" Layout widgets """
|
|
|
|
mainLayout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.TopToBottom)
|
|
mainLayout.setContentsMargins(5, 5, 5, 5)
|
|
mainLayout.addWidget(self.tabWidget)
|
|
|
|
self.setLayout(mainLayout)
|
|
|
|
|
|
class MainWindow(MayaQWidgetDockableMixin, QtWidgets.QMainWindow):
|
|
"""
|
|
Main window
|
|
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
""" init """
|
|
|
|
super(MainWindow, self).__init__(parent)
|
|
|
|
self.thisObjectName = "sanityCheckerWindow"
|
|
self.winTitle = "Sanity Checker"
|
|
self.workspaceControlName = self.thisObjectName + "WorkspaceControl"
|
|
|
|
self.setObjectName(self.thisObjectName)
|
|
self.setWindowTitle(self.winTitle)
|
|
|
|
self.setWindowFlags(QtCore.Qt.Window)
|
|
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
|
|
|
# Create and set central widget
|
|
self.cWidget = CentralWidget()
|
|
self.setCentralWidget(self.cWidget)
|
|
|
|
self.setupMenu()
|
|
|
|
def setupMenu(self):
|
|
""" Setup menu """
|
|
|
|
menu = self.menuBar()
|
|
|
|
# About
|
|
aboutAction = QtWidgets.QAction("&About", self)
|
|
aboutAction.setStatusTip('About this script')
|
|
aboutAction.triggered.connect(self.showAbout)
|
|
|
|
menu.addAction("File")
|
|
helpMenu = menu.addMenu("&Help")
|
|
helpMenu.addAction(aboutAction)
|
|
|
|
def showAbout(self):
|
|
"""
|
|
About message
|
|
"""
|
|
|
|
QtWidgets.QMessageBox.about(self, 'About ', 'test\n')
|
|
|
|
def run(self):
|
|
try:
|
|
cmds.deleteUI(self.workspaceControlName)
|
|
except RuntimeError:
|
|
pass
|
|
|
|
self.show(dockable=True)
|
|
cmds.workspaceControl(
|
|
self.workspaceControlName,
|
|
edit=True,
|
|
initialWidth=270,
|
|
minimumWidth=270,
|
|
dockToControl=['Outliner', 'right'])
|
|
self.raise_()
|
|
|
|
|
|
def main():
|
|
|
|
window = MainWindow()
|
|
window.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|