Difference between revisions of "Python"

From cgwiki
Line 4: Line 4:
 
I'd instance-duplicated out a bunch of vray proxy trees, needed a little randomising. This set of 3 list comprehensions gives a random rotation in y between 0 and 360, a random y scale between 0.8 and 1.2, and shuffles their translation 15 units in x and z.
 
I'd instance-duplicated out a bunch of vray proxy trees, needed a little randomising. This set of 3 list comprehensions gives a random rotation in y between 0 and 360, a random y scale between 0.8 and 1.2, and shuffles their translation 15 units in x and z.
  
<code lang="python">
+
<code>
 
from pymel.core import *
 
from pymel.core import *
 
import random
 
import random
Line 14: Line 14:
 
---++ Set decay region on spotlights, move each light back 200 units on its own axis
 
---++ Set decay region on spotlights, move each light back 200 units on its own axis
 
Was doing a searchlights style shot, the vray volume fog went crazy at the base of the lights. figured it was cos it was crazy high intensity at a single point, so if i could use the light decay region to offset the light intensity away from its center, then offset the translation of the light to compensate, it'd help. It did. :)
 
Was doing a searchlights style shot, the vray volume fog went crazy at the base of the lights. figured it was cos it was crazy high intensity at a single point, so if i could use the light decay region to offset the light intensity away from its center, then offset the translation of the light to compensate, it'd help. It did. :)
 
<source lang="python">
 
from pymel.core import *
 
 
lgts = ls(selection=True)    # I select all the spotlight shapes first
 
for lgt in lgts:
 
    lgt.useDecayRegions.set(1)   
 
    lgt.startDistance1.set(0)
 
    lgt.endDistance1.set(0)
 
    lgt.startDistance2.set(0)
 
    lgt.endDistance2.set(0)
 
    lgt.startDistance3.set(200)
 
    lgt.endDistance3.set(100000)
 
   
 
    g = group(empty=True, parent=lgt.getParent(), name='%s_offset'%lgt.name() )    # create group, parent it to the light transform
 
    g.translate.set(0,0,200)
 
    parent(lgt, g, shape=True, relative=True)    # trick that'll parent the light shape to the group, normally maya doesn't let you do this
 
</source>
 
 
 
---++ A melscripters quickie guide to maya python
 
 
While you gain in terms of string manipulation, access to external libraries and the outside world, its not as pleasantly pythonic as you'd hope. Now I definitely see why there's such a push for pymel (an amazing open source effort to make python in maya behave properly). Enyhoo, if you're dabbling, even if you know some python, its not intuitive to start with. Here's all you need to know:
 
 
<verbatim>
 
import maya.cmds as cmds
 
selection = cmds.ls(selection=True)
 
for each in selection:
 
  print selection
 
</verbatim>
 
 
Note that for the most part you're kind of maintaining two things at once; your maya variables, and your python list of those maya variables. Whereas in mel you can interact with things directly, maya-python always imposes a level of misdirection. The 'ls' command is a good example; you'd expect something like ls(selection) or ls(scene) to work, but you get no such python cleverness; each mel command has been wrappd as a clunky python call, and you have to do all your communication with maya this way.
 
 
That said, once you start doing list comprehensions on maya selections, it becomes difficult to go back to mel. Eg:
 
 
<verbatim>
 
# find all objects that have a 'tag' string attribute with value 'special stuff'
 
 
import maya.cmds as cmds
 
taglist = [ x for x in cmds.ls('*.tag') if 'special_stuff' in cmds.getAttr(x)]
 
print taglist
 
</verbatim>
 
 
 
---++ Getting a nice list of the script paths
 
Annoying that mel returns this as a huge single string, python to the rescue:
 
<verbatim>
 
env = os.getenv('MAYA_SCRIPT_PATH')
 
import pprint
 
pprint.pprint(env.split(':'))
 
</verbatim>
 
 
---+ Python scripts
 
 
Picked up a macbook pro recently, really handy to have a proper unix backend wrapped in a nice interface. A side effect of this has been getting back into python. Pleased to find just about all my old scripts worked without change, so here they are. I've also made a little zip archive of all these scripts for your downloading pleasure.  [[%ATTACHURL%/tokeruscripts.zip][tokeruscripts.zip]]
 
 
Note that this is python as it pertains to sequence wrangling, or batching image conversions, not python in maya. Haven't got into that yet (too comfortable with MEL now to bother relearning), but must make an effort to do so soon, if only for access to the API for prototyping. A google group has been setup for python-in-maya, take a look:  http://groups.google.com/group/python_inside_maya
 
 
#FixFramebuffer
 
---+++ Fix framebuffer pass-after-number naming
 
 
I've mentioned elsewhere that mentalray framebuffers will name stuff in the format image.0001_yourpass.tif, ie the pass comes after the numbering. This messed with my sense of neatness, but to my surprise shake, nuke, fcheck, after effects loaded it fine. However flame kindasorta broke; its compact sequence lister only shows the prefix, so all your passes are named the same. Thats justification for posting this script. To my surprise it runs fine from the maya python interpreter too, so thats nice. Eventually it should be made into a post-render mel to fix things on the fly, but enyhoo, this'll do for now. Run it from the folder of your image files. There's some options so that you can preview the renaming, and to force-rename if the target filename already exists.
 
 
<verbatim>
 
#!/usr/bin/python
 
 
def correctBufferName(f='me014_sh010_leonFB.0001_exampleBuffer.iff'):
 
       
 
    try:
 
        d = {}
 
 
        # grab the prefix, frame+buffer, suffix by splitting on the dot
 
        d["prefix"], d["framebuffer"], d["suffix"] = f.split(".")
 
 
        # now split the framenumber and buffer by splitting on the underscore
 
        d["frame"], d["buffer"] = d["framebuffer"].split("_")
 
 
        # join it back together in the right order, using underscores first
 
        p = "_".join( (d["prefix"], d["buffer"]) )
 
 
        # finally append the suffix with a dot
 
        result = ".".join( (p, d["frame"], d["suffix"]) )
 
 
       
 
    except ValueError:
 
        # file isn't a framebuffer format, just return the name as is.
 
        result = f
 
 
    return result
 
 
def fixBuffer(renderdir='/Users/mattestela/Projects/fixbuffers/single', preview = True, force=False, maxnum=0):
 
    import os
 
 
    os.chdir(renderdir)
 
    print 'processing dir: ' + renderdir
 
    files = os.listdir(renderdir)
 
    if (maxnum > 0): files = files[0:maxnum]
 
 
    for f in files:
 
        newname = correctBufferName(f)
 
        if (f == newname):
 
            if (preview and maxnum):
 
                print f
 
            else:
 
                pass
 
        else:
 
            print 'mv: ' + f + ' >> ' + newname,
 
            #check if file exists first
 
            if ((os.path.exists(renderdir+'/'+newname) == True) and (force==False)):
 
                print ' --- target filename exists, skipping.'
 
            else:
 
                if (preview == False):
 
                    os.rename(f, newname)
 
                else:
 
 
                    print ' (preview) ',
 
                print '...done'
 
 
def usage():
 
  print "usage: fixBuffer [-p|--preview] [-f|--force] [-m maxfiles]"
 
  print "-p,--preview do a dry run, don't rename anything"
 
  print "-f,--force rename even if target file exists"
 
  print "-m,--maxfiles <maxfiles> list a total of <maxfiles>"
 
 
def main():
 
  import getopt, sys
 
  # get options
 
  try:
 
      opts, args = getopt.getopt(sys.argv[1:], "hpfm:", ["help", "preview", "force","maxfiles="])
 
  except getopt.GetoptError, err:
 
      # print help information and exit:
 
      print str(err) # will print something like "option -a not recognized"
 
      usage()
 
      sys.exit(2)
 
  preview = False
 
  force = False
 
  allmode = False
 
  for o, a in opts:
 
      if o in ("-h", "--help"):
 
          usage()
 
          sys.exit()
 
      elif o in ("-p", "--preview"):
 
          preview = True
 
      elif o in ("-f","--force"):
 
          force = True
 
      elif o in ("-a","--all"):
 
          allmode = True
 
      elif o in ("-m","--maxnum"):
 
          maxnum = a
 
      else:
 
          assert False, "unhandled option" 
 
 
  try:
 
      dir = args[-1]
 
  except IndexError:
 
        dir = '.'
 
 
 
  fixBuffer(dir, preview, force, maxnum=0)
 
   
 
if __name__ == '__main__':
 
  print 'current _name is: ' + __name__
 
  main()
 
 
</verbatim>
 
 
 
#ListDir
 
---+++ list a directory of sequences sanely (aka seqls or lseq)
 
 
Most places have a really fast c or perl version of this, here's a python take. Given a folder full of renders, it does this:
 
 
<verbatim>
 
C:\projects\foo\images > seqls.py
 
 
3pad.000-049.jpg
 
ambocc.0000-0499.tif
 
beauty.0000-0204,0206-0266,0268-0499.tif
 
createDummyFiles.py
 
diffuse.0000-0499.tif
 
nopad.0-99.tif
 
seqls.py
 
shadow.0000-0259,0265-0499.tif
 
spec.0000-0384,0386-0499.tif
 
</verbatim>
 
 
Fancier versions like james studdart's uberlister have switches to pre-build command lines for flipping in shake and whatnot. I'll add that eventually.
 
 
The slightly tricky part was recognising missing frames. Was all ready to do some fancy list comprehension/map/lambda silliness, when I read that python 2.3 added a 'set' class. That made the problem easy to solve. Ideal frame range 'A', your real frame range 'B', missing frames are 'A - B'. Isn't python neat? Found a perl version [[http://forums.macosxhints.com/archive/index.php/t-27819.html][here]] if you prefer that sort of thing.
 
 
Some messy code towards the end, sorry 'bout that.
 
 
<verbatim>
 
#! /usr/bin/python
 
 
import os
 
from sets import Set
 
 
files = os.listdir('.')
 
result = {}
 
sortedList = []
 
 
def padFrame(frame,pad):
 
    return '0' * (pad - len(str(frame))) + str(frame)
 
 
for file in files:
 
    try:
 
        prefix, frame, suffix = file.split('.')
 
 
        # build a dictionary of the sequences as {name: frames, suffix}
 
        #
 
        # eg beauty.01.tif ... beauty.99.tif  will convert to
 
        # { beauty : [01,02,...,98,99], tif }
 
 
        try:
 
            result[prefix][0].append(frame)
 
        except KeyError:
 
            # we have a new file sequence, so create a new key:value pair
 
            result[prefix] = [[frame],suffix] 
 
    except ValueError:
 
        # the file isn't in a sequence, add a dummy key:value pair
 
        result[file] = file
 
 
 
for prefix in result:
 
    if result[prefix] != prefix:
 
        frames = result[prefix][0]
 
        frames.sort()
 
 
        # find gaps in sequence
 
        startFrame = int(frames[0])
 
        endFrame = int(frames[-1])
 
        pad = len(frames[0])
 
        idealRange = Set(range(startFrame,endFrame))
 
        realFrames = Set([int(x) for x in frames])
 
        # sets can't be sorted, so cast to a list here
 
        missingFrames = list(idealRange - realFrames)
 
        missingFrames.sort()
 
 
        #calculate fancy ranges
 
        subRanges = []
 
        for gap in missingFrames:
 
            if startFrame != gap:
 
                rangeStart = padFrame(startFrame,pad)
 
                rangeEnd  = padFrame(gap-1,pad)
 
                subRanges.append('-'.join([rangeStart, rangeEnd]))
 
            startFrame = gap+1
 
           
 
        subRanges.append('-'.join([padFrame(startFrame,pad), padFrame(endFrame,pad) ]))
 
        frameRanges = ','.join(subRanges)
 
        suffix = result[prefix][1]
 
        sortedList.append('.'.join([prefix, frameRanges ,suffix]))
 
    else: sortedList.append(prefix)
 
 
# one last sort because its so much fun   
 
sortedList.sort()
 
 
for each in sortedList: print each
 
</verbatim>
 
 
#FakeFolder
 
---+++ fake a folder full of image sequences quickly
 
 
I wrote this to test the script above... :)
 
 
<verbatim>
 
#! /usr/bin/python
 
 
import os
 
 
passes = ['beauty','spec','diffuse','shadow','ambocc']
 
sequenceLength = 952
 
suffix = 'tif'
 
pad = 3
 
 
createFiles = 1
 
deleteFiles = 0
 
showFiles = 0
 
 
for prefix in passes:
 
    for i in range(sequenceLength):
 
        num = '0' * (pad - len(str(i))) + str(i)
 
        filename =  '.'.join([prefix, num ,suffix])
 
        if showFiles: print filename
 
        if createFiles:
 
            fs = os.open(filename, os.O_WRONLY | os.O_CREAT, 0666)
 
            os.close(fs)
 
 
        if deleteFiles: os.remove(filename)
 
</verbatim>
 
 
 
#FlipBook
 
---+++ flipbook given a single frame in a sequence (aka fchecker)
 
This started as some _horrendous_ code many years ago, slowly tidying it up for public re-release.
 
 
Things left to do is make it fall back to shake if it can't find fcheck (or should that be the other way round?), find the path to fcheck/shake rather than be hardcoded, and as an external task, find the best way to integrate this with the window right click menu.
 
 
<verbatim>
 
#! /usr/bin/python
 
 
import re
 
import os
 
 
def fchecker(fname):
 
    try:
 
        prefix, number, suffix = fname.split('.')
 
    except AttributeError:
 
        return 'Could not find sequence number and/or prefix and/or suffix'
 
 
    # finding matching files
 
    files = os.listdir(os.getcwd())
 
    sequence = []
 
    for file in files:
 
        try:
 
            fileprefix, filenumber, filesuffix = file.split('.')
 
            if fileprefix == prefix and filesuffix == suffix:
 
                sequence.append(filenumber)
 
        except:
 
            pass
 
 
    # find startframe, endframe and by-frames
 
    if len(sequence) > 1:
 
        sequence.sort()
 
        startFrame = int(sequence[0])
 
        endFrame = int(sequence[-1])
 
        byFrame = int(sequence[1]) - int(sequence[0])
 
 
        # create command line string
 
        hash = '#' * len(number)
 
        filename = '.'.join([prefix,hash,suffix])
 
        fcheck = 'fcheck -n'
 
        command = ' '.join([fcheck,str(startFrame),str(endFrame),str(byFrame),filename])
 
        return command
 
    else:
 
        return 'no match found'
 
 
if __name__ == '__main__':
 
    import sys
 
    img = sys.argv[1]
 
    fname = os.path.split(img)[1]
 
    command = fchecker(fname)
 
    print command
 
    os.system(command)
 
</verbatim>
 
 
#ShakeFlipbook
 
---+++ start shake flipbook
 
 
checks if the sequence is padded or not, runs shake. handy if you have another script that lists sequences in the format 'mysequence.0001-0353.tif'
 
 
usage: flip.py mysequence.0001-0353.tif
 
 
<verbatim>
 
#!/usr/bin/python
 
 
import os
 
import sys
 
 
def getPad(range):
 
    try:
 
        start,end = range.split('-')
 
    except ValueError:
 
        start = end = range
 
 
    if len(start) == len(end) and start[0] == '0':
 
        padsign = '#'
 
    else:
 
        padsign = '@'
 
 
    return padsign,start,end
 
 
def createCommand(img):
 
    name,range,suffix = img.split('.')
 
 
    padsign,start,end = getPad(range)
 
 
    newname = '.'.join([name,padsign,suffix])
 
    framerange = '-'.join([start,end])
 
    command = ' '.join(['shake', newname,'-t',framerange])
 
 
    return command
 
 
 
if __name__ == "__main__":
 
    try:
 
        img = sys.argv[1]
 
        command = createCommand(img)
 
    except:
 
        print 'usage: r2 name.1-100.pic'
 
        sys.exit()
 
    print command
 
    os.system(command)
 
</verbatim>
 
 
#FrameTorange
 
---+++ copy single frame to range
 
 
usage: copyframe.py thisframe.50.pic 1-49
 
 
<verbatim>
 
#!/usr/bin/python
 
 
import os
 
import sys
 
import shutil
 
 
img = sys.argv[1]
 
startframe = int(sys.argv[2])
 
endframe = int(sys.argv[3])
 
 
name = img.split('.')[0]
 
suffix = img.split('.')[2]
 
 
 
for i in range(startframe,endframe+1):
 
        newfile = '.'.join([name,str(i),suffix])
 
        print ' '.join([img,'=>',newfile,'...']),
 
        shutil.copyfile(img ,newfile )
 
        print('done')
 
</verbatim>
 
 
#ChangePrefix
 
---+++ change sequence prefix
 
 
This one handles pads (cos I just needed it, so I wrote it), will copy to the other scripts later.
 
 
usage: chprefix.py oldname.1-60.pic newprefix
 
 
<verbatim>
 
#!/usr/bin/python
 
 
import os
 
import sys
 
import shutil
 
 
if len(sys.argv) != 3:
 
  print 'usage: chprefix oldName.1-100.pic newName'
 
  sys.exit()
 
 
img = sys.argv[1]
 
newprefix = sys.argv[2]
 
 
startframe = int(img.split('.')[1].split('-')[0])
 
endframe = int(img.split('.')[1].split('-')[1])
 
 
oldprefix = img.split('.')[0]
 
suffix = img.split('.')[2]
 
 
# check if we're padded
 
pad = 0
 
startpadlength = len(img.split('.')[1].split('-')[0])
 
endpadlength = len(img.split('.')[1].split('-')[1])
 
 
if startpadlength == endpadlength:
 
  if img.split('.')[1].split('-')[0][0] == '0':
 
      pad = startpadlength
 
 
 
for i in range(startframe,endframe+1):
 
    number = str(i)
 
    if pad:
 
      number = '0'*(pad-len(str(i)))+str(i)
 
    oldfile = '.'.join([oldprefix,number,suffix])
 
    newfile = '.'.join([newprefix,number,suffix])
 
    print ' '.join([oldfile ,'=>',newfile,'...'] ),
 
    shutil.move(oldfile ,newfile )
 
    print 'done'
 
</verbatim>
 
 
#BatchShake
 
---+++ batching shake scripts
 
 
<verbatim>
 
#!/usr/bin/python
 
 
import os
 
import sys
 
 
cmd = '-reorder rrrr -setalpha 1 -fo'
 
 
filein = sys.argv[1]
 
outdir = sys.argv[2]
 
 
name = filein.split('.')[0]
 
startframe = int(filein.split('.')[1].split('-')[0])
 
endframe = int(filein.split('.')[1].split('-')[1])
 
suffix = filein.split('.')[2]
 
 
name = '.'.join([name,'@',suffix])
 
filein = '-fi ' + name
 
timerange = '-t '+str(startframe)+'-'+str(endframe)
 
fileout = '/'.join([outdir,name])
 
 
try:
 
  os.mkdir(outdir)
 
except OSError:
 
  print outdir,' exists!'
 
 
command =  ' '.join(['shake',filein,timerange,cmd,fileout])
 
print command
 
os.system(command)
 
</verbatim>
 
 
#SmartListdir
 
---+++ smart ls
 
 
Just a little experiment. Ultimately the plan would be to bind this to just 'ls', so that if a directory is under 50 files, call regular ls, otherwise use one of the cleverererer sequence listers like seqls or lseq that float around most post houses. The len(os.listdir()) call is very quick, way faster than I expected it to be, so this may actually prove useful in the long run...
 
 
<verbatim>
 
#!/usr/bin/python
 
 
import os
 
 
def smartls():
 
    if (len(os.listdir('.')) > 50):
 
      os.system('seqls')
 
    else:
 
      os.system('ls')
 
 
smartls()
 
</verbatim>
 
 
 
 
-- Maya.MattEstela - 17 Oct 2007
 
 
 
#PythThread
 
---+++ Threading in Python
 
 
Mel can be a pain in the ass to thread, user Rod Green has written some python to do it easily: [[http://www.rodgreen.com/?p=191][You've got Threads in my Mel! (Timer Event Objects in Maya)]]
 
Here's a shameless copy - because we all know what happened to Bryan Ewert's [[http://ewertb.soundlinker.com/][site]] 
 
 
<verbatim>
 
# imports
 
 
import sys
 
import time
 
import threading
 
import maya.mel as mel
 
import maya.utils as utils
 
import maya.cmds as cmds
 
import string
 
 
# classes
 
 
class TimerObj(threading.Thread):
 
def __init__(self, runTime, command):
 
self.runTime = runTime
 
self.command = command
 
threading.Thread.__init__(self)
 
def run(self):
 
time.sleep(self.runTime)
 
utils.executeDeferred(mel.eval, prepMelCommand(self.command))
 
 
 
# functions
 
 
def prepMelCommand(commandString):
 
return cmds.encodeString(commandString).replace("\\\"","\"")
 
 
def startTimerObj(runTime, command):
 
newTimerObj = TimerObj(runTime, command)
 
newTimerObj.start()
 
</verbatim>
 
 
 
Mel usage:
 
 
<verbatim>
 
global proc startTimer(int $runTime, string $command)
 
{
 
python("import timer;"
 
+"timer.startTimerObj( " + $runTime + ", \"" + encodeString($command) + "\")") ;
 
 
}
 
 
startTimer(10, "print(\"Times Up!\n\") ;print(\"Hello World!\n\") ;") ;
 
</verbatim>
 
 
Drop a comment in Rod Green's blog if you're curious !
 
 
-- Maya.MattBernadat - 28 Aug 2008
 

Revision as of 23:31, 25 February 2014

Realised I haven't updated this page in ages, and I've written loads of little python scripts in the iterim. I'm a total convert to pymel, I suggest you all do the same. As such, I'll make this a dumping ground of little scripts I write as I go, just so I have a place to find them, and examples are always a handy way to learn.

Random scale/rotate of lots of trees

I'd instance-duplicated out a bunch of vray proxy trees, needed a little randomising. This set of 3 list comprehensions gives a random rotation in y between 0 and 360, a random y scale between 0.8 and 1.2, and shuffles their translation 15 units in x and z.

from pymel.core import * import random [ x.rotate.set( 0, random.randrange(0,360),0) for x in ls(selection=True) ] [ x.scale.set( 1, random.uniform(0.8,1.2) ,1) for x in ls(selection=True) ] [ x.translate.set( x.translate.get() + (random.randrange(-15,15),0,random.randrange(-15,15) )) for x in ls(selection=True) ]

---++ Set decay region on spotlights, move each light back 200 units on its own axis Was doing a searchlights style shot, the vray volume fog went crazy at the base of the lights. figured it was cos it was crazy high intensity at a single point, so if i could use the light decay region to offset the light intensity away from its center, then offset the translation of the light to compensate, it'd help. It did. :)