Source code for pyfrost.api.maya

"""Maya Node API.

:author: Benoit Gielly <benoit.gielly@gmail.com>

The intention here is to build a node API specific to each DDC (here, Maya),
so in the main.py we can call that and remove the `cmds` calls.

When a DCC is started, the relevant API is injected in the Main one.

Note:
    This is a Work in Progress for now and not in use.

Example:
    If we were to rewrite the `pyfrost.main.Graph.validate_board` method, we
    could like that::

        def __init__(self):
            self.api = Api()

        def validate_board(self, name=None):
            node = self.api[name]
            if node.exists and node.type == "bifrostBoard":
                return name
            name = name if name else "bifrostGraph"
            board = api.create("bifrostBoard", name)
            return board.name

    That way, all the main code remains clean of DCC commands.
    Obviously, each DCCs APIs must be implemented the same way
    for this to work.

"""
from __future__ import absolute_import

from functools import partial
import logging

from maya import cmds

LOG = logging.getLogger(__name__)


[docs]class MayaAPI(object): """Create a Maya API object."""
[docs] def __repr__(self): return "{}()".format(self.__class__.__name__)
[docs] def __getitem__(self, key): return self.get(key)
[docs] def create(self, nodetype, name=None): """Create new node.""" kwargs = {"name": name} if name else {} node = cmds.createNode(nodetype, **kwargs) return MayaNode(self, node)
[docs] def get(self, name): """Get existing node.""" if name is None: return None name, _, attr = name.partition(".") if cmds.objExists(name): node = MayaNode(self, name) return node[attr] if attr else node msg = "Node '%s' doesn't exists. Use the 'create' method instead." return LOG.debug(msg, name)
[docs]class MayaNode(object): """Get MayaNode object."""
[docs] def __init__(self, api, node): self.api = api self.node = node self.name = str(node)
[docs] def __repr__(self, *args, **kwargs): return '{}("{}")'.format(self.__class__.__name__, self.node)
[docs] def __str__(self): return self.name
[docs] def __getitem__(self, key): return MayaAttr(self, key)
[docs] def type(self): """Get node type.""" return cmds.nodeType(self.name)
[docs] def rename(self, name): """Rename node.""" self.node = cmds.rename(self.name, name) self.name = str(self.node)
[docs]class MayaAttr(object): """Create a Maya Attribute class."""
[docs] def __init__(self, node, name): self.api = node.api self.node = node self.name = name if self.exists(): self._type = cmds.getAttr(self.plug, type=True)
[docs] def __repr__(self): return "{obj.__class__.__name__}({obj.plug})".format(obj=self)
[docs] def __str__(self): return self.plug
@property def plug(self): """Get plug.""" return "{obj.node}.{obj.name}".format(obj=self)
[docs] def exists(self): """Check if node exists.""" return cmds.objExists(self.plug)
@property def value(self): """Get & set attribute's value.""" return self.get() @value.setter def value(self, value): self.set(value) @property def type(self): """Get & set attribute's type.""" return self._type @type.setter def type(self, value): self._type = value
[docs] def add(self, type_, **kwargs): """Add attribute on node.""" # if attr already exists, just set its value if passed. value = kwargs.pop("value", None) if self.exists() and value is not None: self.value = value LOG.debug("'%s' already exists, only setting value.", self.plug) return # update type self.type = type_ # dealing with default value (doesn't work if string attribute...) if value is not None and self.type != "string": kwargs["defaultValue"] = value cmds.addAttr( self.node.name, longName=self.name, dataType=type_, **kwargs ) if self.type == "string": self.value = value
[docs] def get(self): """Set node's attribute value.""" return cmds.getAttr(self.plug)
[docs] def set(self, value): """Set node's attribute value.""" kwargs = {"type": self.type} if self.type == "string" else {} func = partial(cmds.setAttr, self.plug, value, **kwargs) if isinstance(value, (list, tuple)): func = partial(cmds.setAttr, self.plug, *value, **kwargs) return func()
[docs] def connect(self, target): """Connect current node to target.""" if not isinstance(target, MayaAttr): target = self.api[target] cmds.connectAttr(self, target, force=True)
[docs] def disconnect(self, target): """Disconnect current node from target.""" if not isinstance(target, MayaAttr): target = self.api[target] cmds.disconnectAttr(self, target)