Get started

Philosophy

The overall idea is to provide a wide range of python objects representing something. Those objects are subclassable, allowing the developer to code his own objects and benefit from what is already there through inheritance.

Like :
cgp_generic_utils.files.JsonFile represents a json file on the file system cgp_maya_utils.scene.Transform represents a transform node in a maya scene

With that in mind, the API comes with a variety of functions that ease the access, initialization and use of those python objects. Using those functions, you can manipulate, files, nodes, attributes, component and much more.

For example :
cgp_maya_utils.files.entity returns a file/directory object from a file/directory path cgp_maya_utils.scene.attribute returns an attribute object from a maya attribute name

Generic examples

This is a sneak peak of what the generic API has to offer. The most adventurous will have to discover the rest by themselves :)

File/Directory management

Import the file module.

import cgp_generic_utils.files

Creation

For starter, let’s create a file and write its content.

path = '/path/to/file.json'
content = ['a', 'b', 'c', 'd']
createdFile = cgp_generic_utils.files.createFile(path, content=content)
# result: JsonFile('/path/to/file.json')

We could also create a directory.

path = '/path/to/directory'
createdFile = cgp_generic_utils.files.createDirectory(path)
# result: Directory('/path/to/directory')

Manipulation

Now let’s say the file is already there and we want to manipulate it. Use the entity function to get a file object from the path. It will return the file related object depending on the extension of the path. If the related file object is not yet implemented, it will return a basic File object.

path = '/path/to/file.json'
entity = cgp_generic_utils.files.entity(path)
# result: JsonFile('/path/to/file.json')

We now have access to the json file stored on the file system through the JsonFile python object. We could read the content of the file.

content = entity.read()
# result: ['a', 'b', 'c', 'd']

PyFile is another other handy file python object that allows its content to be evaluated as a python module.

Let’s admit the content of the file is this.

CONSTANT = True

def concatenateString(suffix, name):
    print '{0}_{1}'.format(suffix, name)

Let’s import the content of the file as module and manipulate it as python functions.

path = '/path/to/file.py'
entity = cgp_generic_utils.files.entity(path)
module = entity.importAsModule()

print module.CONSTANT
# result: True

module.concatenateString('L', 'arm')
# result: 'L_arm'

Script custom file objects

As previously said, developer can script his own file objects. here is an example from cgp_maya_utils.files.

class ObjFile(cgp_generic_utils.files.File):
    """file object that manipulates a ``.obj`` file on the file system
    """

    _extension = 'obj'

    @classmethod
    @cgp_maya_utils.decorators.KeepCurrentSelection()
    def create(cls, path, content=None, **__):
        """docstring here
        """

        # creation code here

    def import_(self, name):
        """docstring here
        """

        # import code here

Registering custom file objects

Last but not least is the registerFileTypes function. It allows the developer to register his own files objects. By doing so, it allows the files API to acknowledge those objects and manipulate them through entity, createFile and createDirectory.

# key is extension of the File - Value is the file Object
fileTypes = {'ma': MaFile,
             'hip': HipFile,
             'obj': ObjFile}

cgp_generic_utils.files.registerFileTypes(fileTypes)

Using Decorators

Import the decorator module.

import cgp_generic_utils.decorators
import cgp_maya_utils.decorators

Decorate a function

A decorator is a piece of python that encapsulate a function it decorates, will execute some code of its own, then execute the decorated function and finish by executing some other code of its own. It’s a really powerful tool. For example, we could disable the maya viewport to avoid redrawing during the execution of our function.

@cgp_maya_utils.decorators.DisableViewport()
def translateSomeObjects(objects, translateValues):
    for obj in objects:
        maya.cmds.xform(obj, translate=translateValues)

Use as context

In our case, those decorators work also as contexts. contexts work the same as decorators but instead of encapsulating a function, you encapsulate a block of code.

with cgp_maya_utils.decorators.DisableViewport():
    objects = maya.cmds.ls(sl=True)
    translateValues = [1, 2, 3]
    for obj in objects:
        maya.cmds.xform(obj, translate=translateValues)

Script a custom context

A developer can create a custom decorator using the Decorator class provided by the API. Here is an example from cgp_maya_utils.decorators.

class DisableViewport(cgp_generic_utils.decorators.Decorator):
"""decorator that disable viewport drawing and reactivate it
"""

def __init__(self):
    """DisableViewport class initialization
    """

    # this code is executed at the initialization of the decorator before entering the decorator
    # every data gathered here and stored on class attributes are accessible in __enter__ and __exit__
    # in this example, we get the maya viewport to disable
    self._viewport = str(maya.mel.eval('global string $gMainPane; $temp = $gMainPane;'))

def __enter__(self):
    """enter DisableViewport decorator
    """

    # this code is executed when entering the decorator. Before executing the encapsulated code.
    # in this example, we are disabling the viewport
    maya.cmds.refresh(suspend=True)
    maya.cmds.paneLayout(self._viewport, edit=True, manage=False)

def __exit__(self, *args, **kwargs):
    """exit DisableViewport decorator
    """

    # this code is executed when exiting the decorator. after executing the encapsulated code.
    # in this example, we are enabling the viewport back
    maya.cmds.refresh(suspend=False)
    maya.cmds.paneLayout(self._viewport, edit=True, manage=True)

UI development

cgp_generic_utils comes with a bunch of useful classes for UI development.

Create a new tool

The API provides a cgp_generic_utils.qt.Tool object which is a custom QWidget containing the basic functionalities shared by all the tools of you ecosystem. It just needs to be inherited by your own tool.

class MyCustomTool(cgp_generic_utils.qt.Tool):
    """this is my custom tool
    """

    def __init__(self, parent=None):
        """initialize my custom tool
        """

        super(MyCustomTool, self).__init__(parent=parent)

Dialogs library

A variety of dialogs are available in the cgp_generic_utils.qt library. Here is an example.

dialog = cgp_generic_utils.qt.LineEditDialog('dialogTitle',
                                             'dialogLabel',
                                             data=[['label1', 'text1'], ['label2', None]],
                                             entryLabelWidth=35,
                                             size=None,
                                             parent=None,
                                             isFrameless=False)

result = dialog.load()

# if cancel is pressed : result = None
# if ok is pressed : result = ['lineEdit1_text', 'lineEdit2_text']

As usual, it comes with a BaseDialog that is subclassable so a developer can create custom dialogs. He can populate the BaseDialog.contentLayout with custom widgets and overload BaseDialog.validation function to force the dialog to return the data of your choice when Ok is pressed. BaseDialog.rejection function is also overloadable for custom result when cancel is pressed. BaseDialog.setStatus can also be used to manage the clickable/unclikable state of the ok button.

Maya examples

This is a sneak peak of what the maya API has to offer. The most adventurous will have to discover the rest by themselves :)

Node/Attribute manipulation

Node and attribute API works the same as the file API from the generic utils. First, import the module.

import cgp_maya_utils.scene

A node object can be initialized using the node function.

node = cgp_maya_utils.scene.node('myTransformNode')
# result: Transform('myTransformNode')

An attribute object can also be initialized using attribute function or can be queried directly from the node object.

attribute = node.attribute('visibility')
# result: BoolAttribute('myTransformNode.visibility')

attribute = cgp_maya_utils.scene.attribute('myTransformNode.visibility')
# result: BoolAttribute('myTransformNode.visibility')

Connection manipulation

Connection object represents a connection between two attributes. This connection can actually be live, meaning attributes a connected in maya, or can be virtual, meaning the connection object relates to the two attributes but the connection is yet to be created. It can be handy when you need to disconnect a node, do some stuff and then connect back as it was before.

# get the connections of the node
connections = node.connections()

# disconnect the connections
for connection in connections:
    connection.disconnect()

# do some stuff

# reconnect the connections
for connection in connections:
    connection.connect()

Node/Attribute creation and saving

Those objects come with a really handy data method that will return you the data necessary to recreate the related node or attribute. It can be useful in case you want to save nodes into a file on the file system and eventually recreate them afterward, to save and recreate rig templates in case of an autorig, for example.

To save node data in a file, let’s do this.

# get all the joints existing in the scene
nodes = cgp_maya_utils.scene.getNodes(nodeTypes=['joint'])

# get data of all the nodes
data = [node.data() for node in nodes]

# write data into file
path = '/path/to/file.json'
saveFile = cgp_generic_utils.files.createFile(path, content=data)

# result: JsonFile('/path/to/file.json')

To recreate the nodes in a scene by reading their data from the files, let’s do this.

# get the file object
path = '/path/to/file.json'
saveFile = cgp_generic_utils.files.entity(path)

# read the content of the file
content = saveFile.read()

# create nodes
for data in content:
    node = cgp_maya_utils.scene.createNode(data)

To be a bit more specific, the data dictionary of a node contains a key (with its value) for each argument of the Node.create function. createNode function do basically this.

def createNode(data, **extraData):
    data.update(extraData)
    Joint.create(**data)

It leaves you two ways of updating a data dictionary before creating a node.

# first method
node = cgp_maya_utils.scene.createNode(data, worldSpace=True)

# second method
data.update({'worldSpace': True})
node = cgp_maya_utils.scene.createNode(data)

This is the same for attributes, with Attribute.create and createAttribute

Houdini examples

Houdini utils API works the same as the maya utils API. First, import the module.

import cgp_houdini_utils.scene

A node object can be initialized using the node function.

node = cgp_houdini_utils.scene.node('myNode')
# result: Node('/myNode')

A parm object can also be initialized using parm function or can be queried directly from the node object.

attribute = node.parm('parm')
# result: Parm('/node/parm')

attribute = cgp_maya_utils.scene.parm('node', 'parm')
# result: Parm('/node/parm')

These Node / Parm objects allow for subclassing as built-in houdini objects don’t, providing a way to represent and manipulate custom HDA programatically. You could for example create a muscle HDA and have a Muscle python object with its own commands to query / manipulate it.