Initial commit

This commit is contained in:
Jcen 2024-10-01 19:54:21 +08:00
parent 3430b129a0
commit 65ed5cf1a6
8 changed files with 1915 additions and 163 deletions

164
.gitignore vendored
View File

@ -1,162 +1,2 @@
# ---> Python *.un~
# Byte-compiled / optimized / DLL files *.pyc
__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/

View File

@ -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
View File

1295
checker.py Normal file

File diff suppressed because it is too large Load Diff

114
framelayout.py Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

456
modelSanityChecker.py Normal file
View 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()