Initial commit
This commit is contained in:
parent
3430b129a0
commit
65ed5cf1a6
164
.gitignore
vendored
164
.gitignore
vendored
@ -1,162 +1,2 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
*.un~
|
||||
*.pyc
|
||||
|
15
README.md
15
README.md
@ -1,2 +1,15 @@
|
||||
# ModelCheckerForMaya
|
||||
# Model Checker (WIP)
|
||||
|
||||
![img](./images/ui.png)
|
||||
|
||||
## Requirements
|
||||
|
||||
[CheckTools plugins](https://github.com/minoue/CheckTools)
|
||||
|
||||
## Usage
|
||||
|
||||
python
|
||||
```
|
||||
from ModelCheckerForMaya import modelSanityChecker
|
||||
modelSanityChecker.main()
|
||||
```
|
||||
|
0
__init__.py
Normal file
0
__init__.py
Normal file
1295
checker.py
Normal file
1295
checker.py
Normal file
File diff suppressed because it is too large
Load Diff
114
framelayout.py
Normal file
114
framelayout.py
Normal file
@ -0,0 +1,114 @@
|
||||
""" qt framelayout sample """
|
||||
|
||||
from PySide2 import QtWidgets, QtCore
|
||||
from . import icon
|
||||
|
||||
|
||||
class TitleLabel(QtWidgets.QLabel):
|
||||
|
||||
clicked = QtCore.Signal()
|
||||
|
||||
def __init__(self, text="", parent=None):
|
||||
super(TitleLabel, self).__init__(parent)
|
||||
|
||||
self.setText(text)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self.clicked.emit()
|
||||
|
||||
|
||||
class FrameLayout(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, title="", parent=None):
|
||||
super(FrameLayout, self).__init__(parent)
|
||||
|
||||
self.baseTitle = title
|
||||
self.rightArrow = u"\u25b6 "
|
||||
self.downArrow = u"\u25bc "
|
||||
|
||||
titleLayout = QtWidgets.QHBoxLayout()
|
||||
self.title = self.rightArrow + title
|
||||
|
||||
self.titleLabel = TitleLabel(self.title)
|
||||
self.titleLabel.setSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
self.titleLabel.clicked.connect(self.titleClicked)
|
||||
self.statusIconLabel = QtWidgets.QLabel()
|
||||
self.statusIconLabel.setPixmap(icon.neutralIconPixmap)
|
||||
|
||||
self.childrenWidget = QtWidgets.QWidget()
|
||||
self.childrenLayout = QtWidgets.QVBoxLayout()
|
||||
self.childrenLayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.childrenWidget.setLayout(self.childrenLayout)
|
||||
|
||||
titleLayout.addWidget(self.statusIconLabel)
|
||||
titleLayout.addWidget(self.titleLabel)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addLayout(titleLayout)
|
||||
layout.addWidget(self.childrenWidget)
|
||||
self.setLayout(layout)
|
||||
|
||||
# Close frame by default
|
||||
self.childrenWidget.hide()
|
||||
|
||||
def titleClicked(self):
|
||||
"""
|
||||
title clicked action
|
||||
|
||||
"""
|
||||
|
||||
newTitle = ""
|
||||
|
||||
if self.childrenWidget.isVisible():
|
||||
self.childrenWidget.hide()
|
||||
newTitle = self.rightArrow + self.baseTitle
|
||||
else:
|
||||
self.childrenWidget.show()
|
||||
newTitle = self.downArrow + self.baseTitle
|
||||
|
||||
self.titleLabel.setText(newTitle)
|
||||
|
||||
def collapse(self):
|
||||
newTitle = ""
|
||||
self.childrenWidget.hide()
|
||||
newTitle = self.rightArrow + self.baseTitle
|
||||
self.titleLabel.setText(newTitle)
|
||||
|
||||
def expand(self):
|
||||
newTitle = ""
|
||||
self.childrenWidget.show()
|
||||
newTitle = self.rightArrow + self.baseTitle
|
||||
self.titleLabel.setText(newTitle)
|
||||
|
||||
def addWidget(self, widget):
|
||||
# type: (QtWidgets.QWidget) -> None
|
||||
"""
|
||||
Add widgets
|
||||
|
||||
"""
|
||||
|
||||
self.childrenLayout.addWidget(widget)
|
||||
|
||||
def addLayout(self, layout):
|
||||
self.childrenLayout.addLayout(layout)
|
||||
|
||||
def setStatusIcon(self, status):
|
||||
# type: (str) -> None
|
||||
"""
|
||||
Change status icon
|
||||
|
||||
"""
|
||||
|
||||
if status == "good":
|
||||
self.statusIconLabel.setPixmap(icon.goodIconPixmap)
|
||||
elif status == "bad":
|
||||
self.statusIconLabel.setPixmap(icon.errorIconPixmap)
|
||||
elif status == "warning":
|
||||
self.statusIconLabel.setPixmap(icon.warningIconPixmap)
|
||||
elif status == "neutral":
|
||||
self.statusIconLabel.setPixmap(icon.neutralIconPixmap)
|
||||
else:
|
||||
pass
|
34
icon.py
Normal file
34
icon.py
Normal file
@ -0,0 +1,34 @@
|
||||
""" wip
|
||||
|
||||
https://www.iconfinder.com/iconsets/fatcow
|
||||
https://creativecommons.org/licenses/by/3.0/us/
|
||||
"Farm-fresh" by FatCow Web Hosting is licensed under CC BY 3.0
|
||||
|
||||
"""
|
||||
|
||||
from PySide2 import QtCore, QtGui
|
||||
|
||||
|
||||
b64Error = b"""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"""
|
||||
b64Warning = b"""iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFJUlEQVRYR+2WfUzUdRzHX7/fHXeCgsrx6BOiCAJCefIg8tDIykwNzHQzwwdmudZcubk5jXQq6WyVLtaUcjhlVqu5tHRm5dwE5NFDQEQINfEBBIEhAnnc3a/97sHRxXFHW7m2vv/d/b7f3+f1fX/en8/vI/CEl/CE4/OfBRDPbY5b46GSDsoK9uqFtam7yw4BpuEq+rcUWD8/wHdlUkhrTGamOV5FXh5HChv9ck63tP0bAG4FWbFnpybOSQ70u2+O19zqw7WiCwXJ2eVzgf7hQAxXAeHQ2uik6DDf89rXl0LRTswuituI7uuTVNe3paw5WF0ISK5CDBdAXbw1risifYHaq7cU7uosccZpeeARz5Xjpx4l7CgbDTz6JwDEHzbO2jQ1ZMKu8BdnQkUuiCpLHJMeYtZR92Ml1xpvb1n00cU9rhpyOAp4XMiK7Zm9bgXC1XzobQdRYQUwgocGaXoGJblHmZNdPlIuDldUcBVAcW5LzLGwJG1a4FQ3aDgNSiW4WVNtEMBggGnzab7WT32h7kTqroolgNEZhCsAQv5b0UmRwZrzM1+bB5ePgCSBSiLx4xREAQ4uqyLMvwsEESJXUvnlGWpvtKdk7HduSFcA1EVZsTfC5yUEjvVsgZbLoFDACCMv7UtGIULOK9VM9usGgwn8Z9DZHUDdmeLmxOzyYGeGdAYgnto4a1NwiP+u8IUzoe4EiErM11YbSPs0BYUo8cniKib79lgENxkgPI26k5XcaLy3ZYETQzoD8CjOiuvRrpiL6kEF9LRbZBYxA7ya84wZ4MPFVQT5PASjAJIJRmrQe8WgO3qWhOyyIQ05FIDi500xn4fGhmRO0mrgVgkISsydR5RA3c/yz1IRBYnd6ZeYZAaQySSQDDBxNk26dhrKG/Oe31PxpiNDOgIQ9mZMm54Y6ncldlUSNJeCXm+5vbysABn7U1EIEjvTLjFRYwOQGUygUkFgPOWHCylqaI3YkP/r1cE6pCMAVcF7sbrIF6Iix/o/go6b1uDW7VaA1QeeNadg+6JKJgwEMKtgAu8gOu+pqf2ppjb5g3ItoLcvy8EAxOPvzsoMDfX5Ijw9Eu5cspTdwNHBCrD2wHOIosTWhReZ8DgFthASCAKMf5q647U0NNx/I33fxTz7DjkYgHtxVnyvdrkWFS3Q90DW/M/ggtwHDGTmzjN74P1FFQT5dIHJbp88Hrh7oScA3Vc6ErJLPYC+gS+zBzAbb0pUQOaUlHHQfsMa3G6b/FNhpKl1jOWSY3oQFUYw2b9OVs4EmmCun7/L9ZqWvxhy4Alhb8aM6Ymho6/EroqC7juWxuJoalPKKpgsj/vlViw6mIckUIrgOZ7ywzUUNXRFbMi//NiQAwFUBVlxurCkiZG+oe7QK0vvwKPy32oTKW/7m1OQ+04nYZMNlj4w6JLAw4u2hj7qC2/VJmeXPTak7YTw7fqnFodNGXssalkIPOwYerqTU60ysWxbhKUPrL5J8Pi+IQCsmRzlTc03jdRf71yyNKfqO7ksbQAjSrYldEe/PEnprlFAvwtTlVKi6a6nxQPefYhKo/M5yM2NvnYj1d83GWZvL/YEfrcBeJduS2iPWx8KehOY7MpuMFnlSrCZ3uo1hykzn5f3C6ASKctpIH57sQbosAH4/rI57twolSJSkoO7PNE5+9rbPRfkfiagl6TbKTtKZB+02QC8gCBAprKOOcN8uevb5dLqAH4DumwA8ldGbhJqx9Z3PYKTnbK+ckuWRza5gJ/s+h/gD1M9zzASYFmcAAAAAElFTkSuQmCC"""
|
||||
b64Good = b"""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"""
|
||||
b64Neutral = b"""iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC10lEQVRYR+2V309SYRzGn+MhHZPSBKUU0VQm6yI9rsVFrZoLajMpNu5gMn5sede1f4HX3XkhOpjekSvMrYVzbtUFrUl20XCoKQp1FMwKxyzgtPcIzbaUw2Djhnc7d+97ns/7PN/v96VQ5kWVWR8VgIoDFQcKdYAymUwakUg0SlHU7eMtzHHcYiqVGpmenvYD4IS2dyEAtMUyNCaRnHMwTC+UyjZU19TwOr8ODxEObyIQ+IBE4se4y+UeBpAWAiEUQGSxDD1tkMr0gwP3EVpbx/LyMmKxGK8hk8nQ09MDVWcHZudeYC8ed7pcrkdCIIQAUAaD4aZC0bI4OPgAvvkFsOxX0DQNijo6znEc0uk05PIL0N7px+zsc0Qi0VszMzOv88UhBKDabDYv6HR3r3/eCGNrK8yLV1VV/eNwJpPhIVpblbjUrsT8vO+t2+3uJwmdFoUQgDqr1bqv1z/EK5/vv+I5gRyETquF1/sMk5OT9QC+FwvQaLPZdvquXsNqKASaJjc/iZtEkUGXSoWl9+8wMTHRBGC3WIAmu93OdqsvIx7f+5v7ST8l9SCVNmAl+AlOp1MOYKdoAIfDzl5sVmZvnq/FiTscvkTDGB8vDUCj2Wx+qVC299XWSkByPm2R4jw4SGA7vLE0NTV1rxQR1Gk0Gj3D9LrbO1S8CydBHHUGh431EBlKQ36/31uKIqwG0Gw0Gp/I5XJ9Z1c3QFF875OPLDIP+JnAcVhbXQHLsl6Px/MYQLQUbUhCPQugzWg0jspkjQPNLQrUn5eCFtE8QDqVxv63OKKRbcRiu3Mej2cEwCaAn6UYRESDKNUBaGEY5oZarR4Wi8VXyI2zFiCZTH4MBoNjgUDgDYBI1vq874GQQZSrOQJRC6CBjH8AkiwYbwKABADyOOwBOBDyDvDxCXmxju0h+88AEAMgtZE7T6wgIzcJ4Hc+249rFgpQIG/+7RWAigMVB8ruwB+gWP0h6xe84QAAAABJRU5ErkJggg=="""
|
||||
|
||||
tempPixmap = QtGui.QPixmap()
|
||||
|
||||
errorData = QtCore.QByteArray.fromBase64(b64Error)
|
||||
warningData = QtCore.QByteArray.fromBase64(b64Warning)
|
||||
goodData = QtCore.QByteArray.fromBase64(b64Good)
|
||||
neutralData = QtCore.QByteArray.fromBase64(b64Neutral)
|
||||
|
||||
tempPixmap.loadFromData(errorData)
|
||||
errorIconPixmap = tempPixmap.scaled(20, 20, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
|
||||
tempPixmap.loadFromData(warningData)
|
||||
warningIconPixmap = tempPixmap.scaled(20, 20, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
|
||||
tempPixmap.loadFromData(goodData)
|
||||
goodIconPixmap = tempPixmap.scaled(20, 20, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
|
||||
tempPixmap.loadFromData(neutralData)
|
||||
neutralIconPixmap = tempPixmap.scaled(20, 20, QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation)
|
BIN
images/ui.png
Normal file
BIN
images/ui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
456
modelSanityChecker.py
Normal file
456
modelSanityChecker.py
Normal file
@ -0,0 +1,456 @@
|
||||
"""
|
||||
|
||||
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()
|
Loading…
Reference in New Issue
Block a user