Sunday, 5 April 2015

Python snippets (Maya) for the budding TD Part 1

Over the last 2-3 years, I've been involved in Maya based team projects with students.  I've seen all of the common issues that come up - mostly from just not following the protocols they were told to use when working within a team project, and just bad file management in general.

As part of a group project last year for my class, I decided to take a lot of these problems out of their hands and automate them with tools specifically designed to manage parts of the pipeline and work flow.

I picked up a lot of things for scripting Maya that I felt were just worth sharing here.  Obviously for the more experienced TD's, these are likely to be nothing new...  However if you have additional handy tid-bits you'd love to share, please do post them to the comments below for all to benefit from.

As I go through the year, I'll post up various other bits of advice and code... For now, this can be called "Part 1"

Placing tools in the right places

Its important to understand where all of these tools sit where we can call them up from Maya.  All scripts and files should be copied to the documents/maya/2014-x64/scripts folder.  (For other versions of Maya, the '2014-x64' may of course be completely different)

The other folders we can also consider are found under the documents/maya/2014-x64/prefs.  In particular, the two sub-folders icons and shelves allow us to add custom tools to Maya's shelf.  A very handy option.

I mention this primarily because so many people will through the scripts into the documents/maya/scripts folder and then wonder why Maya won't launch them when they try to access them using the script editor.

Getting internal folders

If you find you ever need to locate project or maya folders on a machine (noting that it may be different if the machine has multiple users, or the IT department has customised the configuration of the workstations)...  The internalVar command lets us query this info...

import maya.cmds as cmds

# Get the root documents-maya folder location
print cmds.internalVar(userAppDir=True)
# get the scripts folder
print cmds.internalVar(userScriptDir=True)
# get the prefs folder
print cmds.internalVar(userPrefDir=True)
# get the prefs-icons folder
print cmds.internalVar(userBitmapsDir=True)

Running your scripts on start-up

You can have Maya execute a python script as it starts by simply naming it (make sure the spelling is correct) and placing it into the workstations scripts folder.  This makes it relatively easy to force the project folders and Maya configuration changes to be applied, as well as automatically copy files, etc that may be required (such as updates to scripts or projects).

Another use is to import any modules you plan on using.  If you find you're sick of adding the import maya.cmds line at the start of all your scripts, you can add this to your to do it when Maya starts.

You can read more about Maya python and userSetup (and more) options here.

Working with workspaces

The project folders for Maya are termed workspaces.  To access and set these up, you can utilise Maya's workspace commands.  Here's a few snippets for you to play with...  The root folder for workspaces is usually the documents/maya/projects folder, but obviously some IT departments like to configure things their own way...

import maya.cmds as cmds

# Get the root folder where maya projects reside
print cmds.internalVar(userWorkspaceDir=True)

# Get a list of the workspaces (projects)
listWS = cmds.workspace(listWorkspaces = True)
for wsp in listWS:
    print wsp

# Change Maya to use a workspace(if exists)
  raise Exception ("Project aRandomWorkspace doesn't exist!")

If you want to retrieve the root location of the current project set up in Maya, you can grab it relatively easy.

workspaceFolder = cmds.workspace( q=True, rd=True )

I used this when writing log files for a project.  I found that the default data folder that is created is ideal for this...  I specified the path using something like this.  You could of course set up your own custom folder as well if preferred.

logPath = cmds.workspace( q=True, rd=True ) + 'data'

Setting up Maya units, frame rates and resolutions

Some workstations may have differing set-ups, so its important to make sure in any team production that one machine doesn't throw things out by having a user animating at different frame rates or operating at the wrong scales.  Setting these units are fairly easy with currentUnit()...  They can be inserted into a file...

import maya.cmds as cmds

# Setting up units - in this case we're using Meters and 25fps
cmds.currentUnit( linear='m' )
cmds.currentUnit( time='pal' )

# Set the render resolution to 1280 x 720
cmds.setAttr( 'defaultResolution.width',1280)
cmds.setAttr( 'defaultResolution.height',720)

Autosave is not always a bad option to activate as well, especially to protect users who forget to save and then burst into tears when Maya crashes and takes 2 hours work with it.  However, it can cause Maya to appear to lock up when saving a large project, so be weary its not going to actually cause interference to the user.  Also, when students have been using a free student license of Maya, this can become quite frustrating to always see that message pop-up to tell us this constantly (there are temporary ways to stop this message popping up, but that requires direct tweaking of maya files)

# Set the Autosave every 10 minutes (600 seconds)
cmds.autoSave( int=600,dst=0,prm=False,en=True,lim=True,max=10)

Overwriting hot keys

Setting up custom hot keys are not too difficult, and by changing the one for Save-as, I was able to set up a custom saver that would name and take care of placing files into the right folders automatically.  Note that sometimes Maya has to be exited and restarted to have these kick in after they are set.

The first thing you need to define is a command (or a call to a function you created in Python) so that Maya has something to map to a key.  Usually a command will be in MEL, so writing the MEL command python("pythonFunction()") is important.  In this example, I've set up a command called myNewCmd.  When called, it will simply run a python function called newCmdPython

cmds.nameCommand( 'myNewCmd', c='python("newCmdPython()")' )

Once you've got a command in place, its just a case of then defining the key.  This is where the maya hotkey command comes in.  In this example, I'm mapping my new command above to the Up arrow key.

cmds.hotkey( k='Up', name="myNewCmd")

Alternatively, having the process done manually is not such a bad idea either... As long as your users pay attention and follow instructions.  That's the human issue, and half the reason for automating the processes in the first place.

Adding your scripts to the shelf

One option in the script editor allows scripts to be saved to the shelf.  The problem is that large scripts saved this way mean that any updates force you to delete the shelf button and save again as a new button.  With the scripts on disk, launching them from the shelf can be done with a very simple snippet of code.

    reload (scriptname)
    import scriptname

Note that scriptname is the name of the file on disk without the .py extension.  For instance, a file called would be imported/reloaded as coolstuff

Identifying the user

Often its important to get the users login so that things such as initials or details can be collected for use in logging or file naming. Its actually relatively simple to retrieve their login name using the getpass module.

import getpass
print 'User logged into this machine: %s' % getpass.getuser()

Making folders within folders

Sometimes there's a need to build folder/subfolder structures (maybe a shots folder inside scenes, with subfolders for various versions), and as some of the experienced python-heads will know, just specifying the folder structure using the os.makedirs() will do this automatically without any fancy coding whatsoever.

import os
dirPath = "C:/my/sub/folders"

"Save-as" the current scene file

In a custom saver tool I developed, I wanted to save the current scene with a custom location, name and the users initials.  Maya's file command offers a lot of options for TD scripts that go far beyond the basic snippets of code here.  The code I used (after creating the new file name and path information) to save was:

# Save the current scene as a new fileName
cmds.file( rename=newFileName )
cmds.file( save=True, type='mayaAscii' )

To grab a list of files loaded into Maya

Say you wanted to get a look at all the files loaded into Maya (including the .ma or .mb file), and use it for something such as listing images being used in a project, you can do it like this...

import maya.cmds as cmds

# Get a list of all files in Maya (loaded images, etc)
mayaFiles = cmds.file(query=True,list=True)
for entry in mayaFiles:
    print entry

We can query other file types by changing list=True to something else.  For instance, to query the currently loaded scene file we would use sceneName = True instead.  Of course, the amount of functionality of file is fairly broad so I suggest checking out the documentation for it.

Launching a Maya batch render as an external process

When you do a normal batch render, you would have noticed that Maya is constantly receiving messages from the process in the script editor.  This can cause Maya to get a little sluggish.  Maya is simply launching the render process using a command line tool called Render.exe

Launching the rendering process as a separate process means that Maya isn't using resources polling the process for information about the rendering progress.  This does have the downside that we don't get constant updates of the progress in Maya directly, but often that is not going to be a problem unless the scene has issues.

For this, we look at using a module called subprocess to take care of the task.  subprocess has a lot of other functionality, so its worth reading up on it...

This approach is actually quite useful in terms of keeping workstation Maya performance at its best, and hence I'm listing the whole function here.

import maya.cmds as cmds
from subprocess import Popen

# The render.exe file may be different on your computer, so you may need to change this line.
strCmd = '"D:/Program Files/Autodesk/Maya2014/bin/Render.exe"'

# Retrieve current file details
strSceneName = cmds.file(q = True, sceneName = True)
strRenderPath = '-rd "' + cmds.workspace(q = True, fn = True) + '/images/"'
strUseMentalRay = '-r mr'

# Only render this one if a file is loaded
if strSceneName:
    # create command line to launch external batch process
    strScName = '"' + strSceneName + '"'
    strCmdLine = ("%s %s %s %s" % (strCmd, strUseMentalRay, strRenderPath, strScName))

    # Execute external render process
    raise Warning("This requires a scene to be loaded (or save this one)")

End of part 1...

So there's a few things that are hopefully useful for you.  As mentioned, I'm calling this Part 1, in the hope that I'll post more snippets of code in the future.  Enjoy!