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

How can I query if a scene has unsaved changes?

$
0
0
import maya.cmds as mc
needsSaving = mc.file(query=True, modified=True)
Have yet to find a API call for this: MFileIO (the obvious place for it) doesn't have a similar method.

Using that, you can wrapper the mel script / global procedure:
C:\Program Files\Autodesk\Maya<VERSION>\scripts\others\saveChanges.mel
...to trigger your own save dialog when needed:
import maya.cmds as mc
import maya.mel as mm

def saveCheck():
    """
    return : int : 0 = save canceled. 1 = save not needed, scene saved, or user decided not to save.
    """
    result = 1
    if mc.file(query=True, modified=True):
        result = mm.eval('int $i_result = saveChanges("")')
    return result

Also see:

message attribute

$
0
0
The .message attribute of a node allows for dataless connections. It's handy for connecting nodes to other nodes, to keep track of dependencies. In fact, this is exactly how sets work: The message attribute of a node connects to a multi-attr in the set, and that's how the set tracks its memberships.

You can add your own attributes of type 'message' to any node, then connect the message attributes of other nodes into them (below example has some presumed transforms named 'messageMachine' and 'caller' already in the scene):
# Python code
import maya.cmds as mc

# Add our message attr to messageMachine:
mc.addAttr('messageMachine', longName='incomingMessage', attributeType='message')
# Connect caller to messageMachine:
mc.connectAttr('caller.message', 'messageMachine.incomingMessage')
If you opened messageMachine in the Attribute Editor, under Extra Attributes you'd see the new .incomingMessage attr, with its connection to caller.

Maya states the message attr has "no data" (under the addAttr docs). So rather than querying the value of a message attr, you can instead query its connection:
For example, using messageMachine.incomingMessage:
whosCalling = mc.getAttr('messageMachine.incomingMessage')
# Error: line 1: Message attributes have no data values.
Well, now what?
whosCalling = mc.listConnections('messageMachine.incomingMessage')
print whosCalling
# [u'caller']
The listAttr command tells us exactly what the message attr is connected to, which is about as much 'data' you can derive from it.

Also see:

How can I rename a namespace?

$
0
0
Pretty easily:
import maya.cmds as mc
mc.namespace(rename=["wasCalledThis", "nowCalledThis"])

How can I find the closest point on a mesh?

$
0
0
In Maya v7 or LESS:
closestPointOnMesh.mll ships with Maya's "Bonus Game Tools", and will create a closestPointOnMesh node. You can download this through Autodesk's 'Area'

In Maya v8+:
It now ships with the ability to create a closestPointOnMesh node, no plugin required! I hear it also has a nearestPointOnMesh node, which is "slower". I have yet to test this.

I've not tried it, but it may be worth checking out a solution using MMeshIntersector. There is an example using it online here
Here's a nice snipped from Chad Vernon:
mat = meshPath.inclusiveMatrix()
resultPoint = om.MPoint()
polyIntersect = om.MMeshIntersector()
polyIntersect.create(shape, mat)
ptON = om.MPointOnMesh()
polyIntersect.getClosestPoint(pt, ptON);
resultPoint = om.MPoint(ptON.getPoint().x, ptON.getPoint().y, ptON.getPoint().z)

API : Maya Python API 2.0

$
0
0
Starting with Maya 2012 Hotfix 1, enhancements were made to Python scripting with the new 'Maya Python API 2.0'.
Maya 2015 Docs
The new api modules are found in the maya.api package.
For example, here is the 'old way' to import OpenMaya:
import maya.OpenMaya as om
print type(om.MDagPath())
# <class 'maya.OpenMaya.MDagPath'>
And the new 'api' way:
import maya.api.OpenMaya as om2
print type(om2.MDagPath())
# <type 'OpenMaya.MDagPath'>
As you can see, the 'old way' typed instances as objects, but the new way types them as physical new types.

I've also noticed that it looks like the api objects are no longer being wrappered via swig objects: Calls to someSwigObject.disown() that worked in 2010 will fail on 2012 raising a nice exception:
# AttributeError: 'PyCObject' object has no attribute 'disown' # 
This isn't necessarily a result of API 2.0, but it does have a coincidence in timing....

It should be noted that in 2012 the whole API hadn't been ported over, but is much more robust in 2015.

Here are the list of advantages they post:
  • Array types are full Python sequences, including slice support.
  • Methods which take Maya array parameters will usually also take native Python sequences, such as arrays and tuples. *Exceptions are made in some case for performance reasons.
  • The outputs of methods are usually returned in their return values, not through their parameter lists. Exceptions are made in some cases for performance reasons.
  • Methods which return multiple values (e.g. MFnFluid.getResolution) return them as a tuple or list, eliminating the need for MScriptUtil.
  • Object attributes are preferred over rather than set/get methods. For example you can now write array.sizeIncrement=64.
  • There are more types of exceptions used when methods fail. Not everything is a RuntimeError, as was the case in the old API.
  • The new API is generally faster than the old. Up to three times faster in some cases.

Error: line 1: The specified module could not be found.

$
0
0
When we upgraded to Maya 2015, many plugins weren't loading for some (but not all) people. They'd get this error when they'd try to load them:
// Error: line 1: The specified module could not be found.
As it turns out, the plugins were compiled with the wrong version of Visual Studio (2010): Recompiling with VS 2012 fixed it.

How can I change the pivot location on a node?

$
0
0
PyMel provides a nice pivots parameter to the xform command that doesn't exist in normal Python or mel. You can set both the rotate and scale pivot simultaneously:
import pymel.core as pm
node = pm.PyNode("pSphere1")
pivotPos = [1,2,3]
pm.xform(node, pivots=pivotPos, absolute=True, worldSpace=True)
In regular commands, you need to move both pivots:
import maya.cmds as mc
node = "pSphere1"
pivotPos = [1,2,3]
pm.xform("%s.rotatePivot"%node, translation=pivotPos, absolute=True, worldSpace=True)
pm.xform("%s.scalePivot"%node, translation=pivotPos, absolute=True, worldSpace=True)

PyMel : FBX Export

$
0
0
This is a great example taken from this thread by Geordie Martinez
import pymel.core as pm
pm.loadPlugin("fbxmaya") # LOAD PLUGIN

# EXAMPLE SYNTAX
# pm.mel.FBXCommand(args)

#for example
pm.mel.FBXExport(f="FILENAME")
pm.mel.FBXImport(f="FILENAME", t="TAKE")

Maya.env save location

$
0
0
As of Maya 2015
Windows:
C:\Documents and Settings\<username>\My Documents\maya\<version>
C:\Documents and Settings\username\My Documents\maya
Mac:
/Users/username/Library/Preferences/Autodesk/maya/<version>
/Users/username/Library/Preferences/Autodesk/maya
Linux:
~/maya/<version>
~/maya

How can I change if I'm using Maya 'Complete', or 'Unlimited'?

$
0
0
Presuming you have Maya Unlimited installed (and properly licensed), there are two ways to change what features are available to you.

One way is to add this line to your Maya.env file:
MAYA_LICENSE = complete
or
MAYA_LICENSE = unlimited
Every time you start Maya, it will run in the defined mode.

Another way allows you to launch Maya from the command line (probably with a .bat file) with the license defined. This allows you to say, run temporarily in unlimited when you normally only run in complete (via the Maya.env).
maya -lic=unlimited

How can I query\update Maya's script path?

$
0
0
MAYA_SCRIPT_PATH is the environment variable in question. The below docs apply equaly well to MAYA_PLUG_IN_PATH.
Additions are usually stored and updated in the Maya.env file like:
MAYA_SCRIPT_PATH = c:\myScripts;
The Maya.env is usually stored per maya version:
C:\Documents and Settings\<userName>\My Documents\maya\<version>\Maya.env
The Maya.env file is parsed when Maya launches, so any modifications to it require Maya to be restarted to be seen.

Two examples below to add a new path to the current environment variable, after Maya has launched:

Python notes:

The maya.cmds package doesn't include mel's getenv and putenv, however, Python's os module does contain similar functions (os.getenv, os.putenv). However, based on the below example, os.putenv doesn't actually do anything: It fails to update the path. However, by modifying the dictionary os.environ directly, we are able to make the change.
import os
newPath = r"c:\some\path\to\add"
# Do this so we have a common base to compare against:
comparePath = newPath.lower().replace("\\", '/')
notInPath = True

sysPath = os.getenv('MAYA_PLUG_IN_PATH')
paths = sysPath.split(';')
for path in paths:
    # Make the path we're comparing against the same format
    # as the one we're trying to add:
    compare = path.lower().replace("\\", '/').replace("//", '/')
    if comparePath == compare:
        notInPath = False
        break

if notInPath:
    # Update our plugin path:
    newPath = '%s;%s'%(sysPath, newPath.replace("\\", "/"))
    #os.putenv('MAYA_PLUG_IN_PATH', newPath) # This doesn't work, interesting...
    os.environ['MAYA_PLUG_IN_PATH'] = newPath

Mel notes:

// Define our new path to add:
string $newPath = "c:/temp";

// Get our path, and split into list:
string $msp = `getenv "MAYA_SCRIPT_PATH"`;
string $pathList[];
tokenize $msp ";" $pathList;

// For comparison purposes, make all slashes forward, and add
// a slash to the end
$newPath = fromNativePath($newPath);
if(`match "/$" $newPath` != "/")
	$newPath = ($newPath + "/");

int $found = 0;
// Loop through all the paths, seeing if our path already exists:
for($i=0;$i<size($pathList);$i++)
	{
	// Same comparison check from above:
	string $compare = fromNativePath($pathList[$i]);
	if(`match "/$" $compare` != "/")
		$compare = ($compare + "/");
	if(tolower($newPath) == tolower($compare))
		{
		$found = 1;
		break;
		}
	}

// If no new path was found, add:
if(!$found)
	{
	// The *whole* path needs to be re-added:
	// putenv seems to replace, not append.
	$pathList[size($pathList)] = $newPath;
	string $fullPath = stringArrayToString($pathList, ";");
	putenv "MAYA_SCRIPT_PATH" $fullPath;	
	print ("Added to MAYA_SCRIPT_PATH '" + $newPath + "'\n");
	}
else
	print ("path '" + $newPath + "' is already in the MAYA_SCRIPT_PATH\n");		
To just query what dirs are currently in the path:
string $msp = `getenv "MAYA_SCRIPT_PATH"`;
string $pathList[];
tokenize $msp ";" $pathList;
print $pathList;

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.

Launching multiple custom configured Maya's

$
0
0
I will often work on multiple projects simultaneously, that each have their own set of required preferences. In the course of a day, I may need to access multiple teams data, each having environment variables that need to be set ahead of time, version control software that lives in different depots, and differing mel\Python paths.

The easiest way to do this I've found is to author unique configuration files for each project, and then have a .bat file copy them on top of the standard location that Maya expects to find them. To explain:
  • For each project, author these custom files, each having a unique purpose:
    • projName_userSetup.mel
      • Set custom Maya script paths via recursive searching procedure (to support an ever-expanding mel library, without having to ever type in custom script paths again)
      • Define my own personal prefs (grid color, set linear units, set correct time units, confirm I'm running the correct version of Maya for this project, etc..)
      • Load any project-required plugins.
    • projName_userSetup.py
      • Set custom Python script paths.
    • projName_Maya.env
      • Define project-specific environment variables
      • Don't define Maya Script Path (I do that via custom code in the userSetup.mel)
      • Define plug-in path
    • mayaLauncher_projName.bat
      • Copy projName_userSetup.mel to standard install location as userSetup.mel
      • Copy projName_userSetup.py to standard install location as userSetup.py
      • Copy projName_Maya.env to standard install location as Maya.env
      • Since I use Perforce (P4) as version control software, setup the p4Port, p4Client, and p4User for this project.
      • Launch Maya
I have multiple .bat files on my desktop, that when executed, copies all the appropriate code to where Maya expects to find it, sets up my version control software appropriately, and then launches Maya.

Setting grid options

$
0
0
I like Maya to always have the same grid settings, irregardless of scenes I open (sometimes opening a scene will change my grid). I usually wrapper the below code in a scriptJob to be executed by my userSetup.py. The details of all of this can be found in this mel script:
C:\Program Files\Autodesk\MayaXXXX\scripts\startup\performGridOptions.mel
# Python code
import maya.cmds as mc

size = 1000.0
spacing = 100.0
divisions = 10
axesColor = 1 # black
gridLineColor = 2 # dark gray
divisionLineColor = 3 # mid gray

mc.optionVar(floatValue=['gridSize', size])
mc.optionVar(floatValue=['gridSpacing', spacing])
mc.optionVar(intValue=['gridDivisions', divisions])
mc.displayColor('gridAxis', axesColor )
mc.displayColor('gridHighlight', gridLineColor)
mc.displayColor('grid', divisionLineColor)

# Update the grid command with the value set above to see
# an immediate change to the scene:
# mc.grid('lots of stuff...')

MainMenu


How can I get Python code to execute during Maya startup?

$
0
0
Two ways that I know of:

Method #1 : userSetup.py:

  • Create a userSetup.py module here: (Windows)
<drive>:\Documents and Settings\<username>\My Documents\maya\<Version>\scripts\userSetup.py
  • And put whatever Python code you want to execute at startup in there. This is basically just like the userSetup.mel file, but for Python.

Method #2 : Update PYTHONSTARTUP:

  • If you add an environment variable with the above name, and point it to a valid python module, Python will execute the code in that module before the prompt is displayed in interactive mode.
PYTHONSTARTUP = c:/some/path/someModule.py
  • Note: I haven't tested this in Maya yet, but I know it works with Python outside of Maya. I use method #1.

Method #3 : Author a 'sitecustomize and\or usercustomize modules':

  • See notes on my Python wiki here.
  • These are Python's equivalent to Maya's userSetup.py module.

Also see:

How can I update my Python Path?

$
0
0
Condensed from the Maya docs:

Method #1: userSetup.py
  • Create a userSetup.py module here: (Windows)
<drive>:\Documents and Settings\<username>\My Documents\maya\<Version>\scripts\userSetup.py
  • Append to sys.path in your userSetup.py:
import sys
sys.path.append('c:/myPy/maya/scripts')
Method #2: PYTHONPATH
  • Set PYTHONPATH in your Maya.env file, OR as a Windows environment variable:
PYTHONPATH = c:/myPy/maya/scripts;
    • I've heard that if you have it defined both in your Windows path and in the Maya.env file, the Windows one will override the Maya.env, so be aware.

Two other important things:
  • Make sure your paths don't have a slash at the end. This seems to dissuade Python from recognizing what's in the path.
  • Don't add any python modules to the default Maya script paths that live under ..\My Documents\Maya\scripts or ..\My Documents\Maya\<version>\scripts. Even though, as an exception, the userSetup.py module can live in there (see above), no other python module can. Maya 'just won't see it'. Very... odd...
Also, be sure to check out how Python packages work. See my notes over on my Python Wiki

Sending variable data from Mel to Python

$
0
0
If you have variable data defined in mel, how can you get that into Python?

For example, you want to get the name of the global playback slider, which you know is stored in a mel string variable:
# Python code
import maya.mel as mm

gPlayBackSlider = mm.eval("$g = $gPlayBackSlider")
print gPlayBackSlider 
MayaWindow|mayaMainWindowForm|TimeSliderForm|formLayout16|formLayout17|formLayout46|frameLayout2|timeControl1
As you can see, you need to pass the variable you're after into another temp variable ($g), so Python can catch the return from it.

With the invention of PyMel, this became even easier:
import pymel.core as pm
gPlayBackSlider = pm.language.melGlobals['gPlayBackSlider'] 

Also see:

API : How to compile a plugin

$
0
0
All of the api (OpenMaya) work I do is via the Python bindings. And plugins authored that way don't need to be 'compiled', since they're considered 'scripted plugins'. Sometimes however I'll run into legacy plugins that need recompiled to the latest version of Maya, and Maya itself comes with many examples of c++ authored plugins that do need compiled (living here: C:\Program Files\Autodesk\Maya20XX\devkit\plug-ins).

Talking with co-workers smarter than I on the subject, here are the steps we went through to compile a plugin:
  1. Install Visual Studio.
    1. I'm using Maya 2010 64-bit on Windows 7: I tried installing Visual Studio 2008, but I got compile errors. Switching to Visual Studio 2010 fixed that.
    2. When you first launch Visual Studio, configure it to 'C++'.
  2. Launch Visual Studio and: File -> Open C:\path\to\my\plugin\myPlugin.sln.
    1. .sln are Visual Studio 'solution' files.
  3. Select the 'Release' build configuration, from the drop-down in the 'standard toolbar'.
  4. Press F7 to build the plug-in (Build -> Build Solution): Watch the magic happen in the Output window.
  5. The newly compiled plugin is output to C:\path\to\my\plugin\myPlugin.mll
And that's really all there is to it.

Questions:

OR
Here's a video tutorial by Chad Vernon on how to do it using CMake, for Mac & Linux support.
http://www.chadvernon.com/blog/maya/compiling-maya-plug-ins-with-cmake/

PySide : How can I access a docked window's close callback?

$
0
0
Starting in Maya 2015, they introduced the new MayaQWidgetDockableMixin class that can be used when authoring PySide windows to make them easily dockable. For example:
from PySide.QtGui import QDialog
from PySide.QtCore import Qt
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

class App(MayaQWidgetDockableMixin, QDialog):
    def __init__(self):
        # lots of init code...
        self.setAttribute(Qt.WA_DeleteOnClose)
        # Make dockable:
        self.show(dockable=True, floating=False, area="bottom")
For a normal QDialog, you can override it's closeEvent to do stuff when then window closes:
    def closeEvent(self, event):
        # do stuff!
        super(App, self).closeEvent(event)
However, if your window is docked, this callback is completely skipped.
In Python, running a:
help(MayaQWidgetDockableMixin)
Reveals many things, including the magical dockCloseEventTriggered method. If docked, this method will be called to instead of the closeEvent:
    def dockCloseEventTriggered(self):
        # do stuff!
Note that no event is passed to it.

See Maya 2015's PyQt and PySide Widget Best Practices
Viewing all 610 articles
Browse latest View live


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