PyMEL 1.0 introduces a very big backward incompatibility: importing pymel no longer imports sub-packages, meaning pymel is strictly an entry point for its sub-modules. The user must now explicitly import the sub-modules they wish to use. pymel.core remains the primary module and importing it in batch mode triggers Maya’s full startup sequence, just as importing the top-level pymel module did in previous versions.
Module Alias Description pymel.util utilities which are independent of Maya pymel.versions functions for comparing versions of maya (does not require initializing maya) pymel.mayautils low-level maya utilities (does not require initializing maya) pymel.api OpenMaya classes; requires maya, but does not require initialization of maya.standalone pymel.internal the machinery required to fuse maya.OpenMaya and maya.cmds into pymel.core (formally mayahook) pymel.core the primary sub-package; importing this module initializes maya.standalone in batch mode pymel.core.nodetypes nt contains all node classes, including custom nodes pymel.core.uitypes ui contains all UI classes pymel.core.datatypes dt contains all data classes
Modules with an alias can be accessed by this short name once their parent module is imported.
One of the tenets of PyMEL’s design is that it should add object-oriented programming while still being easy for a novice to use. One way that we tried to accomplish this was by providing access to all of its sub-modules with a single top-level import. However, in the years since this design was first implemented, we’ve come to realize that this has certain ugly drawbacks. The new layout has several advantages:
To keep things simple and to provide an easy upgrade path, PyMEL 1.0 adds a new module, pymel.all, which imports all sub-modules in exactly the same way that the primary pymel module does in previous versions. PyMEL also includes a new tool, pymel.tools.upgradeScripts, which will find and upgrade existing PyMEL scripts, converting all imports of pymel into imports of pymel.all. Additionally, PyMEL 0.9.3 is forward compatible with 1.0, meaning it will also provide a pymel.all module so that “upgraded” code is interoperable between versions.
We don’t take breaking backward compatibility lightly, but we feel strongly that these changes need to be made to ensure the long-term health of the package, and now is the best time to make them, before PyMEL’s rapidly growing user-base gets any larger.
importing pymel.all negates the load time improvements that have been made with 1.0. Importing pymel.core is now the preferred method, but requires some manual work to ensure that all node classes are referenced from their new namespace: pymel.core.nodetypes or pymel.core.nt.
Anyone who has coded GUIs in Maya using both MEL and python will tell you that if there is one thing they miss about MEL (and only one thing), it is the use of indentation to organize layout hierarchy. this is not possible in python because tabs are a syntactical element, indicating code blocks. In this release, PyMEL harnesses python’s with statement to use indentation to streamlines the process of GUI creation.
Here is a comparison of the uiTemplate example from the Maya docs.
First, using maya.cmds:
import maya.cmds as cmds if cmds.uiTemplate( 'ExampleTemplate', exists=True ): cmds.deleteUI( 'ExampleTemplate', uiTemplate=True ) cmds.uiTemplate( 'ExampleTemplate' ) cmds.button( defineTemplate='ExampleTemplate', width=100, height=40, align='left' ) cmds.frameLayout( defineTemplate='ExampleTemplate', borderVisible=True, labelVisible=False ) window = cmds.window(menuBar=True,menuBarVisible=True) cmds.setUITemplate( 'ExampleTemplate', pushTemplate=True ) cmds.columnLayout( rowSpacing=5 ) cmds.frameLayout() cmds.columnLayout() cmds.button( label='One' ) cmds.button( label='Two' ) cmds.button( label='Three' ) cmds.setParent( '..' ) cmds.setParent( '..' ) cmds.frameLayout() cmds.optionMenu() menuItem( label='Red' ) menuItem( label='Green' ) menuItem( label='Blue' ) cmds.setParent( '..' ) cmds.setParent( '..' ) cmds.setUITemplate( popTemplate=True ) cmds.showWindow( window ) menu() menuItem(label='One') menuItem(label='Two') menuItem(label='Sub', subMenu=True) menuItem(label='A') menuItem(label='B') setParent('..', menu=1) menuItem(label='Three')
Now, with PyMEL:
from __future__ import with_statement # this line is only needed for 2008 and 2009 from pymel.core import * template = uiTemplate( 'ExampleTemplate', force=True ) template.define( button, width=100, height=40, align='left' ) template.define( frameLayout, borderVisible=True, labelVisible=False ) with window(menuBar=True,menuBarVisible=True) as win: # start the template block with template: with columnLayout( rowSpacing=5 ): with frameLayout(): with columnLayout(): button( label='One' ) button( label='Two' ) button( label='Three' ) with frameLayout(): with optionMenu(): menuItem( label='Red' ) menuItem( label='Green' ) menuItem( label='Blue' ) # add a menu to an existing window with win: with menu(): menuItem(label='One') menuItem(label='Two') with subMenuItem(label='Sub'): menuItem(label='A') menuItem(label='B') menuItem(label='Three')
Python’s with statement was added in version 2.5 (Maya 2008 and 2009). It’s purpose is to provide automatic “enter” and “exit” functions for class instances that are designed to support it. This is perfect for MEL GUI creation: for example, when we enter the with block using a PyMEL ui.Layout class or a command that creates one, the layout object sets itself to the active default parent, and when the code block ends, it restores the default parent to it’s own parent. There is now little need to ever call setParent. As you can see in the example, the with statement also works with windows, menus, and templates: windows call setParent and showWindow, and templates are automatically “pushed” and “popped”.
Support for python’s ‘with’ statement is made possible by the new Layout class. Layouts also provide a number of methods for walking UI hierarchies. Continuing from our previous example:
# hide all children of the window for ctrl in win.walkChildren(): print ctrl.setVisible(False)
all component types supported
PyMEL now includes a partial replacement of the maya package in order to add tight, low-level integration. This allows us to easily solve several problems that have proven tricky to solve otherwise:
- safe to use PyMEL inside userSetup.py in python standalone
- fixes bug where certain commands don’t return a result the first time they are called
- shared root logger with status-line error and warning colorization
PyMEL Auto-completion in your favorite IDE – such as Eclipse, Wing, and Komodo – is now fast, reliable, and easy to setup. This is accomplished by providing pre-baked maya and pymel “stub” packages, which contain all of the classes and functions of the originals, stripped down to only definitions and documentation strings. Add the path to these packages to your IDE PYTHONPATH and you’re ready to go. Jump over to Seting Up PyMEL Autocompletion in Eclipse for a tutorial.
This version adds support for the creation of pure-python Attribute Editor templates, using a new class uitypes.AETemplate. Here’s a simple example:
class AEmib_amb_occlusionTemplate(ui.AETemplate): def __init__(self, nodeName): print "building", nodeName self.beginScrollLayout() self.beginLayout("Parameters",collapse=0) self.addControl("spread", label="Spread", preventOverride=True) self.addControl("max_distance", label="MaxDistance") self.addControl("reflective", label="Reflective") self.addControl("output_mode", label="OutputMode") self.endLayout() self.suppress("id_nonself") self.dimControl(nodeName, "spread", True) self.endScrollLayout()
For PyMEL to recognize the template, it must be in a module named AETemplates. There’s a new example in the pymel examples directory demonstrating more sophisticated use.
pass a valid regular expression string, compiled regex pattern, or list thereof.
>>> group('top') nt.Transform(u'group1') >>> duplicate('group1') [nt.Transform(u'group2')] >>> group('group2') nt.Transform(u'group3') >>> ls(regex='group\d+\|top') # don't forget to escape pipes `|` [nt.Transform(u'group1|top'), nt.Transform(u'group2|top')] >>> ls(regex='group\d+\|top.*') [nt.Transform(u'group1|top'), nt.Camera(u'group1|top|topShape'), nt.Transform(u'group2|top'), nt.Camera(u'group2|top|topShape')] >>> ls(regex='group\d+\|top.*', cameras=1) [nt.Camera(u'group2|top|topShape'), nt.Camera(u'group1|top|topShape')] >>> ls(regex='\|group\d+\|top.*', cameras=1) # add a leading pipe to search for full path [nt.Camera(u'group1|top|topShape')]