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.JsonFilerepresents a json file on the file systemcgp_maya_utils.scene.Transformrepresents 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.entityreturns a file/directory object from a file/directory pathcgp_maya_utils.scene.attributereturns 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.