Quantcast
Channel: mel wiki
Viewing all 610 articles
Browse latest View live

How can I paint component selection?

$
0
0
I always seem to forget this: To pain-select polygonal components, enter the component mode you wish to pain (vertex, edge, face), and:
  • RMB on the mesh
  • Paint -> Paint Select
There are two runTimeCommands:
ArtPaintSelectTool;
ArtPaintSelectToolOptions;
The first starts the tool, the second starts the tool and opens the Tool Settings window.
They both in turn call to this script:
C:\Program Files\Autodesk\Maya2016\scripts\others\artSelectToolScript.mel
Executing either these two commands:
artSelectToolScript 4;  // execute cmd
artSelectToolScript 3;  // execute cmd, open tool settings
Which are in turn wrappers around the command:
setToolTo $gPaintSelect;
Which is:
global string $gPaintSelect = "artSelectContext";

Understanding MAYA_MODULE_PATH

$
0
0
Main Maya Docs
API MFnPlugin docs, under 'modules'.

Modules are a way to distribute custom plugins. They define paths to text files that specify directories where the plugins, and their dependencies live. Docs give a good overview.
A given module will udpate Maya's MAYA_SCRIPT_PATH, MAYA_PLUGIN_PATH, XBMLANGPATH, & MAYA_PLUG_IN_RESOURCE_PATH based on the info it provides.

It should be called out that Maya's term of 'module' is different from Python's: A Python module is a single .py file.

The docs (here, here) don't seem to describe this too well, but based on looking at modules, you can set one up like this:
  • /moduleRootDir< This is what you add to MAYA_MODULE_PATH
    • /icons<- any required icons go in here
    • /maya_20XX<- There is one of these directories for every version of Maya that has a compiled plugin, the name can actually be anything, it's referenced by the description file below. But for example: /maya_2016, /maya2016.5, /maya_2017, etc.
    • /scripts<- any mel & Python goes in here.
    • /presets<- any (mel?) presets go here.
Also, there needs to be a <someName>.mod text file in the /moduleRootDir that has a weird format described in the link above. Here's an example I've seen used, for a plugin compiled for two versions of Maya:
MySpecialModule.mod
+ MAYAVERSION:2015 PLATFORM:win64 MySpecialModule 1.0 maya_2015\
PATH+:=..\..\MySpecialModule\
[r] scripts: ..\scripts
[r] icons: ..\icons
[r] plug-ins: .

+ MAYAVERSION:2016 PLATFORM:win64 MySpecialModule  1.0 maya_2016\
PATH+:=..\..\MySpecialModule\
[r] scripts: ..\scripts
[r] icons: ..\icons
[r] plug-ins: .
Note the relative paths, that appear to be relative to the version-specific plugin folder listed on the first line of each section.

How can I execute system commands and capture the results?

$
0
0
Via pure Python, you can use subprocess.Popen to do this. My Python Wiki has a subject covering this.

In Maya though, PyMel makes this even easier via its pymel.util.shellOutput command (which appears to wrapper subprocess.Popen):
import pymel.core as pm
data = pm.util.shellOutput("dir /A:L", cwd="c:\\some\\path", convertNewlines=True)
for d in data.split("\n"):
    print d
The above example tests to see if the given directory is a junction.

Some advantages I've found using this over the raw subprocess.Popen:
  • It auto-prevents shells from popping up on screen.
  • It prevents spam from hitting the Script Editor / Output Window.

How can I launch an external app, but not block Maya in the process?

$
0
0
This will open a Windows cmd shell from Maya, but not block Maya in the process:
import subprocess
subprocess.Popen("start cmd", shell=True)

Query Maya & Maya Python Executable Locations

$
0
0
Querying this in a Python script ran in Maya is pretty trivial: maya.exe and mayapy.exe (the Python executable) live in the same directory for Windows. You can also query where Python thinks its install location is (important for imports).
import os
import sys

print sys.executable
print os.path.join(os.path.split(sys.executable)[0], 'mayapy.exe')
print sys.prefix

C:\Program Files\Autodesk\Maya<version>\bin\maya.exe
C:\Program Files\Autodesk\Maya\bin\mayapy.exe
C:/Program Files/Autodesk/Maya<version>/Python
On Mac, it's a bit different:
import sys

print sys.executable
print sys.prefix

/Applications/Autodesk/maya<version>/Maya.app/Contents/MacOS/Maya
/Applications/Autodesk/maya<version>/Maya.app/Contents/Frameworks/Python.framework/Versions/Current
Since the mayapy app lives here:
/Applications/Autodesk/maya<version>/Maya.app/Contents/bin/mayapy

PyMel Also makes this really easy:
import pymel.mayautils as pmu

print pmu.getMayaLocation()
Windows result:
C:\Program Files\Autodesk\Maya2014
Mac result:
/Applications/Autodesk/maya2013/Maya.app/Contents

There's also the MAYA_LOCATION env var, that Maya appears to create on the fly when it launches:
import os
print os.getenv("MAYA_LOCATION")
C:/Program Files/Autodesk/Maya2016 // 

How I like my selection settings

$
0
0
These settings seem to change on me, so I need to get them all down in one place so I know how to set them back again...
http://help.autodesk.com/view/MAYAUL/2016/ENU//index.html?guid=GUID-8C8BA1F4-59EE-4A02-BFE7-9193C0D6F5BE
  • Maya Selection Preferences:
    • Modifiers (If something isn't listed, it means its off)
      • Affects active : If you change from object to component selection mode, the selected object is not affected. This option lets you select objects and components at the same time.
      • Preselection Highlight : Highlights components in a different color as the mouse cursor passes over them.
      • Preserve Component Selections : This option applies to polygon components only. When on, this option backs up your component selection when you switch selection type, and restores the type that you are switching to.
      • Track selection order : This option allows Maya to start tracking the selection order of components.
    • Click box size : 8 : This option controls the size of the selection area around the mouse pointer, or click box.
    • Select dead space : 8 : Lets you select edges and vertices when your cursor is outside of your object, in dead space, by setting a preselection tolerance.
    • Tweak dead space : 120 : Lets you tweak edges and vertices when your cursor is outside of your object, in dead space, by setting a preselection tolerance.
  • Modeling Toolkit:
    • Pick/Marquee : On
    • Camera Based Selection : OFF : This will only pick components you can see.
    • All others: OFF

Maya failing on maya.standalone import

$
0
0
Maya 2016 :
I have batch tools that run, that execute this at the top of the Python module:
# myModule.py
if __name__ == "__main__":
    import maya.standalone
    maya.standalone.initialize(name='python')

# bunch more code...
That code only executes when Maya is called to in batch mode, like:
import subprocess
args = ['C:\\Program Files\\Autodesk\\Maya2016\\bin\\mayapy.exe', 'C:\\path\\to\\myModule.py', '-', 'C:\\path\\to\\some\\file.mb']
subprocess.call(args)
And on 98% of the users that run it, it works fine.

But on the other 2%, this appears for a half second in the shell that pops up, when import maya.standalone is executed:
ImportError: DLL load failed: The specified procedure could not be found
After a lot of troubleshooting, as it turns out: If the BifrostMain.mll plugin is loaded on those machines, it causes these problems. Huh?!?!

How can I specify 'all keys' when cutting or pasting in Python?

$
0
0
In mel, when copying or pasting all keframes, the syntax is pretty simple (replace ... with the rest of the command code):
cutKey -time ":" ... ;
And even the Python command docs say this:
-time ":" is a short form to specify all keys.
But that's not how the Python formatting actually works.
Per Maya Docs -> User Guide -> General -> Python -> Using Python -> Ranges ->
You need to specify the time settings in this format:
mc.cutKey(time=(":",), ...)

How can I find all reference edits, and remove them?

$
0
0

PyMel:

This will filter the edits by type, and by namespace:
import pymel.core as pm

def removeRefEditCmd(editCommand, namespace=None):
    """
    Tool to remove reference edits.

    editCommand : string : Valid values are: "addAttr", "connectAttr", "deleteAttr",
        "disconnectAttr", "parent", "setAttr", "lock" and "unlock".
    namespace : bool\string : Default None.  If None, act on all references in the
        scene.  Otherwise a valid namespace (without semicolon ":")
    """
    allRefs = pm.getReferences()
    for r in allRefs:
        ref = allRefs[r]
        if namespace and namespace != ref.fullNamespace:
            continue
        edits = ref.getReferenceEdits(editCommand=editCommand)
        if edits:
            print "Found %s edits in: %s"%(editCommand, r)
            ref.unload()
            ref.removeReferenceEdits(editCommand=editCommand, force=True,
                                     successfulEdits=True, failedEdits=True)
            ref.load()
            for es in edits:
                print "\t", es
            print "\tRemoved the above %s edits"%editCommand

removeRefEditCmd(editCommand="disconnectAttr")
Here's another way, to only do on certain attrs on certain nodes in a certain namespace:
import pymel.core as pm

nodes = pm.ls("namespace:nodePfx_*.translate")
for node in nodes:
    pm.referenceEdit( node, editCommand='setAttr', removeEdits=True, failedEdits=True, successfulEdits=True )

Python

(Slightly older)
Note, this can print a LOT of stuff depending on what's changed in the reference:
# Python Code
import maya.cmds as mc

allRefs = mc.file(query=True, reference=True)
for ref in allRefs:
    refNode = mc.file(ref, query=True, referenceNode=True)

    print refNode, ref
    editStrings = mc.referenceQuery(ref, topReference=True, editStrings=True, successfulEdits=True)
    for es in editStrings:
        print "\t", es

    # Remove all edits:
    mc.file(unloadReference=refNode)
    mc.file(cleanReference=refNode)
    mc.file(loadReference=refNode)

This tip comes from over at the Maya Station blog:
If you have made changes to your reference file which has then caused incorrect results, For example making changes to an expression then saving the reference file causes you to lose your expression information; simply reloading the reference file will not be enough to get the original data back.

You will need to remove the wrong edits. In order to do this you need to unload your reference from the Reference Editor, and then go to Reference Editor ->List Reference Edits. In the window that pops up select the wrong edits and click on Remove Selected Edits. Once this is done you can Reload you reference, through the reference editor.

How can I query the red/highlighted range on the timeslider?

$
0
0
import pymel.core as pm
tc = pm.language.melGlobals["gPlayBackSlider"]
first,last = pm.timeControl(tc, rangeArray=True, query=True)

How can I query what the highlighted range of the time slider is?

$
0
0
# Python code

import maya.cmds as mc
import maya.mel as mm

# cast mel variable to Python:
gPlayBackSlider = mm.eval("$g = $gPlayBackSlider")

# Is anything highlighted?
rangeHighlighted = mc.timeControl(gPlayBackSlider, query=True, rangeVisible=True)
# What is the highlight range?
highlightedRange = mc.timeControl(gPlayBackSlider, query=True, rangeArray=True)

print rangeHighlighted, highlightedRange
# 1 [9.0, 17.0]
or
import pymel.core as pm
tc = pm.language.melGlobals["gPlayBackSlider"]
first,last = pm.timeControl(tc, rangeArray=True, query=True)

API: How can I author callbacks for Maya events?

$
0
0
The most common way to do this is via mel scriptJobs. But via the OpenMaya API, you can access many 'Message' classes that provide a lot more functionality to areas that scriptJob's don't provide.

It should be noted that if you keep executing the same callback creation code, they'll start to pile up. You'll need to develop a system to track if they've been made, so as to not have a lot of extra duplicate callbacks running in the background.

In the below example, we add a callback function that will be executed just before a new scene is opened via a OpenMaya.MSceneMessage object. As a simple hack, we track its existence with a global mel variable.
# Python code
import maya.mel as mm
import maya.OpenMaya as om

def callback(*args):
    """
    Put your callback execution code in here.
    """
    print "CALLBACK!"

def makeCallback():
    """
    Creates the callback, doesn't allow dupes:
    """
    # Pass mel variable to Python:
    mm.eval("global int $gMyCallbackExists;")
    callbackExists = int(mm.eval("int $callbackExists = $gMyCallbackExists;"))

    if not callbackExists:
        om.MSceneMessage.addCallback(om.MSceneMessage.kBeforeNew, callback)
        mm.eval("global int $gMyCallbackExists = 1")
To create the callback:
makeCallback()
And when making a new scene from the main menu:
file -f -new;
CALLBACK!
// Result: untitled // 

It should be noted that the above system is a bit of an example-hack. In practice, I've ran into problems with associating callbacks with a windows existence: A memory leak can (possibly) form from closing a window that a callback is associated with, and not also unregistering the callback. A common warning I'd get in the Output Window is:
swig/python detected a memory leak of type 'MCallbackId *', no destructor found.
And it would even crash Maya. This thread addresses some of these issues.
Here's some pseudo-code that will assist in the proper creation, and removal, of a callback:
# Below are methods\attributes on some instance of a class:

def makeCallback(self):
    """
    Method that makes the callback.
    """
    # self.callbackId is now of type MCallbackId, which appears to be a swig wrapper of a pointer
    # to an unsigned integer.
    self.callbackId = om.MSceneMessage.addCallback(om.MSceneMessage.kBeforeNew, callback)

def destroyCallback(self):
    """
    Method called when callback should be destroyed.
    """
    # Properly remove the callback:
    om.MMessage.removeCallback(self.callbackId)
    # Call a method on the swig object to 'disown' it, and remove the warning statement:
    self.callbackId.disown()

Here is a general list I've come up with for the various classes, and what they can set callbacks for:
(Maya 2011 API docs)
  • MMessage
    • Listed first, this is the base class for all message callbacks (below). Mainly used to remove a message callback, but has other helper methods.
  • MAnimMessage
    • Callbacks for animation messages: animCuve edited, keyframe edited
  • MCameraSetMessage
    • Callbacks for cameraSet sepcific envent types: Adding\removing camera layers, camera name changes.
  • MCommandMessage
    • Callbacks triggering on mel command execution.
  • MConditionMessage
    • Callbacks for changes to specific conditions, same conditions found in the scriptJob documentation.
  • MContainerMessage
    • Callbacks that inform about changes to published attributes on container node types.
  • MDagMessage
    • Callbacks for dependency graph messages: Parent added, removed. Child added, removed, reordered. Instance added, removed.
  • MDGMessage
    • Callbacks for dependency graph messages: Time changed, node added, node removed, connection made or broken.
  • MEventMessage
    • Callbacks for specific events, same events found in the scriptJob documentation.
  • MLockMessage
    • Callbacks that control how Maya handles locks: On object and attributes.
  • MModelMessage
    • Callbacks for model related messages: Before and after node duplicate, before dag node added (created), during dag node removed (deleted).
  • MNodeMessage
    • Callbacks for dependency node messages of specific dependency nodes: Attribute changed, added, or removed. Plug dirty. Before node deleted. Attr keyable state change.
  • MObjectSetMessage
    • Callbacks for object set messages received by specific sets. Tracks if set members are modified.
  • MPaintMessage
    • Callbacks for vertex color paint
  • MPolyMessage
    • Callbacks for poly component id modification messages: Vertex, edge, or face component id's modified.
  • MSceneMessage
    • Callbacks for scene related messages:
    • Before & after: new file, file import, file open, file export, file save, file save-as, file reference, remove-reference, import reference, export reference, unload reference, software render, each software render frame. plugin loaded, plugin unloaded.
    • After any operation that changes which files are loaded, when an interactive render is interrupted by the user, on interactive or batch startup after initialization, just before Maya exits.
  • MTimerMessage
    • Callbacks that are based on a fixed time interval.
  • MUiMessage
    • Callbacks to track the deletion of UI objects. Window deletion, camera change, 3d view destroyed, 3d view about to render contents, 3d view about to display contents.
  • MUserEventMessage
    • Register user-defined event types, register callbacks with the user-defined event types, and to post user-defined messages.
    • See example usage here: API : How can I author a user event?


There are a few other classes independent from the Message ones:
  • MHwrCallback
    • Callbacks to gain access to Maya's Hardware Rendering device status. You can be notified of device creation, lost reset and deletion.
  • MRenderCallback
    • Callbacks to gain access to Maya's rendering information during software rendering. You can modify Maya's shadow maps, RGB pixmap, and depth map to composite your own rendering effects into Maya's rendering.
  • MCallbackIdArray
    • An array of MCallbackId. These can be passed to MMessage instances.


And via OpenMayaUI:
  • MEvent
    • Used for querying system events such as mouse presses: Mouse button press, release, hold, drag. Delete/backspace key event. Complete key event. Querying\setting event location.



API: How can I make a transform?

$
0
0
(Referenced from 'Complete Maya Programming') Waaaay exciting!
It should be noted that any time you use API calls outside of a plugin to modify the DG, you can't undo the operation.
# Python code
import maya.OpenMaya as om
# optionA:
transFn = om.MFnTransform()
transObj = transFn.create()
name = transFn.name()
# optionB:
transFn = om.MFnTransform()
transObj = transFn.create()
# Illustrating inheritance: 
dagFn = om.MFnDagNode(transObj)
name = dagFn.name()
print name
# transform1
Class hierarchy:
  • MFnBase
    • MFnDependencyNode
      • MFnDagNode
        • MFnTransform

How can I convert from string to int?

$
0
0
Some languages give you the ability to Boolean test strings: You can convert a string to a Boolean, and if the string has any characters, the bool = True. If the string is empty, the bool = False. How to do something similar with mel?

Mel doesn't have Boolean variable types, so we have to fake it with an int. (Although, you can make Boolean attribute types. Odd you can make one but not the other.)

You could try this, but we get a warning, and an int of 0:
string $s = "word";
int $i = $s;
// Warning: line 2: Converting string "word" to an int value of 0. // 
// Result: 0 // 
However, you could use the '?' operator, and get the result you're after:
string $s = "word";
int $i = size($s) ? 1 : 0;
// Result: 1 // 
Show help docs on the ? operator:
showHelp -d "General/_operator.html";


Similar stuff in Python. Python does have Boolean object types, so we first convert the string to Boolean, then to int (or obviously leave off the int part if all you want is the bool). Trying to convert a string directly to an int raises a ValueError.
s = "word"
i = int(bool(s))
# 1

How can I add images to my UI, without hardcoding their paths?

$
0
0
It's fun to add images to your UI's, but the biggest problem is when you give your UI to someone else, usually the hard-coded paths to the imagery break.

Python

One nice thing about authoring modules in Python is they come with a 'special' __file__ attribute. This can let the module itself (or any module that calls to it) know its save location. If you store your images in the same directory as the module, you're good to go.
In the below example, presume there is a smiley.bmp image saved in the same directory as the module:
# iconTextButtonTest.py

import os
import maya.cmds as mc

class App(object):
    def __init__(self):
        # define save location of this module:
        self.path = os.path.dirname(os.path.abspath(__file__))

        if mc.window(self.name, exists=True):
            mc.deleteUI(self.name)

        self.window = mc.window(self.name, resizeToFitChildren=True)
        self.rootLayout = mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        # Create icon based on relative path:
        self.button = mc.iconTextButton( style='iconOnly', image=os.path.join(self.path, "smiley.bmp"))

        mc.showWindow()

App()

Mel

A older mel convention I've used is this: Author an "image finder" script, and save it in the same dir as your images. You can then use that script to define your image path during UI creation.
// save this as imageFinder.mel, and save it in the same directory as your images.
global proc string imageFinder()
	{
	string $description = `whatIs imageFinder`;
	string $path = `substitute "Mel procedure found in: " $description ""`;
	string $dir = dirname($path);
	return $dir;
	}
Now, inside of your UI, you can call to this script to find the location of your images, and embed that path at UI creation time:
// UI code here......
string $imagePath = `imageFinder`;
string $image = ($imagePath + "/foo.bmp");
image -image $image -h 128 -w 128;
// finish UI code....
Notes:
  • This is just and example: You will need a uniquely named "imageFinder.mel" script for each directory you'll be searching for images in. I usually name them based on the script that is calling to the images.
  • The first time you enter the 'imageFinder' script in Maya, it won't work when called to, since Maya will think the imageFinder procudrue was 'entered interactively'. You'll either need to call the source command on the imageFinder script after it's been saved, or simply restart Maya. It will work from that point on.
  • You can use this same technique to have a script's "help" menu\button point to a 'help.html' file; again, with non hard-coded paths.


How can I author an undo context manager?

$
0
0
Starting with Python 2.5, they introduced the new with statement, which is known as a 'context manager'. See official docs:
And notes on my Python Wiki:
In a nutshell, it lets you wrap a block of code in a 'context' that controls how that block will be entered, and exited. It also catches exceptions for you, and returns any that occurred, so it takes care of the try\except clause automatically.

I realized this would be a great way to wrap a block of code in an undo queue, which is often needed when authoring Python\API code in Maya. If this isn't done (based on certain circumstances, usually commands attached to button-press callbacks) when the user undo's, they have to undo every command executed based on the undone function. And if the function had a hundred loops, the user has to press undo a hundred times... Wrapping the code in a undoInfo open\close chunk call, it solves for this. In addition, you need to put your undoInfo calls inside a try\except clause to guarantee that no matter what, you'll close the undo chunk (or your users could get pretty upset with you), and as mentioned above, the with statement takes care of this for you.

I have a solution to this via Python decorators here:
How can I author an undo decorator?
But why not have yet another? :-)
import maya.cmds as mc
import maya.OpenMaya as om

class UndoManager(object):
    """
    This is the context manager class.
    """
    def __enter__(self):
        # Open our undo chunk
        mc.undoInfo(openChunk=True)

    def __exit__(self, exc_type, exc_value, traceback):
        # Close the undo chunk, warn if any exceptions were caught:
        mc.undoInfo(closeChunk=True)
        if exc_type:
            om.MGlobal.displayWarning('%s : %s'%(exc_type, exc_value))
        # If this was false, it would re-raise the exception when complete
        return True 
Simple example usage:
def myFunc():
    print "Executing some code... then:"
    with UndoManager():
        print "Running code in the UndoManager!"
        raise Exception("Oops, an exception :-(")
    print "Finish executing code..."

myFunc()
When executed prints:
Executing some code... then:
Running code in the UndoManager!
# Warning: <type 'exceptions.Exception'> : Oops, an exception :-( # 
Finish executing code...

With UI's:

This works really well when authoring UI's as well. In the below example, we create a UI with button that will create a sphere for every node in the scene, wrapping the sphere creation in our UndoManager():
import maya.cmds as mc

class App(object):

    def __init__(self):
        if mc.window('ucmt', exists=True):
            mc.deleteUI('ucmt')
        mc.window("ucmt", title="Undo ContextManager Test",  resizeToFitChildren=True)
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        mc.button(label='FOO!', command=self.buttonCmd)
        mc.showWindow()

    def buttonCmd(self, *args):
        # Wrap everything below in our context manager:
        with UndoManager():
            for i in range(len(mc.ls())):
                mc.polySphere()

# launch the window
App()
If you press the button to make the spheres, you can undo the operation with a single call to undo. However, if you modify the above code and comment out this line:
        with UndoManager():
and try again, you'll see that you'll have to no undo for every sphere that was made. Yuck!

How can I author an undo decorator?

$
0
0
A common problem I run into when coding in Python in Maya, is the ability to undo: Sometimes, and often this happens when issuing button commands via a UI, when the user wants to undo the last operation, it will undo ever step the function took in the operation. For example, if you had a function that iterated over a loop making spheres, if you wanted to undo this execution, you'd have to undo for every sphere created, rather than undo the whole operation at once.

I've found the way to make this behave properly is wrap the executed commands inside an undoInfo open\close chunk call, and inside that wrapper the code execution in try\finally clause: You need to make sure that no matter what happens, even if the command fails for some reason, you can get out and close your undo chunk.

At first I'd embed this undoInfo\try\finally clause directly in the function being called. But if there were many functions evoking this concept, this could lead to a lot of duplicated code. Enter the Python decorator.

I consider decorators 'convenience wrappers' (that's how I think of them in my head) for your functions. I have docs explaining their usage and creation over on my Python Wiki here.

In the below example, I create a Python class that will act as our decorator code. The great thing about Python and passing arguments to parameters, is that via *args and **kwargs our decorator will intercept all incoming arguments and pass them along properly to the function.
When the decorator runs, it first opens and undo chunk, then tries to execute the decorated function, catching any exceptions it may throw.
When it finishes, it closes the undo chunk, and if there was an exception, it raises it.
# python code
import maya.cmds as mc

class Undo(object):
    """
    Create an undo decorator
    """

    def __init__(self, f):
        # Called when the Undo decorator is created
        self.f = f

    def __call__(self, *args, **kwargs):
        # Called when the decorated function is executed

        mc.undoInfo(openChunk=True)
        try:
            self.f(*args, **kwargs)
        finally:
            mc.undoInfo(closeChunk=True)


@Undo                    # This wraps the below function in the decorator
def sphere(rad=3):
    # Some function that may have a problem undoing.
    #  This code shouldn't, just used as placeholder example.
    mc.polySphere(radius=rad)

# Run our code.  We can now easily undo it if we don't like the results.
sphere()
(try\except clauses cleaned up to try\finally thanks to suggestion Keir Rice)
The irony of the above system is that I authored it to work with button-presses, but the above system can't wrapper a class-based method with @Undo, it will get angry (due to the passing of self to the method..

Here is a different approach using a function-based decorator, rather than a class-based one that will work on class methods. Note how we use the functools.wraps as a decorator inside our decorator? Chaos!
from functools import wraps
import maya.cmds as mc

def undo(fn):
    """
    Create an undo decorator
    """
    @wraps(fn)
    def wrapper(*args, **kwargs):
        mc.undoInfo(openChunk=True)
        try:
            fn(*args, **kwargs)
        finally:
            mc.undoInfo(closeChunk=True)

    return wrapper

# Make an arbitrary class, with a method to decorate:
class Foo(object):

    @undo                        # This wraps the below method in the decorator
    def sphere(self, rad=3):
        mc.polySphere(radius=rad)

# Now call to our method:
f = Foo()
f.sphere()

Also see:

How can I make the Outliner in the Graph Editor not expand connections to incoming channels?

$
0
0
This... 'annoyance' was introduced around Maya 2008 (+- a version?):
If you had objectB.tx connected to objectA.tx, and objectB.tx was keyframed, and you opened the Graph Editor for objectA, you'd see it's 'Translate X' channel expanded, with a pointer to 'objectB.Translate X' under it:

(pretend this is the Outliner in the Graph Editor)
  • objectA
    • Translate X
      • objectB.Translate X

This is annoying enough on it's own, but presuming that there were many connections from B->A, A's Graph Editor could get full of all sorts of unwanted data.
The fix, to turn this "functionality" off:
# Python code
import maya.cmds as mc
# This is the name of the outlinerEditor in the Graph Editor:
graphEd = 'graphEditor1OutlineEd'
mc.outlinerEditor(graphEd, edit=True, expandConnections=True)
My guess is this has something to do with their implementation of animation layers, since the Graph Editor would need to show more than just standard ~animCurves now.

How can I get a list of all incoming connections to a node?

$
0
0
Update: This is a perfect example of overengineering ;) I did all the below work before I realized there is a listHistory node, which does all this for you. Check it out...

Say you have a list of Maya materials, and you want to find all the place2dTexture nodes connected to them.
# Python code
import maya.cmds as mc
def allIncomingConnections(nodes, nodeType=None):
    """
    Based on the nodes, find all incoming connections, and return them.
    This will recursively search through all incoming connections of all inputs.
    
    nodes : string or list : of nodes to find all incoming connections on.
    
    nodeType : None, string, list : Optional, will filter the result by these
       types of nodes.
    
    return : list : Nodes found
    """
    ret = []
    
    # Convert our args to lists if they were passed in as strings:
    if type(nodes) == type(""):
        nodes = [nodes]    
    if nodeType is not None:
        if type(nodeType) == type(""):
            nodeType = [nodeType]
            
    for n in nodes:
        incoming = mc.listConnections(n, source=True, destination=False)
        if incoming is not None:
            for i in incoming:
                if nodeType is not None:
                    if mc.objectType(i) in nodeType:
                        ret.append(i)
                else:
                    ret.append(i)
                nodes.append(i)
                
    # remove dupes:
    ret = list(set(ret))
    return ret
Example:
incoming = allIncomingConnections("myMaterial", "place2dTexture")
print incoming
# [u'place2dTexture643', u'place2dTexture642', u'place2dTexture641']

Servo Tools

$
0
0
Ran across this recently, had to make a note:

Servo Tools for Maya

"Servo Tools For Maya is a Python Plugin that sends rotational values over USB to the Arduino Micro Controller. These values are then converted into Pulse Width Modulation which is used to control multiple Hobby RC Servo Motors."
"Applications for the plugin are only limited to your imagination. Some popular examples could be to drive complex animatronic puppetry or kinetic sculpture art installations."
Homepage:
http://danthompsonsblog.blogspot.com/search/label/Servo%20Tools%20For%20Maya
Download:
http://www.creativecrash.com/maya/downloads/scripts-plugins/utility-external/export/c/servo-tools-for-maya
Also requires:
http://pyserial.sourceforge.net/
Viewing all 610 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>