├── docs
├── .nojekyll
├── objects.inv
├── _static
│ ├── up.png
│ ├── down.png
│ ├── file.png
│ ├── minus.png
│ ├── plus.png
│ ├── comment.png
│ ├── ajax-loader.gif
│ ├── up-pressed.png
│ ├── comment-close.png
│ ├── down-pressed.png
│ ├── comment-bright.png
│ ├── pygments.css
│ ├── classic.css
│ └── sidebar.js
├── _sources
│ ├── modules.rst.txt
│ ├── qualityAssurance.install.rst.txt
│ ├── qualityAssurance.ui.utils.rst.txt
│ ├── qualityAssurance.utils.qa.rst.txt
│ ├── qualityAssurance.checks.uv.rst.txt
│ ├── qualityAssurance.ui.widgets.rst.txt
│ ├── qualityAssurance.ui.window.rst.txt
│ ├── qualityAssurance.utils.api.rst.txt
│ ├── qualityAssurance.utils.path.rst.txt
│ ├── qualityAssurance.utils.skin.rst.txt
│ ├── qualityAssurance.utils.undo.rst.txt
│ ├── qualityAssurance.checks.scene.rst.txt
│ ├── qualityAssurance.collections.rst.txt
│ ├── qualityAssurance.checks.geometry.rst.txt
│ ├── qualityAssurance.checks.rigging.rst.txt
│ ├── qualityAssurance.checks.shaders.rst.txt
│ ├── qualityAssurance.checks.skinning.rst.txt
│ ├── qualityAssurance.checks.textures.rst.txt
│ ├── qualityAssurance.utils.animation.rst.txt
│ ├── qualityAssurance.utils.reference.rst.txt
│ ├── qualityAssurance.checks.animation.rst.txt
│ ├── qualityAssurance.checks.modelling.rst.txt
│ ├── qualityAssurance.utils.decorators.rst.txt
│ ├── qualityAssurance.checks.renderStats.rst.txt
│ ├── qualityAssurance.checks.renderLayers.rst.txt
│ ├── qualityAssurance.ui.rst.txt
│ ├── qualityAssurance.rst.txt
│ ├── index.rst.txt
│ ├── qualityAssurance.utils.rst.txt
│ └── qualityAssurance.checks.rst.txt
├── .buildinfo
├── search.html
├── qualityAssurance.install.html
├── qualityAssurance.utils.undo.html
├── qualityAssurance.ui.html
├── qualityAssurance.utils.decorators.html
├── qualityAssurance.checks.textures.html
├── qualityAssurance.utils.reference.html
├── qualityAssurance.utils.animation.html
├── qualityAssurance.ui.window.html
├── qualityAssurance.utils.html
├── qualityAssurance.ui.utils.html
├── qualityAssurance.checks.uv.html
├── qualityAssurance.checks.skinning.html
├── qualityAssurance.utils.api.html
├── qualityAssurance.collections.html
├── qualityAssurance.checks.rigging.html
├── qualityAssurance.checks.shaders.html
├── modules.html
├── qualityAssurance.checks.renderLayers.html
├── qualityAssurance.utils.skin.html
└── qualityAssurance.utils.path.html
├── .gitignore
├── scripts
├── qualityAssurance
│ ├── ui
│ │ ├── __init__.py
│ │ ├── window.py
│ │ └── utils.py
│ ├── utils
│ │ ├── __init__.py
│ │ ├── reference.py
│ │ ├── undo.py
│ │ ├── animation.py
│ │ ├── decorators.py
│ │ ├── api.py
│ │ ├── path.py
│ │ ├── skin.py
│ │ └── qa.py
│ ├── checks
│ │ ├── textures.py
│ │ ├── rigging.py
│ │ ├── __init__.py
│ │ ├── uv.py
│ │ ├── shaders.py
│ │ ├── renderStats.py
│ │ ├── modelling.py
│ │ └── skinning.py
│ ├── install.py
│ ├── collections
│ │ └── __init__.py
│ └── __init__.py
└── userSetup.py
├── icons
├── QA_fix.png
└── QA_icon.png
├── qualityAssurance.mod
└── README.md
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.py[cod]
3 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/ui/__init__.py:
--------------------------------------------------------------------------------
1 | from .window import show
2 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .qa import QualityAssurance
2 |
--------------------------------------------------------------------------------
/docs/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/objects.inv
--------------------------------------------------------------------------------
/icons/QA_fix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/icons/QA_fix.png
--------------------------------------------------------------------------------
/icons/QA_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/icons/QA_icon.png
--------------------------------------------------------------------------------
/docs/_static/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/up.png
--------------------------------------------------------------------------------
/docs/_static/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/down.png
--------------------------------------------------------------------------------
/docs/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/file.png
--------------------------------------------------------------------------------
/docs/_static/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/minus.png
--------------------------------------------------------------------------------
/docs/_static/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/plus.png
--------------------------------------------------------------------------------
/docs/_static/comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/comment.png
--------------------------------------------------------------------------------
/docs/_static/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/ajax-loader.gif
--------------------------------------------------------------------------------
/docs/_static/up-pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/up-pressed.png
--------------------------------------------------------------------------------
/docs/_static/comment-close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/comment-close.png
--------------------------------------------------------------------------------
/docs/_static/down-pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/down-pressed.png
--------------------------------------------------------------------------------
/docs/_static/comment-bright.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertjoosten/maya-quality-assurance/HEAD/docs/_static/comment-bright.png
--------------------------------------------------------------------------------
/docs/_sources/modules.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance
2 | ================
3 |
4 | .. toctree::
5 | :maxdepth: 4
6 |
7 | qualityAssurance
8 |
--------------------------------------------------------------------------------
/scripts/userSetup.py:
--------------------------------------------------------------------------------
1 | from maya import utils
2 |
3 | import qualityAssurance.install
4 | utils.executeDeferred(qualityAssurance.install.shelf)
5 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.install.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.install module
2 | ================================
3 |
4 | .. automodule:: qualityAssurance.install
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.ui.utils.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.ui\.utils module
2 | ==================================
3 |
4 | .. automodule:: qualityAssurance.ui.utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.qa.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.qa module
2 | ==================================
3 |
4 | .. automodule:: qualityAssurance.utils.qa
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.uv.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.uv module
2 | ===================================
3 |
4 | .. automodule:: qualityAssurance.checks.uv
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.ui.widgets.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.ui\.widgets module
2 | ====================================
3 |
4 | .. automodule:: qualityAssurance.ui.widgets
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.ui.window.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.ui\.window module
2 | ===================================
3 |
4 | .. automodule:: qualityAssurance.ui.window
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.api.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.api module
2 | ===================================
3 |
4 | .. automodule:: qualityAssurance.utils.api
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.path.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.path module
2 | ====================================
3 |
4 | .. automodule:: qualityAssurance.utils.path
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.skin.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.skin module
2 | ====================================
3 |
4 | .. automodule:: qualityAssurance.utils.skin
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.undo.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.undo module
2 | ====================================
3 |
4 | .. automodule:: qualityAssurance.utils.undo
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/.buildinfo:
--------------------------------------------------------------------------------
1 | # Sphinx build info version 1
2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3 | config: f315c9ff75ecd54364139830171b9671
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.scene.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.scene module
2 | ======================================
3 |
4 | .. automodule:: qualityAssurance.checks.scene
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.collections.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.collections package
2 | =====================================
3 |
4 | .. automodule:: qualityAssurance.collections
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.geometry.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.geometry module
2 | =========================================
3 |
4 | .. automodule:: qualityAssurance.checks.geometry
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.rigging.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.rigging module
2 | ========================================
3 |
4 | .. automodule:: qualityAssurance.checks.rigging
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.shaders.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.shaders module
2 | ========================================
3 |
4 | .. automodule:: qualityAssurance.checks.shaders
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.skinning.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.skinning module
2 | =========================================
3 |
4 | .. automodule:: qualityAssurance.checks.skinning
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.textures.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.textures module
2 | =========================================
3 |
4 | .. automodule:: qualityAssurance.checks.textures
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.animation.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.animation module
2 | =========================================
3 |
4 | .. automodule:: qualityAssurance.utils.animation
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.reference.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.reference module
2 | =========================================
3 |
4 | .. automodule:: qualityAssurance.utils.reference
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.animation.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.animation module
2 | ==========================================
3 |
4 | .. automodule:: qualityAssurance.checks.animation
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.modelling.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.modelling module
2 | ==========================================
3 |
4 | .. automodule:: qualityAssurance.checks.modelling
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.decorators.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils\.decorators module
2 | ==========================================
3 |
4 | .. automodule:: qualityAssurance.utils.decorators
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.renderStats.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.renderStats module
2 | ============================================
3 |
4 | .. automodule:: qualityAssurance.checks.renderStats
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.renderLayers.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks\.renderLayers module
2 | =============================================
3 |
4 | .. automodule:: qualityAssurance.checks.renderLayers
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/qualityAssurance.mod:
--------------------------------------------------------------------------------
1 | + MAYAVERSION:2014 qualityAssurance 0.1.0
2 | + MAYAVERSION:2015 qualityAssurance 0.1.0
3 | + MAYAVERSION:2016 qualityAssurance 0.1.0
4 | + MAYAVERSION:2017 qualityAssurance 0.1.0
5 | + MAYAVERSION:2018 qualityAssurance 0.1.0
6 | + MAYAVERSION:2019 qualityAssurance 0.1.0
7 | + MAYAVERSION:2020 qualityAssurance 0.1.0
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.ui.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.ui package
2 | ============================
3 |
4 | .. automodule:: qualityAssurance.ui
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 | Submodules
10 | ----------
11 |
12 | .. toctree::
13 |
14 | qualityAssurance.ui.utils
15 | qualityAssurance.ui.widgets
16 | qualityAssurance.ui.window
17 |
18 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/reference.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 |
3 |
4 | def removeReferenced(nodes):
5 | """
6 | Remove all referenced nodes from list.
7 |
8 | :param list nodes: List of strings
9 | :return: Filtered list without referenced nodes
10 | :rtype: generator
11 | """
12 | for node in nodes:
13 | if cmds.referenceQuery(node, inr=True):
14 | continue
15 |
16 | yield node
17 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/undo.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 |
3 |
4 | class UndoContext(object):
5 | """
6 | This context will create a undo chunk of every commands that is ran within
7 | the context.
8 |
9 | .. code-block:: python
10 |
11 | with UndoContext:
12 | # code
13 | """
14 | def __enter__(self):
15 | cmds.undoInfo(openChunk=True)
16 |
17 | def __exit__(self, *exc_info):
18 | cmds.undoInfo(closeChunk=True)
19 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance package
2 | ========================
3 |
4 | .. automodule:: qualityAssurance
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 | Subpackages
10 | -----------
11 |
12 | .. toctree::
13 |
14 | qualityAssurance.checks
15 | qualityAssurance.collections
16 | qualityAssurance.ui
17 | qualityAssurance.utils
18 |
19 | Submodules
20 | ----------
21 |
22 | .. toctree::
23 |
24 | qualityAssurance.install
25 |
26 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/animation.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 |
3 |
4 | def removeDrivenAnimCurves(animCurves):
5 | """
6 | Remove set driven animation curves from the list.
7 |
8 | :param list animCurves: List of strings
9 | :return: Filtered list without set driven anim curves
10 | :rtype: generator
11 | """
12 | for animCurve in animCurves:
13 | if cmds.listConnections("{0}.input".format(animCurve)):
14 | continue
15 |
16 | yield animCurve
17 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/decorators.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 |
4 | def ifNoErrorsReturn(argument):
5 | """
6 | If the error list is emtpy return the argument provided.
7 |
8 | :param argument:
9 | """
10 | def decorator(func):
11 | @wraps(func)
12 | def wrapper(self, *args, **kwargs):
13 | if not self.errors:
14 | return argument
15 |
16 | return func(self, *args, **kwargs)
17 | return wrapper
18 | return decorator
19 |
--------------------------------------------------------------------------------
/docs/_sources/index.rst.txt:
--------------------------------------------------------------------------------
1 | .. qualityAssurance documentation master file, created by
2 | sphinx-quickstart on Wed Jun 26 08:18:42 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to qualityAssurance's documentation!
7 | ============================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 |
14 |
15 | Indices and tables
16 | ==================
17 |
18 | * :ref:`genindex`
19 | * :ref:`modindex`
20 | * :ref:`search`
21 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.utils.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.utils package
2 | ===============================
3 |
4 | .. automodule:: qualityAssurance.utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 | Submodules
10 | ----------
11 |
12 | .. toctree::
13 |
14 | qualityAssurance.utils.animation
15 | qualityAssurance.utils.api
16 | qualityAssurance.utils.decorators
17 | qualityAssurance.utils.path
18 | qualityAssurance.utils.qa
19 | qualityAssurance.utils.reference
20 | qualityAssurance.utils.skin
21 | qualityAssurance.utils.undo
22 |
23 |
--------------------------------------------------------------------------------
/docs/_sources/qualityAssurance.checks.rst.txt:
--------------------------------------------------------------------------------
1 | qualityAssurance\.checks package
2 | ================================
3 |
4 | .. automodule:: qualityAssurance.checks
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
9 | Submodules
10 | ----------
11 |
12 | .. toctree::
13 |
14 | qualityAssurance.checks.animation
15 | qualityAssurance.checks.geometry
16 | qualityAssurance.checks.modelling
17 | qualityAssurance.checks.renderLayers
18 | qualityAssurance.checks.renderStats
19 | qualityAssurance.checks.rigging
20 | qualityAssurance.checks.scene
21 | qualityAssurance.checks.shaders
22 | qualityAssurance.checks.skinning
23 | qualityAssurance.checks.textures
24 | qualityAssurance.checks.uv
25 |
26 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/api.py:
--------------------------------------------------------------------------------
1 | from maya import cmds, OpenMaya
2 |
3 |
4 | def toMObject(node):
5 | """
6 | Convert a node into a OpenMaya.MObject.
7 |
8 | :param str node:
9 | :return: MObject of parsed node
10 | :rtype: OpenMaya.MObject
11 | """
12 | selectionList = OpenMaya.MSelectionList()
13 | selectionList.add(node)
14 | obj = OpenMaya.MObject()
15 | selectionList.getDependNode(0, obj)
16 |
17 | return obj
18 |
19 |
20 | def toMDagPath(node):
21 | """
22 | Convert a node into a OpenMaya.MDagPath.
23 |
24 | :param str node:
25 | :return: MDagPath of parsed node
26 | :rtype: OpenMaya.MDagPath
27 | """
28 | obj = toMObject(node)
29 | if obj.hasFn(OpenMaya.MFn.kDagNode):
30 | dag = OpenMaya.MDagPath.getAPathTo(obj)
31 | return dag
32 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/ui/window.py:
--------------------------------------------------------------------------------
1 | from . import utils, widgets
2 |
3 |
4 | class QualityAssuranceWindow(utils.QWidget):
5 | def __init__(self, parent, collection):
6 | utils.QWidget.__init__(self, parent)
7 |
8 | # set ui
9 | self.setParent(parent)
10 | self.setWindowFlags(utils.Qt.Window)
11 |
12 | self.setWindowTitle("Quality Assurance")
13 | self.setWindowIcon(
14 | utils.QIcon(utils.getIconPath("QA_icon.png"))
15 | )
16 | self.resize(500, 500)
17 |
18 | # create layout
19 | layout = utils.QVBoxLayout(self)
20 | layout.setContentsMargins(5, 5, 5, 5)
21 | layout.setSpacing(5)
22 |
23 | # add collections
24 | self.collections = widgets.CollectionsWidget(self, collection)
25 | layout.addWidget(self.collections)
26 |
27 | # add container
28 | self.container = widgets.QualityAssuranceWidget(self, collection)
29 | layout.addWidget(self.container)
30 |
31 | # connections
32 | self.collections.currentIndexChanged.connect(self.container.refresh)
33 |
34 |
35 | def show(collection="modelling"):
36 | qa = QualityAssuranceWindow(utils.mayaWindow(), collection)
37 | qa.show()
38 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/textures.py:
--------------------------------------------------------------------------------
1 | import os
2 | from maya import cmds
3 | from ..utils import QualityAssurance, reference
4 |
5 |
6 | class NonExistingTextures(QualityAssurance):
7 | """
8 | Animation curves will be checked to see if they are not set driven keys.
9 | Non set driven keys should not be present in the scene and will be deleted
10 | when fixing this error.
11 | """
12 | def __init__(self):
13 | QualityAssurance.__init__(self)
14 |
15 | self._name = "Non Existing Textures"
16 | self._message = "{0} file(s) contain a link to a not existing texture"
17 | self._categories = ["Textures"]
18 | self._selectable = True
19 |
20 | # ------------------------------------------------------------------------
21 |
22 | def _find(self):
23 | """
24 | :return: Meshes with non-deformer history
25 | :rtype: generator
26 | """
27 | fileNodes = self.ls(type="file")
28 | for fileNode in fileNodes:
29 | path = cmds.getAttr("{0}.fileTextureName".format(fileNode))
30 | if not os.path.exists(path):
31 | yield fileNode
32 |
33 | def _fix(self, fileNode):
34 | """
35 | :param str fileNode:
36 | """
37 | cmds.setAttr("{0}.disableFileLoad".format(fileNode), 1)
38 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/install.py:
--------------------------------------------------------------------------------
1 | from maya import cmds, mel
2 |
3 |
4 | # ----------------------------------------------------------------------------
5 |
6 |
7 | ROOT_PACKAGE = __name__.rsplit(".", 1)[0]
8 |
9 | SHELF_NAME = "MiscTools"
10 | SHELF_TOOL = {
11 | "label": "qualityAssurance",
12 | "command": "import {0}.ui; {0}.ui.show()".format(ROOT_PACKAGE),
13 | "annotation": "Quality assurance framework",
14 | "image1": "QA_icon.png",
15 | "sourceType": "python"
16 | }
17 |
18 |
19 | # ----------------------------------------------------------------------------
20 |
21 |
22 | def shelf():
23 | """
24 | Add a new shelf in Maya with the tools that is provided in the SHELF_TOOL
25 | variable. If the tab exists it will be checked to see if the button is
26 | already added. If this is the case the previous button will be deleted and
27 | a new one will be created in its place.
28 | """
29 | # get top shelf
30 | gShelfTopLevel = mel.eval("$tmpVar=$gShelfTopLevel")
31 |
32 | # get top shelf names
33 | shelves = cmds.tabLayout(gShelfTopLevel, query=1, ca=1)
34 |
35 | # create shelf
36 | if SHELF_NAME not in shelves:
37 | cmds.shelfLayout(SHELF_NAME, parent=gShelfTopLevel)
38 |
39 | # get existing members
40 | names = cmds.shelfLayout(SHELF_NAME, query=True, childArray=True) or []
41 | labels = [cmds.shelfButton(n, query=True, label=True) for n in names]
42 |
43 | # delete existing button
44 | if SHELF_TOOL.get("label") in labels:
45 | index = labels.index(SHELF_TOOL.get("label"))
46 | cmds.deleteUI(names[index])
47 |
48 | # add button
49 | cmds.shelfButton(style="iconOnly", parent=SHELF_NAME, **SHELF_TOOL)
50 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/collections/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | New collections can be added in the COLLECTIONS variable. Since this
3 | COLLECTIONS variable is an OrderedDict, it will keep the order. A
4 | collection can be defined by who will be using it. Currently it is
5 | divided by different specialties. Each specialty contains a list of
6 | categories that will be displayed. The category names link to the categories
7 | defined in the quality assurance checks themselves.
8 | """
9 | from collections import OrderedDict
10 |
11 |
12 | COLLECTION = OrderedDict(
13 | [
14 | (
15 | "animation", [
16 | "Animation",
17 | "Scene"
18 | ]
19 | ),
20 | (
21 | "modelling", [
22 | "Modelling",
23 | "Geometry",
24 | "UV",
25 | "Shaders",
26 | "Render Stats",
27 | "Scene"
28 | ]
29 | ),
30 | (
31 | "rigging", [
32 | "Rigging",
33 | "Skinning",
34 | "Shaders",
35 | "Render Stats",
36 | "Scene"
37 | ]
38 | ),
39 | (
40 | "look-dev", [
41 | "Shaders",
42 | "Textures",
43 | "UV",
44 | "Render Layers",
45 | "Render Stats",
46 | "Scene"
47 | ]
48 | )
49 | ]
50 | )
51 |
52 |
53 | def getCollectionsCategories():
54 | """
55 | :return: List of all collection names
56 | :rtype: list
57 | """
58 | return COLLECTION.keys()
59 |
60 |
61 | def getCollections():
62 | """
63 | :return: Collection overview
64 | :rtype: OrderedDict
65 | """
66 | return COLLECTION
67 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/path.py:
--------------------------------------------------------------------------------
1 | def baseName(name):
2 | """
3 | This function will strip the namespaces and grouping information of a name.
4 | Useful when working with fullPaths but needing the base for naming.
5 |
6 | :param str name:
7 | :return: Base name of string
8 | :rtype: str
9 | """
10 | return name.split("|")[-1].split(":")[-1]
11 |
12 |
13 | def rootName(name):
14 | """
15 | This function will strip the grouping information of a name.
16 | Useful when working with fullPaths but needing the base for naming.
17 |
18 | :param str name:
19 | :return: Root name of string
20 | :rtype: str
21 | """
22 | return name.split("|")[-1]
23 |
24 |
25 | def namespace(name):
26 | """
27 | This function will return the namespace if any of the object
28 |
29 | :param str name:
30 | :returns: namespace
31 | :rtype: str/None
32 | """
33 | if name.find(":") != -1:
34 | return name.split("|")[-1].rsplit(":", 1)[0]
35 |
36 |
37 | # ----------------------------------------------------------------------------
38 |
39 |
40 | def asFlatList(input):
41 | """
42 | Convert the input to a flat list.
43 |
44 | :param input:
45 | :return: Flattened list
46 | :rtype: list
47 | """
48 | if type(input) != list:
49 | # return list with input as content
50 | return [input]
51 |
52 | elif type(input) == list and type(input[0]) == list:
53 | # if the first element of the list is also a list. loop over the lists
54 | # within the lists, and extend them into a new list making the return
55 | # value a combined list.
56 | content = []
57 | for i in input:
58 | content.extend(i)
59 | return content
60 |
61 | elif type(input) == list:
62 | # return input value
63 | return input
64 |
65 | # return empty list
66 | return []
67 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/ui/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | from maya import OpenMaya, OpenMayaUI, cmds
3 |
4 |
5 | # ----------------------------------------------------------------------------
6 |
7 |
8 | # import pyside, do qt version check for maya 2017 >
9 | qtVersion = cmds.about(qtVersion=True)
10 | if qtVersion.startswith("4") or type(qtVersion) not in [str, unicode]:
11 | from PySide.QtGui import *
12 | from PySide.QtCore import *
13 | import shiboken
14 | else:
15 | from PySide2.QtGui import *
16 | from PySide2.QtCore import *
17 | from PySide2.QtWidgets import *
18 | import shiboken2 as shiboken
19 |
20 |
21 | # ----------------------------------------------------------------------------
22 |
23 |
24 | FONT = QFont()
25 | FONT.setFamily("Consolas")
26 |
27 | BOLT_FONT = QFont()
28 | BOLT_FONT.setFamily("Consolas")
29 | BOLT_FONT.setWeight(100)
30 |
31 |
32 | # ----------------------------------------------------------------------------
33 |
34 |
35 | ALIGN_LEFT_STYLESHEET = "QPushButton{text-align: left}"
36 | URGENCY_STYLESHEET = {
37 | 0: "QPushButton{ background-color: green;}",
38 | 1: "QPushButton{ background-color: orange;}",
39 | 2: "QPushButton{ background-color: red;}",
40 | }
41 |
42 | CHECK_ICON = ":/checkboxOff.png"
43 | SELECT_ICON = ":/redSelect.png"
44 | FIX_ICON = ":/interactivePlayback.png"
45 |
46 | COLLAPSE_ICONS = {
47 | True: ":/arrowDown.png",
48 | False: ":/arrowRight.png"
49 | }
50 |
51 |
52 | # ----------------------------------------------------------------------------
53 |
54 |
55 | def mayaWindow():
56 | """
57 | Get Maya's main window.
58 |
59 | :rtype: QMainWindow
60 | """
61 | window = OpenMayaUI.MQtUtil.mainWindow()
62 | window = shiboken.wrapInstance(long(window), QMainWindow)
63 |
64 | return window
65 |
66 |
67 | # ----------------------------------------------------------------------------
68 |
69 |
70 | def getIconPath(name):
71 | """
72 | Get an icon path based on file name. All paths in the XBMLANGPATH variable
73 | processed to see if the provided icon can be found.
74 |
75 | :param str name:
76 | :return: Icon path
77 | :rtype: str/None
78 | """
79 | for path in os.environ.get("XBMLANGPATH").split(os.pathsep):
80 | iconPath = os.path.join(path, name)
81 | if os.path.exists(iconPath):
82 | return iconPath.replace("\\", "/")
83 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/rigging.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 | from ..utils import QualityAssurance, reference
3 |
4 |
5 | class DeleteNonDeformerHistory(QualityAssurance):
6 | """
7 | Meshes will be checked to see if they contain non-deformer history. Be
8 | carefull with this ceck as it is not always nessecary to fix this. History
9 | is not always a bad thing. When fixing this error the partial history will
10 | be baked.
11 | """
12 | def __init__(self):
13 | QualityAssurance.__init__(self)
14 |
15 | self._name = "Non Deformer History"
16 | self._message = "{0} mesh(es) contain non-deformer history nodes"
17 | self._categories = ["Rigging"]
18 | self._selectable = True
19 |
20 | self._ignoreNodeTypes = [
21 | "geometryFilter", "tweak", "groupParts",
22 | "groupId", "shape", "dagPose",
23 | "joint", "shadingEngine", "cluster",
24 | "transform", "diskCache", "time"
25 | ]
26 |
27 | # ------------------------------------------------------------------------
28 |
29 | @property
30 | def ignoreNodeTypes(self):
31 | return self._ignoreNodeTypes
32 |
33 | # ------------------------------------------------------------------------
34 |
35 | def _find(self):
36 | """
37 | :return: Meshes with non-deformer history
38 | :rtype: generator
39 | """
40 | meshes = self.ls(type="mesh")
41 | meshes = reference.removeReferenced(meshes)
42 |
43 | for mesh in meshes:
44 | history = cmds.listHistory(mesh)
45 | deformers = cmds.ls(history, type=self.ignoreNodeTypes)
46 |
47 | if len(history) != len(deformers):
48 | yield mesh
49 |
50 | def _fix(self, mesh):
51 | """
52 | :param str mesh:
53 | """
54 | cmds.bakePartialHistory(mesh, prePostDeformers=True)
55 |
56 |
57 | class DeleteNonSetDrivenAnimation(QualityAssurance):
58 | """
59 | Animation curves will be checked to see if they are not set driven keys.
60 | Non set driven keys should not be present in the scene and will be deleted
61 | when fixing this error.
62 | """
63 | def __init__(self):
64 | QualityAssurance.__init__(self)
65 |
66 | self._name = "Non Set-Driven Animation"
67 | self._message = "{0} non set-driven animation curve(s) in the scene"
68 | self._categories = ["Rigging"]
69 | self._selectable = True
70 |
71 | # ------------------------------------------------------------------------
72 |
73 | def _find(self):
74 | """
75 | :return: Non set driven key animation curves
76 | :rtype: generator
77 | """
78 | animCurves = self.ls(type="animCurve")
79 | animCurves = reference.removeReferenced(animCurves)
80 |
81 | for animCurve in animCurves:
82 | if not cmds.listConnections("{0}.input".format(animCurve)):
83 | yield animCurve
84 |
85 | def _fix(self, animCurve):
86 | """
87 | :param str animCurve:
88 | """
89 | cmds.delete(animCurve)
90 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | New checks can be written in one of the sub modules of this package.
3 | Writing new checks in existing modules they will automatically be picked
4 | up by the script. When adding a new sub module it is important the contents
5 | of the sub module into this one.
6 |
7 | .. code-block:: python
8 |
9 | from .animation import *
10 |
11 | All checks are sorted in order of occurrence in the source code. This can be
12 | used to make sure certain checks are after others.
13 | """
14 | import sys
15 | import inspect
16 | from collections import OrderedDict
17 |
18 | from .uv import *
19 | from .scene import *
20 | from .rigging import *
21 | from .shaders import *
22 | from .textures import *
23 | from .geometry import *
24 | from .skinning import *
25 | from .modelling import *
26 | from .animation import *
27 | from .renderStats import *
28 | from .renderLayers import *
29 |
30 | from .. import collections
31 | from ..utils.qa import QualityAssurance
32 |
33 |
34 | def getChecks():
35 | """
36 | Get all checks available in this module.
37 |
38 | :return: All available error checks
39 | :rtype: list
40 | """
41 | checks = []
42 |
43 | # get quality assurance checks.
44 | for name, obj in inspect.getmembers(sys.modules[__name__]):
45 | if (
46 | inspect.isclass(obj)
47 | and issubclass(obj, QualityAssurance)
48 | and obj.__name__ != QualityAssurance.__name__
49 | ):
50 |
51 | checks.append(obj())
52 |
53 | # sort checks based on location in source code.
54 | checks.sort(
55 | key=lambda x:
56 | inspect.getsource(
57 | inspect.getmodule(x)
58 | ).find(
59 | x.__class__.__name__
60 | )
61 | )
62 |
63 | return checks
64 |
65 |
66 | def getChecksFromCollection(collection):
67 | """
68 | Get all check based on a collections. The collections are stored in the
69 | collection module, or custom collections made by the user are saved to
70 | disk.
71 |
72 | :param str collection:
73 | :return: Ordered dictionary of checks
74 | :rtype: OrderedDict
75 | """
76 | data = getChecksSplitByCategory()
77 |
78 | categories = getChecksCategories()
79 | categories = collections.getCollections().get(collection) or categories
80 |
81 | return OrderedDict(
82 | [
83 | (category, data.get(category))
84 | for category in categories
85 | if data.get(category)
86 | ]
87 | )
88 |
89 |
90 | def getChecksSplitByCategory():
91 | """
92 | Get all of the checks and split them based on category as defined in the
93 | classes themselves.
94 |
95 | :return: All error checks split by category
96 | :rtype: dict
97 | """
98 | data = {}
99 |
100 | for check in getChecks():
101 | for category in check.categories:
102 | if category not in data.keys():
103 | data[category] = []
104 |
105 | data[category].append(check)
106 |
107 | return data
108 |
109 |
110 | def getChecksCategories():
111 | """
112 | Get a list of all the categories used.
113 |
114 | :return: Category list
115 | :rtype: list
116 | """
117 | return getChecksSplitByCategory().keys()
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/uv.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 | from ..utils import QualityAssurance, reference
3 |
4 |
5 | class EmptyUVSets(QualityAssurance):
6 | """
7 | Meshes will be checked to see if they have empty uv sets. When fixing the
8 | uv set will be deleted.
9 | """
10 | def __init__(self):
11 | QualityAssurance.__init__(self)
12 |
13 | self._name = "Empty UV Sets"
14 | self._message = "{0} empty uv set(s)"
15 | self._categories = ["UV"]
16 | self._selectable = False
17 |
18 | # ------------------------------------------------------------------------
19 |
20 | def _find(self):
21 | """
22 | :return: Empty UV Sets
23 | :rtype: generator
24 | """
25 | meshes = cmds.ls(type="mesh", noIntermediate=True, l=True)
26 | meshes = reference.removeReferenced(meshes)
27 |
28 | for mesh in meshes:
29 | uvSets = cmds.polyUVSet(mesh, query=True, allUVSets=True)
30 | uvSetsIndex = cmds.polyUVSet(mesh, query=True, allUVSetsIndices=True)
31 |
32 | for uvSet, index in zip(uvSets, uvSetsIndex):
33 | if index == 0:
34 | continue
35 |
36 | if not cmds.polyEvaluate(mesh, uvcoord=True, uvSetName=uvSet):
37 | yield "{0}.uvSet[{1}].uvSetName".format(mesh, index)
38 |
39 | def _fix(self, meshAttribute):
40 | """
41 | :param str meshAttribute:
42 | """
43 | mesh = meshAttribute.split(".")[0]
44 | uvSet = cmds.getAttr(meshAttribute)
45 |
46 | cmds.polyUVSet(mesh, edit=True, delete=True, uvSet=uvSet)
47 |
48 |
49 | class UnusedUVSets(QualityAssurance):
50 | """
51 | Meshes will be checked to see if they have unused uv sets. When fixing the
52 | uv set will be deleted.
53 | """
54 | def __init__(self):
55 | QualityAssurance.__init__(self)
56 |
57 | self._name = "Unused UV Sets"
58 | self._message = "{0} unused uv set(s)"
59 | self._categories = ["UV"]
60 | self._selectable = False
61 |
62 | self._ignoreUvSets = [
63 | "hairUVSet",
64 | ]
65 |
66 | # ------------------------------------------------------------------------
67 |
68 | @property
69 | def ignoreUvSets(self):
70 | return self._ignoreUvSets
71 |
72 | # ------------------------------------------------------------------------
73 |
74 | def _find(self):
75 | """
76 | :return: Empty UV Sets
77 | :rtype: generator
78 | """
79 | meshes = cmds.ls(type="mesh", noIntermediate=True, l=True)
80 | meshes = reference.removeReferenced(meshes)
81 |
82 | for mesh in meshes:
83 | uvSets = cmds.polyUVSet(mesh, query=True, allUVSets=True)
84 | uvSetsIndex = cmds.polyUVSet(mesh, query=True, allUVSetsIndices=True)
85 |
86 | for uvSet, index in zip(uvSets, uvSetsIndex):
87 | if index == 0 or uvSet in self.ignoreUvSets:
88 | continue
89 |
90 | attr = "{0}.uvSet[{1}].uvSetName".format(mesh, index)
91 | if not cmds.listConnections(attr):
92 | yield attr
93 |
94 | def _fix(self, meshAttribute):
95 | """
96 | :param str meshAttribute:
97 | """
98 | mesh = meshAttribute.split(".")[0]
99 | uvSet = cmds.getAttr(meshAttribute)
100 |
101 | cmds.polyUVSet(mesh, edit=True, delete=True, uvSet=uvSet)
102 |
--------------------------------------------------------------------------------
/docs/search.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | Search — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Search
55 |
56 |
57 |
58 | Please activate JavaScript to enable the search
59 | functionality.
60 |
61 |
62 |
63 | From here you can search these documents. Enter your search
64 | words into the box below and click "search". Note that the search
65 | function will automatically search for all of the words. Pages
66 | containing fewer words won't appear in the result list.
67 |
68 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
85 |
86 |
87 |
99 |
103 |
104 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/skin.py:
--------------------------------------------------------------------------------
1 | from maya import cmds, OpenMaya, OpenMayaAnim
2 |
3 |
4 | def getInfluencesApi(skinFn):
5 | """
6 | Get influence data from a skin cluster
7 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
8 |
9 | :param OpenMayaAnim.MFnSkinCluster skinFn:
10 | :return: Influences dictionary and list
11 | :rtype: tuple()
12 | """
13 | # get the MDagPath for all influence
14 | infDags = OpenMaya.MDagPathArray()
15 | skinFn.influenceObjects(infDags)
16 |
17 | # create dictionary with MDagPath
18 | # create list with full path of influences
19 | infIds = {}
20 | infPaths = []
21 | for x in xrange(infDags.length()):
22 | infPath = infDags[x].fullPathName()
23 | infId = int(skinFn.indexForInfluenceObject(infDags[x]))
24 | infIds[infId] = x
25 | infPaths.append(infPath)
26 |
27 | return infIds, infPaths
28 |
29 |
30 | def getWeightsApiGenerator(skinFn, infIds):
31 | """
32 | Get skin weights from a skin cluster reading its attributes.
33 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
34 |
35 | :param OpenMayaAnim.MFnSkinCluster skinFn:
36 | :param dict infIds:
37 | :return: Skin weights per vertex
38 | :rtype: generator
39 | """
40 | wlPlug = skinFn.findPlug("weightList")
41 | wPlug = skinFn.findPlug("weights")
42 | wlAttr = wlPlug.attribute()
43 | wAttr = wPlug.attribute()
44 | wInfIds = OpenMaya.MIntArray()
45 |
46 | # the weights are stored in dictionary, the key is the vertId,
47 | # the value is another dictionary whose key is the influence id and
48 | # value is the weight for that influence
49 | for vId in xrange(wlPlug.numElements()):
50 | vWeights = {}
51 | # tell the weights attribute which vertex id it represents
52 | wPlug.selectAncestorLogicalIndex(vId, wlAttr)
53 |
54 | # get the indice of all non-zero weights for this vert
55 | wPlug.getExistingArrayAttributeIndices(wInfIds)
56 |
57 | # create a copy of the current wPlug
58 | infPlug = OpenMaya.MPlug(wPlug)
59 | for infId in wInfIds:
60 | # tell the infPlug it represents the current influence id
61 | infPlug.selectAncestorLogicalIndex(infId, wAttr)
62 |
63 | # add this influence and its weight to this verts weights
64 | try:
65 | vWeights[infIds[infId]] = infPlug.asDouble()
66 | except KeyError:
67 | # assumes a removed influence
68 | pass
69 |
70 | yield vWeights
71 |
72 |
73 | def getWeightsApi(skinFn, infIds):
74 | """
75 | Get skin weights from a skin cluster reading its attributes.
76 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
77 |
78 | :param OpenMayaAnim.MFnSkinCluster skinFn:
79 | :param dict infIds:
80 | :return: Dictionary of skin weights.
81 | :rtype: dict
82 | """
83 | weights = {}
84 |
85 | wlPlug = skinFn.findPlug("weightList")
86 | wPlug = skinFn.findPlug("weights")
87 | wlAttr = wlPlug.attribute()
88 | wAttr = wPlug.attribute()
89 | wInfIds = OpenMaya.MIntArray()
90 |
91 | # the weights are stored in dictionary, the key is the vertId,
92 | # the value is another dictionary whose key is the influence id and
93 | # value is the weight for that influence
94 | for vId in xrange(wlPlug.numElements()):
95 | vWeights = {}
96 | # tell the weights attribute which vertex id it represents
97 | wPlug.selectAncestorLogicalIndex(vId, wlAttr)
98 |
99 | # get the indice of all non-zero weights for this vert
100 | wPlug.getExistingArrayAttributeIndices(wInfIds)
101 |
102 | # create a copy of the current wPlug
103 | infPlug = OpenMaya.MPlug(wPlug)
104 | for infId in wInfIds:
105 | # tell the infPlug it represents the current influence id
106 | infPlug.selectAncestorLogicalIndex(infId, wAttr)
107 |
108 | # add this influence and its weight to this verts weights
109 | try:
110 | vWeights[infIds[infId]] = infPlug.asDouble()
111 | except KeyError:
112 | # assumes a removed influence
113 | pass
114 |
115 | weights[vId] = vWeights
116 |
117 | return weights
118 |
--------------------------------------------------------------------------------
/docs/_static/pygments.css:
--------------------------------------------------------------------------------
1 | .highlight .hll { background-color: #ffffcc }
2 | .highlight { background: #eeffcc; }
3 | .highlight .c { color: #408090; font-style: italic } /* Comment */
4 | .highlight .err { border: 1px solid #FF0000 } /* Error */
5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */
6 | .highlight .o { color: #666666 } /* Operator */
7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
9 | .highlight .cp { color: #007020 } /* Comment.Preproc */
10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */
14 | .highlight .ge { font-style: italic } /* Generic.Emph */
15 | .highlight .gr { color: #FF0000 } /* Generic.Error */
16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */
18 | .highlight .go { color: #333333 } /* Generic.Output */
19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
20 | .highlight .gs { font-weight: bold } /* Generic.Strong */
21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */
23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */
27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
28 | .highlight .kt { color: #902000 } /* Keyword.Type */
29 | .highlight .m { color: #208050 } /* Literal.Number */
30 | .highlight .s { color: #4070a0 } /* Literal.String */
31 | .highlight .na { color: #4070a0 } /* Name.Attribute */
32 | .highlight .nb { color: #007020 } /* Name.Builtin */
33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
34 | .highlight .no { color: #60add5 } /* Name.Constant */
35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
37 | .highlight .ne { color: #007020 } /* Name.Exception */
38 | .highlight .nf { color: #06287e } /* Name.Function */
39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */
43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */
46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */
47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */
48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */
49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */
50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */
51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */
53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */
54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */
56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */
60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */
61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */
62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */
63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */
65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # maya-quality-assurance
2 |
3 | Quality assurance framework for Maya. Focused on many parts of a production pipeline, collections are created for animators, modelers, riggers and look-dev.
4 |
5 |
6 |
7 | ## Installation
8 | * Extract the content of the .rar file anywhere on disk.
9 | * Drag the qualityAssurance.mel file in Maya to permanently install the script.
10 |
11 | ## Usage
12 | A button on the MiscTools shelf will be created that will allow easy access to the ui, this way the user doesn't need to worry about any of the code.
13 | If user wishes to not use the shelf button the following commands can be used.
14 |
15 | Display UI:
16 |
17 | ```python
18 | import qualityAssurance.ui
19 | qualityAssurance.ui.show("rigging")
20 | ```
21 |
22 | The show function takes in a collection argument, if you work within one of the specialties you can simply call the show function with the collection you want to see by default. To see all available collections run the following code.
23 |
24 | ```python
25 | import qualityAssurance.collections
26 | print qualityAssurance.collections.getCollectionsCategories()
27 | ```
28 |
29 | ## Adding Quality Assurance Checks
30 | The quality assurance framework is setup so new quality assurance checks can easily be added.
31 |
32 | ### Location
33 | All quality assurance checks are located in the **checks** folder. New checks can be written in one of the sub modules of the **checks** folder. Writing new checks in existing modules will automatically be picked up by the script. When adding a new sub module it is important to import the contents of the sub module into the **__init__.py** file in the **checks** folder.
34 |
35 | ```python
36 | from .animation import *
37 | ```
38 |
39 | All checks are sorted in order of occurrence in the source code. This can be used to make sure certain checks are after others.
40 |
41 | ### Collections
42 | New collections can be added in the **COLLECTIONS** variable. Since this **COLLECTIONS** variable is an OrderedDict, it will keep the order. A collection can be defined by who will be using it. Currently it is divided by different specialties. Each specialty contains a list of categories that will be displayed. The category names link to the categories defined in the quality assurance checks themselves.
43 |
44 | ### Sub Classing
45 | New quality assurance checks can be created by sub classing the **QualityAssurance** base class. It is important to extend the class with a **_find** and **_fix** function and update the meta data in the **__init__** function.
46 |
47 | ```python
48 | from ..utils import QualityAssurance, reference
49 |
50 | class TestCheck(QualityAssurance):
51 | def __init__(self):
52 | QualityAssurance.__init__(self)
53 |
54 | self._name = "Unused Animation"
55 | self._message = "{0} animation curve(s) are unused"
56 | self._categories = ["Animation"]
57 | self._selectable = True
58 |
59 | # ------------------------------------------------------------------------
60 |
61 | def _find(self):
62 | animCurves = self.ls(type="animCurve")
63 | animCurves = reference.removeReferenced(animCurves)
64 |
65 | for animCurve in animCurves:
66 | # check if the output plug is connected
67 | if cmds.listConnections("{0}.output".format(animCurve)):
68 | continue
69 |
70 | # yield error
71 | yield animCurve
72 |
73 | def _fix(self, animCurve):
74 | cmds.delete(animCurve)
75 | ```
76 |
77 | #### Meta Data
78 | * **self._name:** Name of the quality assurance check
79 | * **self._urgency:** Urgency level, 1=orange, 2=red.
80 | * **self._message:** Format-able message when errors are found.
81 | * **self._categories:** List of categories the quality assurance check should part of.
82 | * **self._selectable:** Boolean value if the error list is selectable.
83 |
84 | #### Find and Fix Function
85 | * The **_find** function is a generator that yields errors as they get found.
86 | * The **_fix** function fixes one of these errors at a time. In the example above we could find multiple animation curves, but the fix only deletes one animation curve at a time.
87 |
88 | ## Note
89 | Inspired by Martin Orlowski's **Quality GuAard**, I've decided to write my own quality assurance framework and make it freely available. The project is available on [Git](https://github.com/robertjoosten/maya-quality-assurance). Free for anybody that wishes to contribute to this tool and add additional quality assurance checks.
--------------------------------------------------------------------------------
/docs/qualityAssurance.install.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.install module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.install module
48 |
49 |
50 | qualityAssurance.install.shelf( )
51 | Add a new shelf in Maya with the tools that is provided in the SHELF_TOOL
52 | variable. If the tab exists it will be checked to see if the button is
53 | already added. If this is the case the previous button will be deleted and
54 | a new one will be created in its place.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
84 |
85 |
86 |
98 |
102 |
103 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/shaders.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 | from ..utils import QualityAssurance, reference
3 |
4 |
5 | class NoShadingGroup(QualityAssurance):
6 | """
7 | Meshes will be checked to see if they have a shading group attached.
8 | """
9 | def __init__(self):
10 | QualityAssurance.__init__(self)
11 |
12 | self._name = "No Shading Group Assignment"
13 | self._message = "{0} mesh(es) are not connected to any shading group"
14 | self._categories = ["Shaders"]
15 | self._selectable = True
16 |
17 | # ------------------------------------------------------------------------
18 |
19 | def _find(self):
20 | """
21 | :return: Meshes without shading group attachment
22 | :rtype: generator
23 | """
24 | meshes = self.ls(type="mesh", noIntermediate=True, l=True)
25 | meshes = reference.removeReferenced(meshes)
26 |
27 | for mesh in meshes:
28 | shadingGroups = cmds.listConnections(mesh, type="shadingEngine")
29 | if not shadingGroups:
30 | yield mesh
31 |
32 |
33 | class InitialShadingGroup(QualityAssurance):
34 | """
35 | Meshes will be checked to see if they have the default shading group
36 | attached.
37 | """
38 | def __init__(self):
39 | QualityAssurance.__init__(self)
40 |
41 | self._name = "Initial Shading Group Assignment"
42 | self._message = "{0} object(s) are connected to the initial shading group"
43 | self._categories = ["Shaders"]
44 | self._selectable = True
45 |
46 | # ------------------------------------------------------------------------
47 |
48 | def _find(self):
49 | """
50 | :return: Meshes without shading group attachment
51 | :rtype: generator
52 | """
53 | shadingGroups = cmds.ls(type="shadingEngine")
54 |
55 | if "initialShadingGroup" not in shadingGroups:
56 | return
57 |
58 | connections = cmds.sets("initialShadingGroup", query=True) or []
59 | if not connections:
60 | return
61 |
62 | connections = cmds.ls(connections, l=True)
63 | for connection in connections:
64 | yield connection
65 |
66 |
67 | class FaceAssignedShading(QualityAssurance):
68 | """
69 | Shading groups will be checked to see if they contain component
70 | connections. When fixing it will force a shape connections. If you
71 | intentially have component assigment, it is best to skip this check.
72 | """
73 | def __init__(self):
74 | QualityAssurance.__init__(self)
75 |
76 | self._name = "Face Assignment"
77 | self._urgency = 1
78 | self._message = "{0} shading group(s) contain a component connection"
79 | self._categories = ["Shaders"]
80 | self._selectable = True
81 |
82 | # ------------------------------------------------------------------------
83 |
84 | def _find(self):
85 | """
86 | :return: Empty UV Sets
87 | :rtype: generator
88 | """
89 | shadingGroups = self.ls(type="shadingEngine")
90 |
91 | for shadingGroup in shadingGroups:
92 | connections = cmds.sets(shadingGroup, query=True) or []
93 | components = [
94 | connection.split(".")[0]
95 | for connection in connections
96 | if connection.count(".")
97 | ]
98 |
99 | if components:
100 | yield shadingGroup
101 |
102 | def _fix(self, shadingGroup):
103 | """
104 | :param str node:
105 | """
106 | connections = cmds.sets(shadingGroup, query=True) or []
107 | components = [
108 | connection
109 | for connection in connections
110 | if connection.count(".")
111 | ]
112 |
113 | if not components:
114 | return
115 |
116 | overview = dict()
117 | components = cmds.ls(components, fl=True, l=True)
118 |
119 | # collect faces per object
120 | for face in components:
121 | obj = face.split(".")[0]
122 | if cmds.nodeType(obj) == "transform":
123 | shape = cmds.listRelatives(obj, s=True, ni=True, f=True)[0]
124 | else:
125 | shape = obj[:]
126 |
127 | if not shape in overview.keys():
128 | overview[shape] = []
129 |
130 | overview[shape].append(face)
131 |
132 | # loop overview
133 | for shape, remove in overview.iteritems():
134 | if len(remove) != cmds.polyEvaluate(shape, face=True):
135 | raise RuntimeError("Incomplete Face Assigment")
136 |
137 | cmds.sets(remove, edit=True, remove=shadingGroup)
138 | cmds.sets(shape, edit=True, forceElement=shadingGroup)
139 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.undo.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.undo module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.undo module
48 |
49 |
50 | class qualityAssurance.utils.undo.UndoContext
51 | Bases: object
52 | This context will create a undo chunk of every commands that is ran within
53 | the context.
54 | with UndoContext :
55 | # code
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
87 |
88 |
89 |
101 |
105 |
106 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.ui.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.ui package — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.ui package
48 |
49 |
Submodules
50 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
93 |
94 |
95 |
107 |
111 |
112 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.decorators.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.decorators module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.decorators module
48 |
49 |
50 | qualityAssurance.utils.decorators.ifNoErrorsReturn( argument )
51 | If the error list is emtpy return the argument provided.
52 |
53 |
54 |
55 |
56 | Parameters: argument –
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
89 |
90 |
91 |
103 |
107 |
108 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.textures.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.textures module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.textures module
48 |
49 |
50 | class qualityAssurance.checks.textures.NonExistingTextures
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | Animation curves will be checked to see if they are not set driven keys.
53 | Non set driven keys should not be present in the scene and will be deleted
54 | when fixing this error.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
84 |
85 |
86 |
98 |
102 |
103 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/renderStats.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 | from ..utils import QualityAssurance
3 |
4 |
5 | class PrimaryVisibility(QualityAssurance):
6 | """
7 | Meshes will be checked to see if the primary visibility is on. When
8 | fixing this error the primary visibility will be turned on.
9 | """
10 | def __init__(self):
11 | QualityAssurance.__init__(self)
12 |
13 | self._name = "Primary Visibility"
14 | self._message = "{0} mesh(es) are not visible"
15 | self._categories = ["Render Stats"]
16 | self._selectable = True
17 |
18 | self._attribute = ".primaryVisibility"
19 | self._errorBool = False
20 |
21 | # ------------------------------------------------------------------------
22 |
23 | @property
24 | def attribute(self):
25 | return self._attribute
26 |
27 | @property
28 | def errorBool(self):
29 | return self._errorBool
30 |
31 | # ------------------------------------------------------------------------
32 |
33 | def _find(self):
34 | """
35 | :return: Find meshes that have the attribute set to the error boolean
36 | :rtype: generator
37 | """
38 | meshes = self.ls(type="mesh")
39 |
40 | for mesh in meshes:
41 | if cmds.getAttr(mesh + self.attribute) == self.errorBool:
42 | yield mesh
43 |
44 | def _fix(self, mesh):
45 | """
46 | :param str mesh:
47 | """
48 | cmds.setAttr(mesh + self.attribute, not self.errorBool)
49 |
50 |
51 | class VisibleInRefraction(PrimaryVisibility):
52 | """
53 | Meshes will be checked to see if they are visible in refraction. When
54 | fixing this error the visible in refraction will be turned on.
55 | """
56 | def __init__(self):
57 | PrimaryVisibility.__init__(self)
58 |
59 | self._name = "Visible in Refraction"
60 | self._message = "{0} mesh(es) are not visible in refraction"
61 |
62 | self._attribute = ".visibleInRefractions"
63 | self._errorBool = False
64 |
65 |
66 | class VisibleInReflection(PrimaryVisibility):
67 | """
68 | Meshes will be checked to see if they are visible in reflection. When
69 | fixing this error the visible in reflection will be turned on.
70 | """
71 | def __init__(self):
72 | PrimaryVisibility.__init__(self)
73 |
74 | self._name = "Visible in Reflection"
75 | self._message = "{0} mesh(es) are not visible in reflection"
76 |
77 | self._attribute = ".visibleInReflections"
78 | self._errorBool = False
79 |
80 |
81 | class CastShadows(PrimaryVisibility):
82 | """
83 | Meshes will be checked to see if they cast shadows. When fixing this
84 | error the casting of shadows will be turned on.
85 | """
86 | def __init__(self):
87 | PrimaryVisibility.__init__(self)
88 |
89 | self._name = "Cast Shadows"
90 | self._message = "{0} mesh(es) don't cast shadows"
91 |
92 | self._attribute = ".castsShadows"
93 | self._errorBool = False
94 |
95 |
96 | class ReceiveShadows(PrimaryVisibility):
97 | """
98 | Meshes will be checked to see if they receive shadows. When fixing this
99 | error the receiving of shadows will be turned on.
100 | """
101 | def __init__(self):
102 | PrimaryVisibility.__init__(self)
103 |
104 | self._name = "Receive Shadows"
105 | self._message = "{0} mesh(es) don't receive shadows"
106 |
107 | self._attribute = ".receiveShadows"
108 | self._errorBool = False
109 |
110 |
111 | class SmoothShading(PrimaryVisibility):
112 | """
113 | Meshes will be checked to see if they are smooth shaded. When fixing this
114 | error the smooth shading will be turned on.
115 | """
116 | def __init__(self):
117 | PrimaryVisibility.__init__(self)
118 |
119 | self._name = "Smooth Shading"
120 | self._message = "{0} mesh(es) are not smooth shaded"
121 |
122 | self._attribute = ".smoothShading"
123 | self._errorBool = False
124 |
125 |
126 | class DoubleSided(PrimaryVisibility):
127 | """
128 | Meshes will be checked to see if they are double sided. When fixing this
129 | error double sided will be turned on.
130 | """
131 | def __init__(self):
132 | PrimaryVisibility.__init__(self)
133 |
134 | self._name = "Double Sided"
135 | self._message = "{0} mesh(es) are not double sided"
136 |
137 | self._attribute = ".doubleSided"
138 | self._errorBool = False
139 |
140 |
141 | class Opposite(PrimaryVisibility):
142 | """
143 | Meshes will be checked to see if they are set to opposite. When fixing
144 | this error opposite state will be turned off.
145 | """
146 | def __init__(self):
147 | PrimaryVisibility.__init__(self)
148 |
149 | self._name = "Opposite"
150 | self._message = "{0} mesh(es) are set to opposite"
151 |
152 | self._attribute = ".opposite"
153 | self._errorBool = True
154 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Quality assurance framework for Maya. Focused on many parts of a production
3 | pipeline, collections are created for animators, modelers, riggers and
4 | look-dev.
5 |
6 | .. figure:: /_images/qualityAssuranceExample.png
7 | :align: center
8 |
9 | Installation
10 | ============
11 | * Extract the content of the .rar file anywhere on disk.
12 | * Drag the qualityAssurance.mel file in Maya to permanently install the script.
13 |
14 | Usage
15 | =====
16 | A button on the MiscTools shelf will be created that will allow easy access to
17 | the ui, this way the user doesn't need to worry about any of the code. If user
18 | wishes to not use the shelf button the following commands can be used.
19 |
20 | Display UI
21 |
22 | .. code-block:: python
23 |
24 | import qualityAssurance.ui
25 | qualityAssurance.ui.show("rigging")
26 |
27 | The show function takes in a collection argument, if you work within one of
28 | the specialties you can simply call the show function with the collection
29 | you want to see by default. To see all available collections run the following
30 | code.
31 |
32 | .. code-block:: python
33 |
34 | import qualityAssurance.collections
35 | print qualityAssurance.collections.getCollectionsCategories()
36 |
37 | Adding Quality Assurance Checks
38 | ===============================
39 |
40 | The quality assurance framework is setup so new quality assurance checks can
41 | easily be added.
42 |
43 | Location
44 | --------
45 | All quality assurance checks are located in the **checks** folder. New checks
46 | can be written in one of the sub modules of the **checks** folder. Writing
47 | new checks in existing modules will automatically be picked up by the script.
48 | When adding a new sub module it is important to import the contents of the
49 | sub module into the **__init__.py** file in the **checks** folder.
50 |
51 | .. code-block:: python
52 |
53 | from .animation import *
54 |
55 | All checks are sorted in order of occurrence in the source code. This can be
56 | used to make sure certain checks are after others.
57 |
58 | Collections
59 | -----------
60 | New collections can be added in the **COLLECTIONS** variable. Since this
61 | **COLLECTIONS** variable is an OrderedDict, it will keep the order. A
62 | collection can be defined by who will be using it. Currently it is
63 | divided by different specialties. Each specialty contains a list of
64 | categories that will be displayed. The category names link to the categories
65 | defined in the quality assurance checks themselves.
66 |
67 | Sub Classing
68 | ------------
69 | New quality assurance checks can be created by sub classing the
70 | :class:`qualityAssurance.utils.qa.QualityAssurance` base class. It is important
71 | to extend the class with a **_find** and **_fix** function and update the meta
72 | data in the **__init__** function.
73 |
74 | .. code-block:: python
75 |
76 | from ..utils import QualityAssurance, reference
77 |
78 | class TestCheck(QualityAssurance):
79 | def __init__(self):
80 | QualityAssurance.__init__(self)
81 |
82 | self._name = "Unused Animation"
83 | self._message = "{0} animation curve(s) are unused"
84 | self._categories = ["Animation"]
85 | self._selectable = True
86 |
87 | # ------------------------------------------------------------------------
88 |
89 | def _find(self):
90 | animCurves = self.ls(type="animCurve")
91 | animCurves = reference.removeReferenced(animCurves)
92 |
93 | for animCurve in animCurves:
94 | # check if the output plug is connected
95 | if cmds.listConnections("{0}.output".format(animCurve)):
96 | continue
97 |
98 | # yield error
99 | yield animCurve
100 |
101 | def _fix(self, animCurve):
102 | cmds.delete(animCurve)
103 |
104 | Meta Data
105 | ^^^^^^^^^
106 | * **self._name:** Name of the quality assurance check
107 | * **self._urgency:** Urgency level, 1=orange, 2=red.
108 | * **self._message:** Format-able message when errors are found.
109 | * **self._categories:** List of categories the quality assurance check should part of.
110 | * **self._selectable:** Boolean value if the error list is selectable.
111 |
112 | Find and Fix Function
113 | ^^^^^^^^^^^^^^^^^^^^^
114 | * The **_find** function is a generator that yields errors as they get found.
115 | * The **_fix** function fixes one of these errors at a time. In the example above we could find multiple animation curves, but the fix only deletes one animation curve at a time.
116 |
117 | Note
118 | =====
119 | Inspired by Martin Orlowski's **Quality GuAard**, I've decided to write my own
120 | quality assurance framework and make it freely available. The project is
121 | available on `Git `_.
122 | Free for anybody that wishes to contribute to this tool and add additional
123 | quality assurance checks.
124 | """
125 | __author__ = "Robert Joosten"
126 | __version__ = "0.1.0"
127 | __email__ = "rwm.joosten@gmail.com"
128 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.reference.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.reference module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.reference module
48 |
49 |
50 | qualityAssurance.utils.reference.removeReferenced( nodes )
51 | Remove all referenced nodes from list.
52 |
53 |
54 |
55 |
56 | Parameters: nodes (list ) – List of strings
57 |
58 | Returns: Filtered list without referenced nodes
59 |
60 | Return type: generator
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
93 |
94 |
95 |
107 |
111 |
112 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/modelling.py:
--------------------------------------------------------------------------------
1 | from maya import cmds
2 | from ..utils import QualityAssurance, reference
3 |
4 |
5 | class FreezeTransforms(QualityAssurance):
6 | """
7 | Transforms will be checked to see if they have unfrozen attributes When
8 | fixing this error transforms will be frozen.
9 | """
10 | def __init__(self):
11 | QualityAssurance.__init__(self)
12 |
13 | self._name = "Freeze Transforms"
14 | self._message = "{0} transform(s) are not frozen"
15 | self._categories = ["Modelling"]
16 | self._selectable = True
17 |
18 | self._ignoreNodes = ["|persp", "|front", "|top", "|side"]
19 |
20 | self._attributes = [
21 | ".tx", "ty", "tz",
22 | ".rx", "ry", "rz",
23 | ".sx", "sy", "sz"
24 | ]
25 | self._values = [
26 | 0, 0, 0,
27 | 0, 0, 0,
28 | 1, 1, 1
29 | ]
30 |
31 | # ------------------------------------------------------------------------
32 |
33 | @property
34 | def ignoreNodes(self):
35 | """
36 | :return: Nodes to ignore
37 | :rtype: list
38 | """
39 | return self._ignoreNodes
40 |
41 | @property
42 | def defaultState(self):
43 | """
44 | :return: Default state of attributes with values
45 | :rtype: list
46 | """
47 | return zip(self._attributes, self._values)
48 |
49 | # ------------------------------------------------------------------------
50 |
51 | def _find(self):
52 | """
53 | :return: Unfrozen transforms
54 | :rtype: generator
55 | """
56 | transforms = self.ls(transforms=True, l=True)
57 | transforms = reference.removeReferenced(transforms)
58 |
59 | for transform in transforms:
60 | if transform in self.ignoreNodes:
61 | continue
62 |
63 | for attr, value in self.defaultState:
64 | if not cmds.objExists(transform + attr):
65 | continue
66 |
67 | if cmds.getAttr(transform + attr) != value:
68 | yield transform
69 |
70 | def _fix(self, transform):
71 | """
72 | :param str transform:
73 | """
74 | cmds.makeIdentity(
75 | transform,
76 | apply=True,
77 | translate=True,
78 | rotate=True,
79 | scale=True
80 | )
81 |
82 |
83 | class DeleteHistory(QualityAssurance):
84 | """
85 | Mesh shapes will be checked to see if they have history attached to them.
86 | When fixing this error the history will be deleted.
87 | """
88 | def __init__(self):
89 | QualityAssurance.__init__(self)
90 |
91 | self._name = "History"
92 | self._message = "{0} mesh(es) contain history nodes"
93 | self._categories = ["Modelling"]
94 | self._selectable = True
95 |
96 | self._ignoreNodes = [
97 | "tweak", "groupParts", "groupId",
98 | "shape", "shadingEngine", "mesh"
99 | ]
100 |
101 |
102 | # ------------------------------------------------------------------------
103 |
104 | @property
105 | def ignoreNodes(self):
106 | """
107 | :return: Nodes to ignore
108 | :rtype: list
109 | """
110 | return self._ignoreNodes
111 |
112 | # ------------------------------------------------------------------------
113 |
114 | def _find(self):
115 | """
116 | :return: Meshes with history
117 | :rtype: generator
118 | """
119 |
120 | meshes = self.ls(type="mesh", l=True)
121 | meshes = reference.removeReferenced(meshes)
122 |
123 | for mesh in meshes:
124 | history = cmds.listHistory(mesh) or []
125 | types = [cmds.nodeType(h) for h in history]
126 |
127 | for t in types:
128 | if t in self.ignoreNodes:
129 | continue
130 |
131 | yield mesh
132 | break
133 |
134 | def _fix(self, mesh):
135 | """
136 | :param str mesh:
137 | """
138 | cmds.delete(mesh, ch=True)
139 |
140 |
141 | class DeleteAnimation(QualityAssurance):
142 | """
143 | All animation curves will be added to the error list. When fixing this
144 | error the animation curves will be deleted
145 | """
146 | def __init__(self):
147 | QualityAssurance.__init__(self)
148 |
149 | self._name = "Animation"
150 | self._message = "{0} animation curve(s) in the scene"
151 | self._categories = ["Modelling"]
152 | self._selectable = True
153 |
154 | # ------------------------------------------------------------------------
155 |
156 | def _find(self):
157 | """
158 | :return: Animation curves
159 | :rtype: generator
160 | """
161 |
162 | animCurves = self.ls(type="animCurve")
163 | animCurves = reference.removeReferenced(animCurves)
164 |
165 | for animCurve in animCurves:
166 | yield animCurve
167 |
168 | def _fix(self, animCurve):
169 | """
170 | :param str animCurve:
171 | """
172 | cmds.delete(animCurve)
173 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.animation.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.animation module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.animation module
48 |
49 |
50 | qualityAssurance.utils.animation.removeDrivenAnimCurves( animCurves )
51 | Remove set driven animation curves from the list.
52 |
53 |
54 |
55 |
56 | Parameters: animCurves (list ) – List of strings
57 |
58 | Returns: Filtered list without set driven anim curves
59 |
60 | Return type: generator
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
93 |
94 |
95 |
107 |
111 |
112 |
--------------------------------------------------------------------------------
/docs/_static/classic.css:
--------------------------------------------------------------------------------
1 | /*
2 | * classic.css_t
3 | * ~~~~~~~~~~~~~
4 | *
5 | * Sphinx stylesheet -- classic theme.
6 | *
7 | * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 |
12 | @import url("basic.css");
13 |
14 | /* -- page layout ----------------------------------------------------------- */
15 |
16 | body {
17 | font-family: sans-serif;
18 | font-size: 100%;
19 | background-color: #11303d;
20 | color: #000;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | div.document {
26 | background-color: #1c4e63;
27 | }
28 |
29 | div.documentwrapper {
30 | float: left;
31 | width: 100%;
32 | }
33 |
34 | div.bodywrapper {
35 | margin: 0 0 0 230px;
36 | }
37 |
38 | div.body {
39 | background-color: #ffffff;
40 | color: #000000;
41 | padding: 0 20px 30px 20px;
42 | }
43 |
44 | div.footer {
45 | color: #ffffff;
46 | width: 100%;
47 | padding: 9px 0 9px 0;
48 | text-align: center;
49 | font-size: 75%;
50 | }
51 |
52 | div.footer a {
53 | color: #ffffff;
54 | text-decoration: underline;
55 | }
56 |
57 | div.related {
58 | background-color: #133f52;
59 | line-height: 30px;
60 | color: #ffffff;
61 | }
62 |
63 | div.related a {
64 | color: #ffffff;
65 | }
66 |
67 | div.sphinxsidebar {
68 | }
69 |
70 | div.sphinxsidebar h3 {
71 | font-family: 'Trebuchet MS', sans-serif;
72 | color: #ffffff;
73 | font-size: 1.4em;
74 | font-weight: normal;
75 | margin: 0;
76 | padding: 0;
77 | }
78 |
79 | div.sphinxsidebar h3 a {
80 | color: #ffffff;
81 | }
82 |
83 | div.sphinxsidebar h4 {
84 | font-family: 'Trebuchet MS', sans-serif;
85 | color: #ffffff;
86 | font-size: 1.3em;
87 | font-weight: normal;
88 | margin: 5px 0 0 0;
89 | padding: 0;
90 | }
91 |
92 | div.sphinxsidebar p {
93 | color: #ffffff;
94 | }
95 |
96 | div.sphinxsidebar p.topless {
97 | margin: 5px 10px 10px 10px;
98 | }
99 |
100 | div.sphinxsidebar ul {
101 | margin: 10px;
102 | padding: 0;
103 | color: #ffffff;
104 | }
105 |
106 | div.sphinxsidebar a {
107 | color: #98dbcc;
108 | }
109 |
110 | div.sphinxsidebar input {
111 | border: 1px solid #98dbcc;
112 | font-family: sans-serif;
113 | font-size: 1em;
114 | }
115 |
116 |
117 |
118 | /* -- hyperlink styles ------------------------------------------------------ */
119 |
120 | a {
121 | color: #355f7c;
122 | text-decoration: none;
123 | }
124 |
125 | a:visited {
126 | color: #355f7c;
127 | text-decoration: none;
128 | }
129 |
130 | a:hover {
131 | text-decoration: underline;
132 | }
133 |
134 |
135 |
136 | /* -- body styles ----------------------------------------------------------- */
137 |
138 | div.body h1,
139 | div.body h2,
140 | div.body h3,
141 | div.body h4,
142 | div.body h5,
143 | div.body h6 {
144 | font-family: 'Trebuchet MS', sans-serif;
145 | background-color: #f2f2f2;
146 | font-weight: normal;
147 | color: #20435c;
148 | border-bottom: 1px solid #ccc;
149 | margin: 20px -20px 10px -20px;
150 | padding: 3px 0 3px 10px;
151 | }
152 |
153 | div.body h1 { margin-top: 0; font-size: 200%; }
154 | div.body h2 { font-size: 160%; }
155 | div.body h3 { font-size: 140%; }
156 | div.body h4 { font-size: 120%; }
157 | div.body h5 { font-size: 110%; }
158 | div.body h6 { font-size: 100%; }
159 |
160 | a.headerlink {
161 | color: #c60f0f;
162 | font-size: 0.8em;
163 | padding: 0 4px 0 4px;
164 | text-decoration: none;
165 | }
166 |
167 | a.headerlink:hover {
168 | background-color: #c60f0f;
169 | color: white;
170 | }
171 |
172 | div.body p, div.body dd, div.body li, div.body blockquote {
173 | text-align: justify;
174 | line-height: 130%;
175 | }
176 |
177 | div.admonition p.admonition-title + p {
178 | display: inline;
179 | }
180 |
181 | div.admonition p {
182 | margin-bottom: 5px;
183 | }
184 |
185 | div.admonition pre {
186 | margin-bottom: 5px;
187 | }
188 |
189 | div.admonition ul, div.admonition ol {
190 | margin-bottom: 5px;
191 | }
192 |
193 | div.note {
194 | background-color: #eee;
195 | border: 1px solid #ccc;
196 | }
197 |
198 | div.seealso {
199 | background-color: #ffc;
200 | border: 1px solid #ff6;
201 | }
202 |
203 | div.topic {
204 | background-color: #eee;
205 | }
206 |
207 | div.warning {
208 | background-color: #ffe4e4;
209 | border: 1px solid #f66;
210 | }
211 |
212 | p.admonition-title {
213 | display: inline;
214 | }
215 |
216 | p.admonition-title:after {
217 | content: ":";
218 | }
219 |
220 | pre {
221 | padding: 5px;
222 | background-color: #eeffcc;
223 | color: #333333;
224 | line-height: 120%;
225 | border: 1px solid #ac9;
226 | border-left: none;
227 | border-right: none;
228 | }
229 |
230 | code {
231 | background-color: #ecf0f3;
232 | padding: 0 1px 0 1px;
233 | font-size: 0.95em;
234 | }
235 |
236 | th {
237 | background-color: #ede;
238 | }
239 |
240 | .warning code {
241 | background: #efc2c2;
242 | }
243 |
244 | .note code {
245 | background: #d6d6d6;
246 | }
247 |
248 | .viewcode-back {
249 | font-family: sans-serif;
250 | }
251 |
252 | div.viewcode-block:target {
253 | background-color: #f4debf;
254 | border-top: 1px solid #ac9;
255 | border-bottom: 1px solid #ac9;
256 | }
257 |
258 | div.code-block-caption {
259 | color: #efefef;
260 | background-color: #1c4e63;
261 | }
--------------------------------------------------------------------------------
/docs/qualityAssurance.ui.window.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.ui.window module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.ui.window module
48 |
49 |
50 | class qualityAssurance.ui.window.QualityAssuranceWindow( parent , collection )
51 | Bases: PySide.QtGui.QWidget
52 |
53 |
54 | staticMetaObject = <PySide.QtCore.QMetaObject object>
55 |
56 |
57 |
58 |
59 |
60 |
61 | qualityAssurance.ui.window.show( collection='modelling' )
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
91 |
92 |
93 |
105 |
109 |
110 |
--------------------------------------------------------------------------------
/docs/_static/sidebar.js:
--------------------------------------------------------------------------------
1 | /*
2 | * sidebar.js
3 | * ~~~~~~~~~~
4 | *
5 | * This script makes the Sphinx sidebar collapsible.
6 | *
7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds
8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton
9 | * used to collapse and expand the sidebar.
10 | *
11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden
12 | * and the width of the sidebar and the margin-left of the document
13 | * are decreased. When the sidebar is expanded the opposite happens.
14 | * This script saves a per-browser/per-session cookie used to
15 | * remember the position of the sidebar among the pages.
16 | * Once the browser is closed the cookie is deleted and the position
17 | * reset to the default (expanded).
18 | *
19 | * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
20 | * :license: BSD, see LICENSE for details.
21 | *
22 | */
23 |
24 | $(function() {
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | // global elements used by the functions.
34 | // the 'sidebarbutton' element is defined as global after its
35 | // creation, in the add_sidebar_button function
36 | var bodywrapper = $('.bodywrapper');
37 | var sidebar = $('.sphinxsidebar');
38 | var sidebarwrapper = $('.sphinxsidebarwrapper');
39 |
40 | // for some reason, the document has no sidebar; do not run into errors
41 | if (!sidebar.length) return;
42 |
43 | // original margin-left of the bodywrapper and width of the sidebar
44 | // with the sidebar expanded
45 | var bw_margin_expanded = bodywrapper.css('margin-left');
46 | var ssb_width_expanded = sidebar.width();
47 |
48 | // margin-left of the bodywrapper and width of the sidebar
49 | // with the sidebar collapsed
50 | var bw_margin_collapsed = '.8em';
51 | var ssb_width_collapsed = '.8em';
52 |
53 | // colors used by the current theme
54 | var dark_color = $('.related').css('background-color');
55 | var light_color = $('.document').css('background-color');
56 |
57 | function sidebar_is_collapsed() {
58 | return sidebarwrapper.is(':not(:visible)');
59 | }
60 |
61 | function toggle_sidebar() {
62 | if (sidebar_is_collapsed())
63 | expand_sidebar();
64 | else
65 | collapse_sidebar();
66 | }
67 |
68 | function collapse_sidebar() {
69 | sidebarwrapper.hide();
70 | sidebar.css('width', ssb_width_collapsed);
71 | bodywrapper.css('margin-left', bw_margin_collapsed);
72 | sidebarbutton.css({
73 | 'margin-left': '0',
74 | 'height': bodywrapper.height()
75 | });
76 | sidebarbutton.find('span').text('»');
77 | sidebarbutton.attr('title', _('Expand sidebar'));
78 | document.cookie = 'sidebar=collapsed';
79 | }
80 |
81 | function expand_sidebar() {
82 | bodywrapper.css('margin-left', bw_margin_expanded);
83 | sidebar.css('width', ssb_width_expanded);
84 | sidebarwrapper.show();
85 | sidebarbutton.css({
86 | 'margin-left': ssb_width_expanded-12,
87 | 'height': bodywrapper.height()
88 | });
89 | sidebarbutton.find('span').text('«');
90 | sidebarbutton.attr('title', _('Collapse sidebar'));
91 | document.cookie = 'sidebar=expanded';
92 | }
93 |
94 | function add_sidebar_button() {
95 | sidebarwrapper.css({
96 | 'float': 'left',
97 | 'margin-right': '0',
98 | 'width': ssb_width_expanded - 28
99 | });
100 | // create the button
101 | sidebar.append(
102 | ''
103 | );
104 | var sidebarbutton = $('#sidebarbutton');
105 | light_color = sidebarbutton.css('background-color');
106 | // find the height of the viewport to center the '<<' in the page
107 | var viewport_height;
108 | if (window.innerHeight)
109 | viewport_height = window.innerHeight;
110 | else
111 | viewport_height = $(window).height();
112 | sidebarbutton.find('span').css({
113 | 'display': 'block',
114 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2
115 | });
116 |
117 | sidebarbutton.click(toggle_sidebar);
118 | sidebarbutton.attr('title', _('Collapse sidebar'));
119 | sidebarbutton.css({
120 | 'color': '#FFFFFF',
121 | 'border-left': '1px solid ' + dark_color,
122 | 'font-size': '1.2em',
123 | 'cursor': 'pointer',
124 | 'height': bodywrapper.height(),
125 | 'padding-top': '1px',
126 | 'margin-left': ssb_width_expanded - 12
127 | });
128 |
129 | sidebarbutton.hover(
130 | function () {
131 | $(this).css('background-color', dark_color);
132 | },
133 | function () {
134 | $(this).css('background-color', light_color);
135 | }
136 | );
137 | }
138 |
139 | function set_position_from_cookie() {
140 | if (!document.cookie)
141 | return;
142 | var items = document.cookie.split(';');
143 | for(var k=0; k
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils package — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils package
48 |
49 |
Submodules
50 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
98 |
99 |
100 |
112 |
116 |
117 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.ui.utils.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.ui.utils module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.ui.utils module
48 |
49 |
50 | qualityAssurance.ui.utils.getIconPath( name )
51 | Get an icon path based on file name. All paths in the XBMLANGPATH variable
52 | processed to see if the provided icon can be found.
53 |
54 |
55 |
56 |
57 | Parameters: name (str ) –
58 |
59 | Returns: Icon path
60 |
61 | Return type: str/None
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | qualityAssurance.ui.utils.mayaWindow( )
70 | Get Maya’s main window.
71 |
72 |
73 |
74 |
75 | Return type: QMainWindow
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
108 |
109 |
110 |
122 |
126 |
127 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.uv.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.uv module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.uv module
48 |
49 |
50 | class qualityAssurance.checks.uv.EmptyUVSets
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | Meshes will be checked to see if they have empty uv sets. When fixing the
53 | uv set will be deleted.
54 |
55 |
56 |
57 |
58 | class qualityAssurance.checks.uv.UnusedUVSets
59 | Bases: qualityAssurance.utils.qa.QualityAssurance
60 | Meshes will be checked to see if they have unused uv sets. When fixing the
61 | uv set will be deleted.
62 |
63 |
64 | ignoreUvSets
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
96 |
97 |
98 |
110 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.skinning.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.skinning module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.skinning module
48 |
49 |
50 | class qualityAssurance.checks.skinning.MaximumInfluences
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | Skin clusters will be checked to see if they contain vertices that exceed
53 | the maximum influences. When fixing this error new skin weights will be
54 | applied by removing and normalizing the lowest influences that are
55 | exceeding the maximum amount.
56 | cluster.
57 |
58 |
59 |
60 |
61 | class qualityAssurance.checks.skinning.UnusedInfluences
62 | Bases: qualityAssurance.utils.qa.QualityAssurance
63 | Skin clusters will be checked to see if they contain unused influences.
64 | When fixing this error the unused influences will be removed from the skin
65 | cluster.
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
95 |
96 |
97 |
109 |
113 |
114 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.api.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.api module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.api module
48 |
49 |
50 | qualityAssurance.utils.api.toMDagPath( node )
51 | Convert a node into a OpenMaya.MDagPath.
52 |
53 |
54 |
55 |
56 | Parameters: node (str ) –
57 |
58 | Returns: MDagPath of parsed node
59 |
60 | Return type: OpenMaya.MDagPath
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | qualityAssurance.utils.api.toMObject( node )
69 | Convert a node into a OpenMaya.MObject.
70 |
71 |
72 |
73 |
74 | Parameters: node (str ) –
75 |
76 | Returns: MObject of parsed node
77 |
78 | Return type: OpenMaya.MObject
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
111 |
112 |
113 |
125 |
129 |
130 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.collections.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.collections package — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.collections package
48 |
New collections can be added in the COLLECTIONS variable. Since this
49 | COLLECTIONS variable is an OrderedDict, it will keep the order. A
50 | collection can be defined by who will be using it. Currently it is
51 | divided by different specialties. Each specialty contains a list of
52 | categories that will be displayed. The category names link to the categories
53 | defined in the quality assurance checks themselves.
54 |
55 |
56 | qualityAssurance.collections.getCollections( )
57 |
58 |
59 |
60 |
61 | Returns: Collection overview
62 |
63 | Return type: OrderedDict
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | qualityAssurance.collections.getCollectionsCategories( )
72 |
73 |
74 |
75 |
76 | Returns: List of all collection names
77 |
78 | Return type: list
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
111 |
112 |
113 |
125 |
129 |
130 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.rigging.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.rigging module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.rigging module
48 |
49 |
50 | class qualityAssurance.checks.rigging.DeleteNonDeformerHistory
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | Meshes will be checked to see if they contain non-deformer history. Be
53 | carefull with this ceck as it is not always nessecary to fix this. History
54 | is not always a bad thing. When fixing this error the partial history will
55 | be baked.
56 |
57 |
58 | ignoreNodeTypes
59 |
60 |
61 |
62 |
63 |
64 |
65 | class qualityAssurance.checks.rigging.DeleteNonSetDrivenAnimation
66 | Bases: qualityAssurance.utils.qa.QualityAssurance
67 | Animation curves will be checked to see if they are not set driven keys.
68 | Non set driven keys should not be present in the scene and will be deleted
69 | when fixing this error.
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
99 |
100 |
101 |
113 |
117 |
118 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.shaders.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.shaders module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.shaders module
48 |
49 |
50 | class qualityAssurance.checks.shaders.FaceAssignedShading
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | Shading groups will be checked to see if they contain component
53 | connections. When fixing it will force a shape connections. If you
54 | intentially have component assigment, it is best to skip this check.
55 |
56 |
57 |
58 |
59 | class qualityAssurance.checks.shaders.InitialShadingGroup
60 | Bases: qualityAssurance.utils.qa.QualityAssurance
61 | Meshes will be checked to see if they have the default shading group
62 | attached.
63 |
64 |
65 |
66 |
67 | class qualityAssurance.checks.shaders.NoShadingGroup
68 | Bases: qualityAssurance.utils.qa.QualityAssurance
69 | Meshes will be checked to see if they have a shading group attached.
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
99 |
100 |
101 |
113 |
117 |
118 |
--------------------------------------------------------------------------------
/docs/modules.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance
48 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
115 |
116 |
117 |
129 |
133 |
134 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.checks.renderLayers.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.checks.renderLayers module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.checks.renderLayers module
48 |
49 |
50 | class qualityAssurance.checks.renderLayers.DuplicateAdjustments
51 | Bases: qualityAssurance.utils.qa.QualityAssurance
52 | The scene will be processed for duplicate renderlayer adjustments. When
53 | fixing these errors the duplicate adjustments will be removed.
54 |
55 |
56 |
57 |
58 | class qualityAssurance.checks.renderLayers.MismatchedAdjustments
59 | Bases: qualityAssurance.utils.qa.QualityAssurance
60 | The scene will be processed for mismatched renderlayer adjustments. When
61 | fixing these errors the mismatched adjustments will be reconnected.
62 |
63 |
64 |
65 |
66 | class qualityAssurance.checks.renderLayers.MissingAdjustments
67 | Bases: qualityAssurance.utils.qa.QualityAssurance
68 | The scene will be processed for missing renderlayer adjustments. When
69 | fixing these errors the missing adjustments will be reconnected.
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
99 |
100 |
101 |
113 |
117 |
118 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/checks/skinning.py:
--------------------------------------------------------------------------------
1 | from maya import cmds, OpenMaya, OpenMayaAnim
2 | from ..utils import QualityAssurance, reference, skin, api
3 |
4 |
5 | class UnusedInfluences(QualityAssurance):
6 | """
7 | Skin clusters will be checked to see if they contain unused influences.
8 | When fixing this error the unused influences will be removed from the skin
9 | cluster.
10 | """
11 | def __init__(self):
12 | QualityAssurance.__init__(self)
13 |
14 | self._name = "Unused Influences"
15 | self._urgency = 1
16 | self._message = "{0} skin cluster(s) contain unused influences"
17 | self._categories = ["Skinning"]
18 | self._selectable = True
19 |
20 | # ------------------------------------------------------------------------
21 |
22 | def _find(self):
23 | """
24 | :return: Skin clusters with unused influences
25 | :rtype: generator
26 | """
27 | skinClusters = self.ls(type="skinCluster")
28 | skinClusters = reference.removeReferenced(skinClusters)
29 |
30 | for skinCluster in skinClusters:
31 | mesh = cmds.skinCluster(skinCluster, query=True, geometry=True)
32 | if not mesh:
33 | continue
34 |
35 | weighted = cmds.skinCluster(
36 | skinCluster,
37 | query=True,
38 | weightedInfluence=True
39 | )
40 | influences = cmds.skinCluster(
41 | skinCluster,
42 | query=True,
43 | influence=True
44 | )
45 |
46 | for i in influences:
47 | if i not in weighted:
48 | yield skinCluster
49 | break
50 |
51 | def _fix(self, skinCluster):
52 | """
53 | :param str skinCluster:
54 | """
55 | weighted = cmds.skinCluster(
56 | skinCluster,
57 | query=True,
58 | weightedInfluence=True
59 | )
60 |
61 | influences = cmds.skinCluster(
62 | skinCluster,
63 | query=True,
64 | influence=True
65 | )
66 |
67 | for i in influences:
68 | if i not in weighted:
69 | cmds.skinCluster(skinCluster, edit=True, removeInfluence=i)
70 |
71 |
72 | class MaximumInfluences(QualityAssurance):
73 | """
74 | Skin clusters will be checked to see if they contain vertices that exceed
75 | the maximum influences. When fixing this error new skin weights will be
76 | applied by removing and normalizing the lowest influences that are
77 | exceeding the maximum amount.
78 | cluster.
79 | """
80 | def __init__(self):
81 | QualityAssurance.__init__(self)
82 |
83 | self._name = "Maximum Influences"
84 | self._message = "{0} skin cluster(s) exceed the maximum influences"
85 | self._categories = ["Skinning"]
86 | self._selectable = True
87 |
88 | # ------------------------------------------------------------------------
89 |
90 | def _find(self):
91 | """
92 | :return: Skin clusters which exceed maximum influences.
93 | :rtype: generator
94 | """
95 | # variables
96 | obj = OpenMaya.MObject()
97 |
98 | # get skin cluster iterator
99 | iterator = self.lsApi(nodeType=OpenMaya.MFn.kSkinClusterFilter)
100 |
101 | # iterate skin cluster
102 | while not iterator.isDone():
103 | # variables
104 | iterator.getDependNode(obj)
105 | depNode = OpenMaya.MFnDependencyNode(obj)
106 | skinFn = OpenMayaAnim.MFnSkinCluster(obj)
107 | skinCluster = depNode.name()
108 |
109 | # skin cluster data
110 | maintain = "{0}.maintainMaxInfluences".format(skinCluster)
111 | maintain = cmds.getAttr(maintain)
112 | maxInfluences = "{0}.maxInfluences".format(skinCluster)
113 | maxInfluences = cmds.getAttr(maxInfluences)
114 |
115 | # skip if max influences doesn't have to be maintained
116 | if not maintain:
117 | iterator.next()
118 | continue
119 |
120 | # get influences
121 | infIds, infPaths = skin.getInfluencesApi(skinFn)
122 |
123 | # get weights
124 | for weight in skin.getWeightsApiGenerator(skinFn, infIds):
125 | if len([w for w in weight.values() if w]) > maxInfluences:
126 | yield skinCluster
127 | break
128 |
129 | iterator.next()
130 |
131 | def _fix(self, skinCluster):
132 | """
133 | :param str skinCluster:
134 | """
135 | obj = api.toMObject(skinCluster)
136 | skinFn = OpenMayaAnim.MFnSkinCluster(obj)
137 |
138 | # normalize
139 | normalizePath = "{0}.normalizeWeights".format(skinCluster)
140 | normalize = cmds.getAttr(normalizePath)
141 |
142 | # max influence data
143 | maxInfluencesPath = "{0}.maxInfluences".format(skinCluster)
144 | maxInfluences = cmds.getAttr(maxInfluencesPath)
145 |
146 | # get influences
147 | infIds, infPaths = skin.getInfluencesApi(skinFn)
148 | infIdsLocked = {
149 | i: cmds.getAttr("{0}.liw".format(infPaths[i]))
150 | for _, i in infIds.iteritems()
151 | }
152 |
153 | # get weights
154 | weights = skin.getWeightsApi(skinFn, infIds)
155 |
156 | for vId, vWeights in weights.iteritems():
157 | # variable
158 | nWeights = vWeights.copy()
159 |
160 | # sort weights
161 | ordered = sorted(vWeights.items(), key=lambda x: -x[1])
162 |
163 | keepIndices = [
164 | index
165 | for i, (index, weight) in enumerate(ordered)
166 | if i <= maxInfluences-1
167 | ]
168 | removeIndices = [
169 | index
170 | for i, (index, weight) in enumerate(ordered)
171 | if i > maxInfluences-1
172 | ]
173 |
174 | # remove weights
175 | for i in removeIndices:
176 | nWeights[i] = 0
177 |
178 | # normalize weights
179 | if normalize == 1:
180 | # get normalizable weights
181 | normalizeIndices = [
182 | i
183 | for i in keepIndices
184 | if not infIdsLocked.get(i)
185 | ]
186 |
187 | # if no weights can be normalized, normalize all
188 | if not normalizeIndices:
189 | normalizeIndices = keepIndices
190 |
191 | # get normalizing multiplier
192 | total = sum([vWeights.get(i) for i in normalizeIndices])
193 | multiplier = 1/total
194 |
195 | # normalize indices
196 | for i in normalizeIndices:
197 | nWeights[i] = vWeights.get(i) * multiplier
198 |
199 | # set weights
200 | for infId, infValue in nWeights.items():
201 | infAttr = "{0}.weightList[{1}].weights[{2}]".format(
202 | skinCluster,
203 | vId,
204 | infId
205 | )
206 | cmds.setAttr(infAttr, infValue)
207 |
--------------------------------------------------------------------------------
/scripts/qualityAssurance/utils/qa.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import traceback
3 | from maya import cmds, OpenMaya
4 | from . import decorators, undo, path
5 |
6 |
7 | class QualityAssurance(object):
8 | """
9 | This is the base class that can be used to generate your own quality
10 | assurance checks. Key variables can be overwritten and a _find and _fix
11 | function should be implemented to complete the check, otherwise the class
12 | will not register as findable or checkable.
13 | """
14 | def __init__(self):
15 | # variables
16 | self._name = ""
17 | self._urgency = 2
18 | self._categories = []
19 |
20 | self._message = ""
21 | self._information = self.__doc__
22 |
23 | self._selectable = False
24 | self._onSelected = False
25 |
26 | self._errors = []
27 |
28 | # ------------------------------------------------------------------------
29 |
30 | @property
31 | def name(self):
32 | """
33 | :return: Quality assurance name
34 | :rtype: str
35 | """
36 | return self._name
37 |
38 | @property
39 | @decorators.ifNoErrorsReturn(0)
40 | def state(self):
41 | """
42 | If no errors can be found the state of the check is neutral which is
43 | set to a value of 0. Different levels of urgency can be implemented
44 | with increasing values.
45 |
46 | :return: Current state of quality assurance
47 | :rtype: int
48 | """
49 | return self._urgency
50 |
51 | @property
52 | def categories(self):
53 | """
54 | :return: Categories error check should belong too
55 | :rtype: list
56 | """
57 | return self._categories
58 |
59 | # ------------------------------------------------------------------------
60 |
61 | @property
62 | @decorators.ifNoErrorsReturn("")
63 | def message(self):
64 | """
65 | Message informing the user about the state of the check and the number
66 | of errors. If not errors are found and empty string is returned.
67 |
68 | :return: Quality assurance message
69 | :rtype: str
70 | """
71 | return self._message.format(len(self.errors))
72 |
73 | @property
74 | def information(self):
75 | """
76 | :return: Quality assurance information
77 | :rtype: list
78 | """
79 | return self._information
80 |
81 | # ------------------------------------------------------------------------
82 |
83 | @property
84 | def errors(self):
85 | """
86 | :return: Quality assurance error list
87 | :rtype: list
88 | """
89 | return self._errors
90 |
91 | # ------------------------------------------------------------------------
92 |
93 | def isFindable(self):
94 | """
95 | A quality assurance check becomes findable if a _find method is
96 | implemented,
97 |
98 | :return: Findable state of quality assurance check
99 | :rtype: bool
100 | """
101 | return True if "_find" in dir(self) else False
102 |
103 | def find(self):
104 | """
105 | Runs the _find method and appends all of the errors found to the
106 | error list.
107 | """
108 | # check if is fixable
109 | if not self.isFindable():
110 | raise RuntimeError(
111 | "{0} doesn't allow for error finding!".format(self.name)
112 | )
113 |
114 | # reset errors list
115 | self._errors = []
116 |
117 | # find errors
118 | for error in self._find():
119 | if error in self.errors:
120 | continue
121 |
122 | self.errors.append(error)
123 |
124 | # ------------------------------------------------------------------------
125 |
126 | def isFixable(self):
127 | """
128 | A quality assurance check becomes fixable if a _fix method is
129 | implemented,
130 |
131 | :return: Fixable state of quality assurance check
132 | :rtype: bool
133 | """
134 | return True if "_fix" in dir(self) else False
135 |
136 | def fix(self):
137 | """
138 | Loops over each of the errors found and runs the _fix method on each
139 | error. The entire function is wrapped in an undo block for easy
140 | undoing. Each fix is also wrapped in a try function, meaning that if
141 | an error cannot be fixed, it will remain in the error list but it
142 | error the application. The _fix method should only take in 1 argument.
143 | """
144 | # check if is fixable
145 | if not self.isFixable():
146 | raise RuntimeError(
147 | "{0} doesn't allow for error fixing!".format(self.name)
148 | )
149 |
150 | # remove errors
151 | with undo.UndoContext():
152 | for error in self.errors[:]:
153 | # remove objects that might have been deleted in other
154 | # quality assurance checks.
155 |
156 | if self.isSelectable() and (
157 | type(error) in [str, unicode]
158 | and not cmds.objExists(error)
159 | ):
160 | self._errors.remove(error)
161 | continue
162 |
163 | try:
164 | self._fix(error)
165 | self._errors.remove(error)
166 | except:
167 | traceback.print_exc()
168 |
169 | # ------------------------------------------------------------------------
170 |
171 | @property
172 | def onSelected(self):
173 | """
174 | :return: Only run quality assurance check in selection
175 | :rtype: bool
176 | """
177 | return self._onSelected
178 |
179 | @onSelected.setter
180 | def onSelected(self, value):
181 | """
182 | :param bool value: Only run on selection state
183 | """
184 | self._onSelected = value
185 |
186 | # ------------------------------------------------------------------------
187 |
188 | def isSelectable(self):
189 | """
190 | :return: If the error list is selectable
191 | :rtype: bool
192 | """
193 | return self._selectable
194 |
195 | def select(self):
196 | """
197 | Select all current entries of the error list.
198 | """
199 | if self.isSelectable() and self.errors:
200 | cmds.select(path.asFlatList(self.errors))
201 |
202 | # ------------------------------------------------------------------------
203 |
204 | def ls(self, **kwargs):
205 | """
206 | Subclass of Maya's ls command. This command should be used to get the
207 | nodes in a quality assurance check. This will automatically take into
208 | account of the check should only run on selected objects or not.
209 |
210 | :return: Object list
211 | :rtype: list
212 | """
213 | return cmds.ls(sl=self.onSelected, **kwargs) or []
214 |
215 | def lsApi(self, nodeType=OpenMaya.MFn.kTransform):
216 | """
217 | Get a OpenMaya.MItSelectionList object. This command should be used to
218 | get the nodes in a quality assurance check. This will automatically
219 | take into account of the check should only run on selected objects or
220 | not.
221 |
222 | :return: Object list
223 | :rtype: OpenMaya.MItSelectionList
224 | """
225 | selectionList = OpenMaya.MSelectionList()
226 |
227 | if self.onSelected:
228 | # populate selection list with active selection
229 | OpenMaya.MGlobal.getActiveSelectionList(selectionList)
230 | else:
231 | # populate selection list with all object of nodetype
232 | iterator = OpenMaya.MItDependencyNodes(nodeType)
233 | while not iterator.isDone():
234 | obj = iterator.thisNode()
235 | selectionList.add(obj)
236 | iterator.next()
237 |
238 | return OpenMaya.MItSelectionList(selectionList, nodeType)
239 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.skin.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.skin module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.skin module
48 |
49 |
50 | qualityAssurance.utils.skin.getInfluencesApi( skinFn )
51 | Get influence data from a skin cluster
52 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
53 |
54 |
55 |
56 |
57 | Parameters: skinFn (OpenMayaAnim.MFnSkinCluster ) –
58 |
59 | Returns: Influences dictionary and list
60 |
61 | Return type: tuple()
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | qualityAssurance.utils.skin.getWeightsApi( skinFn , infIds )
70 | Get skin weights from a skin cluster reading its attributes.
71 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
72 |
73 |
74 |
75 |
76 | Parameters:
77 | skinFn (OpenMayaAnim.MFnSkinCluster ) –
78 | infIds (dict ) –
79 |
80 |
81 |
82 | Returns: Dictionary of skin weights.
83 |
84 |
85 | Return type: dict
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | qualityAssurance.utils.skin.getWeightsApiGenerator( skinFn , infIds )
95 | Get skin weights from a skin cluster reading its attributes.
96 | Code written by Tyler Thornock: http://www.charactersetup.com/home.html
97 |
98 |
99 |
100 |
101 | Parameters:
102 | skinFn (OpenMayaAnim.MFnSkinCluster ) –
103 | infIds (dict ) –
104 |
105 |
106 |
107 | Returns: Skin weights per vertex
108 |
109 |
110 | Return type: generator
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
144 |
145 |
146 |
158 |
162 |
163 |
--------------------------------------------------------------------------------
/docs/qualityAssurance.utils.path.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | qualityAssurance.utils.path module — qualityAssurance '' documentation
9 |
10 |
11 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
qualityAssurance.utils.path module
48 |
49 |
50 | qualityAssurance.utils.path.asFlatList( input )
51 | Convert the input to a flat list.
52 |
53 |
54 |
55 |
56 | Parameters: input –
57 |
58 | Returns: Flattened list
59 |
60 | Return type: list
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | qualityAssurance.utils.path.baseName( name )
69 | This function will strip the namespaces and grouping information of a name.
70 | Useful when working with fullPaths but needing the base for naming.
71 |
72 |
73 |
74 |
75 | Parameters: name (str ) –
76 |
77 | Returns: Base name of string
78 |
79 | Return type: str
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | qualityAssurance.utils.path.namespace( name )
88 | This function will return the namespace if any of the object
89 |
90 |
91 |
92 |
93 | Parameters: name (str ) –
94 |
95 | Returns: namespace
96 |
97 | Return type: str/None
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | qualityAssurance.utils.path.rootName( name )
106 | This function will strip the grouping information of a name.
107 | Useful when working with fullPaths but needing the base for naming.
108 |
109 |
110 |
111 |
112 | Parameters: name (str ) –
113 |
114 | Returns: Root name of string
115 |
116 | Return type: str
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
149 |
150 |
151 |
163 |
167 |
168 |
--------------------------------------------------------------------------------