Jan 12

I saw this anydbm module and thought that it could be handy for storing certain user options. Maya already has a built in optionVar command that Python can use. There are a great many reasons to use optionVar’s. I just wanted to give an example of a built in Python library module that lets you create your own files like the optionVar file.

Note that this module creates binary files so I dont think they are as useful as creating your own asci file since asci is readable. As you can see in the example they are so easy to create and update I could see myself using them at some point.

import anydbm

# Create a new database file, use c - creating | r - reading | w - writing | n - newdatabase
db = anydbm.open('C://databasefile', 'w')

# Add new dictionary keys | values to the file
db['python'] = '1'
db['in'] = '2'
db['maya'] = '3'

# Print the data in the file
# As with all dictionarys in Python this will not print the values in order
# This is because Python is optimising how it stores the values so they can be accessed quickly
for k, v in db.iteritems():
    print k, '\t', v

# This will auto overwrite the existing keys | values which makes it easy to update the file
db['python'] = 'is'
db['in'] = 'really'
db['maya'] = 'cool'

# The previous 1 2 3 values no longer exist and now maya will print the new values
for k, v in db.iteritems():
    print k, '\t', v

db.close()

# Note: If no path is given files are stored as binary files in your Maya installation dirrectory
# C:\Program Files\Autodesk\Maya_version\bin\

Next on my list to show will be a Python Maya API utility node,
-RyanT

Dec 16

I thought it worth a post on how eval works in Python. Somone posted a question on cgtalk about how they tried to use eval inside of a Python class and they received errors. They tried to test the code by executing the string in the script editor which worked. The problem is that eval is not for executing code it is for executing expressions. Here is some Python code as an example. Just run this in your Maya Python tab.

import maya.mel as mel
import maya.cmds as cmds

# Mel eval test
def meleval():
	mel.eval('float $foo = 10;')

meleval()

# switch to the mel tab and type:
print $foo;

# this prints 10

# Switch back to the Python tab
# Now try python eval, which is for expressions
print eval( '1+1' )

# prints 2

# Try to get more complicated with eval though and this happens:
eval( 'foo = 10' )

# Error: ('invalid syntax', ('', 1, 5, 'foo = 10'))
# Traceback (most recent call last):
#   File "", line 1, in
# SyntaxError: ('invalid syntax', ('', 1, 5, 'foo = 10')) # 

# so what do you use?
def pyexec():
	exec( 'foo = 10' )
	print foo

pyexec()

# prints 10

# now try to print foo like you did with MEL eval
print foo

# Error: name 'foo' is not defined
# Traceback (most recent call last):
#   File "", line 1, in
# NameError: name 'foo' is not defined # 

# Now why is this?
# The exec executes in the scope that it is executed in
# You can get around this though:

def pyexecglobal():
	exec( 'foo = 5' ) in globals()
	exec( 'foob = 5' ) in locals()
	print foo
	print foob

pyexecglobal()

# Now try:
print foo

# success

print foob

# Error: name 'foob' is not defined
# Traceback (most recent call last):
#   File "", line 1, in
# NameError: name 'foob' is not defined #

So exec is what you want.

-RyanT

Dec 16

Just a brief example but say you are doing a task that locks up Maya and you want to get some feedback. Printing to the script editor is not helpful if you are batching files or you have a script that needs to do several tasks before finishing because Maya will not allow you to see the script editor until its finished.

Alternatively to print you can use the MEL trace command which prints to the output window of Maya. This is not a Python command technically and ends up being a bit messy when concatenating strings.

The last method shown I import the sys module and use __stdout__ which mimics C++ cout the most closely.

Another benefit is printing to the Output Window is much faster than printing to the script editor. Printing to the output window can help you debug certain types of scripts easier.

import sys
import maya.mel as mel
import maya.cmds as cmds

dagNodes = cmds.ls( dagObjects=True )

# print nodes in script editor, slow
print "List of nodes in the scene///////////////////////////////////////"
for dag in dagNodes:
	print ('Dag Node: ' + dag)

# print nodes in output window, messy
mel.eval('trace "List of nodes in the scene///////////////////////////////////////";')
for dag in dagNodes:
	mel.eval('trace (\"' + 'Dag Node: ' + dag + '\");')

# print nodes in output window, using Python sys module
sys.__stdout__.write( 'List of nodes in the scene///////////////////////////////////////\n' )
for dag in dagNodes:
    sys.__stdout__.write( 'Dag Node: ' + dag + '\n')

Have fun,
-RyanT

Dec 09

I answered a question about working with meshes using the python API on cgtalk and thought I would go more in depth on it.

First if you have not written anything using the API you need to know where the documentation is. If I tell you about a class and a function in that class you will only know about what I tell you and that is not very helpful. I looked on Autodesks website and found the latest documentation. This is for Maya 2009. Now there are changes between versions but the main reason for pointing you to this version is they reorganized the pages and added some new help. Also since this is how they will look from now on you should get familiar with it. The location of the docs are here:

Python Command
http://download.autodesk.com/us/maya/2009help/CommandsPython/index.html

Python API
http://download.autodesk.com/us/maya/2009help/API/main.html

Maya has two main ways to interact with it, using either Maya Commands or Maya API. Almost any time a MEL command is created it is also available to Python. There are some exceptions though. Autodesk did not make any of the string MEL commands available to Python because those are specifically made for MEL and do not apply to Python which already has a stronger suite of string functions. This also applies to the MEL commands that interact with your system like creating and reading files and printing to them.

Open up the Python API document and click on the Classes tab. This will show you all the classes available to you with C++ and Python. By default this tab shows the Alphabetical List. With this form of the documentation you also get the “Class List” tab which shows short descriptions of each class.

Today I will show how to get some information from a mesh. This Python script is using the API but a nice feature about Python is you do not need to create a Plugin to do this. It is fine to do so, but you dont have to like you do with C++. So even if you are writing a C++ plugin you could use Python just to quickly build up an idea of what your going to write just doing it in the script editor. No compiling!

Copy and paste this into a Python tab in your script editor and make sure to have a mesh selected so that it has something to work with:

Continue reading »

Dec 05

We were talking at my work the other day how one could create a MEL command with many arguments without using tokens. So a previous example I gave of MEL in how it compares to Python needs to be updated. While creating this version I have to say it is much more error prone than creating a simple Python object like I show here:

Maya Python vs MEL data storage

It is simply much easier to manage and write more complex data in Python. Regardless here is a MEL test I made to show a more complex and robust version. This is for those who would rather use MEL or are simply curious to see what I am talking about.

Continue reading »

Dec 03

I was thinking today it would be handy to send an e-mail to myself when a tool was done. Guess what its fairly easy to do with Python in Maya and here is an example:

import smtplib
import email.Message

# Create a function to send an e-mail
def pyMail(user='', password='', serverURL=None, sender='', to='', subject='', text=''):

	mailfailed = ''
	try:
		# The Message object helps with formating the e-mail for the server to read
		message = email.Message.Message()
		message['To'] = to
		message['From'] = sender
		message['Subject'] = subject
		message.set_payload(text)

		# Connect to the mailsever
		# Also you can specify a specific port like this:
		# mailServer = smtplib.SMTP(serverURL, 365)
		print 'connecting...',
		mailServer = smtplib.SMTP(serverURL)
		mailServer.set_debuglevel(1)

		# (Transport Layer Security) mode. All SMTP commands that follow will be encrypted.
		print 'starting tls...',
		try:
			mailServer.starttls()
			mailServer.ehlo(serverURL)
		except: pass

		# If you pass a user name and password it will be passed to the mail server
		# Note if your server requires auth it will fail without
		# If the server does not require auth it will fail if you pass it something
		print 'logging in...',
		if(user and password):
			mailServer.login(user, password)

		# It is a requirement to add '\n.\n' to the end of a message
		print 'sending...',
		mailfailed = mailServer.sendmail(sender, to, message.as_string())

		print 'disconnecting...',
		try:
			mailServer.quit()
		except: pass

		print 'mail sent.'
	except:
		print 'Error in sending mail.',

	if mailfailed:
		print 'failed:', mailfailed  # some recipients, but not all of them, failed

# Send an e-mail
pyMail( serverURL	= 'mail.server.com',
	sender = 'from@address.com',
	to = 'to@address.com',
	subject = 'hello',
	text = 'world',
	user = 'user',
	password = 'password')

Not to hard ehh? And there is nothing wrong with you creating a python function like this and calling it with MEL if this is all you want from Python.

Happy coding,
-RyanT

Dec 01

Our first teaser trailer for our game has been released. Check it out!

And a bit of info has been released on Game Informer’s website as well.

U2 Among Thieves

Nov 27

Contents:
What import does
Temporarily add a new directory to import from
Permanently add a new directory to import from
Make import work like MEL source

What import does:

Since importing modules is important to understand and anyone first learning how to use Python will need to do this. I figured I would go over how to properly import python modules in Maya. I will go through examples of how to temporarily add a path to your sys.paths so that Python can find it and I will show how to add the path permanently. Last I give an example of a function I made so you can just pass it a path and it returns the module that was imported for you to work with.

First thing you learn with Python scripts is you can not use the menu item file/source script in the Maya script editor. I dont know why Maya shows you both .mel files and .py files since sourcing a .py file will only give you errors. Basically the point here is you dont source Python files. It wont work.

The way you import a python script is admittedly a bit more cumbersome than sourcing a MEL file. Python does not allow you to simply source a given path name to the file. This is not a Maya specific issue, but how python is designed.

Also importing a python script or module is not the same as sourcing in other ways. When you use import, Python checks to see if the module is loaded and if it is not it will read the module and create the data in a namespace. Python also compiles a .pyc file. So thats why you see extra files in your folder after importing a module. If the module has been changed after it was imported and you want to “source” that version then the correct code to use is:

reload( module )

When you import a python script you are to simply do this:

# import the filename into its own namespace with the same name
import module

# import the filename into its own namespace with a specified name
import module as newname

# Import all the definitions and variables of the module into the global namespace
# This defeats the purpose of Python making your code less modular so it should rarely be used
from module import *

# Reference specific variables and definitions into the global namespace from the module
from module import varA, varB

# Allows a variable to be passed to import and returns the module to a variable you specify
# The way this works is a bit odd since even if foo is imported you cant call foo.defname()
# you must in this case use the variable mod to call mod.defname()
mod = __import__( module )

It is not ok to type a file extension or a path name. You can not even use a variable with the correctly named string. You can however use __import__() if you want to give it a variable. I show an example of this later. When you import, Python will look for the script in a set of system paths that Python finds. Python looks in its standard module library paths and also includes the PYTHONPATH environment variable paths.

Maya also automatically adds these paths by default:

C:/Documents and Settings/Ryan Trowbridge/My Documents/maya/2008/prefs/scripts
C:/Documents and Settings/Ryan Trowbridge/My Documents/maya/2008/scripts/
C:/Documents and Settings/Ryan Trowbridge/My Documents/maya/scripts

It is important to note that Maya will not find any scripts in your /My Documents/maya/2008/scripts/ folder. Note that the path has an extra forward slash therefore Python finds nothing. I think the Maya programmers are to blame for that one. This might be fixed in Maya 2009. If you place your script in the /My Documents/maya/scripts path Python files will import fine. From reading on the boards some people seem to be confused about this and I think rightfully so. Thankfully you can fix this with the environment variable as I show later.

First lets look at what paths Python is looking for using the script editor. Open up your Maya script editor and in a python tab execute this code:

import sys

# sys.path is simply an array of system paths that gets created when you first start up maya
syspaths = sys.path

for path in syspaths:
	print path

Continue reading »

Nov 19

These are some examples of how storing data is much cleaner in Python. If you have not used an object oriented language before some of this might sound overly complex and scare you back to MEL. Just remember if you use Python in Maya you dont have to use all of its complexities but its nice to have them if you end up wanting to try other methods of coding.

So on to the good stuff. First I have a simplistic approach to using a python class to help store data from a user selected hierarchy. Second I have a more complex approach to doing the same thing.

To start you should open Maya and create some joints or nodes to create a hierarchy. These use the code examples to see what kind of data you can store and retrieve.

# Python Example V1
import maya.cmds as cmds

# Node list that will store the selected hiearchy
nodeList = []

class Node:

	# Initialize Node
	def __init__(self, node = None, parent=None, childs=None):

		# Create three public attributes inside Node
		# Note that it is easy to add new properties later
		self.node = node
		self.parent = parent
		self.childs = childs

# Function to recurse the selected nodes hiearchy
def recurseNodes( node ):

	print ('recursing node: ' + node )

	# Get the parent node and child nodes
	parent = cmds.listRelatives( node, parent = True )
	childs = cmds.listRelatives( node, children = True )

	# Maya returns an array - if listRelatives returns the parent remove it from the array
	if parent != None:
		parent = parent[0]

	# Create an instance of the "Node" object
	newNode = Node( node, parent, childs )

	# Add new node to nodeList
	nodeList.append( newNode )

	# Recurse children
	if( childs != None ):
		for child in childs:
			recurseNodes( child )

# Get selected node
sel = cmds.ls( sl=True )

# Iterate through selected nodes
for s in sel:
	recurseNodes( s )

# Print saved data in nodeList
# Note it is very simple to get the data you want
# and you are free to make the data much more complex
# for instance you could create a "Node" object for the parent
# and each child and store those in the node instance
# this way you could walk the hiearchy and get data quickly and easily
for node in nodeList:

	print '\n'

	print 'node name:'
	print node.node

	print 'parents name'
	print node.parent

	print 'childrens name(s)'
	print node.childs

Continue reading »

Nov 07

The last time I spoke about how to setup a MMatrix and use it with python. By default there are no vector classes that are part of python. This is not a problem since you can use the Maya API.

The MVector is actualy much easier to work with. You do not need to use the MScriptUtil class with MVector’s you can just simply give it a default value and start working with the new object. For example:

# Import the general maya API classes
import maya.OpenMaya as OpenMaya

# Create two MVectors
vectorA = OpenMaya.MVector(1.0, 1.0, 1.0)
vectorB = OpenMaya.MVector(2.0, 2.0, 2.0)

# Do some operations on them
vectorC = (vectorA + vectorB)
vectorD = (vectorA - vectorB)

# vectorC after adding vectorA and vectorB
print vectorC.x
print vectorC.y
print vectorC.z

# vectorD after subtracting vectorA and vectorB
print vectorD.x
print vectorD.y
print vectorD.z

# get the length of vectorC
print vectorC.length()

# normalize vectorC
vectorC.normalize()

# print the new normalized values
print vectorC.x
print vectorC.y
print vectorC.z

Note that if you want more information on the MVector class you can look it up in the Maya help documents.

Hopefully that helps!
-RyanT