A big part of how I started learning Python was to find out ways to port my old MEL scripts into Python. It’s always easier writing it the second time, not only because I already have the logic of the script down but because Python is a way, way better language than MEL. Seriously, I can hardly stand to look at MEL anymore. Anyways, one of the simpler scripts I wrote when I was starting off with MEL was a copy/paste script for the channel box. Maya is one of the most complex programs I’ve ever encountered and yet there is no built-in way to copy and paste attributes between objects. Whatever.

The script was really handy, but because I was really new at MEL it didn’t copy and paste between separate instances of Maya, and it only copied transform channels. This new one in Python takes advantage of the pickle module to store variables to a temporary file on the user’s hard drive, and retrieve them later when running the paste operation. Here’s what the code looks like:

import pickle
import platform
import maya.cmds as cmds
import maya.mel as mel
def copyChan():
    try:
        obj = cmds.ls(sl=1)[0]
    except IndexError:
        cmds.error('try selecting something.')
    xformChan = cmds.channelBox('mainChannelBox',q=1,sma=1)
    shapeChan = cmds.channelBox('mainChannelBox',q=1,ssa=1)
    # inputChan = cmds.channelBox('mainChannelBox',q=1,sha=1)
    objShape = cmds.channelBox('mainChannelBox',q=1,sol=1)
    bigPickle = []
    xDict = {}
    sDict = {}
    # iDict = {}
    if xformChan != None:
        for a in xformChan:
            xDict[a] = cmds.getAttr(str(obj)+'.'+str(a))
    if shapeChan != None:
        for a in shapeChan:
            sDict[a] = cmds.getAttr(str(objShape[0])+'.'+str(a))
    # now we have two dictionaries of transform channels and shape channels. let's write these somewhere
    # so that we can paste between instances of maya.
    if platform.system() == 'Windows' or platform.system() == 'Microsoft':
		cppath = 'c:\hfCopyPaste.txt'
    else:
		cppath = '/Users/Shared/hfCopyPaste.txt'
    clearfile = open(cppath, 'w')
    # close file immediately to clear it.
    clearfile.close()
    writefile = open(cppath, 'w')
    bigPickle.append(xDict)
    bigPickle.append(sDict)
    pickle.dump(bigPickle,writefile)
    writefile.close()
    print(xDict)
    print(sDict)
    mel.eval('print("Values copied to clipboard.")')

def pasteChan():
    objs = cmds.ls(sl=1)
    if len(objs) < 1:
        cmds.error('you should probably select something to paste to.')
    # load the dicts back from the file and get ready to apply channels
    if platform.system() == 'Windows' or platform.system() == 'Microsoft':
        cppath = 'C:\hfCopyPaste.txt'
    else:
        cppath = '/Users/Shared/hfCopyPaste.txt'
    pastefile = open(cppath, 'r')
    # index 0 is xform channels. index 1 is shape channels.
    channels = pickle.load(pastefile)
    # print(channels)
    xDict = dict(channels[0])
    sDict = dict(channels[1])
    print(xDict)
    print(sDict)
    # now assign each channel to every object in objs.
    for chan, value in xDict.iteritems():
        for obj in objs:
            try:
                cmds.setAttr(obj+'.'+chan, value)
            except:
                pass
    # pasting shape values will be trickier. typically we are only selecting xforms.
    # we'll have to get any associated shapes with each obj in objs and paste channels to them if possible.
    # since we don't know exactly what we're pasting to or what we copied from, we should just try to paste to everything,
    # the selected objects and their shapes.
    allShapes = []
    allShapes.extend(objs)
    for obj in objs:
        shapes = cmds.listRelatives(obj,s=1)
        if shapes != None:
            allShapes.extend(shapes)
    for chan, value in sDict.iteritems():
        for shape in allShapes:
            try:
                cmds.setAttr(shape+'.'+chan, value)
            except:
                pass
    print 'Channels pasted to %d objects' % len(objs)

This script isn’t really doing anything super fancy, but I wanted to point out the pickle module because it’s been so useful for me in storing data between Maya sessions. I’ve been in the process of writing a content management system for Maya using Python for the past month or so, and pickle has been more useful to me than almost any other module except for maybe os. All of the channels from the copied object are written to two dictionaries (associative arrays), xDict and sDict, and those two dictionaries are appended into one list, bigPickle. That list is then written to text and stored locally for use later by Python… the variable data will come right back in just as I stored it when I use pickle.load(). Then you just use the iteritems() method of a dictionary to parse each key and value, and smash them together into a full channel name for use with setAttr. Really handy.

Two other things to point out: one is that running platform.system() doesn’t always return what you expect. On some Windows systems it returns ‘Windows,’ on others it returns ‘Microsoft.’ I don’t really know why this is, but it definitely caused some headaches when I was testing the script with some other people. The systems were all running Windows 7, too! Go figure. The other thing is the “except IndexError” thing at the beginning of copyChan(). I used to be lazy and do catch-all error correction by not defining an error type in my try/except statements. This turned out to bite me in the ass more than once, because the point of a lot of my except statements was to allow me to be a lazy coder and just assume that the error was due to something common like an IndexError, when it wasn’t always the case. I had a serious bug in my code that was causing an I/O Error with certain user input, and I didn’t know because the except statement kept eating it. Watch your stack trace in Maya and keep an eye on what specific errors are happening!

Okay, anyways, here’s how to run this guy. Put the script in your Python scripts folder and import it like this:

import hfCopyPaste as hfCP

Then to copy channels, just highlight them in the Channel Box and run:

hfCP.copyChan()

To paste, select any number of objects and run:

hfCP.pasteChan()

This script as-is requires that you have permission to write to the C: drive on a PC, or /Users/Shared/ on a Mac. I know the Windows part is lazy, it’s easy enough to change if you want to rewrite it for your system. Just edit lines 38 and 59 (27 and 48 if you’re looking at the code embedded on this page).
That’s it! Hope it comes in handy. I use it all the time.

Categories: MayaPython

Related Posts

Houdini

Alembic: from Maya to Houdini and back again.

As part of the pipeline I’m currently writing for Timber, I’ve been working on an input/output system that can pass data back and forth between Maya and Houdini. The goal is to have a Maya Read more…

Maya

automated path substitution in maya.

I’m trying to build a system right now that can automatically substitute environment variables for paths on a per-scene basis. When a user opens a Maya file, I want to parse that file and set Read more…

Maya

blanking the viewport in maya

A while ago I posted about how to manage viewports in Maya in order to prevent the screen from redrawing while doing a bake or Alembic export, which slows things down considerably. It turns out Read more…