<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
<<importTiddlers>>
''WHAT IS IT?''
*This page is all about how to do things in Maya using its scripting language "mel" (Maya Embedded Language). And in addition to that "elf" (mel's "Extended Layer Format"), for UI creation.
*This page mainly lists code samples, and "how-to's" with mel.  There are no "scripts" to 'download'.  The purpose of this page is to be a resource for creating mel procedures and scripts.
*More info on Maya can be found at the new Autodesk [[web site|http://usa.autodesk.com]] (yes, they bought Alias) or [[Highend3D.com|http://www.highend3d.com/maya/]].
*//This page used to be called ''"How to find stuff using mel"'' on other Wiki's:// 
**{{{www.openwiki.com}}} (which appears to be dead) & {{{warpcat.pbwiki.com}}} are some past sites.
''WHAT IT ISN'T:''
*Instructions on how to script\program. I'm presuming you already know a bit of that. But since there are plenty of examples listed, they may help.
''HISTORY:''
*This page started years ago as a personal web page with a bunch of random thoughts as I learned mel. So a lot of these notes are from the gamut of versions... 1.0 through currently, 8.5. You will see some pretty simple things listed, and some more complex things as well. I've thought about nixing the simple stuff, but for others that are learning, that would be counterproductive.
*I have posted it as a Wiki primarily because they're so easy to edit, I can add stuff very quickly while in development, from work, home, or off-site.  I'm currently using [[TiddlyWiki|http://tiddlywiki.com/]] due to the fact it's so easy to search (based on the Tags), and update (since I only have to work on one tiddler at a time, rather than the whole page).
''DISCLAIMER''
*I can't be blamed for anything misrepresented on this page! If it doesn't work, no finger-waving my direction.
*Since I work on a Windows system, the majority of the command line stuff I call out to is Win-centric. If you're using Lunix\Unix\OSX etc, I'm fairly confident those args won't work.... (replace with what is appropriate for your OS)
''CONTRIBUTE YOUR BRAIN:''
*If you'd like to add something to this page, please [[email me|WarpCat]] with your addition, and I'll add it in if appropriate, thanks!
*I give credit to those I get info from, whenever possible.
*If you want to be a full-time contributor/editor, that's an option too.  Let me know if you have interest.
''TAKE IT WITH YOU''
*Since this is a tiddlywiki, it's a 'self contained' wiki.  No servers required to run it, no other installs needed (other than a valid web browser).  Using the //right column// you can download this file (it's only one file), and run it off any other location (memory-stick, cd, hard drive, another web site, etc).  Of course it won't update if you do that, so keep checking back on a regular basis.
----
And if you like how this TiddlyWiki works, check out the home page:
*http://tiddlywiki.com/   -- download TiddlyWiki 
*http://tiddlyspot.com/  -- Get your TiddlyWiki hosted online for free (what you're using right now)
----
[[wiki help]]
While reading a book on the [[Processing|http://www.processing.org/]] language, they had a different take on the standard Maya {{{for}}} loop.  A normal {{{for}}} loop has this format:
{{{
for (initialization; condition; change of condition){ 
	statement; 
	statement; 
	 ... 
} 
}}}
So a simple example:
{{{
for($i=0;$i<5;$i++){
	print($i + " ");
};
0 1 2 3 4 
}}}
I learned that you can actually have //multiple// __initialization__ parameters, along with multiple __conditions__, and multiple __change of conditions__:
{{{
for($i=0, $j=5; $i<$j, $j<10; $i++, $j++){
	print($i + " " + $j + "\n");
}
0 5
1 6
2 7
3 8
4 9
}}}
I'm really not too sure what I'm going to do with this info yet, but... I think it's good to know!  Talking with other programmers, their reply was "oh yeh, //many// other languages do that".  So, I guess I've been left out of the.... //loop//. (so bad, but so good).
Retrospective blog entry.  mel wiki is created!
''Happy New Year!''  I added this new blog section.

I figured out how to move the "mel wiki" icon to the SiteTitle (very exciting).  I also added my first bit of Python tagging, and setup a main [[PYTHON]] category.  Still stuck on Maya 7 though...

Finally, made the 'Tags' tab always be visible in the right hand column, which should make it easier for users to navigate the site by default.
Man, there is a pile of rain coming down here in the San Francisco area!  
Tiddlyspot.com is down for some reason, so all the latest updates are being made offline...
Tiddlywiki is back up again... I wonder what was going on...

Robots!  Not directly related to mel, but I made my first couple of solar powered robots:

The Symet
[img[http://farm3.static.flickr.com/2232/2170294205_36506d0c98_m.jpg][http://www.flickr.com/photos/8064698@N03/sets/72157603647631805/]]

The Hexpummer
[img[http://farm3.static.flickr.com/2129/2174047884_2b66e455ea_m.jpg][http://www.flickr.com/photos/8064698@N03/sets/72157603656270159/]]
Added a new helpful section, the [[Visual Guide of UI Controls]].  Will be updating it slowly over time, in alphabetical order.  Lets the user see the type of UI control before they build it... so you can make your UI's much faster!
Added the new [[All Subjects]] tiddler in the main menu (on the left) of the screen:  It will show ALL the subjects (tiddlers) in the wiki.  Now you can search through the categories, or just __one big page__ of subjects.
I recently got turned onto ''Processing'' through the ''Make'' blogs:
[img[http://processing.org/reference/environment/images/ide.gif]]
*Processing:  http://www.processing.org/
*Make:  http://www.makezine.com/ (which you should read all the time, FYI)
It's a cool (free) language that sort of 'wrappers up' Java.  From their site:
<<<
"Processing is an open source programming language and environment for people who want to program images, animation, and interactions. It is used by students, artists, designers, researchers, and hobbyists for learning, prototyping, and production. It is created to teach fundamentals of computer programming within a visual context and to serve as a software sketchbook and professional production tool. Processing is developed by artists and designers as an alternative to proprietary software tools in the same domain."
<<<
----
Here's some fantastic examples by [[Jared Tarbell|http://www.complexification.net/programmer.html]].  One of the coolest things:  He also gives the ''Processing'' source code away for each of them.  That is awesome.  Share the knowledge!
*http://www.complexification.net/gallery/
[img[http://www.complexification.net/timeline/WTsubstrate.jpg]]
[img[http://www.complexification.net/timeline/WThappyPlaceWideB.jpg]]
[img[http://www.complexification.net/timeline/WTintersectionMomentary.jpg]]
[img[http://www.complexification.net/timeline/WTboxFittingImg.jpg]]
(many more in the above link)
I added a new category (on the left) called [[TROUBLESHOOTING]].  It will take some time to organize other stuff into it, but it's a start.
Added a new section called [[Other Links]].  Thought I'd share some of the other web sites that I enjoy, that have some correlation to brains that enjoy Maya and mel.
[[A month or so ago|2008 02 18]] I talked about [[Processing|http://www.processing.org]].  Since then I've started to learn it, and have been posting some of my first sketches on [[Flickr|http://www.flickr.com/photos/8064698@N03/collections/72157604136742471/]].  Enjoy.
I've FINALLY gotten around to re-learning Python.  In lite of that, I've made a new Python Wiki:
http://pythonwiki.tiddlyspot.com/
While there is still Maya-centric Python stuff on this page, non-Maya related Python stuff I"ll start posting over there.  I'm still a Python noob, so there's nothing too amazing over there yet.  It's like how this wiki started yeaaaars ago.

Finally got around to updating to tiddlywiki v2.4.  No one will really care but me.  But it's the new hottness.
I've finally made a blog to tie together my various wiki's and projects.  It can be found here:
http://warpcat.blogspot.com/
A buddy of mine has started his own mel tiddlywiki as he learns the languange.  Everyone should! ;)
http://brac.tiddlyspot.com/
I thought I'd try and experiment, and create a [[Guest Book|GuestBook]].  I really have no idea how many people use my wiki, how often it is viewed, etc.  I do get an occasional email telling me how someone has used it, which gives me the warm-fuzzies.

If you'd like to 'sign' the [[Guest Book|GuestBook]], go to that link, and follow the directions.  Since his wiki isn't open for public editing (one of the reasons I set it up that way:  I used to get spammed all the time when anyone could edit it), you'll need to //email [[me|WarpCat]] your guest book comment.  That can be a departure from regular website guest book signing, so I could see a reluctance on the part of some users.
As I said, this is an //experiment// ;-)
I've done some retagging when it comes to Python subjects:  I already have an upper-case '[[PYTHON]]' category on the left-column. Before, //all// subjects that were either about Python specifically, or others that had Python code in their examples received that tag.  I've now made a 'lowercase' '[[python]]' tag as well to help differentiate:
*Subjects that are //specifically related to Python features// will get the upper-case //category// tag.
*Subjects that have example Python code in them (or call to the {{{python}}} //mel// command), but aren't 'about' Python specifically, will get the 'lowercase tag'.
I think this will help make searching for //Python-specific// issues easier, and not clutter it with code simply written //in// Python.
Since I'm forcing myself to learn the API (very slowly), I added an [[API]] category.  I'm mainly learning the API via Python, since I don't know much of c++ at all.
Added the [[INFO]] category.  Haven't yet fully populated it, but it was missing:  A place on subjects about querying information about things.
Added the [[HARDWARE]] category.  Currently only has one item.  But it's a cool item.  I'm not sure how I'd use it, but I think it deserves its own category ;)
Below are simple {{{lambda}}} wrappers for converting sequences (like API/~PyMel {{{Points}}} or {{{Vectors}}}) from Maya's 'internal units' (cm) to the current 'ui units' (could be cm, could be... something else) and back again.
{{{
# Using API 2.0, but works just as well with maya.OpenMaya
from maya.api.OpenMaya import MDistance

internalToUi = lambda x:[MDistance.internalToUI(item) for item in x]
uiToInternal = lambda x:[MDistance.uiToInternal(item) for item in x]
}}}
For example:
{{{
import pymel.core as pm

nodeA = pm.PyNode("coneA")
nodeB = pm.PyNode("coneB")

# This returns internal units:
pt_nodeA_rotPiv = nodeA.getRotatePivot(worldSpace=True).cartesian() 
v_nodeA_rotPiv = pm.dt.Vector(pt_nodeA_rotPiv) 

# This expects a vector (or list of 3 vals) of *ui units*, so we convert:
nodeB.setTranslation(internalToUi(v_nodeA_rotPiv), space='world')
}}}
Good blog post here:
http://around-the-corner.typepad.com/adn/2012/09/custom-shapes-in-the-maya-api-1.html
Maya has a whole bunch of both c++ & Python API plugin \ scripted plugin example stuff online [[here|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/examples.html,topicNumber=cpp_ref_examples_html]] (Maya 2013)
Any API call that modifies the DAG (your scene) does this outside the scope of the standard commands undo queue.  Info here on how to make that work via a scripted plugin:
[[API: undoing commands]]
You need to create an actual plugin command ({{{MPxCommand}}}) or node ({{{MPxNode}}}) to properly hook into the Maya commands undo structure. 
And making a scripted plugin around a simple API modification of the DAG is a huge amount of work sometimes.  So, can you hack this sytsem instead?

Thanks to a tip/suggestion from {{{Jonathan Tilden}}}, I came up with the below example.

You can test by making a new poly sphere in your scene and running it:
* First, we iterate over all the vert positions, and using a {{{setAttr}}} command, 'touch' them:  In this case, we move them by 'zero', but it does store that last position in the undo queue. I have found that other commands, like {{{move}}} do //not// 'touch' the verts correctly. 
* Then we run our API code to randomize the verts.
* When you hit undo:  The API code sits outside that system, so it doesn't get undone. Instead, it sees the last time we touched those verts, and sets their positions back.
Conceptually this could be applied to other ~API-derived DAG changes as well.

hack away!
{{{
import random

import maya.cmds as mc
import maya.api.OpenMaya as om2

deformValue = 1.25
meshShapeName = "pSphereShape1"
objectList = om2.MSelectionList()
objectList.add(meshShapeName)
dagPath = objectList.getDagPath(0)

# undoInfo is needed to collect all our setAttrs in a single undo chunk:
mc.undoInfo(openChunk=True)
try:
    # First, 'touch' all the verts to store their position, and update Maya's undo queue:
    for vtxId in range(mc.polyEvaluate(meshShapeName, vertex=True)):
        mc.setAttr(f'{meshShapeName}.pnts[{vtxId}].pntx', 0)
        mc.setAttr(f'{meshShapeName}.pnts[{vtxId}].pnty', 0)
        mc.setAttr(f'{meshShapeName}.pnts[{vtxId}].pntz', 0)

    # Now deform with our API code:
    geomIter = om2.MItGeometry(dagPath)
    while not geomIter.isDone():
        point = geomIter.position()
        point.x += random.uniform(-deformValue, deformValue)
        point.y += random.uniform(-deformValue, deformValue)
        point.z += random.uniform(-deformValue, deformValue)
        geomIter.setPosition(point)
        geomIter.next()
finally:
    mc.undoInfo(closeChunk=True)

}}}
There are edge-cases where certain API calls will want a c++ [[struct|http://www.cplusplus.com/doc/tutorial/structures/]] passed in.  For example, the [[OpenMayaRender.MRenderView|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_render_view.html]] class's {{{updatePixels}}} method expects a pointer to a [[RV_PIXELS|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/struct_r_v___p_i_x_e_l.html]] struct.  How can you do this via the Python API?
As it turns out (in this case at least) they've wrappered that struct for you:
{{{
import maya.OpenMayaRender as omr
rvp = omr.RV_PIXEL()
}}}
I'd be interested to know how many more there are?
This post by Kristine Middlemiss breaks it down very nicely:
*http://around-the-corner.typepad.com/adn/2012/07/setting-a-user-event-within-maya.html
Transcribed from that post for prosperity:
{{{
# Here's how you would register a new user event type called 'myEvent':
import maya.OpenMaya as om
om.MUserEventMessage.registerUserEvent('myEvent')

# To have a function called 'myFunc' execute whenever the event occurs:
def myFunc(data):
    print('Got a myEvent event!')
callbackId = om.MUserEventMessage.addUserEventCallback('myEvent', myFunc)

#To send a 'myEvent' event:
om.MUserEventMessage.postUserEvent('myEvent')

#To remove the callback function when done:
om.MUserEventMessage.removeCallback(callbackId)
}}}
----
Also see:
*[[API: How can I author callbacks for Maya events?]]
# http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_mesh_html
~MFnMesh.closestIntersection

Note, I'e been unable to get this to work via maya.~OpenMaya, but I have got it to work via maya.api.~OpenMaya
Also note the points it reruns are always in internal units (cm) so you may need to convert them to your UI units.
Often times I'll have an MObject, and I want to know what it is exactly.  In normal commands you can use a call to {{{objectType}}}.  But how about in the API.  Below is a round about way to do it.

In a nutshell, you check to see if a given MObject has a given function set.  The list of available function sets is here:
http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_fn_html

But it's often easier to just query which function sets are available for the MObject itself:  The {{{getFunctionSets}}} function below returns a list of them.  Armed with that info, you can then query them.  Also see [[these notes|API: How can I query what function sets are available for an MObject?]].
{{{
import maya.OpenMaya as om

def getMObjectByStr(strName):
    selList = om.MSelectionList()
    selList.add(strName)
    mObject = om.MObject()
    selList.getDependNode(0, mObject)
    return mObject

def getFunctionSets(mObject):
    funcSets = []
    om.MGlobal.getFunctionSetList(mObject, funcSets)
    return funcSets

def isApiType(mObject, apiType):
    if mObject.hasFn(apiType):
        return True
    else:
        return False
}}}
So to use it:
{{{
strName = "myAwesomeMeshShape"
mObject = getMObjectByStr(strName)
print getFunctionSets(mObject)
# [u'kBase', u'kNamedObject', u'kDependencyNode', u'kDagNode', u'kShape', u'kGeometric', u'kSurface', u'kMesh']
# AH, so I can use 'kMesh' :

apiType = om.MFn.kMesh
print isApiType(mObject, apiType)
# True
}}}
When making custom node types via scripted-plugins (or regular 'ol compiled plugins), they need a [[MTypeId|http://download.autodesk.com/us/maya/2011help/api/class_m_type_id.html]] set to a specific value, so that custom node types won't clash:
{{{
import maya.OpenMaya as om
kPluginNodeId = om.MTypeId(0x00113)
}}}
Autodesk divvies-out ranges of addresses to developers, but how do you get these addresses assigned?
Via this web site:
* http://mayaid.autodesk.io
----
This blog post does a great job covering all aspects of these id's, how they're used, and how you can register for them:
http://around-the-corner.typepad.com/adn/2012/09/maya-and-node-types.html
{{{
import maya.OpenMaya as om
import maya.OpenMayaUI as omui

activeView = omui.M3dView.active3dView()
om.MGlobal.selectFromScreen(0, 0, 
                            activeView.portWidth(), 
                            activeView.portHeight(),
                            om.MGlobal.kReplaceList)
}}}
From this post here:
https://groups.google.com/d/msg/python_inside_maya/FxnZzCntpp0/F7Sb4udwLK8J
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}}}).

Also see: [[Compiling Python Libraries for Maya]]

This is a short overview:
#Install [[Visual Studio|http://www.microsoft.com/visualstudio/en-us]].  
##The version of Visual Studio is very important, based on which Maya you are going to compile for.  Check out this subject to understand which version you need installed: [[Maya compiler versions]] & [[Python & Qt versions in Maya]]
##When you first launch Visual Studio, configure it to 'C++'.
#Launch Visual Studio and: File -> Open {{{C:\path\to\my\plugin\myPlugin.sln}}}.
##{{{.sln}}} are Visual Studio 'solution' files.
#Select the 'Release' build configuration, from the drop-down in the 'standard toolbar'.
#Press F7 to build the plug-in (Build -> Build Solution):  Watch the magic happen in the Output window.
#The newly compiled plugin is output to {{{C:\path\to\my\plugin\myPlugin.mll}}}
And that's really all there is to it.
----
Questions:
*Can you use [[Visual Studio Express|http://www.microsoft.com/express]]?
**Looks like there is evidence [[on the web|http://www.creativecrash.com/tutorials/build-plugins-with-visual-c-express-edition]] that [[this is possible|http://www.daisukemaki.com/projects_maya_api_cplusplus.php]].
*Can you use other compliers other than Visual Studio?
**I've read of Codeblocks, Xcode, and possibly even Eclipse working.
----
''OR''
Here's a video tutorial by Chad Vernon on how to do it using [[CMake|http://www.cmake.org/]], for Mac & Linux support.
http://www.chadvernon.com/blog/maya/compiling-maya-plug-ins-with-cmake/
----
Also see:
*[[Remote debugging with Visual Studio]]
Starting with Maya 2012 Hotfix 1, enhancements were made to Python scripting with the new 'Maya Python API 2.0'. 
[[Maya 2015 Docs|http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=__py_ref_index_html]]
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.
Maya has as lot of examples of Python API code, but they're all mixed together with 'old' and new.  As of 2020, here's all the new 2.0 stuff:
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_camera_message_cmd_2camera_message_test2_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2lasso_tool_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2locator_helper_shape_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2marquee_tool_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2modules_2_j_s_o_n_pattern_creator_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2modules_2py_json_attr_pattern_info_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_api_mesh_shape_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_blind_double_data_cmd_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_brick_shader_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_convert_vertices_to_faces_cmd_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_custom_primitive_generator_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_depth_shader_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_draw_foot_printby_render_utilities_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_foot_print_node_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_foot_print_node_geometry_override_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_foot_print_node_geometry_override_animated_material_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_frame_context_test_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_geometry_replicator_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_hello_world_cmd_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_json_attr_pattern_factory_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_meta_data_cmd_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_panel_canvas_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_panel_canvas_info_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_point_on_mesh_info_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_ui_draw_manager_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_vertex_buffer_generator_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_vertex_buffer_mutator_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_view_render_override_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2py_what_is_cmd_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_scripted_2square_scale_manip_context_8py_example_html
* https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_view_render_override_from_fragments_2py_ground_reflection_renderer_8py_example_html
Nice informative overview:
http://discourse.techart.online/t/maya-api-file-translators-openmaya-vs-c-speeds/10293
The below code is a copy from a post by Peter Scott, [[HERE|https://groups.google.com/g/python_inside_maya/c/4sSiJiJiVh4/m/oCbg3D8tAAAJ]].
Thanks Peter!

{{{
import maya.api.OpenMaya as om2
import maya.cmds as cmds

new_time_value = 24.0  # frame to set as context
mTime = om2.MTime()
mTime.value = new_time_value
mDGContext = om2.MDGContext(mTime)

node = 'C_vine_grp'  # Name of Maya node to query
mSelList = om2.MSelectionList()
mSelList.add(node)
mObject2 = mSelList.getDependNode(0)
mFnDepNode2 = om2.MFnDependencyNode(mObject2)

mPlug = mFnDepNode2.findPlug('worldMatrix', False)
mPlugAttr = mFnDepNode2.findPlug('worldMatrix', False).attribute()
mPlug.selectAncestorLogicalIndex(0, mPlugAttr)

mPlugFuture = mPlug.asMObject(mDGContext)
mFnMatrixData = om2.MFnMatrixData(mPlugFuture)

mMatrix = om2.MMatrix(mFnMatrixData.matrix())
mTransformationMatrix = om2.MTransformationMatrix(mMatrix)
transforms = mTransformationMatrix.translation(4)
world_translation = [transforms[0], transforms[1], transforms[2]]

print('World translation of "{}" at frame {} is: {}'.format(node, new_time_value, world_translation))
}}}
I recently ran into the problem of trying to query a 'double array' attr via the API.   In the below example, I create a "doubleArray" attr via commands on a node, then try to query it via the API.  
{{{
import maya.cmds as mc
import maya.OpenMaya as om

node = 'pSphere1'
attr = "doubleArrayTest"

#---------------------------
# Via cmds, add the attr to our node:
mc.addAttr(node, longName=attr, dataType='doubleArray')
mc.setAttr('%s.%s'%(node,attr), (2, 3.14159, 2.782), type="doubleArray")
print mc.getAttr('%s.%s'%(node,attr))
# [2.0, 3.14159, 2.782]
print mc.getAttr("%s.%s"%(node,attr), type=True)
# doubleArray

#-----------------------
# Now, in the API:
selList = om.MSelectionList()
selList.add(node)
mObject = om.MObject()
selList.getDependNode(0, mObject)
plug = om.MFnDependencyNode(mObject).findPlug(attr)
print plug.name()
# pSphere1.doubleArrayTest

# So, the API is getting confused:
print plug.isArray()
# False
print plug.numElements()
# // Error: Encountered exception: (kFailure): Data type is not valid here // 
indexArray = om.MIntArray()
plug.getExistingArrayAttributeIndices(indexArray)
# // Error: Encountered exception: (kFailure): Object does not exist // 

# Let's try something else:
# Get val as an MObject (say whaaa?!)
plugValObj = plug.asMObject()  
# Create a function to act on that object:
fnDoubleArray = om.MFnDoubleArrayData(plugValObj)
# Get just the array data:
doubleArray = fnDoubleArray.array()
print doubleArray
#[2.0, 3.14159, 2.782] 
# Finally!
}}}
----
Docs:
*[[MFnDoubleArrayData|http://docs.autodesk.com/MAYAUL/2014/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_double_array_data.html,topicNumber=cpp_ref_class_m_fn_double_array_data_html]]
*[[MFnDependencyNode|http://docs.autodesk.com/MAYAUL/2014/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_dependency_node.html,topicNumber=cpp_ref_class_m_fn_dependency_node_html]]
*[[MPlug|http://docs.autodesk.com/MAYAUL/2014/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_plug.html,topicNumber=cpp_ref_class_m_plug_html]]
----
Also see:
*[[API: Attribute Creation & Usage]]
*[[API: How can I query and set an attribute?]]
When authoring scripted plugins via the Python API... as it turns out, if you need to make relative imports, certain types will cause errors.
Presuming this lives in a package called {{{myPackage}}}, these will fail:
{{{
from .myModule import someFunction
from . import myModule
}}}
But this will work,
{{{
from myPackage.myModule import someFunction
}}}
If you want to loop over items in the API, the {{{MIt}}} classes help you do that.  High level notes below.
These are all part of the {{{OpenMaya}}} module, unless otherwise noted.
EXAMPLES BELOW
!!!~MSelectionLists (lists of ~MObjects):
* [[MItSelectionList|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_selection_list_html]] : Iterate over the items in the selection list.
!!!All Nodes:
* [[MItDependencyNodes|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_dependency_nodes_html]] : Use the dependency node iterator to traverse all the nodes in Maya's Dependency Graph.
!!!Node Hierarchies:
*[[MItDag|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_dag_html]] : Use the DAG iterator to traverse the DAG (parent/child relationships)
!!!Node Connections:
*[[MItDependencyGraph|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_dependency_graph_html]] : Iterate over Dependency Graph (DG) Nodes or Plugs starting at a specified root Node or Plug.
!!!Animation:
* ~OpenMayaAnim.[[MItKeyframe|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_keyframe_html]] : Iterate over the keyframes of a particular Anim Curve Node, and query and edit the keyframe to which the iterator points.
!!!Components:
*[[MItGeometry|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_geometry_html]] : This class is the iterator class for geometry data, and can be used to loop over the ''~CVs'' of NURBS, the ''points'' of subds & lattices, and the ''vertices'' of polygonal meshes. Generic/higher level iteration over components.
** These aren't necessarily subclasses, but they go into finer detail over components.  Left out all the '~MItSubd' classes, since I really never use them.
** NURBS:
*** [[MItCurveCV|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_curve_c_v_html]] : Iterator class for NURBS curve control vertices (~CVs). The iteration can be for a given curve or for a group of ~CVs.
*** [[MItSurfaceCV|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_surface_c_v_html]] : NURBS surface CV iterator.
** Mesh:
*** [[MItMeshEdge|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_mesh_edge_html]] :  Edge iterator for polygonal surfaces.
*** [[MItMeshFaceVertex|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_mesh_face_vertex_html]] : The iterator for face vertices on polygonal surfaces
*** [[MItMeshPolygon|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_mesh_polygon_html]] : This class is the iterator for polygonal surfaces (meshes).  It iterates over their //faces//.
*** [[MItMeshVertex|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_mesh_vertex_html]] : Class iterator for polygonal vertices.
!!!References/Assemblies:
* [[MItEdits|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_edits_html]] : Use the edits iterator to traverse all the edits on a reference or assembly.
!!!Particles:
*[[MItInstancer|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_it_instancer_html]] : This class  provides methods for iterating over all the dag paths to the shapes created in the scene by the replacement of particles by dag nodes.
!!!Utilities:
* [[MIteratorType|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_iterator_type_html]] : Thisclass is used on iterators where more than one type of filters can be specified. It also provides functionalities to set and get the filter list or individual types of filter. This class should be used in conjunction with DAG/DG/~DependencyNodes iterators for using filter list (list of {{{MFn::Type}}} objects) on them, thus enabling faster traversal throgh iterators.
----
Examples

Iterate over the selected components on some node.  In this example, I'll pick some verts on a couple different mesh.
Based on the selected components. we'll iterate over their parental mesh nodes via a {{{MItSelectionList}}}, and over the components via a {{{MItGeometry}}}.
Note that even though you only pick components (and not the mesh nodes directly), the {{{MItSelectionList}}} auto sorts them into 'categories' by their parental mesh.
Also see: 
* [[API : Understanding component level looping, and MObjects]]
{{{
import maya.OpenMaya as om

# Select some components (verts, cvs) on one or more mesh:
selList = om.MSelectionList() # MSelectionList is a list of MObjects
om.MGlobal.getActiveSelectionList(selList)
iterSelList = om.MItSelectionList(selList)
while not iterSelList.isDone():
    # The node for the selected components:
    nodeDagPath = om.MDagPath()
    # The selected components, if any:
    componentsObject = om.MObject()
    # Define the path to our object and components.
    iterSelList.getDagPath(nodeDagPath, componentsObject)
    print nodeDagPath.fullPathName()
    if not componentsObject.isNull():
        # Make an iterator for the components:
        iterGeo = om.MItGeometry(nodeDagPath, componentsObject)
        while not iterGeo.isDone():
            # Make a point object for the current loop:
            componentPos = iterGeo.position(om.MSpace.kWorld)
            # And print its info:
            print "\tcomponent -index: %s   -pos: %s %s %s"%(iterGeo.index(),
                                                             componentPos.x, componentPos.y, componentPos.z)
            iterGeo.next()
    iterSelList.next()
}}}
Prints:
{{{
|pSphere1|pSphereShape1
	component -index: 261   -pos: 1.66245281696 1.20784258842 1.49297451973
	component -index: 262   -pos: 1.20784258842 1.66245269775 1.49297451973
	component -index: 280   -pos: 1.70814728737 0.555010676384 1.7960511446

|pCube1|pCubeShape1
	component -index: 0   -pos: 2.99528660484 -1.26999998093 1.26999998093
	component -index: 1   -pos: 5.53528656669 -1.26999998093 1.26999998093
	component -index: 2   -pos: 2.99528660484 1.26999998093 1.26999998093
	component -index: 3   -pos: 5.53528656669 1.26999998093 1.26999998093
}}}
Initially pulled a bunch of data from this informative post:
http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/maya/MSelectionList2.html

''VERY INTERESTING'' : If you have multiple components (verts, edges, cvs, etc) selected on multiple nodes (mesh, NURBS), the api calls automatically lump them into 'vert mesh lists'.  Meaning, the first {{{MItSelectionList}}} will loop over each //node// you have picked (via selecting the components), and then the second {{{MItGeometry}}} loops over the selected components on said node.  This is different than say, picking a bunch of verts and listing them via the {{{ls}}} command.
{{{
# Python code
import maya.OpenMaya as om

# First, select a bunch of components on one or more nodes.
# Make a MSelectionList container object:  These are "lists of MObject's"
objectList = om.MSelectionList()
# Fill with selection, as MObjects, enabling 'ordered selection' (if that is important):
om.MGlobal.getActiveSelectionList(objectList, True)
# Create an iterator to loop over our selection:
iterObjectList = om.MItSelectionList(objectList)
while not iterObjectList.isDone():
    # The selected node:
    nodeDagPath = om.MDagPath()
    # The selected components, if any:
    componentsObject = om.MObject()
    # Define the path to our object and components.
    iterObjectList.getDagPath(nodeDagPath, componentsObject)
    print nodeDagPath.fullPathName()
    if not componentsObject.isNull():
        # Make an iterator for them:
        iterGeo = om.MItGeometry(nodeDagPath, componentsObject)
        while not iterGeo.isDone():
            # Make a point object for the current loop:
            componentPos = iterGeo.position(om.MSpace.kWorld)
            # And print its info:
            print "\tcomponent -index: %s   -pos: %s %s %s"%(iterGeo.index(),
                                                             componentPos.x, componentPos.y, componentPos.z)
            iterGeo.next()
    iterObjectList.next()
}}}
Example print.  Regardless what order we picked the components in across multiple objects, the always get lumped together:
{{{
|myNodeA|myNodeAShape
	component -index: 3380   -pos: 37.918586731 28.5343780518 132.385604858
	component -index: 3394   -pos: 35.4213676453 27.519777298 135.702865601
|myNodeB|myNodeBShape
	component -index: 308   -pos: 52.6544418335 -2.37073945999 103.18737793
	component -index: 309   -pos: 52.5206260681 1.12697529793 103.657699585
}}}
----
Also see:
* [[API : Understanding MIterators]]
{{{
# Python code 

import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

def getSkinCluster(shape):
    """
    shape : string : name of shape node to query skincluster on
    return : MFnSkinCluster : MFnSkinCluster node assigned to the mesh, or None.
    """
    # Create an MDagPath for our shape node:
    selList = om.MSelectionList()
    selList.add(shape)
    mDagPath = om.MDagPath()
    selList.getDagPath(0, mDagPath)

    # Make a dependency graph iterator, passing in our MDagPath as the root of the
    # system.  Set the Direction to 'From source to destination' and Level to be
    # 'Visit each Plug at most once'
    mItDependencyGraph = om.MItDependencyGraph(mDagPath.node(),
                                               om.MItDependencyGraph.kDownstream,
                                               om.MItDependencyGraph.kPlugLevel)
    # Start walking through our shape node's dependency graph:
    while not mItDependencyGraph.isDone():
        # Get an MObject for the current item in the graph:
        mObject = mItDependencyGraph.currentItem()
        # If the MObject can have a MFnSkinCluster function applied, then it must
        # be a skincluster:
        if mObject.hasFn(om.MFn.kSkinClusterFilter):
            # return the MFnSkinCluster object for our MObject:
            return oma.MFnSkinCluster(mObject)
        mItDependencyGraph.next()
}}}
{{{
shape = 'someNodesShape'
sCluster = getSkinCluster(shape)
# Print the name of the skincluster:
print sCluster.name()
# skinCluster14
}}}
The above example is based on info I found in this thread:
http://groups.google.com/group/python_inside_maya/browse_thread/thread/1b865f8c3c0c81c2
And did a re-write on.
''~OpenMaya Python'':
{{{
import maya.OpenMaya as om

# Make a MSelectionList container object:
selList = om.MSelectionList()

# Fill our container object with the active selection:
om.MGlobal.getActiveSelectionList(selList)

# Make a list to gather the results:
sel = []

# Fill the result list with strings of the selection:
selList.getSelectionStrings(sel)

print sel
}}}
Man, that is clunky! :-P  Compared to:
''Python'':
{{{
import maya.cmds as mc
sel = mc.ls(selection=True)
print sel
}}}
''MEL'':
{{{
string $sel[] = `ls - selection`;
print $sel;
}}}
When dealing with the API, all linear '//internal units//' are held in cm (unless overridden, but I've never seen this happen).  But if you're passing that data to mel/Python commands, they expect the values to be in whatever '//ui unit//' has been set.  By default, the ui unit is cm as well, but many studios will change this value to something else.  In my case, it's currently inches...
If these two values line up you won't notice any problems in your code.  But if you author code that expects the ui units to be cm and they're not, chaos will ensue.

In the below example, we grab the center-point of the bounding-box of a node, then convert that position into the current ui-units:
{{{
import maya.OpenMaya as om

node = 'pSphere1'

# Get the MDagPath for a node:
selList = om.MSelectionList() # MSelectionList
selList.add(node)
mDagPath = om.MDagPath() # MDagPath
selList.getDagPath(0, mDagPath)

# Find the centerpoint based on bounding box, this will be in cm:
dagNodeFunc = om.MFnDagNode(mDagPath) # MFnDagNode
boundingBox = dagNodeFunc.boundingBox() # MBoundingBox
centerPoint = boundingBox.center() # MPoint

#-----------------------
# Now that we have some data, convert it from internal to ui units:

# Convert from cm to current units:
center = []
unitType = om.MDistance.uiUnit() # Get the current UI unit:
for i in range(3):
    distance = om.MDistance(centerPoint[i]) # MDistance, as cm
    converted = distance.asUnits(unitType) # double, converted
    center.append(converted)

print center
}}}
----
Also see:
*[[API:  How can I find the working units?]]
*[[API : Converting from internal units to ui (and back again)]]
The below classes compare with the mel {{{currentUnit}}} command.

The below classes have the ability to query both the //internal// units, and the //ui// units (except {{{MTime}}}).  The internal units, while they can be changed, usually shouldn't be.  Defaults for internal units are:
*Linear : Centimeters
*Angular : ?  Mine comes up as {{{kInvalid}}}, which is odd.  I'd expect it to be Radians though.
*Time : Has no differentiation between internal\ui:  There is only ui units.  I think when Maya installs this is 24fps (film).
UI units are the ones you can change via Maya's prefs, or via mel.
!!!Linear:
[[OpenMaya.MDistance|http://download.autodesk.com/us/maya/2010help/api/class_m_distance.html]]
{{{
import maya.OpenMaya as om
uiLinearUnit = om.MDistance.uiUnit()
}}}
This returns an int value that corresponds to the {{{Unit}}} enum on the {{{MDistance}}} class.  Which corresponds to these constants:
| 1 | kInches |
| 2 | kFeet |
| 3 | kYards |
| 4 | kMiles |
| 5 | kMillimeters |
| 6 | kCentimeters |
| 7 | kKilometers |
| 8 | kMeters |
!!!Angular:
[[OpenMaya.MAngle|http://download.autodesk.com/us/maya/2010help/API/class_m_angle.html]]
{{{
import maya.OpenMaya as om
uiAngularUnit = om.MAngle.uiUnit()
}}}
This returns an int value that corresponds to the {{{Unit}}} enum on the {{{MAngle}}} class.  Which corresponds to these constants:
| 1 | kInvalid |
| 2 | kRadians |
| 3 | kDegrees |
| 4 | kAngMinutes |
| 5 | kAngSeconds |
| 6 | kLast |
!!!Time:
[[OpenMaya.MTime|http://download.autodesk.com/us/maya/2010help/api/class_m_time.html]]
{{{
import maya.OpenMaya as om
uiTimeUnit = om.MTime.uiUnit()
}}}
This returns an int value that corresponds to the {{{Unit}}} enum on the {{{MTime}}} class. There are a lot of constant values, check the [[docs|http://download.autodesk.com/us/maya/2010help/api/class_m_time.html#ffadecde942c8f44b9ec1ce62aa9da51]] for the specifics.
----
Also see:
*[[API : Converting from internal units to ui (and back again)]]
*[[API:  How can I convert from linear internal units to ui units?]]

(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}}}

Newer docs here:
*[[PySide : Access Qt .ui widget data in Maya]]
*[[PySide : Convert a Maya control into a widget]]
----
Starting with Maya 2011 they introduced a new API class [[MQtUtil|http://download.autodesk.com/global/docs/mayasdk2012/en_us/cpp_ref/class_m_qt_util.html]].
Notes from the docs:
<<<
The safest way to use the Qt API from within Maya is to create your own Qt window and populate it with your own controls.

While it is possible to use the Qt API to modify existing Maya UI elements, such as those created using Maya commands from MEL or Python, such modifications are not supported by Autodesk and could lead to Maya becoming unstable or inoperable.

In practice, you will likely find a number of modifications which appear safe, such as changing a control's text or reorganizing the items on a menu. However, Autodesk provides no guarantees that the underlying implementations of those UI elements won't change from one release of Maya to another, potentially in ways that may make formerly safe usage become unsafe. So if you choose to incorporate such actions into your plug-in be aware that you may have to modify your code in future versions of Maya.
<<<
Here is a very simple example using it:
{{{
import maya.cmds as mc
import maya.OpenMayaUI as omui

# Create the window:
class App(object):
    def __init__(self):
        self.name = "tempWin"
        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))
        self.button = mc.button("awesomeButton", label="this is an awesome button")
        mc.showWindow()
# Display window, capture an instance:
app = App()

butQtClasses = mc.objectTypeUI(app.button, superClasses=True)
qwidget = omui.MQtUtil.findControl(app.button)

print app.button, butQtClasses
print qwidget, type(qwidget)
}}}
prints:
{{{
tempWin|columnLayout6|awesomeButton [u'QPushButton', u'QAbstractButton', u'QWidget', u'QObject']
_b056e53200000000_p_QWidget <type 'PySwigObject'>
}}}
From here, you can see that the Qt widget has been wrapped in a Python 'swig' object.
Notes, notes notes... window data via the API.


----
from {{{maya.OpenMayaUI}}}:
*{{{M3dView}}} : Main class used to access window info.
*{{{MDrawInfo}}} : used in the draw methods of {{{MPxSurfaceShapeUI}}}
**{{{MSelectInfo}}} : used in {{{MPxSurfaceShapeUI::select}}}
----
from {{{maya.OpenMayaMPx}}}
*{{{MPx3dModelView}}}  :  Creates {{{modelEditor}}}s.
*{{{MPxModelEditorCommand}}} :  Creates commands for {{{modelEditors}}}.
*{{{MPxControlCommand}}}
*{{{MPxUIControl}}}
**{{{MPxUITableControl}}}
*{{{MPxSurfaceShapeU}}}
*{{{MPxGlBuffer}}}
----
Query the current camera for the active view,  top left corner of viewport, and viewport width\height.  Similar stuff to what you can do with the window mel command.
{{{
import maya.OpenMayaUI as omui
import maya.OpenMaya as om

camPath = om.MDagPath()
activeView = omui.M3dView.active3dView()
activeView.getCamera(camPath)
camName = camPath.fullPathName()

# c++ pointer hoop-jumping:
xUtil = om.MScriptUtil()
xUtil.createFromInt(0)
xPtr = xUtil.asIntPtr()
yUtil = om.MScriptUtil()
yUtil.createFromInt(0)
yPtr = yUtil.asIntPtr()

activeView.getScreenPosition(xPtr, yPtr)
x = om.MScriptUtil.getInt(xPtr)
y = om.MScriptUtil.getInt(yPtr)
pw = activeView.portWidth()
ph = activeView.portHeight()

print camName, "- Top Left:",  x, y," - width/height:", pw, ph 
# |persp|perspShape - Top Left: 405 45  - width/height: 701 1060
}}}
Mel has the {{{polyColorPerVertex}}} command, that will change the color of one vertex at a time.  This works fine you're doing it on less than 100 verts at a time.  But on more than 1000, it really starts to slow down.

Enter the API.  The {{{MFnMesh}}} class has a {{{setVertexColors}}} method which allows you to set multiple colors at once.  And it is //much// faster.

It also has the {{{assignColor}}} and {{{assignColors}}} method, that works on vertex-faces.  However, it seems super clunky to use (entirely based on color sets).  See example at the bottom of this thread:
https://discourse.techart.online/t/maya-api-vertex-colors/9914/2

Pseudo code to use it:
{{{
import maya.OpenMaya as om

# Build an empty vertex ID array:
idArray = om.MIntArray()
# Now fill it with the vert ID's on the object to color.
# Could be some, or all of them.

# Build an empty color array:
colorArray = om.MColorArray()
# Now fill it with the colors for each of the vert ID's 
# defined above.

# Get a MDagPath for the object we're coloring:
selList = om.MSelectionList()
selList.add(myObject)
mDagPath = om.MDagPath()
selList.getDagPath(0, mDagPath)

# Get a MFnMesh for our object:
mFnMesh = om.MFnMesh(mDagPath)

# Apply the colors:
mFnMesh.setVertexColors(colorArray, idArray)
}}}
It should be noted that since you're using the API to modify the scene graph, there is no undo available.  To get undo, you'd need to author this as a scripted plugin command.
Several subjects below, here's what we have:
#Overview of API ~Attribute-Related Concepts
#Attribute Property Defaults
#Overview of API attribute creation classes
#Creation
#Example Usage
#Differences between 'compound' and 'array'
#Understanding Array Indices (physical & logical)
#Networked and non-networked plugs
#Additional examples
----
''Important note'':  
In Maya, the term 'attribute' can mean two different things based on where it's used: either the 'user side' or the 'programmer side':
*On the user side, they interact with attributes on nodes, querying\setting their values, keyframing them, etc.  The only point of entry is "the attribute".  The attribute 'has a value', for example.  The value and the attribute go hand in hand.
*On the programmer side, an 'attribute' is:
**Quoted from 'Complete Maya Programming': "...a template or blueprint for how a piece of data in the node should be //created//. The important distinction from the user's perspective is that the attribute doesn't actually hold any data.  It simply provides a specification for the data.  Given this specification, the actual data is created.".
**Basically, it's a description for the type of data that can be stored, but that's only part of the equation (one of four main parts).
The next section describes the four areas that are required for data storage\retrieval on a node in the API.
!!!Overview of API ~Attribute-Related Concepts:
Sort of in order of data-flow (presuming you're doing value assignment, rather than query):
*''Plugs'':
**A plug is an instance of an attribute on a node.
**They allow you to get and set data, make connections, lock attributes, etc.
**Following the house analogy from above, a plug is like a gate-keeper:  It can lock the gate, pass data from outside the gate to inside the gate (and vice-versa), and even connect gates between two different houses together.
**They are represented as [[MPlug|http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html]] and [[MPlugArray|http://download.autodesk.com/us/maya/2010help/API/class_m_plug_array.html]] objects.  
**They can be created & queried via [[MFnDependencyNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html]] (& its descendants), queried via [[MAnimUtil|http://download.autodesk.com/us/maya/2010help/API/class_m_anim_util.html]], are passed as an argument to {{{MPxNode.compute()}}}, and additionally manipulated via [[MDgModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_d_g_modifier.html]].
*''Attributes'':
**Attributes in the API define the name and type of data a node can store.  
**Attributes themselves don't store data, they define what type of data can be stored.
**Analogies help:  Picture a house as a representation of a node.  Each attribute is represented as a gate around the house:  Only if a package can 'fit through a certain gate' can it be allowed into the house.  Each gate defines access to one type of 'package', but it itself doesn't store the package.
**API attributes differ from user-interaction in Maya on a node.  When in Maya you execute "{{{setAttr sphere02.translsateX 3;}}}" it feels like you've set the {{{translateX}}} attribute to the value of {{{3}}}.  But behind the scenes, it's not being stored that way:   The {{{translateX}}} attribute is simply the gate through which you've squeezed your numeric package.  Where that value actually lives is discussed below.
**They are represented as [[MObject|http://download.autodesk.com/us/maya/2010help/API/class_m_object.html]] objects, and created\manipulated (at time of creation) via [[MFnAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_attribute.html]] and descendant classes.
*''Data Handles'':
**When a node is computing data (in its {{{compute()}}} method) and needs to access data in its data block, it gains that access through a 'data handle'.
**Data handles allow you to query and set data living in a data block, as defined by a given attribute.
**Data handles can't make connections between attributes or lock them (for example), they only query\set data.  But because of this they can operate much faster than plugs.  In addition, they can query a 'dirty' attribute without forcing a recompute of the DG which can allow for even greater optimization.
**Following the analogy, a data handle is like a butler who can put data in storage (received from the plug), and take data out of storage, passing it to the plug.
**They are represented as [[MDataHandle|http://download.autodesk.com/us/maya/2010help/API/class_m_data_handle.html]] and [[MArrayDataHandle|http://download.autodesk.com/us/maya/2010help/API/class_m_array_data_handle.html]] objects.  They are created via [[MDataBlock|http://download.autodesk.com/us/maya/2010help/API/class_m_data_block.html]]s.
*''Data Blocks'':
**The data block is the location in the node where the attribute values are stored.
**Whenever a new instance of a node is created, a new data block is created for it.
**Using the house analogy, the data block is the store-room of the house where all the packages are stored.  The butler can put stuff in and take it out.
**They are represented as [[MDataBlock|http://download.autodesk.com/us/maya/2010help/API/class_m_data_block.html]] objects, and passed to the {{{MPxNode.compute()}}} method as an argument: That's the only way (I'm aware of) you can access them.
Another way to visualize the analogy:
| Plug | Attribute | Data Handle | Data Block |
| Gate Keeper  | The Gate | Butler | Storeroom |
*Imagine that a node is a house.  
*The gate keeper (plug) receives packages (data), can lock\unlock the gate, let the packages pass through the gate (if they fit) to the butler, magically connect this gate to the gate on another house (so that they share packages), or get packages from the butler.
*The gate itself (attribute) limits what type of packages are allowed into the house (or what type of package is retrieved from it).  If the package doesn't fit, an alarm (error) goes off.
*Presuming the package (data) fits through the gate (attribute), if the gatekeeper wants the package put in the house (or wants to get a packaged stored in the house), he needs to have the butler (data handle) do it for him.
*All packages are kept in the storeroom of the home (data block), which the butler knows a quick path to.
*Finally, the gate keeper can have direct access to the storeroom if he's tricky (but he's slower than the butler), and if you slip the butler a $20 (and you sneak in via {{{compute()}}}) you can access the storeroom directly.
So you could say:
*A package (data) is given to the gatekeeper (plug).  Since it fits through the gate (attribute), the gatekeeper passes it to the butler (data handle).  The butler than puts it in the storeroom (data block).
----
----
----
!!!Attribute Property Defaults
When a new attribute is created, these are the default states of its various properties.  They are modifiable via [[MFnAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_attribute.html]] (attribute superclass).
*Readable.
*Writable.
*Connectable.
*Storable.
*Cached.
*Not arrays.
*Have indices that matter.
*Do not use an array builder.
*Not keyable.
*Not hidden.
*Not used as colors.
*Not indeterminant.
*Set to disconnect behavior kNothing.
!!!Overview of API attribute creation classes:
All of these inherit from [[MFnBase|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_base.html]]
*[[MFnAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_attribute.html]] : Used for changing the attribute defaults listed above (among others).
**[[MFnCompoundAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_compound_attribute.html]] : For the creation of 'compound' attributes:  Attributes that can store multiple different data types.
**[[MFnEnumAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_enum_attribute.html]]  :  For the creation of 'enum' attribute types.
**[[MFnGenericAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_generic_attribute.html]]  :  Creation of attrs that can accept specific data types as defined by [[MFnData|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_data.html]].  Similar but different from {{{MFnTypedAttribute}}}, see docs [[here|http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=files/Technical_Notes_Generic_Attributes.htm,topicNumber=d28e16031]]
**[[MFnLightDataAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_light_data_attribute.html]] : For the creation of 'light data' attribute types.
**[[MFnMatrixAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_matrix_attribute.html]]  :  For the creation of 'matrix' attribute types.
**[[MFnMessageAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_message_attribute.html]]  :  For the creation of 'message' attribute types.
**[[MFnNumricAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_numeric_attribute.html]]  :  For all 'numric' attribute types as defined by [[MFnNumricData|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_numeric_data.html]].  These include boolean, byte, character, short 2short, 3short, int, 2int, 3int, long, 2long, 3long, float, 2float, 3float, double, 2double, 3double, 4double, and finally 'color' and 'point'.
**[[MFnTypedAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_typed_attribute.html]]  :  Creation of attrs that can accept specific data types as defined by [[MFnData|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_data.html]].  These include: numeric (based on [[MFnNumricData|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_numeric_data.html]]), plugin, pluginGeometry, string, matrix, stringArray, doubleArray, intArray, pointArray, vectorArray, componentList, mesh, lattice, nurbsCurve, nurbsSurface, sphere, dynArrayAttrs, dynSweptGeometry, subdSurface, nObject.
**[[MFnUnitAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_unit_attribute.html]]  :  For creation of attributes that store angle, distance, and time.
[img[http://download.autodesk.com/us/maya/2010help/API/class_m_fn_attribute__inherit__graph.gif]]
!!!Creation:
Placeholders for the node's attributes are initially added as a class-attribute for easy access later.  At this point, then can be filled with {{{MObject}}}s (since that's what they'll be ultimately), or just be filled with {{{None}}} since at this point they're not used.
{{{
import maya.OpenMaya as ompx
class Foo(ompx.MPxNode):
    attr_someInput = om.MObject() # or None
    attr_someOutput = om.MObject() # or None
}}}
They are actually created and added to the node inside the plugin's 'initializer' classmethod (or external function.  We'll use classmethod below).  For each attr, they go through one or more of these steps:
#Creation : An attribute is created.  You can imagine it just floating in space...
#Modification :  After creation, you configure how it should behave.
#Attach to node  :  Take it from floating in space, and actually attach it to a specific node.
#Defining affects relationship  :  Set if it affects any other attribute.  ''Special note'':  This step must occur after any affected attributes have already been attached to the node (via {{{MPxNode.addAttribute()}}}).
{{{
    @classmethod
    def nodeInitializer(cls):
        # Create the function object that will create the attributes:
        mfnNumericAttribute = om.MFnNumericAttribute()

        # 1. Creation:
        cls.attr_someInput = mfnNumericAttribute.create("someInput", "si", om.MFnNumericData.kFloat)
        # 2. Modification:
        mfnNumericAttribute.setChannelBox(True) # default is False
        mfnNumericAttribute.setKeyable(True)  # default is False
        # 3. Attach to node:
        cls.addAttribute(cls.attr_someInput)

        # 1. Creation:
        cls.attr_someOutput = mfnNumericAttribute.create("someOutput", "so", om.MFnNumericData.kFloat)
        # 2. Modification:
        mfnNumericAttribute.setWritable(False) # Default is True.  This is an output, can't write to it.
        mfnNumericAttribute.setStorable(False) # Default is True.  Do not save value with scene, since it's an output.
        # 3. Attach to node:
        cls.addAttribute(cls.attr_someOutput)

        # 4. Affects relationships for "someInput" attr.  This needs to be executed after
        #     the "someOutput" attr was added to the node.
        cls.attributeAffects(cls.attr_someInput, cls.attr_someOutput)
        # 4. Affects relationships for "someOutput":
        # None, this is an output
}}}
----
Another attribute type to make is a numric type with children.  A transforms {{{.translate}}} attribute illustrates this:  There is a root 'translate' attribute you can set, connect to, etc.  But in addition, you can set\connect to its individual children as well:
{{{
translate : float 3
--> translateX : float
--> translateY : float
--> translateZ : float
}}}
How can you set this up in the API?  You first create each of the child attributes, and then, when creating the parent, you pass them as a creation argument.  Here's a simple example snippet:
{{{
    @classmethod
    def nodeInitializer(cls):
        mfnNumericAttribute = om.MFnNumericAttribute()

        # Create the children attrs:
        cls.attr_transX = mfnNumericAttribute.create("translateX", "tx", om.MFnNumericData.kFloat)
        cls.attr_transY = mfnNumericAttribute.create("translateY", "ty", om.MFnNumericData.kFloat)
        cls.attr_transZ = mfnNumericAttribute.create("translateZ", "tz", om.MFnNumericData.kFloat)
        # Now create the parent attr by passing in the children:
        cls.attr_trans = mfnNumericAttribute.create("translate", "t", cls.attr_transX, cls.attr_transY, cls.attr_transZ)
        mfnNumericAttribute.setChannelBox(True)
        mfnNumericAttribute.setKeyable(True)
        cls.addAttribute(cls.attr_trans)
}}}
Some things to note:  
*The {{{MFnAttribute}}} docs say "This form of the create method allows the creation of compound attributes out of numeric attributes."    So, even though we build using a {{{MFnAttribute}}}, it ends up being {{{MFnCompoundAttribute}}}? (I've yet to figure out what's going on here).
**When making compound attrs, their children all need to be added to to the class via {{{cls.addAttribute()}}}. However, in the examples I've seen, when making 'child array attrs', they //don't// need to be added that way: only their parent does (like in the above example).
*You modify the parents parameters ({{{setChannelBox}}}, etc) not the children, since the children inherit the parameters.
*By passing in three {{{kFloat}}} children, it implies that the {{{someInput}}} attr is now a {{{k3float}}}.
If you didn't care about the child attributes, you could simply create the attribute this way:
{{{
    @classmethod
    def nodeInitializer(cls):
        mfnNumericAttribute = om.MFnNumericAttribute()

        cls.attr_trans = mfnNumericAttribute.create("translate", "t", om.MFnNumericData.k3Float)
        mfnNumericAttribute.setChannelBox(True) 
        mfnNumericAttribute.setKeyable(True)
        cls.addAttribute(cls.attr_trans)
}}}
So you can still manipulate {{{translate}}} as you're used to, but you no longer gain gain access to the children, like {{{translateX}}}.
!!!Example Usage:
The attrs are accessed inside the {{{compute()}}} method.  Take note of a few things:
#First there is a query if the given input plug is an instance of the attribute that needs computing.
#Next, a data handle based on that instanced attribute is generated pointing inside of the data block.
#Work is done on the data.
#Finally, another data handle is created pointing to the data defined by the instanced output attribute, the output data is updated, and the input plug is set to clean.
{{{
    def compute(self, plug, dataBlock):
        """
        This is an overridden method of the MPxNode class.  Does the "work" the node is to perform.

        Parameters:
        plug : MPlug : http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html
        dataBlock : MDataBlock : http://download.autodesk.com/us/maya/2010help/API/class_m_data_block.html
        """
        # Check that the requested recompute is our output value
        if (plug == Foo.attr_someOutput):
            # Read the input values, returns a MDataHandle, which is  a smart pointer
            # back into the MDataBlock (dataBlock)
            # http://download.autodesk.com/us/maya/2010help/API/class_m_data_handle.html
            dataHandle = dataBlock.inputValue(Foo.attr_someInput)

            # Compute the output values.  In this case, it is a simple mult operation:
            outVal = dataHandle.asFloat() * 10

            # Get a handle to the output value and store the new value.
            handle = dataBlock.outputValue(Foo.attr_someOutput)
            handle.setFloat(outVal)

            # From the docs:  Tells the dependency graph that the given attribute
            # has been updated and is now clean. This should be called after the
            # data in the plug has been recalculated from the inputs of the node.
            dataBlock.setClean(plug)
        else:
            # c++ requires this return, it seems that the Python API doesn't
            # based on my experiments.  Doesn't hurt to leave it though.
            return om.MStatus.kUnknownParameter
}}}
!!!Differences between 'compound' and 'array'
*''compound'' is an *attribute type*, like float, string, double etc.  You can't have a "compound float" attribute in the same way you can't have a "string double".  But you can have a compound attr that has child string, double, and float attributes.  Compound attributes themselves store no values:  They are simply parents of other attribute types.  
**Compound attributes are considered 'parents', while they contain 'child' attributes.
**The child of a compound attribute can be any other attribute type, including another compound attribute, which has children (which could be compound attributes, etc).  Examples of this are the mesh node's {{{colorPerVertex}}} compound attribute.  It has a child compound attribute {{{vertexColor}}}, which has a child compound attribute {{{vertexFaceColor}}}.
*''array'' (also known as 'multi' when being created via mel, I think...) is a *property of an attribute*, like being readable, writable, keyable, etc. In theory any attribute type can be made array, *including compound attrs*.  
**An example of this is a mesh node's {{{uvSet}}} attr: It is a compound attr, but is //also an array//, which gets confusing fast when trying to access it and its children.  
**Array attributes contain multiple 'element attributes', one for each index in the array.  These in turn can be any type, including compound, which in turn could also be array, etc. 
**Array elements are not 'children attributes', since only compound attrs have official 'children'.  Array attributes are a single attr, that holds multiple values in a list.
!!!Understanding Array Indices (physical & logical)
Given an attribute that is also array, you can reference its elements two different ways, via the "physical indices" and the "logical indices".
*''Physical index'' : Range from 0 -> {{{numElements()}}}-1.  Given four plugs for elements A, B, C, and D, A is index 0, B1, C2, D3.  If plug B was deleted, now: A0, C1, D2:  As you can see, the physical index for a given plug can change based on creation and deletion.
*''Logical index'' : For a given element, this is an assigned\fixed\absolute index that will never change regardless of creation or deletion of other indices.  You can use the plugs {{{getExistingArrayAttributeIndices()}}} method to populate a {{{MIntArray}}} with the existing indices.
**Referring to an array element in MEL uses the logical index.  MEL can't get the physical index.
**Connections between attributes are based on their *logical indices*.  This is needed since the logical indices don't change (and you don't want connections to change after they've been made...).
You can query these indices via {{{MPlug}}}.  Below is some example code and results showing the differences between them.  When calling to {{{elementByLogicalIndex()}}}, if the given index doesn't yet exist (it is a 'sparse array'), it is created and populated with a default value for that attribute.
{{{
plg = myPlg.elementByLogicalIndex(2)
plg.setDouble(11)
plg = myPlg.elementByLogicalIndex(10)
plg.setDouble(27)
plg = myPlg.elementByLogicalIndex(0)
plg.setDouble(100)
}}}
This would be the result of the physical \ logical indices:
{{{
Physical 0 \ Logical 0  : 100
Physical 1 \ Logical 2  : 11
Physical 2 \ Logical 10 : 27
}}}
!!!Networked and non-networked plugs
I'll be honest, I really don't understand the ins and outs of these yet.  But some notes:
*''non-networked plug'' : user created plugs used to establish new connections to an attr, get or set a value on an attr.  When one of these is used, a networked version of the plug is created and added to the dependency node network. A non-networked plug contains an array of array indices that plot the path from the root plug to this plug.
*''networked plug'' : Dependency node plugs, can't be explicitely created: only referenced by users.  They also describe the 'tree' of plugs indicating connections made to the attributes of the node.
!!!Additional Examples
*[[API: Simple scripted plugin node]] : A working overview of a bunch of what the above covers.
*[[API: Find all child attributes under a compound attribute]]
*[[API: Find all attributes & plugs on a node]]
Just some early notes...  See a working example here:
[[API: Simple scripted plugin argument passing]]

Docs:
*[[MSyntax|http://download.autodesk.com/us/maya/2010help/API/class_m_syntax.html]]
*[[MArgParser|http://download.autodesk.com/us/maya/2010help/API/class_m_arg_parser.html]]
**[[MArgDatabase|http://download.autodesk.com/us/maya/2010help/API/class_m_arg_database.html]]
*[[MArgList|http://download.autodesk.com/us/maya/2010help/API/class_m_arg_list.html]]
----
A major issue I've ran into is the (apparent) lack of ability to pass array data into an argument.  A thread here discusses it pretty well:
http://forums.cgsociety.org/archive/index.php/t-701733.html
It appears that the main hack is to pass in a single string you can then later split into the array items of your choice, which is //terrible//...  From that form post: ''"MEL commands //don't// have the ability to accept a string array as a single flag argument."'', which seems to be true based on experimentation.
----
When authoring a scripted plugin command to accept arguments, you do this by by authoring a 'syntax' function that is passed to the {{{OpenMayaMPx.MFnPlugin.registerCommand()}}} method inside the {{{initializePlugin()}}} function.  The syntax function can look something like this:
{{{
import maya.OpenMaya as om
def newSyntax():
    syntax = om.MSyntax()
    # Start adding flags and whatnot here to pass your arguments to.
}}}
And the snippet for registering the command, which includes passing of the syntax ({{{cmdName}}} & {{{cmdCreator}}} being authored elsewhere):
{{{
import maya.OpenMayaMPx as ompx
def initializePlugin(mobject):
    mfnPlugin = ompx.MFnPlugin(mobject)
    mfnPlugin.registerCommand(cmdName, cmdCreator, newSyntax)
}}}
You can then capture the argument data in the {{{doIt()}}} method of the scripted plugin:
{{{
import maya.OpenMayaMPx as ompx
class MyCmd(ompx.MPxCommand):
    def __init__(self):
        ompx.MPxCommand.__init__(self)

    def doIt(self, argList):
        argData = om.MArgDatabase(newSyntax(), argList)
}}}
The {{{argList}}} parameter recieves an {{{MArgList}}} argument.  You can then pass this arg into a {{{MArgDatabase}}} object, which is a subclass of {{{MArgParser}}}, or start accessing data in the {{{MArgList}}} directly.
Scratch-pad as I become more familiar with the various API classes.  Also see:
[[API: class organization]]
----
Starting with Maya 2013, they offered a [[API Class Taxonomy|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/_maya.html,topicNumber=cpp_ref__maya_html]] online, which is all classes arranged according to subject/concept.
----
This is mainly a place where I can note classes I've used, there are obviously more than what is listed here.
!M
Notes:
*Maya's base utility classes.
!!![[M3dView|http://download.autodesk.com/us/maya/2010help/API/class_m3d_view.html]]
"provides methods for working with 3D model views. 3D views are based on OpenGL drawing areas"
!!![[MAnimControl|http://download.autodesk.com/us/maya/2010help/API/class_m_anim_control.html]]
"Control over animation playback and values"
!!![[MAnimUtil|http://download.autodesk.com/us/maya/2010help/API/class_m_anim_util.html]]
"...a static class which provides methods which determine if an object is being animated, which attributes are animated for a given object and which animation curves are used to animate a given attribute."
*Methods include {{{isAnimated}}}, {{{findAnimatedPlugs}}}, {{{findAnimation}}}
!!![[MBoundingBox|http://download.autodesk.com/us/maya/2010help/API/class_m_bounding_box.html]]
"Implementation of a 3D bounding box"
!!![[MColor|http://download.autodesk.com/us/maya/2010help/API/class_m_color.html]]
"... used to store values of color attribute."
!!![[MDagPath|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_path.html]]
"Provides methods for obtaining one or all Paths to a specified DAG Node..."
*{{{node()}}} : returns an {{{MObject}}}
*{{{fullPathName()}}} : Returns string of the full path to the node.
*{{{isInstanced()}}} : Returns True\False.
Use [[MFnDagNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dag_node.html]] function set to operate on this type.
See:
*[[API: MObject and MDagPath]]
!!![[MDGModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_d_g_modifier.html]]
"...is used to change the structure of the //dependency graph// (DG). This includes adding nodes, making new connections, and removing existing connections."
*Only usable in scripted plugins.  If you call to API commands and want to be able to undo them, you need execute those operations through this class.  See this example [[API: undoing commands]].
*Used for node creation, deletion, attribute connection, disconnection, attribute addition, attribute removal, node renaming, mel execution, etc.
>''[[MDagModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_modifier.html]]''
>Inherits from {{{MDGModifier}}}
>"...used to change the structure of the DAG (subset of the DG, focusing on transforms\shapes). This includes adding nodes, making new connections, and removing existing connections.
!!![[MDistance|http://download.autodesk.com/us/maya/2010help/API/class_m_distance.html]]
"... provides a fundamental type for the Maya API to hold and manipulate linear data. All API methods that require or return distance information do so through variables of this type."
*Great for converting from ui units to system units and vice-versa.
!!![[MEulerRotation|http://download.autodesk.com/us/maya/2010help/API/class_m_euler_rotation.html]]
"This class provides methods for working with euler angle rotations."
!!![[MFileIO|http://download.autodesk.com/us/maya/2010help/API/class_m_file_i_o.html]]
"Methods for opening, saving, importing, exporting, and referencing files."
Also gives query methods for many data items as well (like the scene name, for example).
*Closely mirrors functionality found in the mel {{{file}}} command.
!!![[MGLFunctionTable|http://download.autodesk.com/us/maya/2010help/API/class_m_g_l_function_table.html]] 
"...is a utility class which provides wrappers for the basic functions in the OpenGL API"
*An instance of this class can be obtained from [[MHardwareRenderer.glFunctionTable()|  http://download.autodesk.com/us/maya/2010help/API/class_m_hardware_renderer.html]]
!!![[MGlobal|http://download.autodesk.com/us/maya/2010help/API/class_m_global.html]]
"Provide methods for selection, 3D-views, time, model manipulation and MEL commands."
Many, many usable methods in here.
*{{{deleteNode(MObject)}}} : Delete the given node.
*{{{executeCommand(...)}}} :  Has a variety of parameter signatures, executes a mel command.
*{{{getActiveSelectionList(MSelectionList)}}} : Fill a {{{MSelectionList}}} with what is currently selected ({{{MObject}}}s).
*{{{getFunctionSetList	(MObject, MStringArray)}}} : Get a list of strings representing the function sets that will accept this object
*{{{select(MObject)}}} : Put the given object on the active selection list.
*{{{select(MDagPath, MObject) : Put the given object ({{{MDagPath}}}) and components ({{{MObject}}}) on the active selection list.
*{{{selectByName(string)}}} : Puts objects that match the give name on the active selection list, can use regular expressions.
*{{{setOptionVarValue(string, int)}}} : Set an optionVar with an int value.  More similar commands for setting other data types.
*{{{sourceFile(string)}}} :  Sources a mel script.
*{{{viewFrame(double)}}} : Sets the current frame.
!!![[MImage|http://download.autodesk.com/us/maya/2010help/API/class_m_image.html]]
"...provides access to some of Maya's image manipulation functionality."
!!![[MImageFileInfo|http://download.autodesk.com/us/maya/2010help/API/class_m_image_file_info.html]]
"...used to describe the characteristics of an image file, such as dimensions, channel count, and pixel format."
!!![[MObject|http://download.autodesk.com/us/maya/2010help/API/class_m_object.html]]
"The generic class for accessing all Maya internal modelling, animation and rendering Objects, collectively referred to as Model Objects, through the API. This includes all Dependency Graph (DG) Nodes, of which Directed Acyclic Graph (DAG) Nodes are a subset."
*{{{isNull()}}} : Returns True\False on whether it is a valid {{{MObject}}} or not.
*{{{hasFn(MFn.kSomeConstant)}}} : Query to determine if the {{{MObject}}} is comaptible with a given Function Set.function.
Use [[MFnDependencyNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html]] function set to operate on this type.
See:
*[[API: MObject and MDagPath]]
!!![[MMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix.html]]
"A matrix math class for 4x4 matrices of doubles."
!!![[MPlug|http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html]]
"A plug is a point on a dependency node where a particular attribute can be connected."
*Similar to the mel commands {{{connectionInfo}}}, {{{isConnected}}}, {{{getAttr}}}, {{{setAttr}}} 
!!![[MPoint|http://download.autodesk.com/us/maya/2010help/API/class_m_point.html]]
"This class provides an implementation of a point. Numerous convienence operators are provided to help with the manipulation of points. This includes operators that work with the {{{MVector}}} and {{{MMatrix}}} classes."
!!![[MProgressWindow|http://download.autodesk.com/us/maya/2010help/API/class_m_progress_window.html]]
"...manages a window containing a status message, a graphical progress gauge, and optionally a "Hit ESC to Cancel" label for interruptable operations."
*This is like the mel {{{progressWindow}}} command.
!!![[MQuaternion|http://download.autodesk.com/us/maya/2010help/API/class_m_quaternion.html]]
"...provides methods for working with Quaternions"
!!![[MScriptUtil|http://download.autodesk.com/us/maya/2010help/API/class_m_script_util.html]]
"Utility class for working with pointers to basic types such as int, float and arrays of these types."
*Used heavily by Python scripting in the API to make it jive with c++ syntax.
*Some good notes [[HERE|http://www.chadvernon.com/blog/resources/maya-api-programming/mscriptutil/]].
!!![[MSelectionList|http://download.autodesk.com/us/maya/2010help/API/class_m_selection_list.html]]
"A list of {{{MObject}}}s."
*{{{add()}}} :  Accepts strings (using wildcards), {{{MObject}}}, {{{MDagPath}}}, and {{{MPlug}}}.
*{{{clear()}}} : Empty the selection list.
*{{{getDagPath(int, MDagPath, MObject)}}} :  For the given index, get the dag path {{{MDagPath}}} and component {{{MObject}}} (if present).
*{{{getDependNode(int, MObject)}}} : Get the {{{MObject}}} at the given index.
*{{{getPlug	(int, MPlug)}}} : Get the plug (attribute) at the given index).
*{{{getSelectionStrings(MStringArray)}}} : Gets the string representations of the items in the selection list.
*{{{getSelectionStrings(int, MStringArray)}}} :  Gets the string representations of the items in the selection list at the given index.
*{{{length()}}} Returns the length of the list.
!!![[MTime|http://download.autodesk.com/us/maya/2010help/API/class_m_time.html]]
"Set and retrieve animation time values in various unit systems."
!!![[MTransformationMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_transformation_matrix.html]]
"...allows the manipulation of the individual transformation components (eg scale, rotation, shear, etc) of a four by four transformation matrix."
!!![[MVector|http://download.autodesk.com/us/maya/2010help/API/class_m_vector.html]]
"A vector math class for vectors of doubles."

!~MFn
Notes:
*Wrappers to make accessing common object easier. 'Fn' = 'Function'. They wrapper "functionality". Any class with this prefix is a function set used to operate on {{{MObject}}}s of a particular type.
*All {{{MFn}}} classes (except for {{{MFn}}} itself) inherit from [[MFnBase|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_base.html]].  The below hierarchy illustrates the class inheritance tree.
*The //mel// command {{{nodeType}}}, via it's {{{apiType}}} flag can be used to query the {{{MFn}}} type of a DAG node, which can then be used to find what type of function set to use.
*Bit of craziness:  Since Maya stores the Python API in different packages (see [[API: class organization]]), it's possible that this inheritance can span multiple packages.  For example, this is the inheritance tree for {{{MFnSkinCluster}}}, which as you'll see inherits from two different packages; {{{OpenMaya}}} and {{{OpenMayaAnim}}}:
**{{{OpenMaya.MFnBase}}}  (superclass)
***{{{OpenMaya.MFnDependencyNode}}}
****{{{OpenMayaAnim.MFnGeometryFilter}}}
*****{{{OpenMayaAnim.MFnSkinCluster}}} (subclass)
!!![[MFn|http://download.autodesk.com/us/maya/2010help/API/class_m_fn.html]]
"{{{MFn}}} encapsulates all API Function Set type identifiers used for RTTI in the API."
Meaning, the {{{MFn}}} class holds the constants ({{{MFn.k*}}}) that can be tested against to determine what type function objects can be attached to an {{{MObject}}}.  Used with {{{MObject.hasFn()}}}
!!![[MFnBase|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_base.html]]
It has two useful methods:
*{{{setObject(MObject)}}} that allows for an {{{MObject}}} to be assigned to the function set after it was created.
*{{{object()}}}, which returns the {{{MObject}}} assigned to the function set.
----
>[[MFnAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_attribute.html]]
>"Dependency node attribute function set."
----
>[[MFnComponent|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_component.html]]
>"This is the base class for all function sets which deal with component objects.  Components are [[MObjects|API: MObject and MDagPath]] which hold index information for shapes.   Examples of these types are mesh vertices (single indexed), nurbs surface CVs (double indexed), and lattice points (triple indexed)."
----
>>[[MFnSingleIndexedComponent|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_single_indexed_component.html]]
>>[[MFnDoubleIndexedComponent |http://download.autodesk.com/us/maya/2010help/API/class_m_fn_double_indexed_component.html]]
>>[[MFnTripleIndexedComponent |http://download.autodesk.com/us/maya/2010help/API/class_m_fn_triple_indexed_component.html]]
----
>[[MFnDependencyNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html]]
>"...allows the creation and manipulation of dependency graph nodes ({{{MObject}}}). Traversal of the dependency graph is possible using the getConnections method."
>* {{{MFnDependencyNode()}}} : Creation, no passed in object.
>* {{{MFnDependencyNode(MObject)}}} :  Creation, passing in an {{{MObject}}}.
>* {{{create(string)}}} : (has other signatures, but this one is easiest) : ''Creates a new dependency node with the given type''. The new node is placed into the dependency graph.
>* Many methods for node creation, getting connections, name setting, plug (attr) finding, attribute addition and removal, locking & unlocking, query if referenced, query parent namepace, query if name is unique, query if made via plug-in, and more...
----
>>[[MFnAnimCurve|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_anim_curve.html]]
>>"Create, query and edit Anim Curve Nodes and the keys internal to those Nodes."
>>*Many, many methods for interacting with animCurves.
----
>>[[MFnExpression|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_expression.html]]
>>"...used to create, edit, and query expression nodes"
----
>>[[MFnGeometryFilter|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_geometry_filter.html]]
>>"...the function set for geometry filters, the node that is the base class for deformers. Geometry filter nodes include ''clusters, ffds, nonlinears, user-defined deformers, sculpts, wires and blendShapes''. The purpose of the geometry filter is to connect to the geometry that is deformed. The geometry filter is independent of any algorithm that calculates the deformation."
----
>>>[[MFnSkinCluster|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_skin_cluster.html]]
>>>"...the function set for skinClusters"
----
>>[[MFnDagNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dag_node.html]]
>>"Provides methods for attaching Function Sets to, querying, and adding children to DAG Nodes ({{{MDagPath}}}). Particularly useful when used in conjunction with the DAG Iterator class ({{{MItDag}}})."
>>* {{{MFnDagNode()}}} : Creation with no dag path. 
>>* {{{MFnDagNode(MDagPath)}}} : Creation based on a dag path.
>>*Many methods for querying parents, querying children, duplication, node creation, parenting, unparenting, etc.
----
>>>[[MFnMesh|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_mesh.html]]
>>>"...provides access to polygonal meshes"
----
>>>[[MFnTransform|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_transform.html]]
>>>"... provides access to transformation DAG nodes called transforms."
>>>* Creation, transformation, value query, etc.
----
>>>>[[MFnIkHandle|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_ik_handle.html]]
>>>>"This is the function set for inverse kinematics (IK) handles. An ik handle specifies the joints in a skeleton that are effected by an attached ik solver."
----
>>>>[[MFnIkJoint|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_ik_joint.html]]
>>>>"...function set for joints."
----
>[[MFnData|http://download.autodesk.com/us/maya/2010help/api/class_m_fn_data.html]]
>* "... is the parent class for all dependency graph data function sets. Conceptually, data objects are what flow through the connections in the dependency graph.  Each node in the dependency graph has a data block associated with it. The data block holds the data objects for all of the node's attributes (see {{{MDataBlock}}}). The data block is only available during the compute method of a node. A data handle ({{{MDataHandle}}}) can be created to access a particular attribute's data inside of the data block."
----
>>[[MFnMatrixData|http://download.autodesk.com/us/maya/2010help/api/class_m_fn_matrix_data.html]]
>>*"...allows the creation and manipulation of {{{MMatrix}}} data objects for use in the dependency graph."
>>*This can be used //within user-created// nodes to access matrix data.

!~MIt
Notes:
*Used for iterating though data (for looping through things). 'It' = 'Iterator'. They wrapper "iteration". These classes are iterators and work on {{{MObject}}}s similar to the way a function set ({{{MFn}}} classes) does.
!!![[MItDag|http://download.autodesk.com/us/maya/2010help/API/class_m_it_dag.html]]
"Use the DAG iterator to traverse the DAG either depth first or breadth first, visiting each node and, if desired, retrieving the node (as an {{{MObject}}})."
See:
*[[API: OpenMaya.MItDag]]
!!![[MItDependencyGraph|http://download.autodesk.com/us/maya/2010help/API/class_m_it_dependency_graph.html]]
"Iterate over Dependency Graph (DG) Nodes or Plugs starting at a specified root Node or Plug."
See:
*[[API: OpenMaya.MItDependencyGraph]]
!!![[MItDependencyNodes|http://download.autodesk.com/us/maya/2010help/API/class_m_it_dependency_nodes.html]]
"Use the dependency node iterator to traverse all the nodes in Maya's Dependency Graph."
See:
*[[API: OpenMaya.MItDependencyNodes]]
!!![[MItGeometry|http://download.autodesk.com/us/maya/2010help/API/class_m_it_geometry.html]]
"... is the iterator class for geometry data, and can be used to loop over the ~CVs of NURBS, the points of subds & lattices, and the vertices of polygonal meshes."
*{{{MItGeometry(MDagPath, MObject)}}} : Create a new object, passing in the path to the active node ({{{MDagPath}}}) and an {{{MObject}}} of the components to iterate over.
*{{{index()}}} : Get the index of the current component.
*{{{position(OpenMaya.MSpace.kWorld)}}} : Get the position of the current component as a {{{MPoint}}}.
*{{{setPosition(MPoing)}}} : Set the position of the current point.
!!![[MitMeshEdge|http://download.autodesk.com/us/maya/2010help/API/class_m_it_mesh_edge.html]]
"...is the edge iterator for polygonal surfaces"
!!![[MItMeshFaceVertex|http://download.autodesk.com/us/maya/2010help/API/class_m_it_mesh_face_vertex.html]]
"...is the iterator for face vertices on polygonal surfaces."
!!![[MItMeshPolygon|http://download.autodesk.com/us/maya/2010help/API/class_m_it_mesh_polygon.html]]
"... is the iterator for polygonal surfaces (meshes)."
*Good for querying or setting all verts on a mesh at once (for example)
!!![[MItMeshVertex|http://download.autodesk.com/us/maya/2010help/API/class_m_it_mesh_vertex.html]]
"...is the iterator for polygonal vertices."
!!![[MItSelectionList|http://download.autodesk.com/us/maya/2010help/API/class_m_it_selection_list.html]]
"Iterate over the items in the selection list."
*{{{MItSelectionList(MSelectionList)}}} :  Creation, based on a pre-existing {{{MSelectionList}}}
*{{{getDagPath(MDagPath, MObject)}}} :  Get the path to the current object ({{{MDagPath}}}) and a {{{MObject}}} representing any selected components.
!!![[MItKeyframe|http://download.autodesk.com/us/maya/2010help/API/class_m_it_keyframe.html]]
"Iterate over the keyframes of a particular Anim Curve Node..."

!~MPx
Notes:
*Used for creating custom plugins. 'Px' = 'Proxy'. They are API classes designed for you to derive from and create your own object types.
!!![[MPxCommand|http://download.autodesk.com/us/maya/2010help/API/class_m_px_command.html]]
"This is the proxy class for creating MEL commands through the API."
!!![[MPxNode|http://download.autodesk.com/us/maya/2010help/API/class_m_px_node.html]]
"Base class for user defined dependency nodes."
Nice simple example copied from here:
https://forums.autodesk.com/t5/maya-programming/create-mesh-from-list/td-p/7575371
{{{
# using Python API 2.0 for Maya
import maya.api.OpenMaya as om2

meshFn = om2.MFnMesh()

# list of vertex points
vertices = [om2.MPoint(-0.5, -0.5, 0.5),
            om2.MPoint(0.5, -0.5, 0.5), 
            om2.MPoint(0.5, 0.5, 0.5), 
            om2.MPoint(-0.5, 0.5, 0.5), 
            om2.MPoint(-0.5, 0.5, -0.5), 
            om2.MPoint(0.5, 0.5, -0.5), 
            om2.MPoint(0.5, -0.5, -0.5),
            om2.MPoint(-0.5, -0.5, -0.5)]

# list of number of vertices per polygon
# A cube has 6 faces of 4 vertices each
polygonFaces = [4] * 6

# list of vertex indices that make the 
# the polygons in our mesh
polygonConnects = [0,1,2,3,1,6,5,2,7,6,5,4,3,2,5,4,3,0,7,4,0,1,6,7]

# create the mesh
meshFn.create(vertices, polygonFaces, polygonConnects )
}}}
If you've authored a custom scripted-plugin [[MPxNode|http://download.autodesk.com/us/maya/2010help/API/class_m_px_node.html]] (or a derived class, like [[MPxLocatorNode|http://download.autodesk.com/us/maya/2010help/API/class_m_px_locator_node.html]]), via ~OpenGL you can draw text to the screen that follows it.  Below is a code snippet showing parts of the class that you can use to implement this functionality.

I pulled the concepts of this from [[this example|http://download.autodesk.com/us/maya/2010help/API/foot_print_manip_8py-example.html]]

I should note that I've encountered a bug where the text seems to translate 1.5x greater than that of the node... and I have yet to find a solution :(
{{{
import maya.OpenMaya as om
import maya.OpenMayaUI as omui
import maya.OpenMayaMPx as ompx

class Foo(ompx.MPxLocatorNode):

    def __init__(self):
        """
        Superclass override.
        """
        ompx.MPxLocatorNode.__init__(self)
        # Save an empty MDagPath that will later be the path to our node.
        self.mDagPath = om.MDagPath()

    def draw(self, view, path, style, status):
        """
        Superclass override, where the text drawing happens.
        """
        view.beginGL()  # M3dView
        view.drawText("TestText!", self.nodeTranslation(), omui.M3dView.kLeft)
        view.endGL()

    def nodeTranslation(self):
        """
        Custom method that returns a MPoint object based on the worldspace
        position of our locator node.
        """
        # Get the dag path to this object:
        om.MDagPath.getAPathTo(self.thisMObject(), self.mDagPath)
        # Pop from the shape to the transform
        self.mDagPath.pop()
        # Find the translation of the node in worldspace:
        transformFn = om.MFnTransform(self.mDagPath) # MFnTransform
        vec = transformFn.getTranslation(om.MSpace.kWorld) # MVector
        # Convert from internal units (cm) to whatever the UI is using:
        return om.MPoint(om.MDistance.internalToUI(vec.x),
                         om.MDistance.internalToUI(vec.y),
                         om.MDistance.internalToUI(vec.z))
}}}
Docs:
*[[M3dView|http://download.autodesk.com/us/maya/2011help/API/class_m3d_view.html]]
*[[MFnTransform|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_transform.html]]
*[[MPxLocatorNode|http://download.autodesk.com/us/maya/2010help/API/class_m_px_locator_node.html]]
*[[MPxNode|http://download.autodesk.com/us/maya/2010help/API/class_m_px_node.html]]
*[[MDagPath|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_path.html]]
*[[MDistance|http://download.autodesk.com/us/maya/2010help/API/class_m_distance.html]]
*[[MPoint|http://download.autodesk.com/us/maya/2011help/API/class_m_point.html]]
The below code is something I came up with to list all the attributes on a node via the API, the values stored in their plugs, and the API attribute type.  It seems //really// clunky, and maybe in six months I'll find some function that does this in two lines.  But it seems like the API makes you test against which function set the attribute belongs to, and then have to query for a very specific type of data.  After I authored this I cam across Bryan Ewerts post [[here|http://ewertb.soundlinker.com/api/api.029.php]], which uses a very similar system, so I'm guessing I'm on the right track.
Some notes:
*I had to wrap every query in a try\except clause, since some data would return None even though it was defined as a given type.
*I have yet to figure out how to query string data.  It just fails :(
*As noted below, the code ''fails to trace array attribute element plug data''.  It shows a weird {{{[-1]}}} for the index, and won't dig any deeper.  However, a link to a solution is provided at the end of this topic.
Some helper functions:
{{{
import maya.OpenMaya as om

def nameToMObject(name):
    """
    return an MObject based on the provided string node name.
    """
    selectionList = om.MSelectionList()
    selectionList.add(name)
    node = om.MObject()
    selectionList.getDependNode( 0, node )
    return node

def getAllPlugs(mObject):
    """
    return a list of all the plugs for all the attributes on the given MObject.
    """
    ret = []
    depNodeFn = om.MFnDependencyNode(mObject)
    attrCount = depNodeFn.attributeCount()
    for i in range(attrCount):
        attrObject = depNodeFn.attribute(i)
        mPlug = depNodeFn.findPlug(attrObject)
        ret.append(mPlug)
    return ret
}}}
Main code:
{{{
objectName = 'pPlaneShape1'
mObject = nameToMObject(objectName)
plugs = getAllPlugs(mObject)

# Query\print values:
for plug in plugs:
    attr = plug.attribute() # MObject for the coresponding attribute
    val = []
    #--------------------------------------------------------
    # Now test to see what type of attr it is!

    # Is it numeric?
    if attr.hasFn(om.MFn.kNumericAttribute):
        fnAttr = om.MFnNumericAttribute(attr)
        unitType = fnAttr.unitType()
        if unitType == om.MFnNumericData.kBoolean:
            try:
                val = plug.asBool()
            except:
                pass
        elif unitType == om.MFnNumericData.kChar:
            try:
                val = plug.asChar()
            except:
                pass
        elif unitType == om.MFnNumericData.kShort:
            try:
                val = plug.asShort()
            except:
                pass
        elif unitType == om.MFnNumericData.k2Short:
            for i in range(2):
                try:
                    val.append(plug.child(i).asShort())
                except:
                    pass
        elif unitType == om.MFnNumericData.k3Short:
            for i in range(3):
                try:
                    val.append(plug.child(i).asShort())
                except:
                    pass
        elif unitType == om.MFnNumericData.kLong or unitType == om.MFnNumericData.kInt:
            try:
                val = plug.asInt()
            except:
                pass
        elif unitType == om.MFnNumericData.k2Long or unitType == om.MFnNumericData.k2Int:
            for i in range(2):
                try:
                    val.append(plug.child(i).asInt())
                except:
                    pass
        elif unitType == om.MFnNumericData.k3Long or unitType == om.MFnNumericData.k3Int:
            for i in range(3):
                try:
                    val.append(plug.child(i).asInt())
                except:
                    pass
        elif unitType == om.MFnNumericData.kFloat:
            try:
                val = plug.asFloat()
            except:
                pass
        elif unitType == om.MFnNumericData.k2Float:
            for i in range(2):
                try:
                    val.append(plug.child(i).asFloat())
                except:
                    pass
        elif unitType == om.MFnNumericData.k3Float:
            for i in range(3):
                try:
                    val.append(plug.child(i).asFloat())
                except:
                    pass
        elif unitType == om.MFnNumericData.kDouble:
            try:
                val = plug.asDouble()
            except:
                pass
        elif unitType == om.MFnNumericData.k2Double:
            for i in range(2):
                try:
                    val.append(plug.child(i).asDouble())
                except:
                    pass
        elif unitType == om.MFnNumericData.k3Double:
            for i in range(3):
                try:
                    val.append(plug.child(i).asDouble())
                except:
                    pass

    # Is it 'typed'?
    elif attr.hasFn(om.MFn.kTypedAttribute):
        # These can be string attrs
        attrType = om.MFnTypedAttribute(attr).attrType()
        if attrType == om.MFnData.kString:
            # NEITHER SOLUTION HERE IS WORKING!
            #stringData = om.MFnStringData(plug.asMObject()) 
            #val = stringData.string()
            #val = plug.asString()  # This cause it to break! :(
            val = "<string: can't query>"
        elif attrType == om.MFnData.kMatrix:
            try:
                matrixData = om.MFnMatrixData(plug.asMObject())
                matrix = matrixData.matrix()
                val = [matrix(i,j) for i in range(4) for j in range(4)]
            except:
                pass

    # Is it  angle, time, unit (distance), or enum?
    elif attr.hasFn(om.MFn.kDoubleAngleAttribute):
        mAngle = plug.asMAngle()
        val = mAngle.value()
    elif attr.hasFn(om.MFn.kTimeAttribute):
        mTime = plug.asMTime()
        val = mTime.value()
    elif attr.hasFn(om.MFn.kUnitAttribute):
        mDist = plug.asMDistance()
        val = mDist.value()
    elif attr.hasFn(om.MFn.kEnumAttribute):
        val = plug.asInt()

    # Could add more code for Generic, Light, & Message.  Compound is a type as
    # well, but only holds other attrs (has no 'value'), and the previous code
    # extracts all the child plugs from any compounds that exist.

    if isinstance(val, list):
        if not len(val):
            val = None
    print plug.name(), val, attr.apiTypeStr()
}}}
Prints a lot of stuff:
{{{
pPlaneShape1.message None kMessageAttribute
pPlaneShape1.caching False kNumericAttribute
pPlaneShape1.isHistoricallyInteresting None kNumericAttribute
pPlaneShape1.nodeState 0 kEnumAttribute
etc...
}}}
Furthermore, it won't print out the values for any //array attrs//.  For example, presuming you have a poly mesh and you've modified the UV's, you'd have multiple UV attrs being tweaked on.  But the above code will only print this:
{{{
pPlaneShape1.uvSet None kCompoundAttribute
pPlaneShape1.uvSet[-1].uvSetName <string: can't query> kTypedAttribute
pPlaneShape1.uvSet[-1].uvSetPoints [0.0, 0.0] kAttribute2Float
pPlaneShape1.uvSet[-1].uvSetPoints[-1].uvSetPointsU 0.0 kNumericAttribute
pPlaneShape1.uvSet[-1].uvSetPoints[-1].uvSetPointsV 0.0 kNumericAttribute
pPlaneShape1.uvSet[-1].uvSetTweakLocation None kTypedAttribute
}}}
Which I guess tells you that these are array attributes, but doesn't tell you how many of each there are, or the element plug values (other than the default vals listed).
I have a solution for compound\array attrs here: [[API: Find all child attributes under a compound attribute]]
After much trial and error, I've whipped up the below code to query all the child attributes under a given compound attribute, and print their names \ types.

This was complicated by a variety of factors:
#It took me a while to realize that compound attrs can also be array.  I hadn't quite grasped the differences between them:  Compound is a type, like float or string.  Array is a property of an attribute, like keyable, locked, hidden, etc.
#I was able to query compound children, but I wasn't understanding how to query an array of compound attrs children.  This was further messed up by the fact the {{{MPlug.getExistingArrayAttributeIndices()}}} method will return negative index values, which cause all sorts of problems.  Not sure why it does that, but skipping over them solves the problem.
#Understanding the differences between logical and physical index arrays.  In a nutshell, you want to use logical most of the time...
For more info on those topics, please see this subject: [[API: Attribute Creation & Usage]]

In our example we use a polygonal plane with four verts, called {{{pPlane1}}} (but make reference to its shape node, {{{pPlaneShape1}}}).
{{{
import maya.OpenMaya as om

# The node name, and attribute name, to query:
node = "pPlaneShape1" 
attr = "uvSet"

# Get an MObject by string name:
selList = om.MSelectionList()
selList.add(node)
mObject = om.MObject()
selList.getDependNode(0, mObject)

# Attach a function set to the shape and get the plug to start querying:
mFnDependencyNode = om.MFnDependencyNode(mObject)
rootPlug = mFnDependencyNode.findPlug(attr)

# Get all child plugs for our root, presuming it is a compound attr, since they're
# the only type that have 'children':
plugs = [rootPlug]
for plug in plugs:
    # If the type is compound, and it is also set to array:
    if plug.isCompound() and plug.isArray():
        # Find the logical indices of the array:
        logicalIndices = om.MIntArray()
        plug.getExistingArrayAttributeIndices(logicalIndices)
        for i in range(logicalIndices.length()):
            # getExistingArrayAttributeIndices() can return negative index values
            # for some reason, so we need to be sure to *not* deal with those,
            # since obviously bad things would happen....
            if logicalIndices[i] >= 0:
                # Now find the element plug of that index, which is a compound
                # type attribute:
                elementPlug = plug.elementByLogicalIndex(logicalIndices[i])
                # And query the children of that compound attr:
                for j in range(elementPlug.numChildren()):
                    plugs.append(elementPlug.child(j))
    # If it is compound, but not array:
    elif plug.isCompound():
        # Just get the children of that compound attr:
        for i in range(plug.numChildren()):
            plugs.append(plug.child(i))

for plug in plugs:
    attrObj = plug.attribute() # MObject
    print plug.name(), attrObj.apiTypeStr()
}}}
Gives us this result:
{{{
pPlaneShape1.uvSet kCompoundAttribute
pPlaneShape1.uvSet[0].uvSetName kTypedAttribute
pPlaneShape1.uvSet[0].uvSetPoints kAttribute2Float
pPlaneShape1.uvSet[0].uvSetTweakLocation kTypedAttribute
pPlaneShape1.uvSet[0].uvSetPoints[0].uvSetPointsU kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[0].uvSetPointsV kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[1].uvSetPointsU kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[1].uvSetPointsV kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[2].uvSetPointsU kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[2].uvSetPointsV kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[3].uvSetPointsU kNumericAttribute
pPlaneShape1.uvSet[0].uvSetPoints[3].uvSetPointsV kNumericAttribute
}}}
It should be noted that not all attrs are so verbose.  For example, if you set this and re-run it:
{{{
attr = "pnts"
}}}
All it prints is:
{{{
pPlaneShape1.pnts kAttribute3Float
}}}
Even though we know there is more than one point in the plane.  If you grab all the verts and component level, manually move them with the translate manip, and re-run it, it prints:
{{{
pPlaneShape1.pnts kAttribute3Float
pPlaneShape1.pnts[0].pntx kFloatLinearAttribute
pPlaneShape1.pnts[0].pnty kFloatLinearAttribute
pPlaneShape1.pnts[0].pntz kFloatLinearAttribute
pPlaneShape1.pnts[1].pntx kFloatLinearAttribute
pPlaneShape1.pnts[1].pnty kFloatLinearAttribute
pPlaneShape1.pnts[1].pntz kFloatLinearAttribute
pPlaneShape1.pnts[2].pntx kFloatLinearAttribute
pPlaneShape1.pnts[2].pnty kFloatLinearAttribute
pPlaneShape1.pnts[2].pntz kFloatLinearAttribute
pPlaneShape1.pnts[3].pntx kFloatLinearAttribute
pPlaneShape1.pnts[3].pnty kFloatLinearAttribute
pPlaneShape1.pnts[3].pntz kFloatLinearAttribute
}}}
Which tells me that in some cases, the plugs don't exist until some type of user interaction.  Something even crazier I've found:  If instead of grabbing all the verts by hand and deleting them, if I instead run the command:
{{{
delete -constructionHistory;
}}}
On the mesh, the attrs will appear as well.  It must be triggering some kind of massive "you are dirty" flag on all the plugs so they suddenly show up.... or something....

 I have yet to find a more elegant solution for this :(
----
Also see:
*[[API: Find all attributes & plugs on a node]]
*[[How can I get a list of all the child multi-attrs, of a given attr name?]] (using the command engine, rather than the API)
* [[Find child attributes]]
''API Code Snippet''

In this example, I set all variable names to be the type of object they are, to make it easier to understand how objects are returned and used.  I don't recommend doing this in regular code, just using it as an explanation tool
__Highlights:__
*Create {{{MItDag}}} 'DAG iterator object' to loop over all mesh.
*Create {{{MDagPath}}} 'DAG path object' for each mesh, giving access to it's path/name.
*Create {{{MFnMesh}}} 'polygonal surface function set' object, allowing for actions to be performed on the current {{{MDagPath}}} object.
{{{
# Print the name of all the mesh in the scene & their vert counts.

import maya.OpenMaya as om

# Make a MItDag iterator object, for mesh only:
MItDag = om.MItDag(om.MItDag.kDepthFirst, om.MFn.kMesh)

# Start looping over each mesh:
while not MItDag.isDone():

    # Make a MDagPath object that will store the path of 
    # the mesh:
    MDagPath = om.MDagPath()

    # Set the MDagPath object to the path of the current mesh 
    # as defined by the current loop of the iterator:
    MItDag.getPath(MDagPath)

    # Create a new MFnMesh object, set to the current dag path.
    # We can now do operations on that mesh via that object.
    MFnMesh = om.MFnMesh(MDagPath)

    # As an example, find the number of verts on the mesh
    # from the MFnMesh object:
    numVerts = MFnMesh.numVertices()

    # get the name of our node from the MDagPath object:
    name = MDagPath.fullPathName()

    # Print results:
    print name, numVerts

    # Advance to the next mesh in our iterator:
    MItDag.next()
}}}
Example via the ~OpenMaya Python API of finding the closest vertex on a polygonal mesh to a given transform.
I've not tried it, but it may be worth checking out a solution using [[MMeshIntersector|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_mesh_intersector.html]].  There is an example using it online [[here|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/closest_point_cmd_8cpp-example.html,topicNumber=cpp_ref_closest_point_cmd_8cpp_example_html,hash=_a33]]
For fun, I made two different implementations.  They both do the same thing, just go about it different ways.
{{{
# This first code chunk is required for both examples:

# Python code
from operator import itemgetter
import maya.cmds as mc
import maya.OpenMaya as om

def getMDagPath(nodeName):
    """
    Convenience function that returns a MDagPath for a given Maya DAG node.
    """
    selList = om.MSelectionList()
    selList.add(nodeName)
    mDagPath = om.MDagPath()
    selList.getDagPath(0, mDagPath)
    return mDagPath
}}}
!!!Example A:
In this example, we fill a {{{MPointArray}}} with all the vert positions at once, then do our distance comparisons over that array, filling our final sorted dictionary with the distance results.
{{{
def closestVertToPoint_A(mesh, target):
    """
    Parameters:
    mesh : string : Polygonal mesh to query vert positions on.
    target : string : Any transform to find the closest vertex to.

    return : string : Name of the vertex closest to target.
    """
    # Maya node definitions:-------------
    # Get a MDagPath for our mesh:
    node_mDagPath = getMDagPath(mesh)
    # Get a MDagPath for our target:
    target_mDagPath = getMDagPath(target)

    # Target Stuff:-------------
    # Get a MFnTransform for our target:
    mFnTransform = om.MFnTransform(target_mDagPath)
    # Get a MPoint for it's worldspace position:
    transform_mPoint = mFnTransform.rotatePivot(om.MSpace.kWorld)

    # Mesh stuff:-------------
    # Get the MFnMesh:
    mFnMesh = om.MFnMesh(node_mDagPath)
    # Build a point array to hold our points:
    mPointArray = om.MPointArray()
    # Fill the array with points:
    mFnMesh.getPoints(mPointArray)

    # Find closest vert ID: -------------------------
    # This is a dictionary, each key:value pair will be vertID:distance
    distances = {}
    for i in range(mPointArray.length()):
        distances[i] = transform_mPoint.distanceTo(mPointArray[i])
    # Sort our dictionary by values (the distances), not keys (the vert ID's).
    # This turns the dictionary into a list, fyi.
    closest = sorted(distances.items(), key=itemgetter(1))[0]

    # Return it: ----------------------
    # Create the vertex name from the ID:
    vertName = '%s.vtx[%s]'%(mesh, closest[0])
    return vertName
}}}
!!!Example B:
In this example, we iterate over each vertex querying it's position and doing the distance computation before adding the result to the final sorted dictionary.
{{{
def closestVertToPoint_B(mesh, target):
    """
    Parameters:
    mesh : string : Polygonal mesh to query vert positions on.
    target : string : Any transform to find the closest vertex to.

    return : string : Name of the vertex closest to target.
    """
    # Maya node definitions:-------------
    # Get a MDagPath for our mesh:
    node_mDagPath = getMDagPath(mesh)
    # Get a MDagPath for our target:
    target_mDagPath = getMDagPath(target)

    # Target Stuff:-------------
    # Get a MFnTransform for our target:
    mFnTransform = om.MFnTransform(target_mDagPath)
    # Get a MPoint for it's worldspace position:
    transform_mPoint = mFnTransform.rotatePivot(om.MSpace.kWorld)

    # Mesh stuff:-------------
    # This is a dictionary, each key:value pair will be vertID:distance
    distances = {}
    # Get the MItMeshVertex:
    mItMeshVertex = om.MItMeshVertex(node_mDagPath)
    while not mItMeshVertex.isDone():
        # For the current vert, get it's position:
        mPoint = mItMeshVertex.position(om.MSpace.kWorld)
        # Find distance:
        distance = transform_mPoint.distanceTo(mPoint)
        # Get the current vert id:
        vertId = mItMeshVertex.index()
        distances[vertId] = distance
        mItMeshVertex.next()

    # Find closest vert ID: -------------------------
    for i in range(mPointArray.length()):
        distances[i] = transform_mPoint.distanceTo(mPointArray[i])
    # Sort our dictionary by values (the distances), not keys (the vert ID's).
    # This turns the dictionary into a list, fyi.
    closest = sorted(distances.items(), key=itemgetter(1))[0]

    # Return it: ----------------------
    # Create the vertex name from the ID:
    vertName = '%s.vtx[%s]'%(mesh, closest[0])
    return vertName
}}}
!!!The results
Do it.  Run a timer to see which one is faster:
{{{
import time
# Polygonal mesh with verts to query:
mesh = 'pSphere1'
# Target we want to find the closest vert to:
target = 'locator1'

# Time A:
start_A = time.time()
closestPoint_A = closestVertToPoint_A(mesh, target)
end_A = time.time()
total_A = end_A - start_A

# Time B:
start_B = time.time()
closestPoint_B = closestVertToPoint_B(mesh, target)
end_B = time.time()
total_B = end_B - start_B

print "Total time for example A:", total_A
print "Total time for example B:", total_B
print "Closest vert:", closestPoint_A
mc.select(closestPoint_A)
}}}
I made a sphere with 100 subdivision (9902 verts), and these were the results:
{{{
Total time for example A: 0.031
Total time for example B: 0.047
Closest vert: pSphere1.vtx[4298]
}}}
So it looks like A is about 1.5x  faster than B.
{{{
import maya.OpenMaya as om

node = "pPlaneShape1"
attr = "uvSetPoints"

# Get an MObject by string name:
selList = om.MSelectionList()
selList.add(node)
mObject = om.MObject()
selList.getDependNode(0, mObject)

# Get a plug based on the attribute string name:
mFnDependencyNode = om.MFnDependencyNode(mObject)
plug = mFnDependencyNode.findPlug(attr)
print plug.name()
}}}
{{{
# pPlaneShape1.uvSet[-1].uvSetPoints
}}}
*http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html
*http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html
Docs for [[MNodeMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_node_message.html]]
There is the {{{scriptJob}}}'s {{{attributeChange}}} argument of course.  But if you wanted to do this via the api, the below code functions.  
{{{
import maya.OpenMaya as om

# Get an MObject for our node:
node = 'pSphere1'
selList = om.MSelectionList()
selList.add(node)
mObject = om.MObject()
selList.getDependNode(0, mObject)

# Callback code:
def callback(msg, plug, otherPlug, *args):
    if msg != 2056:
        # Magic number for 'setAttr'!
        return
    if 'translateX' not in plug.name():
        return
    print "Message : ", msg
    val = om.MDistance.internalToUI(plug.asDouble())
    print "\tplug:", plug.name(), val
    print "\totherPlug:", otherPlug.name()
    print "\tClient Data:", args

# Create the callback:
cid = om.MNodeMessage.addAttributeChangedCallback(mObject, callback, "arbitraryClientData")

#----
# Delete the callback later:
om.MMessage.removeCallback(cid)
}}}
What's crazy is, running the above code without the msg filter on the first line prints:
{{{
Message : 2052
}}}
And, 2052 isn't any of the ~AttributeMessage enums (below).
Furthermore the value for 'setAttr' is 2056, //also// not in the below enums.
So very confused.
{{{
kAttributeAdded 64
kAttributeArrayAdded 4096
kAttributeArrayRemoved 8192
kAttributeEval 4
kAttributeKeyable 512
kAttributeLocked 16
kAttributeRemoved 128
kAttributeRenamed 256
kAttributeSet 8
kAttributeUnkeyable 1024
kAttributeUnlocked 32
kConnectionBroken 2
kConnectionMade 1
kDefaultAction 0
kDoAction 2
kDoNotDoAction 1
kIncomingDirection 2048
kKeyChangeInvalid 0
kKeyChangeLast 3
kLast 32768
kMakeKeyable 1
kMakeUnkeyable 2
kOtherPlugSet 16384
}}}
----
Also see:
*[[API: How can I author callbacks for Maya events?]]
The most common way to do this is via mel [[scriptJobs|http://download.autodesk.com/us/maya/2011help/Commands/scriptJob.html]] or the [[callbacks|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/callbacks.html]] command.  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|http://www.mail-archive.com/python_inside_maya@googlegroups.com/msg00200.html]] 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|http://download.autodesk.com/us/maya/2011help/API/classes.html]])
*[[MMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_message.html]]
**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|http://download.autodesk.com/us/maya/2011help/API/class_m_anim_message.html]]
**Callbacks for animation messages:  animCuve edited, keyframe edited
*[[MCameraSetMessage|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-ocumentation/cpp_ref/class_m_camera_set_message.html]]
**Callbacks for {{{cameraSet}}} sepcific envent types:  Adding\removing camera layers, camera name changes.
*[[MCommandMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_command_message.html]]
**Callbacks triggering on //mel command// execution.
*[[MConditionMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_condition_message.html]]
**Callbacks for changes to specific //conditions//, same conditions found in the [[scriptJob|http://download.autodesk.com/us/maya/2010help/CommandsPython/scriptJob.html]] documentation.
*[[MContainerMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_container_message.html]]
**Callbacks that inform about changes to published attributes on container node types.
*[[MDagMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_dag_message.html]]
**Callbacks for dependency graph messages:  Parent added, removed.   Child added, removed, reordered. Instance added, removed.
*[[MDGMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_d_g_message.html]]
**Callbacks for dependency graph messages:  Time changed, node added, node removed, connection made or broken.
*[[MEventMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_event_message.html]]
**Callbacks for specific //events//, same events found in the [[scriptJob|http://download.autodesk.com/us/maya/2010help/CommandsPython/scriptJob.html]] documentation.
*[[MLockMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_lock_message.html]]
**Callbacks that control how Maya handles locks:  On object and attributes.
*[[MModelMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_model_message.html]]
**Callbacks for model related messages:  Before and after node duplicate, before dag node added (created), during dag node removed (deleted).
*[[MNodeMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_node_message.html]]
**Callbacks for dependency node messages of specific dependency nodes:  Attribute changed, added, or removed.  Plug dirty.  Before node deleted.  Attr keyable state change.
*[[MObjectSetMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_object_set_message.html]]
**Callbacks for object set messages received by specific sets.  Tracks if set members are modified.
*[[MPaintMessage|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_paint_message_html]]
** Callbacks for vertex color paint
*[[MPolyMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_poly_message.html]]
**Callbacks for poly component id modification messages:  Vertex, edge, or face component id's modified.
*[[MSceneMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_scene_message.html]]
**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.
**It should be noted in Maya 2017, they updated the API to expose the new methods the end in ...{{{Check}}}, that allow internal logic to define if Maya should halt execution after or not. I've been unable to get these to work in API1.0, but have had success in API 2.0: The calling funcs need to return a bool based on success.
*[[MTimerMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_timer_message.html]]
**Callbacks that are based on a fixed time interval.  
*[[MUiMessage|http://download.autodesk.com/us/maya/2011help/API/class_m_ui_message.html]]
**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|http://download.autodesk.com/us/maya/2011help/API/class_m_user_event_message.html]]
**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|http://download.autodesk.com/us/maya/2011help/API/class_m_hwr_callback.html]]
**Callbacks to gain access to Maya's Hardware Rendering device status. You can be notified of device creation, lost reset and deletion.
*[[MRenderCallback|http://download.autodesk.com/us/maya/2011help/API/class_m_render_callback.html]]
**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|http://download.autodesk.com/us/maya/2011help/API/class_m_callback_id_array.html]]
**An array of {{{MCallbackId}}}.  These can be passed to {{{MMessage}}} instances.

----
And via {{{OpenMayaUI}}}:
*[[MEvent|http://download.autodesk.com/us/maya/2011help/API/class_m_event.html]]
**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.



Docs: [[MFnDependencyNode|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dependency_node.html]]
{{{
import maya.OpenMaya as om

depNodeFunc = om.MFnDependencyNode()
node = depNodeFunc.create("transform") # MObject
}}}
Or if you don't care about the actual {{{MFnDependencyNode}}} class object, you can skip its storage:
{{{
node = MFnDependencyNode().create("transform")
}}}
As you can see from the example, the {{{MFnDependencyNode}}} class can easily create a physical Maya node (in this case called 'transform1' in the Outliner) via its {{{create()}}} method, which is returned as a {{{MObject}}}.  Furthermore, {{{MFnDependencyNode}}} is the superclass of many other {{{MFn}}} classes, so the {{{create()}}} method can be called from them as well.  For example, this is the class inheritance tree for the {{{MFnSkinCluster}}} class:
*{{{OpenMaya.MFnBase}}} (superclass)
**{{{OpenMaya.MFnDependencyNode}}}
***{{{OpenMayaAnim.MFnGeometryFilter}}}
****{{{OpenMayaAnim.MFnSkinCluster}}} (subclass)
So let's build a {{{skinCluster}}} Maya node:
{{{
import maya.OpenMayaAnim as oma

skinClusterFn = oma.MFnSkinCluster()
# Call the create() method from the MFnDependencyNode superclass.  Could
# just as easily make any other node type with this call, not just a skinCluster:
skinCluster = skinClusterFn.create("skinCluster") # MObject
}}}
----
It should be noted that creating nodes this way bypasses the undo queue.  If you want to have this be undoable, you'll have to manage it through a scripted plugin, see: [[API: undoing commands]].  This makes use of [[OpenMaya.MDagModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_modifier.html]] and its superclass [[OpenMaya.MDGModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_d_g_modifier.html]].
----
In addition to above, {{{OpenMaya.MGlobal.addToModel(MObject)}}} can also be used.  However, I'm not sure (yet) how to make an {{{MObject}}} representing a node that doesn't actually already exist in the DG.
When dealing with node names, Maya commands like {{{ls}}} return back the //string// representation of the nodes.  Things can get complicated really quick when there are duplicate names in the scene, or when nodes get renamed:  You can no longer accurately reference them by their string name.

{{{MDagPath}}} objects instead store a pointer to where the node lives in memory:  Other than deleting the node, this location won't change, even if the name does.  Because of this, duplicate node names and node renaming issues suddenly go away.

The below functions will convert a list of string names to {{{MDagPath}}} objects, and convert a list of {{{MDagPath}}} objects back to strings.
{{{
import maya.cmds as mc
import maya.OpenMaya as om

def MDP(names):
    """
    Will filter out any non-DAG objects.

    Parameters:
    names : list : List of string names to convert to MDagPath objects.

    Return : list : List of MDagpPath objects.
    """
    dag = mc.ls(names, dagObjects=True)
    mdp = []
    selList = om.MSelectionList()
    for name in dag:
        selList.clear()
        mDagPath = om.MDagPath()
        selList.add(name)
        selList.getDagPath(0, mDagPath)
        mdp.append(mDagPath)
    return mdp

def STR(mdp):
    """
    Parameters :
    mdp : list : List of MDagPath objects.

    Return : list : List of the coresponding full path string names.
    """
    return [item.fullPathName() for item in mdp]
}}}
Picking the {{{persp}}} camera and it's shape {{{perspShape}}}:
{{{
dpaths = MDP(mc.ls(selection=True))
print dpaths
strs = STR(dpaths)
print strs

[<maya.OpenMaya.MDagPath; proxy of <Swig Object of type 'MDagPath *' at 0x0000000023318510> >, <maya.OpenMaya.MDagPath; proxy of <Swig Object of type 'MDagPath *' at 0x0000000023318570> >]
[u'|persp', u'|persp|perspShape']
}}}
Presuming you need to call to //mel// someplace in your scripted plugin (rather than the Python version of the command), the {{{maya.OpenMaya.MGlobal.executeCommand()}}} method will do the trick:
{{{
# Python code:
import maya.OpenMaya as om
om.MGlobal.executeCommand("cylinder")
}}}
That's the most simple way to do it.  There are also solutions for capturing the results:
{{{
import maya.OpenMaya as om

cmdResult = om.MCommandResult()
theResult = []

melCmd = 'string $b[] = `polySphere`;'
display, undo = True, True
om.MGlobal.executeCommand(melCmd, cmdResult, display, undo)
cmdResult.getResult(theResult)
print theResult
}}}
When executed, this is printed in the Script Editor:
{{{
string $b[] = `polySphere`;
// Result: pSphere1 polySphere1 // 
[u'pSphere1', u'polySphere1']
}}}
*[[OpenMaya.MGlobal|http://download.autodesk.com/us/maya/2010help/API/class_m_global.html]]
*[[OpenMaya.MCommandResult|http://download.autodesk.com/us/maya/2010help/API/class_m_command_result.html]]
I wanted to find the centerpoint of a node using the API.  Below is one way to do it.  The only real wrinkle is that the API, as usual, returns the results as cm, and in my case, I'm working in inches (yuck).  So you have to do that conversion.

In a nutshell, I find the bounding box for the node, find the bounding box's center point, then convert that location into the proper linear units.
{{{
import maya.cmds as mc
import maya.OpenMaya as om

# Name of the node to query:
node = 'myFancyMesh'

# Get a MDagPath for our node:
selList = om.MSelectionList() # MSelectionList
selList.add(node)
mDagPath = om.MDagPath() # MDagPath
selList.getDagPath(0, mDagPath)

# Find the centerpoint:
dagNodeFunc = om.MFnDagNode(mDagPath) # MfnDagNode
boundingBox = dagNodeFunc.boundingBox() # MBoundingBox
centerPoint = boundingBox.center() # MPoint

# Convert from cm to current units:
center = []
unitType = om.MDistance.uiUnit()
for i in range(3):
    distance = om.MDistance(centerPoint[i]) # MDistance, as cm
    converted = distance.asUnits(unitType) # double, converted
    center.append(converted)
    
print center
}}}
Little more work than the mel {{{listRelatives}}} command ;)
{{{
import maya.OpenMaya as om

# Presume dagPath is a MDagPath object
dagPathFunc = om.MFnDagNode(dagPath)
kids = []
for i in range(dagPathFunc.childCount()):
    kids.append(dagPathFunc.child(i))

# kids is a list of MObjects, one for each child node.
}}}
Wow, so easy:
{{{
# Python code
import maya.OpenMaya as om
import maya.OpenMayaUI as omui

fileType = 'jpg'
imageFile = 'c:/temp/mayaScreen.%s'%fileType
mimage = om.MImage()
view = omui.M3dView.active3dView()
view.readColorBuffer(mimage, True)
mimage.writeToFile(imageFile, fileType)
}}}
From the {{{MImage}}} docs, these image formats are supported for output:
*iff, sgi, pic, tif, als, gif, rla, jpg
Docs:
*[[M3dView|http://download.autodesk.com/us/maya/2011help/API/class_m3d_view.html]]
*[[MImage|http://download.autodesk.com/us/maya/2011help/API/class_m_image.html]]
API to the rescue.  Amazingly easy.

Example module that will execute a function ({{{elapsedTimeFunc()}}}) based on a set amount of time in seconds in Maya.  Modify the contents of {{{elapsedTimeFunc()}}} (but not it's parameter signature) however you'd like.  The argument to {{{clientData}}} can be anything you want:  It could even be another function that is executed inside of {{{elapsedTimeFunc()}}}.
{{{
# timeCallback.py
import maya.OpenMaya as om

callbackId = None

def elapsedTimeFunc(elapsedTime, lastTime, clientData):
    print "\nElaspsed time:", elapsedTime
    print "Last Time:", lastTime
    print "Client Data:", clientData

def makeTimeCallback(seconds, clientData):
    global callbackId
    callbackId = om.MTimerMessage.addTimerCallback(seconds, elapsedTimeFunc, clientData)

def removeTimerCallback():
    global callbackId
    if not callbackId:
        return
    om.MMessage.removeCallback(callbackId)
    callbackId.disown()
    callbackId = None
}}}
To execute it in another module:
{{{
import timeCallback
timeCallback.makeTimeCallback(5, "some data")
}}}
Then, every five seconds, it will print:
{{{
Elaspsed time: 0.00076920655556
Last Time: 4.1576094601e+21
Client Data: some data

Elaspsed time: 5.00000333786
Last Time: 0.00532985245809
Client Data: some data

Elaspsed time: 5.00000476837
Last Time: 0.00644026743248
Client Data: some data

etc...
}}}
Finally, to remove the callback cleanly:
{{{
import timeCallback
timeCallback.removeTimerCallback()
}}}
You could very easily make a auto-save tool based on a certain time interval.  Or do some really annoying stuff to your coworkers ;)
Example:  Create a physical Maya {{{skinCluster}}} node (returned as {{{MObject}}} in script), find it's {{{maxInfluence}}} attribute as an {{{MPlug}}}, and then set the plug value
{{{
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

influenceCount = 4
skinClusterFn = oma.MFnSkinCluster() # MFnSkinCluster
skinCluster = skinClusterFn.create("skinCluster") # MObject
plug = om.MFnDependencyNode(skinCluster).findPlug('maxInfluences') # MPlug
plug.setInt(influenceCount)
}}}
Docs:
*[[MFnDependencyNode|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dependency_node.html]]
*[[MPlug|http://download.autodesk.com/us/maya/2011help/API/class_m_plug.html]]
----
Also see:
*[[API : Querying Array Attributes]]
Based on the current selection, print a list of which function sets are available to those ~MObjects.

In this example, I set all variable names to be the type of object they are, to make it easier to understand how objects are returned and used. I don't recommend doing this in regular code, just using it as an explanation tool
{{{
# Python code
import maya.OpenMaya as om    
    
# Make a MSelectionList container object:
MSelectionList = om.MSelectionList()
# Fill with selection, as MObjects
om.MGlobal.getActiveSelectionList(MSelectionList)
# Create an iterator to loop over our selection:
MItSelectionList = om.MItSelectionList(MSelectionList)

# Start looping over each node:
while not MItSelectionList.isDone():
    # get the name of our node from the MItSelectionList object:
    MDagPath = om.MDagPath()
    MItSelectionList.getDagPath(MDagPath)
    name = MDagPath.fullPathName()

    # Define the current MObject being worked on:
    MObject = om.MObject()
    MItSelectionList.getDependNode(MObject)
    # get Function Sets:
    funcSets = []
    om.MGlobal.getFunctionSetList(MObject, funcSets)

    # Print results:
    print "--Name:", name
    print "\tfuncSets:", funcSets

    # Advance to the next node in our iterator:
    MItSelectionList.next()
}}}
Prints:
{{{
--Name: |side|sideShape
	funcSets: [u'kBase', u'kNamedObject', u'kDependencyNode', u'kDagNode', u'kShape', u'kCamera']
--Name: |pSphere1|pSphereShape1
	funcSets: [u'kBase', u'kNamedObject', u'kDependencyNode', u'kDagNode', u'kShape', u'kGeometric', u'kSurface', u'kMesh']
}}}
When you interactively execute //mel// code in the Script Editor, you'll see a result line print what you've done.  For example, if you execute this //mel// code in the Script Editor:
{{{
float $aa = 23;
}}}
You'll see it print this above:
{{{
float $aa = 23;
// Result: 23 // 
}}}
And if you execute this //Python// code in the script editor:
{{{
aa = 23
}}}
All you'll see above is the same line repeated, no {{{// Result:}}}.
This same issue is present when evaluating mel code via Python via {{{maya.mel.eval}}}, you //still// won't see the {{{// Result:}}} print line.  

But what if you //really// wanted to?

While doing some random API code browsing I found a way to do it. And after looking at it some more, I realized it could be done a lot more simplistically, so now there are two versions below, 'new' and 'old'.  New just prints the results.  The old method tries to return the results as well.
!!!New Solution
{{{
import maya.OpenMaya as om

melCmd = 'string $stuffx = "asdf"'
display, undo = True, True
om.MGlobal.executeCommand(melCmd, display, undo)
}}}
When executed prints in the Script Editor:
{{{
// Result: asdf // 
}}}
----
If you wanted to go one step further and capture the return in the process:
{{{
import maya.OpenMaya as om

cmdResult = om.MCommandResult()
theResult = []
melCmd = 'string $b[] = `polySphere`;'
display, undo = True, True
om.MGlobal.executeCommand(melCmd, cmdResult, display, undo)
cmdResult.getResult(theResult)
print theResult
}}}
{{{
// Result: pSphere1 polySphere1 // 
[u'pSphere1', u'polySphere1']
}}}
*[[OpenMaya.MGlobal|http://download.autodesk.com/us/maya/2010help/API/class_m_global.html]]
*[[OpenMaya.MCommandResult|http://download.autodesk.com/us/maya/2010help/API/class_m_command_result.html]]
!!!Old Solution
It's a bit lacking in some areas:  Won't print\return results from string, string arrays, matrix, and matrix arrays, but otherwise it works.  It will evaluate the passed in mel command, and then provide the standard {{{// Result:}}} line when complete, plus return (where available) the result.
{{{
import maya.OpenMaya as om

def executePrintMelResult(melCmd):
    result = om.MCommandResult()
    om.MGlobal.executeCommand(melCmd, result)
    rType = result.resultType()

    val = None

    if rType == 1:
        # kInt
        util = om.MScriptUtil()
        util.createFromInt(0)
        pointer = util.asIntPtr()
        result.getResult(pointer)
        val = util.getInt(pointer)
        print "// Result: %s //"%val

    if rType == 2:
        # kIntArray
        intArray = om.MIntArray()
        result.getResult(intArray)
        val = []
        for i in range(intArray.length()):
            val.append(intArray[i])
        print "// Result: %s //"%val

    if rType == 3:
        # kDouble
        util = om.MScriptUtil()
        util.createFromDouble(0.0)
        pointer = util.asDoublePtr()
        result.getResult(pointer)
        val = util.getDouble(pointer)
        print "// Result: %s //"%val

    if rType == 4:
        # kDoubleArray
        doubleArray = om.MDoubleArray()
        result.getResult(doubleArray)
        val = []
        for i in range(doubleArray.length()):
            val.append(doubleArray[i])
        print "// Result: %s //"%val

    if rType == 5 or rType == 6:
        # kString, kStringArray
        # Not supported in Python :(
        pass

    if rType == 7:
        # kVector
        vector = om.MVector()
        result.getResult(vector)
        val = vector
        print "// Result: <<%s %s %s>> //"%(val.x, val.y, val.z)

    if rType == 8:
        # kVectorArray
        vectorA = om.MVectorArray()
        result.getResult(vectorA)
        val = vectorA
        # Maya only prints the first item in the array:
        print "// Result: <<%s %s %s>> //"%(val[0].x, val[0].y, val[0].z)

    if rType == 9 or rType == 10:
        # kMatrix or kMatrixArray
        # Result gathring not supported by MCommandResult()
        pass

    return val
}}}
If you executed this:
{{{
melCmd = 'float $myVal = 5.5'
result = executePrintMelResult(melCmd)
}}}
In the Script Editor you'll see it print:
{{{
// Result: 5.5 //
}}}
YES!  :-P
Inside the {{{compute()}}} method of a {{{MPxNode}}} in a scripted plugin, I wanted a way to track down the {{{MObject}}} for the node at the other end of an incoming connection.  Maybe there is a better way to do this, but the solution I came up with below.

The code says : If the output attr needs to be computed, then track down the node feeding some (unrelated, in this case) input attr.
{{{
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

class MyNode(ompx.MPxNode):

    someOutAttr = om.MObject() 
    someInAttr = om.MObject()

    def compute(self, plug, data):
        if plug == MyNode.someOutAttr:
                # Get an function set for this plugin node:
                thisNodeFn = om.MFnDependencyNode(self.thisMObject())

                # Get an MPlug for the input attr:
                thisPlug = thisNodeFn.findPlug(MyNode.someInAttr)

                # Fill an array with MPlug inputs to this attr:
                plugArray = om.MPlugArray()
                thisPlug.connectedTo(plugArray, True, False)

                # Get the node (as a MObject) connected to the MPlug for the input object:
                inputNode = plugArray[0].node()

                # And print its name, for confirmation:
                mFnDependNode = om.MFnDependencyNode(inputNode)
                nodeName = mFnDependNode.name()
                print nodeName

# The rest of the plugin code....
}}}
----
Also see:
*[[API: Querying input and output attrs on nodes and deformers]]
The API makes use of a [[MComputation|http://download.autodesk.com/us/maya/2010help/API/class_m_computation.html]] class that allows for the detection of user input (the {{{Esc}}} key), and will cancel it's operation when detected.

Here's a really simple example of it in use:
{{{
import maya.OpenMaya as om
def someOperation(i):
    print i

def doCompute(someFunc, loopRange):
    """
    Parameters:
    someFunc : A callable function object, taking a single arg for the current
        loop number.
    loopRange : int : How many loops to perform.
    """
    computation = om.MComputation()
    computation.beginComputation()
    for i in range(loopRange):
        someFunc(i)
        if computation.isInterruptRequested():
            print "Operation cancled by user."
            break
    computation.endComputation()

doCompute(someOperation, 10000)
}}}
After execution, if Maya is given focus and the user presses the {{{Esc}}} key, the computation will quit.

This is similar to the behavior of the [[progressWindow|http://download.autodesk.com/us/maya/2010help/CommandsPython/progressWindow.html]]  //command//, but without the gui, and executed via the API.
This isn't an actual solution, but I posted this question to the forums, and you can find the replies here:
https://groups.google.com/forum/?fromgroups=#!topic/python_inside_maya/QK2N0QYYXWI

If you're doing any work via the API, you need to know about these two objects.  This is my own personal description:
>{{{MObject}}} is a Lego-brick.  {{{MDagPath}}} tells you about where that brick lives in the model and what bricks effect it.
Online Docs for API 2.0 versions:
*[[MObject|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=Maya_SDK_py_ref_class_open_maya_1_1_m_object_html]]
*[[MDagPath|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=Maya_SDK_py_ref_class_open_maya_1_1_m_dag_path_html]]
!!!Difference between {{{MObject}}}s and {{{MDagPath}}}s:
''My personal notes:''
(when I say 'object', I'm referring to a Python class-based instance.  When I say 'node', I'm referring to an actual node in Maya).
*A {{{MObject}}} is an object that describes a specific instance of a node\attribute in Maya.  In non-API related mel scripting, you define a node by it's //string name// in a variable.  If the name of the node changes, your code will no longer be able to work on it since the variable name is old.  If on the other hand you were working with the {{{MObject}}} for a node directly, the name is but one of many things that describe it:  Change the name, and you code will still continue to work on the same node because it's looking directly at the object that describes the node, not just its name.
*A {{{MDagPath}}} is an object that describes the path to where in the DAG hierarchy a {{{MObject}}} lives, presuming the {{{MObject}}} is a DAG node.  An {{{MDagPath}}} is //not// 'the DAG //node//':  It represents the path to the DAG //object//.
Picture a bunch of balloons tied together in a huge bunch, with some balloons tied to other balloons in chains, while other balloons have no ropes and are floating around independent from the bunch:  A {{{MObject}}} is any balloon (a DG node), and the balloons tied up represent DAG nodes (a subset of the DG).  {{{MDagPath}}}s represent the ropes (hierarchy path) connecting a balloon all the way back to the base of the bunch.  The non-connected balloons (A non-DAG node, like a {{{set}}} or material, for example) has no rope, so it couldn't have a {{{MDagPath}}} associated with it, since it isn't part of the DAG hierarchy.

Another way to think about it:  In Windows Explorer (Mac Finder, etc), all files have a path to them, showing where they live in the directory hierarchy.  A {{{MObject}}} represents the file, and the {{{MDagPath}}} the path to the file. 

Basically, every node in Maya can be expressed as a {{{MObject}}}, but only those nodes in the DAG (nodes that can be parented) can have a {{{MDagPath}}} associated with them.  
----
(From the {{{MFnDagNode}}} class [[reference|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dag_node.html]])
There are two ways to specify a DAG node in Maya. 
#The first is to use an {{{MObject}}} handle, which acts as a pointer to a specific node in the DAG. Given only an {{{MObject}}}, ''it is not possible to do world space operations on a DAG node because there may be more than one path through the DAG to any given node (due to instancing)''. In other words, it is not possible to identify a particular //instance// only given an {{{MObject}}}.
#In many cases it is preferable to use a DAG path ({{{MDagPath}}}) to specify a //DAG// node (rather than a //DG// node.  Remember, DAG nodes have parent\child hierarchies, DG nodes don't). ''A DAG path always refers to a specific instance of an object''. This makes it possible to perform unambiguous world space transformations.
So a {{{MDagPath}}} can find the {{{MObject}}} associated with it, but not easily vice-versa, since if an {{{MObject}}} has been instanced, it can have multiple {{{MDagPath}}}s.
----
''{{{MObject}}} description (from its docs):''
<<<
~MObject is the generic class for accessing all Maya internal modelling, animation and rendering Objects, collectively referred to as Model Objects, through the API. This includes ''all Dependency Graph (DG) Nodes, of which Directed Acyclic Graph (DAG) Nodes'' are a subset.

Each instance of an ~MObject represents a specific ''Node'' or ''Attribute on a Node'' in the DG. Under the C++ class scheme ~MObjects simply have type ~MObject with no base or derived hierarchy. However, ~MObjects may be many different types of Model objects, from Attributes to transforms, including components of geometry such as Control Vertices (CV), faces, edges and vertices. This allows many different types of objects to be moved accessed through the API without undue concern on the part of plug-in developers for the type of object being manipulated.
<<<
----
''{{{MDagPath}}} description (from its docs):''
<<<
A DAG path is a path from the world node to a particular object in the DAG.
<<<
----
*{{{MObject}}} objects can be assigned to the [[MFnDependencyNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html]] function set.
**You can use {{{OpenMaya.MGlobal.getFunctionSetList(MObject)}}} to get a list (of strings) representing which function sets can be used on an {{{MObject}}}.
*{{{MDagPath}}} objects can be assigned to the [[MFnDagNode|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dag_node.html]] function set (which inherits from the {{{MFnDependencyNode}}}).
----
!Get {{{MObject}}} & {{{MDagPath}}} by string name:
There are a variety of ways to get {{{MObject}}}s and {{{MDagPath}}}s via the return of API calls.  But if you need to generate one based on a given object name in Maya, here's how you can go about doing it:

Python API 2.0:  ([[MSelectionList docs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=Maya_SDK_py_ref_class_open_maya_1_1_m_selection_list_html]])
{{{
import maya.api.OpenMaya as om2

node = 'group1|pCube1'

mObject = om.MSelectionList().add(node).getDependNode(0)
mDagPath = om.MSelectionList().add(node).getDagPath(0)
}}}
Python API 1.0
{{{
# Python code
import maya.OpenMaya as om

# Define our node by name.  
# Make sure to pass in the full path to avoid any name-clashing:
node = 'group1|pCube1'

# Create a selection list (they hold MObjects):
selList = om.MSelectionList()
# Add our node by-name to the sel list.  If there is a name-clash,
# this will raise a RuntimeError:
selList.add(node)
}}}
Now that we have a populated {{{MSelectionList}}} you can extract data from it.  
Here we get an {{{MDagPath}}}:
{{{
# Create an empty MDagPath object:
mDagPath = om.MDagPath()
# Extract the first item from our MSelectionList as an MDagPath:
selList.getDagPath(0, mDagPath)
}}}
Here we get an {{{MObject}}}:
{{{
# Create an empty MObject object:
mObject = om.MObject()
# Extract the first item from our MSelectionList as an MObject:
selList.getDependNode(0, mObject)
}}}
!Get {{{MObject}}} from {{{MDagPath}}}:
{{{
mObject = mDagPath.node()
}}}
!Get {{{MDagPath}}} from {{{MObject}}}:
Let's go the other way now.  A {{{MDagPath}}} can easily find it's {{{MObject}}} (shown above).  But to go the other direction requires a bit more work.  Continuing from the example above...
{{{
dPath = None
if om.MObject.hasFn(mObject, om.MFn.kDagNode):
    dPath = om.MDagPath()
    om.MDagPath.getAPathTo(mObject, dPath)
}}}
What's going on?  We test to see if our {{{mObject}}} has the {{{kDagNode}}} function.  If it does, that means that it //is/// a DAG node, thus has a path we can query.  We then make an empty {{{MDagPath}}} object, and fill it with the path to the {{{MObject}}}.  It should be noted that if the {{{MObject}}} is an instance, this method will return the first path found.  You can use {{{MDagPath.getAllPathsTo()}}} to find all instances.
!Get a nodes string name from a {{{MObject}}} or {{{MDagPath}}}:
Going the other way again, once you have a {{{MObject}}} or {{{MDagPath}}}, how can you query the string name of the Maya node they wrapper?
!!!{{{MObject}}}:
Here we create a {{{MFnDependencyNode}}}, and can pull the name from that:
{{{
mFnDependNode = om.MFnDependencyNode(mObject)
nodeName = mFnDependNode.name()
}}}
!!!{{{MDagPath}}}:
The {{{MDagPath}}} can access the name directly:
{{{
nodeName = mDagPath.fullPathName()
}}}
You can also extract it from a {{{MFnDagNode}}} function set:
{{{
mFnDagNode = om.MFnDagNode(mDagPath)
nodeName = mFnDagNode.fullPathName()
}}}


A really good blog post covers their creation and usage here:
http://around-the-corner.typepad.com/adn/2012/10/maya-custom-manipulators-.html

All are derived from {{{OpenMayaUI}}}: 
*[[MFnManip3D|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_manip3_d.html]]   (inherits from {{{OpenMaya.MFnTransform}}}).
**[[MFnCircleSweepManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_free_point_triad_manip.html]]
**[[MFnCurveSegmentManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_curve_segment_manip.html]]
**[[MFnDirectionManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_direction_manip.html]]
**[[MFnDiscManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_disc_manip.html]]
**[[MFnDistanceManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_distance_manip.html]]
**[[MFnFreePointTriadManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_free_point_triad_manip.html]]
**[[MFnPointOnCurveManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_point_on_curve_manip.html]]
**[[MFnPointOnSurfaceManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_point_on_surface_manip.html]]
**[[MFnRotateManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_rotate_manip.html]]
**[[MFnScaleManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_scale_manip.html]]
**[[MFnStateManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_state_manip.html]]
**[[MFnToggleManip|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_fn_toggle_manip.html]]
Derived from {{{OpenMayaMPx}}}:
*[[MPxManipulatorNode|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_px_manipulator_node.html]] (inherits from {{{OpenMayaMPx.MPxNode}}})
The {{{OpenMayaRender}}} package via the  the  [[MHardwareRenderer|http://download.autodesk.com/us/maya/2011help/API/class_m_hardware_renderer.html]] class provides access to a [[MGLFunctionTable|http://download.autodesk.com/us/maya/2010help/api/class_m_g_l_function_table.html]] class.  It is a "utility class which provides wrappers for the basic functions in the ~OpenGL API".  As of Maya 2011, it supports up to ~OpenGL 2.0.
~OpenGL is on a more current version than Maya uses.  Legacy docs to 2.1 can be found [[here|http://www.opengl.org/sdk/docs/man/]]

These calls can be used (among many other places) in the {{{draw()}}} method of the [[MPxLocatorNode|http://download.autodesk.com/us/maya/2010help/api/class_m_px_locator_node.html]], see [[API: Simple scripted plugin locator node]].

Example:
{{{
import maya.OpenMayaRender as omr

renderer = omr.MHardwareRenderer.theRenderer() # returns MHardwareRenderer
glFT = renderer.glFunctionTable() # returns MGLFunctionTable

for d in sorted(dir(glFT)):
    print d
}}}
This will print a lot of stuff:
{{{
...
glProgramParameters4dvNV
glProgramParameters4fvNV
glProgramString
glPushAttrib
glPushClientAttrib
glPushMatrix
glPushName
glRasterPos2d
glRasterPos2dv
glRasterPos2f
glRasterPos2fv
...
}}}
Should note there are no docs for the static (class) method {{{theRenderer()}}} in the {{{MHardwareRenderer}}} docs, weird.
----
''In addition'', the {{{OpenMayaRender}}} module itself has many ~OpenGL related constants you can access.  Which, like the above docs say, are prefixed with a '{{{M}}}'. So if you were looking for the ~OpenGL constant {{{GL_LIGHTING}}}, you would find its value in 
{{{
OpenMayaRender.MGL_LIGHTING
}}}
They can be used like so (from a scripted plugin example):
{{{
glFT.glPushAttrib(omr.MGL_CURRENT_BIT)
}}}
Examples using [[OpenMaya.MItDag|http://download.autodesk.com/us/maya/2011help/API/class_m_it_dag.html]]
The {{{MItDag}}} class will iterate over all nodes in the DAG (directed, acyclic graph), which is a subset of the DG (dependency graph).  DAG nodes have a transform|shape relationship.

The below example runs most of the methods on the class, exposing the values to be printed.
{{{
import maya.OpenMaya as om

def example_MItDag():

    # This iterator can be filtered by node type as well, via MFn.k* types
    dagIterator = om.MItDag()  # MItDag
    print "MItDag iteration info:"
    while not dagIterator.isDone():
        #----------------------------------------------------
        # Querying info specifically on the MItDag node:

        currentItem = dagIterator.currentItem() # MObject
        root = dagIterator.root() # MObject
        depth = dagIterator.depth() # int

        path = om.MDagPath() # MDagPath
        dagIterator.getPath(path)

        pathArray = om.MDagPathArray() # MDagPathArray
        dagIterator.getAllPaths(pathArray)

        fullPathName = dagIterator.fullPathName() # string
        partialPathName = dagIterator.partialPathName() # string

        isInstanced = dagIterator.isInstanced() # bool
        instances = 0
        instanceCount = dagIterator.instanceCount(instances) # unsigned int

        willTransverseUnderWorld = dagIterator.willTraverseUnderWorld() # bool

        # Let's get the function sets for this item as well, which basically tell
        # us what this thing is:
        funcSets = []
        om.MGlobal.getFunctionSetList(currentItem, funcSets)

        #----------------------------------------------------
        # Print info:
        currentItemFunc = om.MFnDependencyNode(currentItem)
        rootFunc = om.MFnDependencyNode(root)
        pathFunc = om.MFnDagNode(path)

        print "\t", currentItemFunc.name(), " : Current Item (MFnDependencyNode.name())"
        print "\t\tRoot (MFnDependencyNode.name()):", rootFunc.name()
        print "\t\tDepth (int):", depth
        print "\t\tPath (MFnDagNode.fullPathName()):", pathFunc.fullPathName()
        print "\t\tPath Array Items (MDagPath.fullPathName()):"
        for i in range(pathArray.length()):
            arrayItem = pathArray[i] # MDagPath
            arrayItemFunc = om.MFnDagNode(arrayItem)
            print "\t\t\t", arrayItemFunc.fullPathName()
        print "\t\tFull Path Name (string):", fullPathName
        print "\t\tPartial Path Name (string):", partialPathName
        print "\t\tIs Instanced? (bool):", isInstanced
        print "\t\tInstances: (int):", instances
        print "\t\tWill Transverse Underworld? (bool):", willTransverseUnderWorld
        print "\t\t'Current Item' function sets (MGlobal.getFunctionSetList()):"
        for fset in funcSets:
            print "\t\t\t", fset

        dagIterator.next()
}}}
Running it prints:
{{{
example_MItDag()

MItDag iteration info:
	world  : Current Item (MFnDependencyNode.name())
		Root (MFnDependencyNode.name()): world
		Depth (int): 0
		Path (MFnDagNode.fullPathName()): 
		Path Array Items (MFnDagNode.fullPathName()):
			
		Full Path Name (string): 
		Partial Path Name (string): 
		Is Instanced? (bool): False
		Instances: (int): 0
		Will Transverse Underworld? (bool): False
		Function Sets (MGlobal.getFunctionSetList()):
			kBase
			kNamedObject
			kDependencyNode
			kDagNode
			kWorld
	groundPlane_transform  : Current Item (MFnDependencyNode.name())
		Root (MFnDependencyNode.name()): world
		Depth (int): 1
		Path (MFnDagNode.fullPathName()): |groundPlane_transform
		Path Array Items (MFnDagNode.fullPathName()):
			|groundPlane_transform
		Full Path Name (string): |groundPlane_transform
		Partial Path Name (string): groundPlane_transform
		Is Instanced? (bool): False
		Instances: (int): 0
		Will Transverse Underworld? (bool): False
		Function Sets (MGlobal.getFunctionSetList()):
			kBase
			kNamedObject
			kDependencyNode
			kDagNode
			kTransform
etc...
}}}
Examples using [[OpenMaya.MItDependencyGraph|http://download.autodesk.com/us/maya/2010help/API/class_m_it_dependency_graph.html]]
The {{{MItDependencyGraph}}} is used for transversing connections to a node.  The below example will print every node connected to the incoming side of the given 'nodeName':
{{{
import maya.OpenMaya as om

def example_MItDependencyGraph():

    # Convert a node name to a MObject, to pass to our MItDependencyGraph class:
    nodeName = 'myAwesomeNode'
    selList = om.MSelectionList()
    selList.add(nodeName)
    node = om.MObject()
    selList.getDependNode(0, node)

    # Iterate through all incoming connections to our node:
    dependGraphIter = om.MItDependencyGraph(node,
                                            om.MItDependencyGraph.kUpstream,
                                            om.MItDependencyGraph.kPlugLevel)
    print "%s: All Incoming Connected Nodes"%nodeName
    while not dependGraphIter.isDone():
        currentItem = dependGraphIter.currentItem() # MObject
        currentItemFunc = om.MFnDependencyNode(currentItem)
        name = currentItemFunc.name()
        print "\t", name
        dependGraphIter.next()
}}}
Examples using [[OpenMaya.MItDependencyNodes|http://download.autodesk.com/us/maya/2010help/API/class_m_it_dependency_nodes.html]]
The {{{MItDependencyNodes}}} class will iterate over all nodes in the DG (dependency graph), basically all nodes in the scene.  

The below example runs most of the methods on the class, exposing the values to be printed.
{{{
import maya.OpenMaya as om

def example_MItDependencyNodes():

    # This list can be filtered by type as well, via MFn.k* types
    # Let's only look for 'partition' nodes:
    iterFilter = om.MFn.kPartition
    dependIterator = om.MItDependencyNodes(iterFilter) # MItDependencyNodes

    print "MItDependencyNodes iteration info:\n"
    while not dependIterator.isDone():

        #----------------------------------------------------
        # Querying info specifically on the MItDag node:

        # Really, this is all it has to query:
        thisNode = dependIterator.thisNode() # MObject

        # Let's get the function sets for this item as well, which basically tell
        # us what this thing is:
        funcSets = []
        om.MGlobal.getFunctionSetList(thisNode, funcSets)

        #----------------------------------------------------
        # Print info:

        thisNodeFunc = om.MFnDependencyNode(thisNode)
        print "\t", thisNodeFunc.name(), " : This Node (MObject.name())"
        print "\t\t'This Node' function sets (MGlobal.getFunctionSetList()):"
        for fset in funcSets:
            print "\t\t\t", fset

        dependIterator.next()
}}}
Running it prints:
{{{
example_MItDependencyNodes()

MItDependencyNodes iteration info:

	renderPartition  : This Node (MObject.name())
		'This Node' function sets (MGlobal.getFunctionSetList()):
			kBase
			kNamedObject
			kDependencyNode
			kPartition
	characterPartition  : This Node (MObject.name())
		'This Node' function sets (MGlobal.getFunctionSetList()):
			kBase
			kNamedObject
			kDependencyNode
			kPartition
}}}
Short version:  As of Maya 2010, this is not doable.  The {{{MSyntax}}} class doesn't support the passing in of array items, and the {{{makeFlagMultiUse}}} method requires you to pass in your args like:
{{{
// mel
myCmd -arg "a" -arg "b" -arg "c";
}}}
rather than an actual array:
{{{
// mel
myCmd -arg {"a","b","c"};
}}}
This is further compounded that (as of this authoring) it seems like Python has problems querying multi-use flags.  You can't pass the same parameter name over and over in a Python function:
{{{
# Invalid Python syntax:
myCmd(arg="a", arg="b", arg="c")
}}}
So you have to pass in multi-flag data as a //list// (array):
{{{
# Python
myCmd(arg=["a","b","c"])
}}}
But it doesn't seem to work in my own scripted plugins, and other blog posts confirm this :(  
However, standard Maya commands //do work// this way via Python, but I'm guessing it's because they're authored via c++, rather than Python.
Even crazier is that if I execute my scripted plugin command via Python and it fails when querying the mult-use arguments, calling to it from mel (per the top example) //does// work.  Buuuuugy.
*[[MSyntax|http://download.autodesk.com/us/maya/2010help/API/class_m_syntax.html]] docs 
*[[Forum Posts|http://groups.google.com/group/python_inside_maya/search?group=python_inside_maya&q=MSyntax+array&qt_g=Search+this+group]]
Another thread here discusses it pretty well:  http://forums.cgsociety.org/archive/index.php/t-701733.html
It appears that the main hack is to pass in a single string you can then later split into the array items of your choice, which is //terrible//...  From that form post: ''"MEL commands //don't// have the ability to accept a string array as a single flag argument."'', which seems to be true based on experimentation.
----
!However
The giant however:  You can pass a list of object names (or a single name) as strings into a command, as the first argument.  As it turns out, these names are arbitrary:  They don't necessarily have to match any objects in the scene.  Code snippets to set this up:
{{{
# Inside your scripted plugin class...
    def doIt(self, argList):
        argData = om.MArgDatabase(self.syntax(), argList)
        strings = []
        argData.getObjects(strings)
        if len(strings):
            print '\tPassed-in string arguments: "%s"'%'", "'.join(strings)
        # .....

    @staticmethod
    def newSyntax():
        syntax = om.MSyntax()
        syntax.setObjectType(om.MSyntax.kStringObjects)
        # .....
}}}
{{{
# Then calling to the command via Python:
myCmd(["stringA", "stringB"])
}}}
{{{
// Via mel:
myCmd "stringA" "stringB";
}}}
As you can see, this allows you to pass an actual list in via Python.   Trying to pass these values as a string array in mel causes an error.
I have an example of this in action here: [[API: Simple scripted plugin argument passing]]
When authoring custom nodes or deformers, you'll sometime want access to their built-in (default, Maya authored) attributes.  For example, let's say you're making a deformer, and you want access to the {{{envelope}}} attribute inside the {{{deform()}}} method.  How do you get access to that?

Maya has provided a magical, semi-secret location for storing those attributes inside the {{{OpenMayaMPx.cvar}}} '{{{swigvarlink}}}' datatype.  It appears to have {{{MObject}}} representations of all the standard attrs one would need to access.  You extract them via the syntax "<Node Type>_<Attribute Name>".  For example, if you wanted to access the '{{{.envelope}}}' attr on a given deformer, you'd access it via '{{{OpenMayaMPx.cvar.MPxDeformerNode_envelope}}}'.

I've found two spots in the Maya docs that talk about them:
<<<
[[Example: Bounding Box Deformer|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/files/GUID-10CE99A6-2C32-49E1-85ED-2E2F6782CF23.htm]]
"Built-in Deformer Node Attributes - By inheriting from {{{MPxDeformerNode}}}, your class will have access to the input and output mesh attributes, available via {{{OpenMayaMPx.cvar.MPxDeformerNode_inputGeom}}} and {{{OpenMayaMPx.cvar.MPxDeformerNode_outputGeom}}} respectively. These variables are generated by the SWIG tool during the C++ header file conversion to Python. By the same process, your deformer also has access to the built-in "envelope" attribute, available via {{{OpenMayaMPx.cvar.MPxDeformerNode_envelope}}}. This envelope attribute is used to specify the amount of influence your deformer has on the mesh."
<<<
<<<
[[Using the Maya Python API|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/files/Maya_Python_API_Using_the_Maya_Python_API.htm]]
''Accessing static {{{MObjects}}} of an {{{MPx}}} class''
The proxy classes provide some standard information to a developer about the node that is being used. This includes attribute objects that are used to define the node. To access a static class {{{MObject}}} in the Maya Python API, similar code can be used:
{{{
envelope = OpenMayaMPx.cvar.MPxDeformerNode_envelope
}}}
After making this call, the envelope will be an {{{MObject}}} for {{{MPxDeformerNode::envelope}}}.
<<<
If you wanted to print a list, you'd have to jump through some hoops since the {{{swigvarlink}}} object doesn't support slicing or index access... but you //can// print it (looks like a {{{tuple}}}):
{{{
import maya.OpenMayaMPx as ompx
items = [item.strip() for item in str(ompx.cvar)[1:-1].split(',')]
for item in sorted(items):
    print item
}}}
{{{
MPxCameraSet_active
MPxCameraSet_camera
MPxCameraSet_cameraLayer
MPxCameraSet_order
...
}}}
With a bit more work on the above code, you can get a list of the supported node types:
{{{
MPxCameraSet
MPxConstraint
MPxDeformerNode
MPxEmitterNode
MPxFieldNode
MPxFluidEmitterNode
MPxHardwareShader
MPxHwShaderNode
MPxImagePlane
MPxLocatorNode
MPxManipulatorNode
MPxNode
MPxObjectSet
MPxParticleAttributeMapperNode
MPxSpringNode
MPxSurfaceShape
MPxTransform
MPxTransformationMatrix
}}}
So given all that, here are some use cases:
{{{
import maya.OpenMayaMPx as ompx
}}}
----
Inside the {{{deform()}}} method of a {{{OpenMayaMPx.MPxDeformerNode}}}, you need to query the value of the (standard, built-in) {{{envelope}}} attribute:
{{{
envelope_attr = ompx.cvar.MPxDeformerNode_envelope
envelopeHandle = dataBlock.inputValue(envelope_attr) # MDataHandle
envelopeValue = envelopeHandle.asFloat()
}}}
----
In the {{{nodeInitializer()}}} function of a {{{OpenMayaMPx.MPxDeformerNode}}}, you need to setup an {{{attributeAffects}}} relationship between a custom '{{{deform}}}' attribute, and the (standard, built-in) {{{outputGeometry}}} attr on the node:
{{{
outputGeom = ompx.cvar.MPxDeformerNode_outputGeom
MyDeformer.attributeAffects(MyDeformer.attr_deform, outputGeom)
}}}
----
Inside the {{{compute()}}} method of a {{{OpenMayaMPx.MPxNode}}}, you want to query the (standard, built-in) {{{caching}}} attr of the node:
{{{
caching_attr = ompx.cvar.MPxNode_caching
cachingHandle = dataBlock.inputValue(caching_attr)
cachingValue = envelopeHandle.asBool()
}}}
----
----
Also see:
*[[API: How can a custom node query a connected object in the compute method?]]
This is an expansion on my [[API: Simple scripted plugin command]] example.  It shows various ways to capture flag argument data as passed into a scripted-plugin based command.  It also exposes an obnoxious Python bug...

The below example scripted plugin will create a command called {{{argExample}}}.  The command doesn't do anything other than print the arguments passed to the various flags on the command.  Furthermore, you can pass object names to it as the first positional argument, and if it finds none, will instead try to operate on what you have selected.
{{{
# spArgExampleCmd.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

# -----------------------------------
# Plugin code

class ArgExample(ompx.MPxCommand):
    """
    http://download.autodesk.com/us/maya/2010help/API/class_m_px_command.html
    """
    # Name this command will use when executed via mel\Python:
    commandName = "argExample"

    # Setup our flags:
    flag_floatValue = '-fv'; flag_floatValueLong = '-floatValue'
    flag_stringValue = '-sv'; flag_stringValueLong = '-stringValue'
    flag_multiFloat = '-mf'; flag_multiFloatLong = '-multiFloat'
    flag_justFlag = '-jf'; flag_justFlagLong = '-justFlag'

    def __init__(self):
        """
        Initalize our superclass:
        """
        ompx.MPxCommand.__init__(self)

    def doIt(self, argList):
        """
        Superclass override.

        Parameters :
        argList : MArgList : http://download.autodesk.com/us/maya/2010help/API/class_m_arg_list.html
        """
        # Query what args, if any, were passed in via the MArgList.  MArgDatabase
        # inherits from MArgParser:
        # http://download.autodesk.com/us/maya/2010help/API/class_m_arg_parser.html
        # http://download.autodesk.com/us/maya/2010help/API/class_m_arg_database.html
        print "%s executed:" % self.commandName
        argData = om.MArgDatabase(self.syntax(), argList)

        # See what objects, if any, were passed into the command.  It should be
        # noted that these are actually just arbitrary string names:  They don't
        # necessarily need to match an existing node-name in the Maya scene.
        objects = []
        argData.getObjects(objects)
        if len(objects):
            print '\tPassed-in object arguments: "%s"'%'", "'.join(objects)
        # If no objects were passed in, see if any are selected:
        else:
            # If nothing was passed in, see if anything is selected:
            selList = om.MSelectionList()
            om.MGlobal.getActiveSelectionList(selList)
            if selList.length():
                print "\t%s objects are selected to work on." % selList.length()

        # Query the flag argument values:

        if argData.isFlagSet(ArgExample.flag_floatValue):
            index = 0 # flags can have multiple multiple arg indices, this only has one
            floatValue = argData.flagArgumentDouble(ArgExample.flag_floatValue, index)
            print "\t-floatValue", floatValue

        if argData.isFlagSet(ArgExample.flag_stringValue):
            index = 0
            floatValue = argData.flagArgumentString(ArgExample.flag_stringValue, index)
            print "\t-stringValue", floatValue

        if argData.isFlagSet(ArgExample.flag_multiFloat):
            multiFloatVals = []
            flagUses = argData.numberOfFlagUses(ArgExample.flag_multiFloat)
            print "\t-multiFloat : Used %s time(s)" % flagUses
            for i in range(flagUses):
                # Create a list to grab a single item from another list.  Strange but true.
                flagArgList = om.MArgList()
                argData.getFlagArgumentList(ArgExample.flag_multiFloat, i, flagArgList)
                if flagArgList.length():
                    multiFloatVals.append(flagArgList.asDouble(0))
            if multiFloatVals:
                for val in multiFloatVals:
                    print "\t\t", val
            else:
                print "\t\tSorry, it's a known bug you can't query mult-arg values via Python :-("
                print "\t\tTry executing this came code via mel, it will work :-S"

        if argData.isFlagSet(ArgExample.flag_justFlag):
            print "\t-justFlag : Is set!"

    #---------------------------------------------------
    # Command & syntax creation:

    # 'Creator Function' needs to return a pointer new instance of this type of class,
    # based on how the c++ code works.
    # As you can see, the OpenMayaMPx module has it's own functions (for which
    # I can find no online documentation).
    @classmethod
    def cmdCreator(cls):
        return ompx.asMPxPtr(cls())

    @staticmethod
    def newSyntax():
        """
        Returns an MSyntax that can be passed to the MFnPlugin inside the
        initializePlugin funcition.
        This syntax is based around the custom flags that the command can accept.
        Important:  Maya's notes state that this function must have a name
        other than 'syntax()'.
        """
        # http://download.autodesk.com/us/maya/2010help/API/class_m_syntax.html
        syntax = om.MSyntax()

        # Tell the syntax to grab any object-names passed in to be accepted as
        # strings (either a single string, or as a list of strings) rather than
        # MObjects.  This is always the first (but optional) argument of the
        # command.  If needed, you could specify a minimum number of object names
        # to be passed in as well.
        syntax.setObjectType(om.MSyntax.kStringObjects)

        # There isn't a constant for float, so we use double:
        syntax.addFlag(ArgExample.flag_floatValue, ArgExample.flag_floatValueLong, om.MSyntax.kDouble)
        # Our string flag:
        syntax.addFlag(ArgExample.flag_stringValue, ArgExample.flag_stringValueLong, om.MSyntax.kString)
        # Multi-use float flag:
        syntax.addFlag(ArgExample.flag_multiFloat, ArgExample.flag_multiFloatLong, om.MSyntax.kDouble)
        syntax.makeFlagMultiUse(ArgExample.flag_multiFloat)
        # Just a flag, takes no arguments:
        syntax.addFlag(ArgExample.flag_justFlag, ArgExample.flag_justFlagLong)

        return syntax

# -----------------------------------
# Plugin registration\degregistration block
# initializePlugin & uninitializePlugin are *required functions*

def initializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject, "Eric Pavey", "1.0")
    try:
        # Try to register our plug by passing in the kPluginCmdName, and the command creator:
        # we use the '.registerCommand()' method since we're trying to build a
        # command.  Other plugin types use different registration functions.  The
        # args do differ, check docs.
        mfnPlugin.registerCommand(ArgExample.commandName,
                                  ArgExample.cmdCreator,
                                  ArgExample.newSyntax)
    except(Exception):
        om.MGlobal.displayError("\nArgExample registration failed\n")

def uninitializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject)
    try:
        # Try to deregister our plug by passing in the kPluginCmdName, and the command creator:
        mfnPlugin.deregisterCommand(ArgExample.commandName)
    except(Exception):
        om.MGlobal.displayError("\nArgExample deregistration failed\n")
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to run the command.  But you'll get different results if you execute it in mel, or Python:
{{{
# Python code
import maya.cmds as mc
mc.argExample(["spam", "eggs"], floatValue=23, stringValue="chocolate", multiFloat=[6,2,3], justFlag=True)
}}}
{{{
argExample executed:
	Passed-in object arguments: "spam", "eggs"
	-floatValue 23.0
	-stringValue chocolate
	-multiFloat : Used 3 time(s)
		Sorry, it's a known bug you can't query mult-arg values via Python :-(
		Try executing this came code via mel, it will work :-S
	-justFlag : Is set!
}}}
As you can see, Python can't seem to handle the mult-use flag :(
But executing this in mel...
{{{
// mel code
argExample -floatValue 23 -stringValue "chocolate" -multiFloat 6 -multiFloat 2 -multiFloat 3 -justFlag "spam" "eggs";
}}}
{{{
argExample executed:
	Passed-in object arguments: "spam", "eggs"
	-floatValue 23.0
	-stringValue chocolate
	-multiFloat : Used 3 time(s)
		6.0
		2.0
		3.0
	-justFlag : Is set!
}}}
Lo and behold, the multi-use args show up!  Buggy, buggy, buggy :(
Example of authoring a simple scripted plugin //command//.  It features value setting, querying, and undoing.

This scripted plugin is authored slightly different from other 'simple scripted plugin' examples I've done:  
*The command name is a class attribute rather than a module constant.
*The command creator and new syntax functions have been made class staticmethods, rather than being external functions.  
It behaves the exact same as other solutions, just a different way of doing it.

Why would you want to do it this way, compared to the other alternatives I've presented?  The main reason I've found is if you're authoring multiple scripted plugins //in the same module//:  To help organize this special data, lumping it in with the class makes it easier to find and distinguish against the others.  

Here is another [[good, even more simple example|http://download.autodesk.com/us/maya/2011help/API/hello_world_cmd_8py-example.html]] from the Maya API docs.

When it comes to creating a scripted plugin //command//, these are the major things required:
* A class inheriting from {{{maya.OpenMayaMPx.MPxCommand}}}.
**The name of this class is up to you, it is independent from the name of the node being created.  The node name is actually defined in {{{initializePlugin()}}}
**Must initialize the superclass in {{{__init__()}}}
**Override the {{{doIt()}}} method to parse any given arguments, and setup states for undo\redo.
**Override the {{{redoIt()}}} method that does the actual work of the command.
**Override the {{{undo()}}} method to support undoing, which should reset the state before the command was executed.
**Override the {{{isUndoable()}}} method to tell the command it actually //can// undo.
*Author 'node creator' and 'new syntax' functions.  The names are arbitrary, but {{{cmdCreator()}}} and {{{newSyntax()}}} are good ideas.  These are both used by the {{{initializePlugin()}}} function.  The syntax function creates a {{{MSyntax}}} object that parses all the incoming flags to the command.
*  Author {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.  Other than the overridden methods above, these are the only items in the code that need specific names.
It should be noted that if you don't care about flags, or undoing, the only method you need to override is {{{doit()}}}, and you can skip making the syntax function.
{{{
# spHelloWorldCmd.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

# -----------------------------------
# Plugin code

class HelloWorldCommand(ompx.MPxCommand):
    """
    This class will create a command that when executed, will print "Hello World"
    with a number next to it showing how many times the command has been executed.
    If you edit the command, you can pass in a new value, and if you query the
    command it will return the current value.

    http://download.autodesk.com/us/maya/2010help/API/class_m_px_command.html
    """
    # Name this command will use when executed via mel\Python:
    commandName = "helloWorld"

    # This will store a value we can query, set, or print.
    value = 0

    # Setup our flags:
    flag_value = '-v'; flag_valueLong = '-value'
    flag_help = "-h"; flag_helpLong = "-help"

    # Text displayed when the user queries its help:
    helpText = """
    The helloWorld command does these things:
    * Running the command will print 'Hello World #' with the number of times
        it has been executed.
    * You can pass in a -value to set the current number.
    * If you -query it, it will return the current value.
    * Full undo/redo is supported.
    * And of course if you see this, you know it has -help..."""

    def __init__(self):
        """
        Initalize our superclass:
        """
        ompx.MPxCommand.__init__(self)
        # Is the command being queried or edited?
        self.isQuery = False
        self.isEdit = False

    def doIt(self, argList):
        """
        Superclass override.
        This method is called from script when this command is called.
        It should set up any class data necessary for redo/undo,
        parse any given arguments, and then call redoIt.

        Parameters :
        argList : MArgList : http://download.autodesk.com/us/maya/2010help/API/class_m_arg_list.html
        """
        # Query what args, if any, were passed in.  MArgDatabase inherits from
        # MArgParser:
        # http://download.autodesk.com/us/maya/2010help/API/class_m_arg_parser.html
        # http://download.autodesk.com/us/maya/2010help/API/class_m_arg_database.html
        # The syntax being queried is the one that was passed to the MFnPlugin
        # inside the initializePlugin() funcition.
        argData = om.MArgDatabase(self.syntax(), argList)
        # Are we in query mode?
        self.isQuery = argData.isQuery()
        # Support undoing by storing the previous state of the command:
        self.prevVal = HelloWorldCommand.value

        # If the user is querying help, display it, and reutrn out of the command.
        if argData.isFlagSet(HelloWorldCommand.flag_help):
            self.setResult(HelloWorldCommand.helpText)
            return om.MStatus.kSuccess

        # If the user passes in a value, and we're not in query mode, set the value:
        if argData.isFlagSet(HelloWorldCommand.flag_value) and not self.isQuery:
            index = 0 # flags can have multiple multiple arg indices, this only has one
            HelloWorldCommand.value = argData.flagArgumentInt(HelloWorldCommand.flag_value, index)
            # so, we're obviously editing it...
            self.isEdit = True

        return self.redoIt()

    def redoIt(self):
        """
        Superclass override:  Any actual work the command should do is done in here.
        """
        # If we're in query mode, return the value:
        if self.isQuery:
            self.setResult(HelloWorldCommand.value)
        # Otherwise, if the command hasn't already been edited, execute the
        # command code, which is in this case as simple update value and print.
        # It could obviously be much more robust.
        elif not self.isEdit:
            HelloWorldCommand.value += 1
            print "Hello World #%s" % HelloWorldCommand.value

    def undoIt(self):
        """
        Superclass override:  Reset the state if undone.
        """
        HelloWorldCommand.value = self.prevVal

    def isUndoable(self):
        """
        Superclass override:  Enable undoing.
        """
        return True

    #---------------------------------------------------
    # Command & syntax creation:

    # 'Creator Function' needs to return a pointer new instance of this type of class,
    # based on how the c++ code works.
    # As you can see, the OpenMayaMPx module has it's own functions (for which
    # I can find no online documentation).
    @classmethod
    def cmdCreator(cls):
        return ompx.asMPxPtr(cls())

    @staticmethod
    def newSyntax():
        """
        Returns an MSyntax that can be passed to the MFnPlugin inside the
        initializePlugin funcition.
        This syntax is based around the custom flags that the command can accept.
        Important:  Maya's notes state that this function must have a name
        other than 'syntax()'.
        """
        # http://download.autodesk.com/us/maya/2010help/API/class_m_syntax.html
        syntax = om.MSyntax()
        syntax.addFlag(HelloWorldCommand.flag_value, HelloWorldCommand.flag_valueLong,
                       om.MSyntax.kLong)
        syntax.addFlag(HelloWorldCommand.flag_help, HelloWorldCommand.flag_helpLong)
        # Allow this syntax to be both queryable, and editable:
        syntax.enableQuery(True)
        syntax.enableEdit(True)
        return syntax

# -----------------------------------
# Plugin registration\degregistration block
# initializePlugin & uninitializePlugin are *required functions*

def initializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject, "Eric Pavey", "1.0")
    try:
        # Try to register our plug by passing in the kPluginCmdName, and the command creator:
        # we use the '.registerCommand()' method since we're trying to build a
        # command.  Other plugin types use different registration functions.  The
        # args do differ, check docs.
        mfnPlugin.registerCommand(HelloWorldCommand.commandName,
                                  HelloWorldCommand.cmdCreator,
                                  HelloWorldCommand.newSyntax)
    except(Exception):
        om.MGlobal.displayError("\nHelloWorldCommand registration failed\n")

def uninitializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject)
    try:
        # Try to deregister our plug by passing in the kPluginCmdName, and the command creator:
        mfnPlugin.deregisterCommand(HelloWorldCommand.commandName)
    except(Exception):
        om.MGlobal.displayError("\nHelloWorldCommand deregistration failed\n")
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to type in the script editor (works in mel too):
{{{
import maya.cmds as mc

mc.helloWorld()
# Hello World #1
mc.helloWorld()
# Hello World #2
}}}
{{{
mc.helloWorld(value=23)
v = mc.helloWorld(query=True)
print v
# 23
mc.helloWorld()
# Hello World #24
}}}
{{{
mc.helloWorld(help=True)
# Result: 
    The helloWorld command does these things:
    * Running the command will print 'Hello World #' with the number of times
        it has been executed.
    * You can pass in a -value to set the current number.
    * If you -query it, it will return the current value.
    * Full undo/redo is supported.
    * And of course if you see this, you know it has -help... # 
}}}
I wanted to build a bare-minimum framework for authoring a deformer node via a scripted plugin. Maya comes with a simple example [[here|http://download.autodesk.com/us/maya/2011help/API/y_twist_node_8py-example.html]],
which I stripped down even more, added a bunch of notes, etc.

Long after I authored this, found a good blog post on general API deformer stuff: [[Maya's Deformer Architecture|http://around-the-corner.typepad.com/adn/2012/09/maya-deformer-architecture.html]]

When the deformer is applied to a node, it will randomly offset the node's points based on its {{{deform}}} attr.

When it comes to creating a locator node, these are the major things required:
* A class inheriting from {{{maya.OpenMayaMPx.MPxDeformerNode}}}.
**It in turn inherits from {{{maya.OpenMayaMPx.MPxNode}}}
**The name of this class is up to you, it is independent from the name of the node being created.  The node name is actually defined in {{{initializePlugin()}}}
**Must initialize the superclass in {{{__init__()}}}
**Override the {{{deform()}}} method, put your point deformation code in there.
*Author 'node creator' and 'node initializer' functions.  The names are arbitrary, but {{{nodeCreator()}}} and {{{nodeInitializer()}}} are good ideas.  These are both used by the {{{initializePlugin()}}} function.
*  Author {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.  Other than the overridden methods above, these are the only items in the code that need specific names.
{{{
# spSimpleDeformer.py
import random

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

#------------------
# Constants:
# c++ uses 'k' to specify constants, Python usually uses all caps.
# Using k here since we're dealing with Maya legacy, but ultimately it doesn't
# matter.
kPluginNodeType = "spSimpleDeformer"
# This identifier should be provided by Autodesk, to make sure it will never
# clash with any other node type.  I just made this one up.
# http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
kPluginNodeId = om.MTypeId(0x00113)

class SimpleDeformer(ompx.MPxDeformerNode):
    """
    Scripted deformer node creation
    http://download.autodesk.com/us/maya/2011help/API/class_m_px_deformer_node.html
    Inherits from:
    http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html
    """
    # Define any input and output attrs at the class level.
    # These are MObject representations of the attributes on the physical
    # Maya node.  They are just placeholder:  They are replaced\populated by
    # nodeInitializer()
    # In fact, they could be None, but since ultimately they'll be MObjects,
    # just stick some dummy ones in there.
    attr_deform = om.MObject()

    def __init__(self):
        """
        Make sure to initilaize the superclass.  Not much else happens here.
        """
        ompx.MPxDeformerNode.__init__(self)

    def deform(self, dataBlock, geomIter, matrix, multiIndex):
        """
        Overridden method of MPxDeformerNode, where the deformation magic happens.

        Parameters:
        dataBlock : MDataBlock : The node's datablock.  Provides storage for the
            data being received by or sent by the node. It is the data for the
            plugs and attributes of the node.
            http://download.autodesk.com/us/maya/2011help/API/class_m_data_block.html
        geomIter : MItGeometry : An iterator for the current geometry being deformed.
            http://download.autodesk.com/us/maya/2011help/API/class_m_it_geometry.html
        matrix : MMatrix : the geometry's world space transformation matrix.
            http://download.autodesk.com/us/maya/2011help/API/class_m_matrix.html
        multiIndex : unsigned int : The index corresponding to the requested output
            geometry.
        """
        # Get the deform attribute value from the datablock:
        # http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html
        deformHandle = dataBlock.inputValue(self.attr_deform) # MDataHandle
        deformValue = deformHandle.asDouble() # double

        # Get the Maya node's envelope attr value.  The MPxDeformerNode has a
        # static ''envelope' attribute that can be queried to access this.
        # It appears that the envelope attribute on the Maya node is auto-created.
        # Furthermore, it seems the the OpenMayaMPx.cvar is a 'swigvarlink'
        # (whatever that is) that holds constants for use elsewhere in the API
        envelope_attr = ompx.cvar.MPxDeformerNode_envelope
        envelopeHandle = dataBlock.inputValue(envelope_attr) # MDataHandle
        envelopeValue = envelopeHandle.asFloat()

        # Iterate over the objects points to deform it:
        while geomIter.isDone() == False:
            # We use the index of each point as the seed to its random function
            # so that the noise won't change randomly as the 'deform' attr is
            # changed, or the node is moved through space.
            random.seed(geomIter.index())

            # http://download.autodesk.com/us/maya/2011help/API/class_m_point.html
            point = geomIter.position() # MPoint
            # Do a simple random operation on each point:
            point.x = point.x + random.uniform(-deformValue, deformValue) * envelopeValue
            point.y = point.y + random.uniform(-deformValue, deformValue) * envelopeValue
            point.z = point.z + random.uniform(-deformValue, deformValue) * envelopeValue

            # Re-set the position of the item after it's been recalculated:
            geomIter.setPosition(point)
            geomIter.next()

#---------------------------------------------------
# Node creation and initialization:

# creator
def nodeCreator():
    return ompx.asMPxPtr(SimpleDeformer())

# initializer
def nodeInitializer():
    """
    This function sets up all the input and output attributes on the node:  What
    type of data they should expect, if they're inputs\outputs, keyable, connectable,
    default values, etc.  Also defines the relationships between attrs using
    attributeAffects():  If an input changes, you should update the output.
    """
    # Create the deform attribute:
    nAttr = om.MFnNumericAttribute()
    SimpleDeformer.attr_deform = nAttr.create("deform", "df",
                                              om.MFnNumericData.kDouble, 0.0)
    nAttr.setKeyable(True) # False by default

    # Add the attribute to the node:
    try:
        # MPxDeformerNode has a static 'outputGeom' attribute that needs to be
        # told to be affected when deformation occurs.  This maps to the
        # 'outputGeometry' attribute on the Maya node.
        SimpleDeformer.addAttribute(SimpleDeformer.attr_deform)
        outputGeom = ompx.cvar.MPxDeformerNode_outputGeom
        # Make sure that when the user adjusts the attr, it affects the geometry.
        SimpleDeformer.attributeAffects(SimpleDeformer.attr_deform, outputGeom)
    except:
        om.MGlobal.displayError("Failed to create attributes of %s node\n",
                                kPluginNodeType)

#------------------------------------------------------
# Loading and unloading:

# initialize the script plug-in
def initializePlugin(mobject):
    mplugin = ompx.MFnPlugin(mobject)
    try:
        mplugin.registerNode(kPluginNodeType, kPluginNodeId, nodeCreator,
                             nodeInitializer, ompx.MPxNode.kDeformerNode)
    except:
        om.MGlobal.displayError("Failed to register node: %s\n"%kPluginNodeType)

# uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = ompx.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(kPluginNodeId)
    except:
        om.MGlobal.displayError("Failed to unregister node: %s\n"%kPluginNodeType)
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), pick a node in your scene, then create the deformer:
{{{
import maya.cmds as mc
d = mc.deformer(type='spSimpleDeformer')
# [u'spSimpleDeformer1']
}}}
Since you're creating the node via the {{{deformer}}} command, you can easily capture the return value of your newly created node name.
You can now modify the deformer node's {{{deform}}} attribute, and watch the magic happen.
I wanted to build a bare-minimum framework for authoring a file translator via a scripted plugin. Maya comes with a simple example here:
http://download.autodesk.com/us/maya/2011help/API/custom_node_file_translator_8py-example.html
Which I modified and added notes to in the example below.

A nice advantage of making these is once they're authored and the plugin is enabled, you can drag & drop these file types in Maya to trigger the results.  But if you're looking to do that, this may be easier: [[How can you override Maya's drag & drop behavior?]].

Once this plugin is imported, you can export and import the new 'sft' data via the Maya UI, or a script, just like any other type.  This example simply exports the names of all nodes in the scene (or selected items) to a custom text file.  The importer will read the text file, and print the contents to the script editor.  But obviously this is just a simple framework that can be expanded upon.

When it comes to creating a file translator, these are the major things required:
* A class inheriting from {{{maya.OpenMayaMPx.MPxFileTranslator}}}.
**It in turn inherits from {{{maya.OpenMayaMPx.MPxMayaAsciiFilter}}}
**The name of this class is up to you, it is independent from the name of the node being created.  The node name is actually defined in {{{initializePlugin()}}}
**Must initialize the superclass in {{{__init__()}}}
**Depending on the functionality you desire, must override the methods {{{writer}}}, {{{reader}}}, {{{haveWriteMethod}}}, {{{haveReadMethod}}}, {{{filter}}}, {{{defaultExtension}}}, among others.
*Author a 'creator' function \ class method (we do it as a class method here).  The name is arbitrary, but {{{creator()}}} is a good ideas.  It is used by the {{{initializePlugin()}}} function.
*  Author {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.  Other than the overridden methods above, these are the only items in the code that need specific names.
{{{
# spSimpleFileTranslator.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

# -----------------------------------
# Plugin code

class SimpleFileTranslator(ompx.MPxFileTranslator):
    """
    This class will create a new file translator type that will write out a simple
    text file of all the nodes in the scene.  When reading the save data, it
    will print the result.

    http://download.autodesk.com/us/maya/2010help/API/class_m_px_file_translator.html
    """
    # Define the name of our file translator in Maya:
    name = "spSimpleFileTranslator"
    # Define the extension it will use. 'sft' = 'simple file translator'
    extension = 'sft'

    def __init__(self):
        """
        Initalize our superclass:
        """
        ompx.MPxFileTranslator.__init__(self)

    #----------------------------------------------------
    # Overridden superclass methods:
    # There are more than this, but we only need to use these for this example.

    def writer(self, fileObject, optionString, accessMode):
        """
        What is called to when writing a file.  In our example, we write out a
        simple txt file containing all the node names in the scene.

        This code supports exporting the whole scene, or only what's selected.

        Parameters:
        fileObject : MFileObject :
            http://download.autodesk.com/us/maya/2010help/API/class_m_file_object.html
        optionsString : string (MString in c++)
        accessMode : MFileTranslator.FileAccessMode enumerator :  Unknown mode (0),
            import into new scene (1), reference (2), import into current scene (3),
            save (4), export (5), export selected (6).
            http://download.autodesk.com/us/maya/2010help/API/class_m_px_file_translator.html#237971b037a8b0cbb697d61a01da4b5d
        """
        fullName = fileObject.resolvedFullName()
        try:
            with open(fullName, "w") as f:
                # If the whole scene is being exported.  The enum value is 5:
                if accessMode == ompx.MPxFileTranslator.kExportAccessMode:
                    f.write("# Simple text file of all nodes in the scene.\r\n")
                    mItDependNode = om.MItDependencyNodes()
                    # http://download.autodesk.com/us/maya/2010help/API/class_m_it_dependency_nodes.html
                    while not mItDependNode.isDone():
                        # Extract the MObject, and build a function set to query its name:
                        # http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html
                        mFnDependNode = om.MFnDependencyNode(mItDependNode.thisNode())
                        nodeName = mFnDependNode.name()
                        f.write('%s\r\n'%nodeName)
                        mItDependNode.next()
                # If we're exporting selected.  The enum value is 6:
                elif accessMode == ompx.MPxFileTranslator.kExportActiveAccessMode:
                    f.write("# Simple text file of selected nodes at time of export.\r\n")
                    selList = om.MSelectionList()
                    om.MGlobal.getActiveSelectionList(selList)
                    # http://download.autodesk.com/us/maya/2010help/API/class_m_it_selection_list.html
                    mItSelectionList = om.MItSelectionList(selList)
                    mObject = om.MObject()
                    while not mItSelectionList.isDone():
                        mItSelectionList.getDependNode(mObject)
                        mFnDependNode = om.MFnDependencyNode(mObject)
                        nodeName = mFnDependNode.name()
                        f.write('%s\r\n'%nodeName)
                        mItSelectionList.next()
                else:
                    raise Exception("Invalid accessMode argument: %s"%accessMode)
        except:
            om.MGlobal.displayError("Failed to write file information\n")
            raise

    def reader(self, fileObject, optionString, accessMode):
        """
        What is called to when reading a file.  In our case, it just parses a
        text file and prints the result in the Script Editor.

        Parameters:
        fileObject : MFileObject :
            http://download.autodesk.com/us/maya/2010help/API/class_m_file_object.html
        optionsString : string (MString in c++)
        accessMode : MFileTranslator.FileAccessMode enumerator : Unknown mode (0),
            import into new scene (1), reference (2), import into current scene (3),
            save (4), export (5), export selected (6).
            http://download.autodesk.com/us/maya/2010help/API/class_m_px_file_translator.html#237971b037a8b0cbb697d61a01da4b5d
        """
        lines = []
        try:
            fullName = fileObject.resolvedFullName()
            with open(fullName,"r") as f:
                lines = [item.strip() for item in f]
        except:
            om.MGlobal.displayError("Failed to read file information\n")
            raise
        print "\nContents of file:"
        for line in lines:
            print "\t", line
        om.MGlobal.displayInfo("Contents from file %s"%fullName)

    def haveWriteMethod(self):
        """
        The default is False:  Change to True if this can write a file.
        """
        return True

    def haveReadMethod(self):
        """
        The default is False:  Change to True if this can read a file.
        """
        return True

    def filter(self):
        """
        Return the custom filter used for this file type.  Default is "*.*"
        You can embed multiple filters like "*.txt;*.blah", etc.
        """
        return "*.%s"%SimpleFileTranslator.extension

    def defaultExtension(self):
        """
        Return the custom extension for this file type.  Default is an empty
        string.  If this is provided, this is what allows the plugin to support drag & drop
        Only supports a single extension though :( 
        """
        return SimpleFileTranslator.extension

    #---------------------------------------------------
    # Translator creation method:

    # 'Creator Function' needs to return a pointer new instance of this type of
    # class, based on how the c++ code works.
    # As you can see, the OpenMayaMPx module has it's own functions (for which
    # I can find no online documentation).
    @classmethod
    def creator(cls):
        return ompx.asMPxPtr(cls())

# -----------------------------------
# Plugin registration\degregistration block
# initializePlugin & uninitializePlugin are *required functions*

def initializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject, "Eric Pavey", "1.0")
    try:
        # Try to register our plug by passing in the commandName, and the
        # translatorCreator: we use the '.registerFileTranslator()' method since
        # we're trying to build a file translator.  Other plugin types use
        # different registration functions.  The args do differ, check docs.
        # http://download.autodesk.com/us/maya/2010help/API/class_m_fn_plugin.html
        mfnPlugin.registerFileTranslator(SimpleFileTranslator.name,
                                         None,
                                         SimpleFileTranslator.creator)
    except:
        om.MGlobal.displayError("SimpleFileTranslator registration failed")
        raise

def uninitializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject)
    try:
        # Try to deregister our plug by passing in the kPluginCmdName, and the command creator:
        mfnPlugin.deregisterFileTranslator(SimpleFileTranslator.name)
    except:
        om.MGlobal.displayError("SimpleFileTranslator deregistration failed")
        raise
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you can export and import the data either via the Maya UI (File->Export All, File->Export Selected, File->Import) and choose the new 'sft' file-type, or you can use these commands:
{{{
import maya.cmds as mc
# To export:
mc.file("C:/temp/sftTest.sft", type="spSimpleFileTranslator", exportAll=True)
# To import:
mc.file("C:/temp/sftTest.sft", type="spSimpleFileTranslator", i=True)
}}}
I wanted to build a bare-minimum framework for authoring a locator node via a scripted plugin. Maya comes with a pretty simple example here:
http://download.autodesk.com/us/maya/2011help/API/foot_print_node_8py-example.html
Which I stripped down even more, added a bunch of notes, etc.

The below example creates a new square-shaped node called '{{{spSimpleLocator}}}' with a single '{{{size}}}' custon attribute.  It will be a semi-transparent green in shaded mode.  There is a bug in the ~OpenGL code however:  It will occlude wireframe display behind it.  I don't know much about ~OpenGL code, hopefully can address in the future.

When it comes to creating a locator node, these are the major things required:
* A class inheriting from {{{maya.OpenMayaMPx.MPxLocatorNode}}}.
**It in turn inherits from {{{maya.OpenMayaMPx.MPxNode}}}
**The name of this class is up to you, it is independent from the name of the node being created.  The node name is actually defined in {{{initializePlugin()}}}
**Must initialize the superclass in {{{__init__()}}}
**Optional: Override the {{{compute()}}} method to do any extra internal-node computation.  Not needed for standard locator behavior.
**Override the {{{draw()}}} method, put your ~OpenGL draw calls in there.
**Override the {{{isBounded()}}} and {{{boundingBox()}}} methods.
*Author 'node creator' and 'node initializer' functions.  The names are arbitrary, but {{{nodeCreator()}}} and {{{nodeInitializer()}}} are good ideas.  These are both used by the {{{initializePlugin()}}} function.
*  Author {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.  Other than the overridden methods above, these are the only items in the code that need specific names.
{{{
# spSimpleLocator.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx
import maya.OpenMayaRender as omr
import maya.OpenMayaUI as omui

#------------------
# Constants:
# c++ uses 'k' to specify constants, Python usually uses all caps.
# Using k here since we're dealing with Maya legacy, but ultimately it doesn't matter.
kPluginNodeType = "spSimpleLocator"
# This identifier should be provided by Autodesk, to make sure it will never
# clash with any other node type.  I just made this one up.
# http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
kPluginNodeId = om.MTypeId(0x00112)

#------------------
# globals

# This static (class) method creates an instance of MHardwareRenderer.  There is
# no web documentation for the 'theRenderer()', weird.
# http://download.autodesk.com/us/maya/2011help/API/class_m_hardware_renderer.html
renderer = omr.MHardwareRenderer.theRenderer()

# This creates an instance of MGLFunctionTable, giving us access to OpenGL calls.
# http://download.autodesk.com/us/maya/2011help/API/class_m_g_l_function_table.html
glFT = renderer.glFunctionTable()

# Vert positions that define the shape of our locator.  If these change, you'll
# also need to update the boundingBox() method.
locShape = ([-.5, .5, 0],
            [.5, .5, 0],
            [.5, -.5, 0],
            [-.5, -.5, 0],
            [-.5, .5, 0])

class SimpleLocator(ompx.MPxLocatorNode):
    """
    Scripted locator node creation.
    http://download.autodesk.com/us/maya/2011help/API/class_m_px_locator_node.html
    Inherits from MPxNode:
    http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html
    """
    # Define any input and output attrs at the class level.
    # These are MObject representations of the attributes on the physical
    # Maya node.  They are just placeholder:  They are replaced\populated by nodeInitializer()
    # In fact, they could be None, but since ultimately they'll be MObjects,
    # just stick some dummy ones in there.
    attr_size = om.MObject()

    def __init__(self):
        """
        Make sure to initilaize the superclass.  Not much else happens here.
        """
        ompx.MPxLocatorNode.__init__(self)

    def compute(self, plug, dataBlock):
        """
        Overridden method of the MPxNode superclass.  Doesn't do much since we're
        not computing any internal values.  But in theory, you could.
        """
        return om.kUnknownParameter

    def draw(self, view, path, style, status):
        """
        Overridden method of MPxLocatorNode.  Allows the drawing of the locator
        via OpenGL.

        Parameters:
        view : M3dView, http://download.autodesk.com/us/maya/2011help/API/class_m3d_view.html,
            the 3D view that is being drawn into.
        path : MDagPath, http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html,
            the path to the locator in the DAG.
        style : M3dView MDisplayStyle enum value,
            http://download.autodesk.com/us/maya/2011help/API/class_m3d_view.html.
            the style to draw the locator in.
        status : M3dView MDisplayStatus enum value,
            http://download.autodesk.com/us/maya/2011help/API/class_m3d_view.html,
            the selection status of the locator.
        """
        thisNode = self.thisMObject() # superclass method, returns MObject
        plug = om.MPlug(thisNode, self.attr_size) # returns MPlug
        sizeVal = plug.asMDistance() # returns MDistance
        multiplier = sizeVal.asCentimeters() # returns double

        # Start the OpenGL drawing.  Time to put your OpenGL hat on.
        view.beginGL()

        # If we're in any kind of shaded view, draw a 'filled-in' view of the locator:
        if style == omui.M3dView.kFlatShaded or style == omui.M3dView.kGouraudShaded:
            # To enable alpha blending on our locator faces, we need to set this GL state:
            # http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml
            glFT.glEnable(omr.MGL_BLEND)
            # Methods starting with 'gl' are direct calls to OpenGL.
            # Also, OpenMayaRender has many OpenGL related constants that begin
            # with capital 'M', that can be passed into the GL calls.
            # Both of these concepts are shown in this doc:
            # http://www.opengl.org/sdk/docs/man/xhtml/glPushAttrib.xml
            glFT.glPushAttrib(omr.MGL_CURRENT_BIT)

            # Setup the draw colors based on if the locator is the lead picked
            # object or not, or not picked at all. For the below examples they're
            # all the same value.
            if status == omui.M3dView.kLead:
                # If it is the first object picked.
                glFT.glColor4f(0, 1, 0, .5)
            if status == omui.M3dView.kActive:
                # If it's picked, but not lead.
                glFT.glColor4f(0, 1, 0, 0.5)
                # Here is another way to set colors based on Maya's color palette,
                # but you can't do alpha:
                #view.setDrawColor( 13, omui.M3dView.kActiveColors )
            if status == omui.M3dView.kDormant:
                glFT.glColor4f(0, 1, 0, 0.5)
                #view.setDrawColor( 13, omui.M3dView.kDormantColors )

            # Draw the physical triangles to fill in the shape:
            # http://www.opengl.org/sdk/docs/man/xhtml/glBegin.xml
            glFT.glBegin(omr.MGL_TRIANGLE_FAN )
            for i in range(len(locShape)-1):
                glFT.glVertex3f(locShape[i][0]*multiplier, locShape[i][1]*multiplier, locShape[i][2]*multiplier)
            glFT.glEnd()

            # Turn off alpha blending:
            glFT.glDisable(omr.MGL_BLEND)
            glFT.glPopAttrib() # need to pop for each push.

        # Now start drawing the wireframe as well.  I've yet to figure out what
        # defines the wireframe color in Maya, since the 'locator' color in the
        # prefs don't seem to apply to this custom node.
        glFT.glBegin(omr.MGL_LINES)
        for i in range(len(locShape)-1):
            glFT.glVertex3f( locShape[i][0]*multiplier, locShape[i][1]*multiplier, locShape[i][2]*multiplier )
            glFT.glVertex3f( locShape[i+1][0]*multiplier, locShape[i+1][1]*multiplier, locShape[i+1][2]*multiplier )
        glFT.glEnd()

        view.endGL() # End all OpenGL drawing.

    def isBounded(self):
        """
        Overridden method of MPxLocatorNode.  If return True, must supply an overridden
        boundingBox method to compute bounding box.
        """
        return True

    def boundingBox(self):
        """
        Overridden method of MPxLocatorNode.  Must return a MBoundingBox object
        specifying the bounding box of this node.
        http://download.autodesk.com/us/maya/2011help/API/class_m_bounding_box.html
        """
        thisNode = self.thisMObject() # MObject
        plug = om.MPlug(thisNode, self.attr_size) # MPlug
        sizeVal = plug.asMDistance() # MDistance
        multiplier = sizeVal.asCentimeters() # double

        # Two corners of our square shaped locator:
        corner1 = om.MPoint(-.5, .5, 0)
        corner2 = om.MPoint(.5, -.5, 0)
        corner1 = corner1 * multiplier
        corner2 = corner2 * multiplier
        bbox = om.MBoundingBox( corner1, corner2 ) # MBoundingBox

        return bbox

#---------------------------------------------------
# Node creation and initialization:

# creator
def nodeCreator():
    return ompx.asMPxPtr( SimpleLocator() )

# initializer
def nodeInitializer():
    """
    This function sets up all the input and output attributes on the node:  What
    type of data they should expect, if they're inputs\outputs, keyable, connectable,
    default values, etc.  Also defines the relationships between attrs using
    attributeAffects():  If an input changes, you should update the output.
    """
    # Create the 'size' attribute.  This will end up overriding the locators
    # default 'localScale' attr.
    # http://download.autodesk.com/us/maya/2011help/API/class_m_fn_unit_attribute.html
    unitFn = om.MFnUnitAttribute()
    # Returns an MObject, assignes it to the class variable:
    SimpleLocator.attr_size = unitFn.create("size", "in", om.MFnUnitAttribute.kDistance)
    unitFn.setChannelBox(True) # default is False
    unitFn.setKeyable(True)  # default is False
    unitFn.setDefault(1.0)
    SimpleLocator.addAttribute( SimpleLocator.attr_size )

#------------------------------------------------------
# Loading and unloading:

# initialize the script plug-in
def initializePlugin(mobject):
    mplugin = ompx.MFnPlugin(mobject)
    try:
        mplugin.registerNode(kPluginNodeType, kPluginNodeId, nodeCreator,
                             nodeInitializer, ompx.MPxNode.kLocatorNode)
    except:
        om.MGlobal.displayError("Failed to register node: %s"%kPluginNodeType)

# uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = ompx.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(kPluginNodeId)
    except:
        om.MGlobal.displayError("Failed to deregister node: %s"%kPluginNodeType)
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to create it and play with it:
{{{
import maya.cmds as mc
mc.createNode('spSimpleLocator')
}}}
I should note that after I authored this, I found another good resource on scripted plugin locator creation here:
http://www.fevrierdorian.com/blog/post/2010/02/12/Creating-custom-locator-with-Maya-s-Python-API
I wanted to build a bare-minimum framework for authoring nodes via a scripted plugin.  Maya comes with a pretty simple example here:
http://download.autodesk.com/us/maya/2011help/API/circle_node_8py-example.html
Which I stripped down even more, added a bunch of notes, etc.

The below example creates a new node called '{{{simpleNodeA}}}' that has two inputs.  Internally, it simply adds those values together, which can then be captured from its single output attribute.

When it comes to creating a node, these are the major things required:
*A class inheriting from {{{maya.OpenMayaMPx.MPxNode}}}.
**The name of this class is up to you, it is independent from the name of the node being created.  The node name is actually defined in {{{initializePlugin()}}}
**Must initialize the superclass in {{{__init__()}}}.
**Must override the {{{compute()}}} method:  This is where the node does it's work.
*Author 'node creator' and 'node initializer' functions.  The names are arbitrary, but {{{nodeCreator()}}} and {{{nodeInitializer()}}} are good ideas.  These are both used by the {{{initializePlugin()}}} function.  The 'node creator' does very little, while the 'node initializer' does a bunch of attribute setup (presuming your node has attributes).
*Author {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.  Other than the overridden methods above, these are the only items in the code that need specific names.
{{{
# spSimpleNode.py

import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

#-------------------------------------------------------------
# Constants

# c++ uses 'k' to specify constants, Python usually uses all caps.
# Using k here since we're dealing with Maya legacy, but ultimately it doesn't matter.

# What the node-type will be called in Maya.
kPluginNodeType = "simpleNodeA" 
# This identifier should be provided by Autodesk, to make sure it will never
# clash with any other node type.  I just made this one up.
# http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html
kPluginNodeId = om.MTypeId(0x00111)
kVendor = "My Name or organization"
kVersion = "1.0"

#----------------------------------------------------------
# Plugin code:

# Node definition
class SimpleNode(ompx.MPxNode):
    """
    Scripted plugin node creation.
    http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html
    """
    # Define any input and output attrs at the class level.
    # These are MObject representations of the attributes on the physical
    # Maya node.  They are just placeholder:  They are replaced\populated by nodeInitializer()
    # In fact, they could be None, but since ultimately they'll be MObjects,
    # just stick some dummy ones in there.
    attr_inputA = om.MObject()
    attr_inputB = om.MObject()
    attr_output = om.MObject()

    def __init__(self):
        """
        Make sure to initilaize the superclass.  Not much else happens here.
        """
        ompx.MPxNode.__init__(self)

    def compute(self, plug, dataBlock):
        """
        This is an overridden method of the MPxNode class.  Does the "work" the
        node is to perform.

        Parameters:
        plug : MPlug : http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html
        dataBlock : MDataBlock : http://download.autodesk.com/us/maya/2010help/API/class_m_data_block.html
        """
        # Check that the requested recompute is our output value
        if (plug == SimpleNode.attr_output):
            # Read the input values, returns a MDataHandle, which is  a smart pointer
            # back into the MDataBlock (dataBlock)
            # http://download.autodesk.com/us/maya/2010help/API/class_m_data_handle.html
            dataHandleA = dataBlock.inputValue(SimpleNode.attr_inputA)
            dataHandleB = dataBlock.inputValue(SimpleNode.attr_inputB)

            # Compute the output values.  In this case, it is a simple addition operation.
            outVal = dataHandleA.asFloat() + dataHandleB.asFloat()

            # Get a handle to the output value and store the new value.
            handle = dataBlock.outputValue(SimpleNode.attr_output)
            handle.setFloat(outVal)

            # From the docs:  Tells the dependency graph that the given attribute
            # has been updated and is now clean. This should be called after the
            # data in the plug has been recalculated from the inputs of the node.
            dataBlock.setClean(plug)
        #else:
            # c++ requires this return, but it appears that the Python API doesn't:
            #return om.MStatus.kUnknownParameter

#---------------------------------------------------
# Node creation and initialization:

# Creator function.  It should be noted that while historically this is an external
# function, you could instead, inside the initializePlugin function, call to this
# via a lambda.  Meaning, instead of this line (below):
#    mfnPlugin.registerNode(kPluginNodeType, kPluginNodeId, nodeCreator, nodeInitializer)
# You could do this:
#    mfnPlugin.registerNode(kPluginNodeType, kPluginNodeId, lambda:ompx.asMPxPtr(SimpleNode()), nodeInitializer)
# And in the process completely remove the below function.
def nodeCreator():
    return ompx.asMPxPtr( SimpleNode() )

# initializer
def nodeInitializer():
    """
    This function sets up all the input and output attributes on the node:  What
    type of data they should expect, if they're inputs\outputs, keyable, connectable,
    default values, etc.  Also defines the relationships between attrs using
    attributeAffects():  If an input changes, you should update the output.
    """
    # http://download.autodesk.com/us/maya/2010help/API/class_m_fn_numeric_attribute.html
    mfnNumericAttribute = om.MFnNumericAttribute()

    # Setup the input attributes.  This returns an MObject.
    SimpleNode.attr_inputA = mfnNumericAttribute.create("inputA", "ina", om.MFnNumericData.kFloat, 1.0)
    # The setStorable() method is inheirted from MFnAttribute
    mfnNumericAttribute.setChannelBox(True) # default is False
    mfnNumericAttribute.setKeyable(True)  # default is False

    SimpleNode.attr_inputB = mfnNumericAttribute.create("inputB", "inb", om.MFnNumericData.kFloat, 10.0)
    mfnNumericAttribute.setChannelBox(True) # default is False
    mfnNumericAttribute.setKeyable(True) # default is False

    # Setup the output attributes
    SimpleNode.attr_output = mfnNumericAttribute.create("output", "o", om.MFnNumericData.kFloat, 0.0)
    mfnNumericAttribute.setWritable(False) # Default is True.  This is an output, can't write to it.
    mfnNumericAttribute.setStorable(False) # Default is True.  Do not save value with scene, since it's an output.

    # Now that we've made the attributes, add them to the node.
    # addAttribute() is a static method inherited from MPxNode.
    SimpleNode.addAttribute(SimpleNode.attr_inputA)
    SimpleNode.addAttribute(SimpleNode.attr_inputB)
    SimpleNode.addAttribute(SimpleNode.attr_output)

    # Set the attribute dependencies:  This tells the system that when an input
    # is modified, the output needs to be recomputed.
    # attributeAffects() is a static method inherited from MPxNode.
    SimpleNode.attributeAffects(SimpleNode.attr_inputA, SimpleNode.attr_output)
    SimpleNode.attributeAffects(SimpleNode.attr_inputB, SimpleNode.attr_output)

#------------------------------------------------------
# Loading and unloading:

def initializePlugin(mobject):
    # Initialize the script plug-in
    # http://download.autodesk.com/us/maya/2011help/API/class_m_fn_plugin.html
    mfnPlugin = ompx.MFnPlugin(mobject, kVendor, kVersion)
    try:
        mfnPlugin.registerNode(kPluginNodeType, kPluginNodeId, nodeCreator, nodeInitializer)
    except:
        om.MGlobal.displayError("Failed to register node: %s" % kPluginNodeType)
        raise

def uninitializePlugin(mobject):
    # Uninitialize the script plug-in
    mplugin = ompx.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode(kPluginNodeId)
    except:
        om.MGlobal.displayError("Failed to deregister node: %s" % kPluginNodeType)
        raise
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to create it, and connect it's output to something else.  Below examples are in Python, but you can just as easily access this via mel.
{{{
import maya.cmds as mc

simpleNode = mc.createNode('simpleNodeA')
null = mc.group(empty=True)
mc.connectAttr('%s.output'%simpleNode, '%s.tx'%null)
# Result: Connected simpleNodeA1.output to null1.translate.translateX. # 
}}}
Optionally, you could do this:
{{{
import maya.cmds as mc
import spSimpleNode

simpleNode = mc.createNode(spSimpleNode.kPluginNodeType)
null = mc.group(empty=True)
mc.connectAttr('%s.output'%simpleNode, '%s.tx'%null)
# Result: Connected simpleNodeA1.output to null1.translate.translateX. # 
}}}
While Python comes with its own built-in types, sometimes you need to access a specific API type wrapper.  This is not a rigid:  What one considers a 'type' is up for consideration here, I just grabbed classes that fit the bill in my own head.
All are part of {{{maya.OpenMaya}}}
!!!Angle
*[[MAngle|http://download.autodesk.com/us/maya/2010help/API/class_m_angle.html]]
!!!Color
*[[MColor|http://download.autodesk.com/us/maya/2010help/API/class_m_color.html]]
*[[MColorArray|http://download.autodesk.com/us/maya/2010help/API/class_m_color_array.html]]
!!!Dag Path:
*[[MDagPath|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_path.html]]
*[[MDagPathArray|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_path_array.html]]
!!!Distance
*[[MDistance|http://download.autodesk.com/us/maya/2010help/API/class_m_distance.html]]
!!!Double
*[[MDoubleArray|http://download.autodesk.com/us/maya/2010help/API/class_m_double_array.html]]
!!!Euler Rotation
*[[MEulerRotation|http://download.autodesk.com/us/maya/2010help/API/class_m_euler_rotation.html]]
!!!Float
*[[MFloatArray|http://download.autodesk.com/us/maya/2010help/API/class_m_float_array.html]]
!!!Int
*[[MIntArray|http://download.autodesk.com/us/maya/2010help/API/class_m_int_array.html]]
!!!Point
*[[MPoint|http://download.autodesk.com/us/maya/2010help/API/class_m_point.html]]
*[[MFloatPoint|http://download.autodesk.com/us/maya/2010help/API/class_m_float_point.html]]
*[[MPointArray|http://download.autodesk.com/us/maya/2010help/API/class_m_point_array.html]]
*[[MFloatPointArray|http://download.autodesk.com/us/maya/2010help/API/class_m_float_point_array.html]]
!!!Matrix
*[[MMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix.html]]
*[[MFloatMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_float_matrix.html]]
*[[MTransformationMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_transformation_matrix.html]]
*[[MMatrixArray|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix_array.html]]
!!!Object:
*[[MObject|http://download.autodesk.com/us/maya/2010help/API/class_m_object.html]]
*[[MObjectArray|http://download.autodesk.com/us/maya/2010help/API/class_m_object_array.html]]
!!!Plug
*[[MPlug|http://download.autodesk.com/us/maya/2010help/API/class_m_plug.html]]
*[[MPlugArray|http://download.autodesk.com/us/maya/2010help/API/class_m_plug_array.html]]
!!!Pointers & References
*See [[OpenMaya.MScriptUtil]]
!!!Quaternion
*[[MQuaternion|http://download.autodesk.com/us/maya/2010help/API/class_m_quaternion.html]]
!!!String
Note:  These aren't available in the Python ~OpenMaya API
*[[MString|http://download.autodesk.com/us/maya/2010help/API/class_m_string.html]]
*[[MStringArray|http://download.autodesk.com/us/maya/2010help/API/class_m_string_array.html]]
!!!Time
*[[MTime|http://download.autodesk.com/us/maya/2010help/API/class_m_time.html]]
*[[MTimeArray|http://download.autodesk.com/us/maya/2010help/API/class_m_time_array.html]]
!!!Unsigned Int
*[[MUintArray|http://download.autodesk.com/us/maya/2010help/API/class_m_uint_array.html]]
*[[MUint64Array|http://download.autodesk.com/us/maya/2010help/API/class_m_uint64_array.html]]
!!!Vector
*[[MVector|http://download.autodesk.com/us/maya/2010help/API/class_m_vector.html]]
*[[MFloatVector|http://download.autodesk.com/us/maya/2010help/API/class_m_float_vector.html]]
*[[MVectorArray|http://download.autodesk.com/us/maya/2010help/API/class_m_vector_array.html]]
*[[MFloatVectorArray|http://download.autodesk.com/us/maya/2010help/API/class_m_float_vector_array.html]]
Pseudo code:
{{{
# Some scripted plugin...
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx
class Test(ompx.MPxCommand):
    def doIt(self, args):
        defaultArg = 5.0
        index = args.flagIndex("d", "default")
        if om.MArgList.kInvalidArgIndex != index:
            defaultArg = args.asDouble(index+1)

# lots more code...   
}}}
So what object type is {{{args}}}?
The passed in args are turned into an object type of {{{OpenMaya.MArgList}}}, so you have direct access to all of its methods (simple example above querying for a flag).
Docs:
http://download.autodesk.com/us/maya/2009help/API/class_m_arg_list.html
Scratchpad of API stuff. A lot of this is just pulled directly from the Maya docs.  And it should be noted this is entirely based on the @@//Python//@@ implementation of the API.  No c++ stuff in here (other than helpful comparisons for learning the Python side of things).
----
I would be nice to link to this, but....
*"Maya Help -> Developer Resources -> API Guide -> ''Using the Maya Python API''"
You need to import Python's ~OpenMaya module to get at most of the functionality.  Import others  to do other things: like {{{OpenMayaMPx}}} if you're going to author a scripted pluigin
{{{
import maya.OpenMaya  as om
import maya.OpenMayaMPx as ompx
}}}
----
Online Resources:
*http://groups.google.com/group/python_inside_maya
*[[This thread|http://groups.google.com/group/python_inside_maya/browse_thread/thread/9fa689ab6edf834a#]] in the 'python_inside_maya' Google groups.
*http://www.comet-cartoons.com/3ddocs/mayaAPI/
*http://www.rtrowbridge.com/blog/category/maya-python-api/
*The {{{OpenMaya.MScriptUtil}}} is a common class the Python API programmer will need to use. Some examples here:
**http://www.chadvernon.com/blog/resources/maya-api-programming/mscriptutil/
Good quality official documentation:
*http://download.autodesk.com/us/maya/2009help/API/
*http://download.autodesk.com/us/maya/2010help/API/
*http://download.autodesk.com/us/maya/2011help/API/
*http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html
*http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html
If you have an Autodesk support subscription, you can download their "__API and Game Dev__" podcasts and associated files here:
*http://subscription.autodesk.com -> Training -> Autodesk Maya -> Maya Video Podcasts -> Scripting and the API
This appears to be free, I have yet to actually look at it yet:
*http://download.autodesk.com/media/adn/Maya_Intermediate_Webcast.zip
----
''Differences between {{{c++}}} and {{{Python}}}'' (based on my ignorance of c++, take this with a grain of salt.)
*Pointers:
**See online reference for an overview: http://oreilly.com/catalog/pcp3/chapter/ch13.html
*Namespaces: 
**c++ uses double colon {{{::}}} to define namespaces, Python uses single period '{{{.}}}':
{{{
# c++:
MGlobal::displayInfo()
# Python:
OpenMaya.MGlobal.displayInfo()
}}}
*Passing in args:
**In the c++ docs, if you see something like this:
{{{
MFnMesh ( const  MObject & object,  MStatus * ReturnStatus ) 
}}}
**What do the arg symbols mean?
**''{{{&}}}'' :  The ampersand specify that the function is expecting an object to be passed in as a //reference//.  The object can be modified, but not always.
**''{{{*}}}'' : The asterisk specifies a //pointer to an object// (in c++ terms).  This object //will be modified// by the function.  Often times you need to use the {{{OpenMaya.MScriptUtil}}} class to create pointers to be passed in.
**Some pseudo c++ code:
{{{
MObject foo;
MStatus result;
MFnMesh(foo, result);
if result.success():
    do something involving foo maybe...
}}}
It should be noted that in the Python API, these two values can be considered the same thing for the most part:  Pass by reference.
----
Constants / Enumerators:
*Any name with a {{{k}}} in the front is a //constant//, or an //enumerator//.  These could be considered variables with pre-defined values.
*{{{k}}} is an old-school 'Hungarian-notation' way of specifying a constant value.  Just a convention.
*A listing can be found [[here|http://download.autodesk.com/us/maya/2009help/API/functions_eval_0x6b.html#index_k]].
Examples:
{{{
MItDag.kDepthFirst
MFn.kMesh
}}}
----
Example of ~OpenMaya ''M'' Class at work:
{{{
import maya.OpenMaya as om
vector1 = om.MVector(0,1,0)
vector2 = om.MVector(1,0,0)
vector3 = om.MVector(0,0,2)
newV = vector1 + vector2 + vector3
print "newVector %f, %f, %f " % (newV.x, newV.y, newV.z)
# newVector 1.000000, 1.000000, 2.000000
}}}
----
Maya's API object hierarchy is handled a bit differently from a standard OOP approach of object inheritance:  In standard OOP, objects hold both the data (attributes) and functions (methods).  Conceptually, the Maya API has a split, with attributes (the data) living on one side of the fence and methods (functions) acting on that data on the other.  These are referred to as {{{function sets}}}, and in OOP these classes are called {{{functors}}}.  Using some {{{c++}}} pseudo-code:
First, presume we have this object hierarchy, and function hierarchy (each is a class):
{{{
Object (superclass) 
---- FooObj (subclass)
---- GooObj (subclass)

ObjFunc (superclass)
---- FooFunc (subclass)
---- GooFunc (subclass)
}}}
The {{{Object}}} objects contain data, plus member functions to access their data.  The {{{ObjFunc}}} objects have corresponding member functions to access the {{{Object}}} data, but they themselves don't contain any of that data:  The data is passed to them:
{{{
FooObj fooData;
FooFunc fooFn;

fooFn.setObj(fooData);
fooFn.doStuff();
}}}
As you can see, you make an {{{FooObj}}} type object called {{{fooData}}} (which inherits from {{{Object}}}) , and a corresponding {{{FooFunc}}} object (inheriting from {{{ObjFunc}}}) called {{{fooFn}}}. You pass {{{objData}}} into {{{objFunc}}}, and then do something to it.  {{{FooFunc}}} objects only work on {{{FooObj}}} objects, {{{GooFunc}}} objects only work on {{{GooObj}}} objects.
It should be noted that while the function class define an interface for doing work on the data, it's the data classes that do the real work.  Wacky!

''In Maya, you are only ever given access to an class called @@~MObject@@''.  This is the "root" object type in Maya.  Accessing all the hiden data in Maya is done through its {{{function set}}} hierarchy.  In actuality, ~MObject is just a //handle// (or a pointer) to another object inside the Maya core.
There is a root 'function' as well, which is @@''~MFnBase''@@.
This topic name is a bit abstract, but here's the idea:  Via the {{{OpenMaya}}} API, various classes 'go together'.  These are my notes to help show which ones connect to which.

*[[OpenMaya.MItDag|http://download.autodesk.com/us/maya/2011help/API/class_m_it_dag.html]] : Use the DAG iterator to traverse the DAG either depth first or breadth first, visiting each node.
**[[OpenMaya.MFnDagNode|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dag_node.html]] Generic function set which can be used on any dag node.
*[[OpenMaya.MItDependencyGraph|http://download.autodesk.com/us/maya/2011help/API/class_m_it_dependency_graph.html]] : Iterate over Dependency Graph (DG) Nodes or Plugs starting at a specified root Node or Plug.
**OpenMaya.MObject
**OpenMaya.MPlug
**OpenMaya.MObjectArray
**OpenMaya.MPlugArray
**OpenMaya.MDGModifier
**OpenMaya.MFnAttribute
*[[OpenMaya.MItDependencyNodes|http://download.autodesk.com/us/maya/2011help/API/class_m_it_dependency_nodes.html]] : Traverse all the nodes in Maya's Dependency Graph.
**[[OpenMaya.MFnDependencyNode|http://download.autodesk.com/us/maya/2011help/API/class_m_fn_dependency_node.html]] : creation and manipulation of dependency graph nodes
*[[OpenMaya.MSelectionList|http://download.autodesk.com/us/maya/2011help/API/class_m_selection_list.html]]
**[[OpenMaya.MItSelectionList|http://download.autodesk.com/us/maya/2011help/API/class_m_it_selection_list.html]]
Classes are organized into these main groups:  {{{M}}}, {{{MFn}}}, {{{MIt}}}, & {{{MPx}}}.
Also see: [[API: Commonly used classes]]
*@@{{{M}}}@@ classes are Maya's base utility classes. ''M'' stands for "Model".  Most, although not all, of these classes are “Wrappers”. Examples of this class are:
**@@{{{MObject}}}@@ is the base class of all objects.  They are operated on via {{{MFn}}} function sets, and {{{MIt}}} iterators.
**@@{{{MPlug}}}@@ is the API interface for an attribute
**@@{{{MSelectionList}}}@@ handles everything to do with selections
**@@{{{MScriptUtil}}}@@ is key when doing API work from Python
**@@{{{MGlobal}}}@@ has a lot of random goodies. (could be called "m-misc.")
**many more...
*@@{{{MFn}}}@@ classes ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___m_fn.html]]) are wrappers to make accessing common object easier.  'Fn' = 'Function'.  They wrapper "functionality".  Any class with this prefix is a //''function set''// used to operate on {{{MObject}}}s of a particular type.  All {{{MFn}}} classes (other than {{{MFn}}} itself) live as part of a class inheritance hierarchy, the root being {{{MFnBase}}}.  This differentiates them from the other class types ({{{M}}}, {{{MIt}}}, {{{MPx}}}) which //don't// have a class inheritance hierarchy.  Examples:
**@@{{{MFnDagNode}}}@@ / @@{{{MFnDependencyNode}}}@@ give you access to the DG/DAG.  
**@@{{{MFnMesh}}}@@ makes mesh access easy.
**@@{{{MFnLambertShader}}}@@ exposes lots of material goodness
**many more...
*@@{{{MIt}}}@@ (mit) classes are for iterating though data (for looping through things).  'It' = 'Iterator'.  They wrapper "iteration".  These classes are iterators and work on ~MObjects similar to the way a function set (~{{{MFn}}} classes) does. For example, {{{MItCurveCV}}} is used to operate on an individual NURBS curve CV (there is no ~MFnNurbsCurveCV), or, iteratively, on all the CV's of a curve.  They work on ~MObjects similar to the way a function set does.   Examples:
**@@{{{MItDagNode}}}@@ iterates through the entire DAG (all nodes)
**@@{{{MITMesh}}}@@
**@@{{{MItGeometry}}}@@ iterates through all aspects of a mesh.
**many more...
*@@{{{MPx}}}@@ classes are for creating custom plugins.  'Px' = 'Proxy'.  See [[Authoring Scripted Plugins via Python]].  They are API classes designed for you to derive from and create your own object types.  Examples:
**@@{{{MPxCommand}}}@@ : Allows you to create custom //mel commands//.
**@@{{{MPxFileTranslator}}}@@ : custom file import/export
**@@{{{MPxNode}}}@@ : For making custom DG nodes.
**@@{{{MPxContext}}}@@ : Custom context creation
**@@{{{MPxLocatorNode}}}@@ : Custom locator type creation.
**many more...
All Maya classes are contained in different Python packages (which closely mirror the c++ ones).  All the below classes in ''{{{bold}}}'' are children of the {{{maya}}} package (as in {{{maya.OpenMaya}}}).

It should be noted that the organization of Maya's API ''docs'' and its Python ''packages'' //are not the same//.  Below describes how the Python //packages// are organized.  For example, Maya's {{{OpenMaya}}} //docs// will list that the {{{MPxBakeEngine}}} class is part of it, but in fact it isn't (it's part of {{{OpenMayaMPx}}}).  I think the docs try to lump similar classes together, which is nice, but it makes it harder to find them in the API, IMO.
*''{{{maya.OpenMaya}}}''  ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___open_maya.html]]) 
**Contains {{{M}}}, {{{MFn}}}, & {{{MIt}}} classes.
*''{{{maya.OpenMayaMPx}}}''   ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___m_px.html]])
**Contains {{{MFn}}} & {{{MPx}}} classes.
*''{{{maya.OpenMayaAnim}}}'' ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___open_maya_anim.html]]) Animation related classes.
**Contains {{{M}}}, {{{MFn}}}, & {{{MIt}}} classes.
*''{{{maya.OpenMayaUI}}}'' ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___open_maya_u_i.html]]) Clases for manipulating the UI.
**Contains {{{M}}} & {{{MFn}}} classes.
*''{{{maya.OpenMayaFX}}}'' ([[docs|http://download.autodesk.com/us/maya/2009help/API/group___open_maya_f_x.html]]) API modules for FX.
**Contains {{{M}}} & {{{MFn}}} classes.
*''{{{maya.OpenMayaRender}}}''([[docs|http://download.autodesk.com/us/maya/2009help/API/group___open_maya_render.html]]) API module for rendering.
**Contains {{{M}}} & {{{MFn}}} classes.
*''{{{maya.OpenMayaCloth}}}'' API for cloth systems
**Contains {{{M}}} classes.
I'm guessing those are closely tied to these .dll files:
*{{{\Autodesk\Maya20XX\bin\}}}
**{{{OpenMaya.dll}}}
**{{{OpenMayaAnim.dll}}}
**{{{OpenMayaFX.dll}}}
**{{{OpenMayaRender.dll}}}
**{{{OpenMayaUI.dll}}}
Furthermore, the c++ header files they wrapper live here:
{{{
\Autodesk\Maya20XX\include\maya\*.h
}}}
----
If you want to see how the modules are organized, you can use the below code to have a view into some of the more popular ones.  It will collect all the class types into lists (in addition to any constants in the module, and 'other' classes), and display them grouped together:
{{{
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx
import maya.OpenMayaAnim as oma
import maya.OpenMayaUI as omui
import maya.OpenMayaFX as omfx

for mod in [om, ompx, oma, omui, omfx]:

    M = []
    MFn = []
    MIt = []
    MPx = []
    others = []
    constants = []
    desc = ["M", "MFn", "MIt", "MPx", "others", "constants"]

    for item in sorted(dir(mod)):
        if item.startswith("MPx"):
            MPx.append(item)
        elif item.startswith("MFn"):
            MFn.append(item)
        elif item.startswith("MIt"):
            MIt.append(item)
        elif item.startswith("M"):
            if item[1].isupper() or item[1].isdigit():
                M.append(item)
            else:
                others.append(item)
        elif item.startswith('k') and item[1].isupper():
            constants.append(item)
        else:
            others.append(item)

    print "\n",mod
    for i,items in enumerate([M, MFn, MIt, MPx, others, constants]):
        print "\n\t----%s----"%desc[i]
        for m in sorted(items):
            print "\t\t", m
}}}
Prints (a //lot// of stuff.  Truncated below):
{{{
<module 'maya.OpenMaya' from 'c:\Program Files\Autodesk\Maya2010\Python\lib\site-packages\maya\OpenMaya.pyc'>

	----M----
		MAngle
		MAngle_internalToUI
		MAngle_internalUnit
		MAngle_setInternalUnit
		MAngle_setUIUnit
		MAngle_swigregister
		etc...
	----MFn----
		MFn
		MFnAmbientLight
		MFnAmbientLight_swigregister
		MFnAnisotropyShader
		MFnAnisotropyShader_swigregister
		etc...
	----MIt----
		MItCurveCV
		MItCurveCV_swigregister
		MItDag
		MItDag_swigregister
		MItDependencyGraph
		etc...
	----MPx----

	----others----
		NULL
		_OpenMaya
		__builtins__
		__doc__
		etc...
	----constants----
		kDefaultNodeType
		kEulerRotationEpsilon
		kMFnMeshInstanceUnspecified
		kMFnMeshPointTolerance
		kMFnMeshTolerance
		etc...
etc...
}}}




I give a more robust example of this in action here: [[API: undoing commands]]
But to make it concise here:  If you want a scripted plugin based on a [[MPxCommand|http://download.autodesk.com/us/maya/2010help/API/class_m_px_command.html]] to return some type of value, you need to do that via its {{{setResult}}} method.  Code snippet below:
{{{
import maya.OpenMayaMPx as ompx

class Foo(ompx.MPxCommand):
    def __init__(self):
        ompx.MPxCommand.__init__(self)
        # A list later filled with data to return:
        self.returnList = []

    def doIt(self, argList):
        # bunch of code, filling self.returnList...
        # then at the end, to set the return value:
        self.setResult(self.returnList)
}}}
Also see [[API : Hacking the undo queue]] if you feel like trying to dodge everything written below ;)
----
I've found one downside to using Maya Python API calls that change the DG in scripts (rather than scripted plugins): It bypasses Maya's undo queue:  I've tried wrappering the API code chunks in an [[undoQueue|How can I author an undo context manager?]], but it's had no effect.  Why?  Because Maya's command engine only undoes actual registered //commands//.  What counts as a valid thing that //can// be undone?
*A command (based on [[MPxCommand|http://download.autodesk.com/us/maya/2011help/API/class_m_px_command.html]]) authored via the Maya Python API as a scripted plugin (that supports undoing).
*A command (based on [[MPxCommand|http://download.autodesk.com/us/maya/2011help/API/class_m_px_command.html]]) authored via c++ as a 'regular' plugin (that supports undoing). 
*Any standard Maya commands (whether executed via mel or Python).
*A mel procedure \ script or Python function \ class \ module with calls to the above items.
What can't be undone?
*Piecemeal bits of Maya Python API code put in a function.  A Maya Python API class isn't a //command//:  It's a piece of code which can be used to //create a command// (or custom node, etc..).  Maya undoes //whole registered commands//, not the parts and pieces which make those commands work.   Which is explained in [[this note|http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=files/Maya_Python_API_Differences_between_the_C_Maya_API_and_Maya_Python_API.htm,topicNumber=d28e15702]] in the Maya docs:
>It is possible to mix Maya Python API calls along with Maya command calls in the same script. Undoing the operations of such a script will not be correct if the Maya Python API calls modify the model and do not properly support undo using the [[MPxCommand|http://download.autodesk.com/us/maya/2011help/API/class_m_px_command.html]] class.
!How to support undoing:
''If you want to undo API calls, you need to author them in a scripted plugin that supports undoing''.
There is a general rule that "any API call that modifies the DG will need to be able to be undone".  I've found two main techniques for doing this, I'll call them 'easy', and 'hard': 
*The easy method makes use of a {{{MDGModifier}}}, which more or less handles all the nasty undoing stuff for you by encapsulating all of the changes within it:  You register the changes with the {{{MDGModifier}}} inside the {{{doIt()}}} method, but they aren't 'done' until the {{{redoIt()}}} method is called, or 'undone' when the {{{undoIt()}}} method is called.
*But there are instances of API calls that modify the DG that don't seem to play nicely with the {{{MDGModifier}}} ^^([[MFnMesh.setVertexColors()|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_mesh.html#7394a3ef2fb9663c11018447270f1e33]] is an example of one that, while giving an arg to pass in a {{{MDGModifier}}}, seems to have no effect)^^.  In that case you need to track the before & after states of the DG when the command executes and manually author code to set the state back when undone.  This method is obviously, harder.
Pseudo-code for each is listed here:

''Easy Method'', example using a {{{MDGModifier}}} to handle doing, and undoing.  In this case we call to some fictitious mel {{{awesomeCommand}}} that will change the DG in some way.  But you could have used any of the other methods on the {{{MDGModifier}}} to make changes as well. 
{{{
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

class Eggs(ompx.MPxCommand):

    def __init__(self):
        ompx.MPxCommand.__init__(self)
        # Create the MDGModifier that will execute and undo our changes:
        self.dgMod = om.MDGModifier()

    def doIt(self):
        # This example registers a mel command to be  
        # executed.  It stores the change to be executed
        # later, by the dgMod.doIt() and dgMod.undoIt() methods.
        self.dgMod.commandToExecute("awesomeCommand")
        # Return the method that does the work:
        return self.redoIt()

    def redoIt(self):
        # Here the MDGModifier executes the command:
        return self.dgMod.doIt()

    def undoIt(self):
        # Here the MDGModifier 'undoes' the command:
        return self.dgMod.undoIt()

    def isUndoable(self):
        return True
}}}
''Hard method'', example *not* using a {{{MDGModifier}}}.  Extra custom methods are added to query and set the before\after states.
{{{
import maya.OpenMayaMPx as ompx

class Spam(ompx.MPxCommand):

    # Superclass overidden methods:

    def __init__(self):
        ompx.MPxCommand.__init__(self)

    def doIt(self):
        # Get the current (will be 'previous' when undone) state:
        self.prevValue = self.getCurrentState()
        # Define what the new state should be:
        self.newValue = self.getNewState()
        return self.redoIt()

    def redoIt(self):
        # Do work to apply the new state.
        self.action(self.newValue)

    def undoIt(self):
        # Do work to apply the previous state.
        self.action(self.prevValue)

    def isUndoable(self):
        return True

    # Custom methods, fill in with your own code.

    def getCurrentState(self):
        # Return the current\previous state of things
        pass

    def getNewState(self):
        # Return the new\updated state of things
        pass

    def action(self, state):
        # Do the work on the passed in state value, whether it's the previous
        # state, or the new state.
        pass
}}}
! 'Roll your own' Undo Queue Practical Example
Using Python API 2.0

This example will create a command that will offset the selected nodes by 10 units on XYZ, and then undo that operation, all via API calls.
{{{
import os
import sys

import maya.cmds as mc
import maya.api.OpenMaya as om2

#------------------

def maya_useNewAPI():
    r"""
    The presence of this function tells Maya that the plugin produces, and
    expects to be passed, objects created using the Maya Python API 2.0.
    """
    pass

#------------------

# command
class PyCusomUndoCmd(om2.MPxCommand):
    # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_px_command_html

    commandName = "pyUndoCmd"

    #-------------

    @classmethod
    def cmdCreator(cls):
        return cls()

    #-------------
    # Superclass overides:

    def __init__(self):
        om2.MPxCommand.__init__(self)

    def doIt(self, args):
        r"""
        Executed when calling to the command.
        """
        # Get the current state.  Used for undo.
        self.prevValue = self.getCurrentState()
        # Define what the new state should be.  This is what is applied 'now', or
        # later if a 'redo' is executed after an 'undo':
        self.newValue = self.getNewState()
        # Now, go do the actual work in the redoIt method (brain teaser time):
        return self.redoIt()

    def redoIt(self):
        r"""
        Any actual work the command should do is done in here,
        during either the initial execution iside doIt, or when a 'redo' is executed
        after an undo.
        """
        self.action(self.newValue)

    def undoIt(self):
        r"""
        Reset the state if undo is executed.
        """
        self.action(self.prevValue)

    def isUndoable(self):
        return True

    #----------
    # Custom methods, not API overides.  This is where your custom code does to
    # do all the plugin-related work:

    def getCurrentState(self):
        r"""
        Return the current\previous state of things, based on the selected transform
        nodes (but doesn't do any actual work).
        """
        transforms = mc.ls(selection=True, type='transform', long=True)
        oldState = {}
        for tf in transforms:
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_selection_list_html
            selList = om2.MSelectionList()
            selList.add(tf)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_dag_path_html
            dagPath = selList.getDagPath(0)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_transform_html
            transformFunc = om2.MFnTransform(dagPath)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_transformation_matrix_html
            tfMtx = transformFunc.transformation()
            oldState[tf] = tfMtx
        return oldState

    def getNewState(self):
        r"""
        Return the new\updated state of things, based on the selected transform
        nodes (but doesn't do any actual work).
        """
        transforms = mc.ls(selection=True, type='transform', long=True)
        newState = {}
        for tf in transforms:
            #https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_selection_list_html
            selList = om2.MSelectionList()
            selList.add(tf)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_dag_path_html
            dagPath = selList.getDagPath(0)
            #https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_transform_html
            transformFunc = om2.MFnTransform(dagPath)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_transformation_matrix_html
            tfMtx = transformFunc.transformation()
            # Just add 10 to the position of the matrix:
            tfMtx.translateBy(om2.MVector(10,10,10), om2.MSpace.kObject)
            newState[tf] = tfMtx
        return newState

    def action(self, state):
        r"""
        Do the work on the saved state, whether it's the 'previous
        state', or the 'new state'.
        """
        for tf in state:
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_selection_list_html
            selList = om2.MSelectionList()
            selList.add(tf)
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_dag_path_html
            dagPath = selList.getDagPath(0)
            tfMtx = state[tf]
            # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_transformation_matrix_html
            transformFunc = om2.MFnTransform(dagPath)
            transformFunc.setTransformation(tfMtx)

#------------------

def initializePlugin(plugin):
    # Initialize the plug-in
    # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_plugin_html
    pluginFn = om2.MFnPlugin(plugin)
    try:
        pluginFn.registerCommand(PyCusomUndoCmd.commandName,
                                 PyCusomUndoCmd.cmdCreator)
                                 #PyCusomUndoCmd.createSyntax)
    except:
        om2.MGlobal.displayError("Failed to register command: %s\n"%PyCusomUndoCmd.commandName)
        raise

def uninitializePlugin(plugin):
    # Uninitialize the plug-in
    # https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__py_ref_class_open_maya_1_1_m_fn_plugin_html
    pluginFn = om2.MFnPlugin(plugin)
    try:
        pluginFn.deregisterCommand(PyCusomUndoCmd.commandName)
    except:
        om2.MGlobal.displayError("Failed to unregister command: %s\n"%PyCusomUndoCmd.commandName)
        raise
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to run the command in either mel or Python, based on the selected nodes:
{{{
import maya.cmds as mc
mc.pyUndoCmd()
}}}
----
! MDGModifier Practical Example
Using API 1.0

The below example is a fully-functional "easy version" from above:  We author a scripted plugin that creates a command that will find all nodes with duplicate names in the scene, and rename them to have unique names.  And, you can undo the operation of course.  Additionally (just for additional example) it does one other important thing:  It ''returns a list of strings'' of the new names, using {{{OpenMayaMPx.MPxCommand.setResults}}}.

The key magic is managing all changes to the DG through a [[MDgModifier|http://download.autodesk.com/us/maya/2010help/API/class_m_d_g_modifier.html]].  You're also required to implement and override the [[MPxCommand|http://download.autodesk.com/us/maya/2010help/API/class_m_px_command.html]] methods {{{undoIt()}}}, {{{redoIt()}}}, and {{{isUndoable()}}}.  Also note the special return value from {{{doIt()}}}.
{{{
# spRenameDupesCmd.py

import re
import maya.cmds as mc
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

# Define what our command's name is.  Prefixing with 'k' is old-skool Hungarian-notation
# convention for defining something as a constant.  In Python this is usually done with
# all caps, but this makes it more consistent with Maya c++ plugin code.
kPluginCmdName= "renameDupes"

# -----------------------------------
# Plugin code

# We're building a *command* (rather than say, a DAG node), so we
# instance the MPxCommand object:
class RenameDupesCmd(ompx.MPxCommand):
    """
    Rename all duplicate nodes in the scene.  Return a list of the newly named nodes,
    and select them upon completion.
    """

    # plugin initializer:
    def __init__(self):
        # initalize our superclass:
        ompx.MPxCommand.__init__(self)

	# The key to getting Maya to undo is to manage it all through the
	# MDGModifier class:
        self.dgMod = om.MDGModifier()
        # Keep track of the things we want to return:
        self.returnList = []

    def doIt(self, argList):
	# Keep track of the nodes we rename, so they can be later picked:
        picker = []
        # DG nodes always have unique names, since they're not parened.  So we only need
        # to iterate over the DAG.
        dagIterator = om.MItDag()  # MItDag
        while not dagIterator.isDone():
            currentItem = dagIterator.currentItem() # MObject
            currentItemFunc = om.MFnDependencyNode(currentItem) # MFnDependencyNode
	    # If the object doesn't have a unique name, and if it's not from a
	    # referenced file, and if it's not 'locked', then rename it:
	    if not currentItemFunc.hasUniqueName() \
	       and not currentItemFunc.isFromReferencedFile() \
	       and not currentItemFunc.isLocked():
                # Generate a name for the node that is it's leaf name, with no numbers
                # on the end, with the addition of the hash char, which will cause the
                # new name to have an incremented number:
                fullname = dagIterator.fullPathName()
                leaf = fullname.split('|')[-1]
                endNum = re.findall('[0-9]+$', leaf)

                # These calls to the dgMod only store the change for later execution,
                # they don't actually do the change now.
                if len(endNum):
                    noNum = leaf[:-len(endNum[0])]+"#"
                    self.dgMod.renameNode(currentItem, noNum)
                else:
                    self.dgMod.renameNode(currentItem, '%s#'%leaf)

		# Get the new name, and add it to our list:
		dPath = om.MDagPath()
		om.MDagPath.getAPathTo(currentItem, dPath)
		picker.append(dPath.fullPathName())
                # Add the renamed item to our return list:
                self.returnList.append(dPath.fullPathName())
            dagIterator.next()

	if len(picker):
	    self.displayInfo('Renamed %s node(s)'%len(picker))
	    # We use a mel command here, rather than the API.  Why not?
	    mc.select(picker)
	else:
	    self.displayInfo('No duplicate nodes to rename')

        # This tells the command what to return upon completion:
        self.setResult(self.returnList)
	# Required to support undoing:
	return self.redoIt()

    def isUndoable(self):
        return True

    def undoIt(self):
        # Undo any renaming previously done.
	return self.dgMod.undoIt()

    def redoIt(self):
        # Actually do the renaming.
	return self.dgMod.doIt()

    # 'Creator Function' needs to return a pointer new instance of this type of class,
    # based on how the c++ code works.
    # As you can see, the OpenMayaMPx module has it's own functions (for which
    # I can find no online documentation).
    @classmethod
    def cmdCreator(cls):
        return ompx.asMPxPtr(cls())

# -----------------------------------
# Plugin registration\degregistration block
# initializePlugin & uninitializePlugin are *required functions*

def initializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject)
    try:
        # Try to register our plug by passing in the kPluginCmdName, and the command creator:
        # we use the '.registerCommand()' method since we're trying to build a
        # command.  Other plugin types use different registration functions.  The
        # args do differ, check docs.
        mfnPlugin.registerCommand(kPluginCmdName, RenameDupesCmd.cmdCreator)
    except Exception:
        om.MGlobal.displayError("\nRenameDupesCmd registration failed\n")

def uninitializePlugin(mobject):
    # create a MFnPlugin
    mfnPlugin = ompx.MFnPlugin(mobject)
    try:
        # Try to deregister our plug by passing in the kPluginCmdName, and the command creator:
        mfnPlugin.deregisterCommand(kPluginCmdName)
    except Exception:
        om.MGlobal.displayError("\nRenameDupesCmd deregistration failed\n")
}}}
After this is loaded in the plugin manager (see [[How can I load \ unload a Python scripted plugin?]]), you should be able to run the comnmand in either mel or Python (and capture the return if you want to):
{{{
import maya.cmds as mc
mc.renameDupes()
# Renamed 2 node(s) # 
}}}
http://packages.python.org/MRV/
*"MRV is a multi-platform python development environment to ease rapid development of maintainable, reliable and high-performance code to be used in and around Autodesk Maya."
<<gradient horiz #ddddff  #ffffff  >>''WHAT IS IT?''
*This page is all about how to do things in Maya using its scripting language "mel" (Maya Embedded Language). And in addition to that "elf" (mel's "Extended Layer Format"), for UI creation.  Starting with Maya v8.5, I've been adding [[Python|http://www.python.org]] examples as well, since you can now script with Python //in// Maya.  Starting with Maya 2010, I finally got around to teaching myself the ~OpenMaya API, in 2012 ~PyMel, and in 2014 ~PySide/~PyQt, so there's newness for that showing up.
*This page mainly lists code samples, and "how-to's" with mel.  There are no "scripts" for download.  The purpose of this page is to be a resource for creating mel procedures and scripts.
*More info on Maya can be found in the [[Maya Links]] section.
*//This page used to be called ''"How to find stuff using mel"'' on other Wiki's:// 
**[[www.openwiki.com|http://www.openwiki.com/ow.asp?How+to+find+stuff+using+mel]] (which appears to be down half the time...) & [[warpcat.pbwiki.com|http://warpcat.pbwiki.com/HowToFindStuffUsingMel]] are some past sites.
''WHAT IT ISN'T:''
*Instructions on //how// to script\program. I'm presuming you already know a bit of that. But since there are plenty of examples listed, they may help.
''HISTORY:''
*This page started //years// ago (2000?) as a personal web page with a bunch of random thoughts as I learned mel. So a lot of these notes are from the gamut of versions... 1.0 through currently, >insert current version here<. You will see some pretty simple things listed, and some more complex things as well. I've thought about nixing the simple stuff, but for others that are learning, that would be counterproductive.
*I have posted it as a Wiki primarily because they're so easy to edit, I can add stuff very quickly while in development, from work, home, or off-site.  I'm currently using [[TiddlyWiki|http://tiddlywiki.com/]] due to the fact it's so easy to search (based on the Tags), and update (since I only have to work on one tiddler at a time, rather than the whole page).  Only thing I don't like is you can't add comments :(
''DISCLAIMER''
*I can't be blamed for anything misrepresented on this page! If it doesn't work, no finger-waving my direction.
*Since I work on a Windows system, the majority of the command line stuff I call out to is Win-centric. If you're using Lunix\Unix\OSX etc, I'm fairly confident those args won't work.... (replace with what is appropriate for your OS)
*@@You are free to use any of this information  in your personal or professional work.  I only ask that you give me credit where credit is due.@@ : [[Copyright Information]].
''CONTRIBUTE YOUR BRAIN:''
*If you'd like to add something to this page, please [[email me|WarpCat]] with your addition, and I'll add it in if appropriate, thanks!
*I give credit to those I get info from, whenever possible.
''TAKE IT WITH YOU''
*Since this is a tiddlywiki, it's a 'self contained' wiki.  No servers required to run it, no other installs needed (other than a valid web browser).  Using the //right column// you can download this file (it's only one file), and run it off any other location (memory-stick, cd, hard drive, another web site, etc).  Of course it won't update if you do that, so keep checking back on a regular basis.
----
And if you like how this TiddlyWiki works, check out the home page:
*http://tiddlywiki.com/   -- download TiddlyWiki 
*http://tiddlyspot.com/  -- Get your TiddlyWiki hosted online for free (what you're using right now)
All of these will open the documentation in a web browser.
{{{
# Python code
import maya.cmds as mc

# show Python docs for xform:
mc.help('xform', language='python', doc=True)
# show mel docs for ls:
mc.help('ls', language='mel', doc=True)

# show the global documentation for mel:
mc.help(documentation=True, language='mel')
# show the global documentation for Python:
mc.help(documentation=True, language='python')

# show the global documentation for the API:
mc.showHelp("API/main.html", docs=True)

# show the global documentation for node types:
mc.showHelp("Nodes/index.html", docs=True)
}}}
When you access the Maya menu command here:
*Skin -> Edit Smooth Skin -> Paint Skin Weights Tool [Options]
What code does it execute?

That menu command calls to this {{{runTimeCommand}}}:
{{{
ArtPaintSkinWeightsToolOptions;
}}}
Which in turn is a wrapper for this command:
{{{
artAttrSkinToolScript 3;
}}}
Which is a global procedure called to in this script (on Vista 64):
{{{
C:\Program Files\Autodesk\Maya<version>\scripts\others\artAttrSkinToolScript.mel
}}}
----
The code that generates the tool options in the Attribute Editor lives here:
{{{
C:\Program Files\Autodesk\Maya2013\scripts\others\artAttrSkinProperties.mel
}}}
Super easy way to access your computers clipboard data from within Maya, via Python:
{{{
from PySide import QtGui

def copy(text):
    QtGui.QClipboard().setText(text)

def paste():
    return QtGui.QClipboard().text()
}}}
{{{
copy("this is my text")
print paste()
# this is my text
}}}
Maya comes with a few built-in texel density mel scripts:

C:\Program Files\Autodesk\Maya2019\scripts\others
* texCalculateTexelDensity.mel
* texGetTexelDensity.mel
* texSetTexelDensity.mel
That can be accessed via the 'UV Editor -> UV Toolkit Panel -> Transform -> Texel Density'
Or just call to them directly.
Below are a list of //all// the subjects (tiddlers) in the wiki.  Note that there will be ones for site maintenance etc, that should be disregarded.
----
<<list all>>
As I learned [[here|https://tech-artists.org/t/alternative-to-usersetup-mel-in-maya-modules/14375]]:

If you name a mel script {{{someName_load.mel}}} and place it in a dir scene by the {{{MAYA_SCRIPT_PATH}}} on startup, it will execute just like {{{userSetup.mel}}}.

The [[cacheEvaluator|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/cacheEvaluator.html]] command is generated based on the {{{cacheEvaluator.mll}}} plugin.

It appears to be managed via a {{{maya.plugin.evaluator.cache_preferences.py}}} module, living here by default:
{{{
c:\Program Files\Autodesk\Maya20XX\Python\Lib\site-packages\maya\plugin\evaluator\cache_preferences.py
}}}
That directory is filled with other evaluator-related Python moudles.

This system is initialized during Maya startup via the {{{/Maya20XX/scripts/startup/initEvluationManager.mel}}}, by running the Python {{{cache_preferences_initialize()}}}.

There is an int {{{optionVar}}} called {{{cachedPlaybackEnable}}} that tracks whether it should be enabled or not:  It appears Maya immediately tracks any changes to that {{{optionVar}}}, and will enable/disable based on its state.
What are the current selected animation layers?
{{{
// global string array:
print $gSelectedAnimLayers;
}}}
You can also use these mel procs (living in {{{layerEditor.mel}}}) to both find the selected anim layer, and pick the contents (this is executed by 'Layers -> Select Objects'):
{{{
string $lLayers[] = getSelectedAnimLayer("AnimLayerTab");
layerEditorSelectObjectAnimLayer($lLayers);
}}}
All {{{getSelectedAnimLayer()}}} is really doing is this:
{{{
string $list[] = `treeView -query -selectItem ("AnimLayerTabanimLayerEditor")`;
}}}
----
Pop the animLayer editor into it's own window:
{{{
openFloatingAnimLayerEditor();
}}}
Use {{{createAnimLayerEditor(string $parentLayout, string $toolName )}}} To put it into a UI of your choosing.
----
Delete empty animLayers:
{{{
deleteEmptyAnimLayers();
}}}
----
How can I highlight/select the root animation layer (by default, named {{{BaseAnimation}}}) and no others?
{{{
string $rootLayer = `animLayer -query -root`;
if(size($rootLayer)){
	string $animLayers[] = `ls -type "animLayer"`;
	for($i=0;$i<size($animLayers);$i++){
		animLayerEditorOnSelect($animLayers[$i], 0); // From layerEditor.mel
	}
	animLayerEditorOnSelect($rootLayer, 1);
}
}}}
Or via ~PyMel:
{{{
import pymel.core as pm

rootLayer = pm.animLayer(query=True, root=True)
if rootLayer:
    layers = pm.ls(type='animLayer')
    for layer in layers:
        pm.animLayer(layers, edit=True, selected=False)
    pm.animLayer(rootLayer, edit=True, selected=True)
}}}
NOTE:  I've encountered that on some people's machines the pymel commands fail, and I had to revert back to mel wrappers :S
----
Commands:
*{{{animLayer}}}
----
Where is the anim layer mel code at?
{{{
\Autodesk\Maya20XX\scripts\startup\layerEditor.mel
\Autodesk\Maya20XX\scripts\others\performAnimLayerAddObjects.mel
}}}
----
How is the UI structured?
*The 'Layer Editor' ui in the Channel Box is managed by a form Layout.
*The section below the buttons is a {{{treeView}}} control
----
How are new anim layers made when you press the button?
* A 'Run Time Command' called {{{SelectedAnimLayer}}} is executed, which calls to the global proc {{{layerEditorCreateAnimLayer( true, false)}}} (which lives in {{{layerEditor.mel}}}).
*This in turn runs the {{{animLayer}}} command which physically builds the layer.



Some links:
*http://www.akeric.com/blog/?p=1463 (my own blog)
*http://zoomy.net/2009/07/26/basic-arduino-to-maya-communication/
https://docs.arnoldrenderer.com/display/A5AFMUG/Arnold+for+Maya+User+Guide
This is a scratchpad of info as I learn more about this subject.  A lot of this is just pulled directly from the Maya docs.
More notes over on [[API: basics]]
Official online examples here: [[API : Example Code]]
Or you can find examples on your local drive:
{{{
C:\Program Files\Autodesk\Maya20XX\devkit\plug-ins\scripted\*.py
}}}
----
The [[Maya Learning Channel|http://www.youtube.com/user/MayaHowTos/videos?view=0]] Youtube page has a growing series of videos as well:
*[[Loading and executing a Python plug-in in Maya|http://www.youtube.com/watch?v=Wf03mSCdh-Q]].
*[[Creating a Python command plug-in - Part 1: Undo and redo|http://www.youtube.com/watch?v=BZyXe3MhEyI&list=PL2611366C5C04BF9B&index=2]]
*[[Creating a Python command plug-in - Part 2: Custom flags|http://www.youtube.com/watch?v=v1d8fCtIROI&feature=youtube_gdata]]
----
Collection of simple example scripted plugins I've been authoring for the wiki can be shown by clicking on this tag:
*[[scripted plugin]]
----
Notes:
*May be good to prefix all scripted plugin .py files with something... like "sp", so you know they're different from regulard Python scripts?  Scripted Plugins can be loaded in the Maya plugin manager, regular Python scripts can't.
----
If you have Autodesk gold support, you can access their podcasts [[here|http://subscription.autodesk.com/sp/servlet/elearning/course?catID=11484791&cfID=Autodesk+Maya&siteID=11564774]].  There are several called (such as) "API & Game Dev I" and  "API & Game Dev II" by Nathan Martz, lead programmer over at Double Fine Productions, that cover a lot of the API basics (starting with the second video).
----
Writing a scripted plug-in requires the definition of some specialized functions within the plug-in. All scripted plug-in must:
*Define {{{initializePlugin()}}} and {{{uninitializePlugin()}}} entry and exit functions.
**Register and unregister the proxy class within these entry points.
*Implement a creator function (arbitrarily named, but something like {{{creator()}}} is probably a good idea).
*Properly initialize the plugin proxy class via the {{{__init__()}}} method. 
Depending on the type of plugin you're authoring, there will be other required functions (an 'initializer' in the case of node creation) or proxy class methods ({{{doIt()}}} in the case of a command, {{{compute()}}} in the case of a node, etc).
----
Plugins have different 'flavors', or classes.
Plugins have different categories.  All subclasses of the {{{maya.OpenMayaMPx}}} module.  It should be noted that this module has functions the subclasses can inherit (like {{{asMPxPtr}}} for example), but I can find no online documentation for the {{{OpenMayaMPx}}} specifically.
>[[Docs for all plugin classes|http://download.autodesk.com/us/maya/2011help/API/group___m_px.html]]
>In the plugin class's documentation, the sections listed as "__Public Member Functions__" are the inherited superclass methods at your disposal when writing your own plugins.
*{{{maya.OpenMayaMPx}}}
**{{{MPxCommand}}} : make custom mel commands.
***http://download.autodesk.com/us/maya/2011help/API/class_m_px_command.html
***Can be created to run in {{{creation}}}, {{{query}}}, and {{{edit}}} modes.
**{{{MPxFileTranslatorCommand}}} : custom file format import/export, read/write.  These interface with Maya's UI, allowing you to select the custom file types in file browsers, etc...
***http://download.autodesk.com/us/maya/2011help/API/class_m_px_file_translator.html
**{{{MPxNode}}} : For making custom DG nodes.
***http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html
**{{{MPxLocator}}} : A type of {{{MPxNode}}} for custom drawing
***http://download.autodesk.com/us/maya/2009help/API/class_m_px_locator_node.html
**{{{MPXContext}}} : (and {{{ContextCreatorCmd}}}) : For handling mouse\user inputs
***http://download.autodesk.com/us/maya/2011help/API/class_m_px_context.html
** many more!
Each category has similar basic structure, but with extra rules.  
----
''Top level overview of plugin structure:''
*Plugin class/classes implements core functionality.  
**Derives from an ~MPx class.  Each type of plugin is implemented as python class that will be derived from.
**Implements functionality by overriding interface functions.  Maya gives us a template, and we override that template based on our needs.
**Specific methods needing implementation based on plugin type:
***'doIt' method:
****What actually does the work in the instance of the plugin //command// object.
***'compute' method:
****What actually does the work in the instance of the plugin //node// object.
***Different plugins have other required method implementations.
*Special functions (I author these as classmethods):
**Creator Function:
***Returns a new instance of the class
***Needs to be one per plugin class being authored.
**Syntax Function:
***When creating commands that can accept flags, this special function needs to be setup to create them.
*Registration/Deregistration:
**If multiple plugins classes are being authored in the same Python module, they can share the same registration\deregistration block
**Tells Maya: about new plugins, their types, hooks needed to instance them.
Common Python plugin modules:
{{{
# All plugin functionality lives here:
import maya.OpenMayaMPx as ompx
}}}
I like to wrapper Maya's nodes in classes, and write mini-API's around them.  Often times, to make sure the users won't accidentally mess with the nodes, I use Maya's {{{lockNode}}} command to lock the node after I modify it.  But it can become a pain to constantly have to author blocks of code that 'unlock the node.... do some work... relock the node'.
To help make this easier, one can author either a {{{with}}} 'context manager' for use within a method or function, or a 'decorator' that can wrapper a method or function.
!Decorator
{{{
import maya.cmds as mc

def unlockRelock(func):
    """
    An 'unlock/relock' decorator: It's designed specifically to look for a
    self.node attr on the instance method it wrappers, and attempt to unlock
    that Maya node, execute the method, then relock that Maya node.  Note, the
    node/attr doesn't have to exist at time of method call.
    """
    def wrapped(*args, **kwargs):
        itself = args[0] # This is the 'self' arg passed to the method this wrappers.
        if hasattr(itself, "node"):
            if mc.objExists(itself.node):
                try:
                    mc.lockNode(itself.node, lock=False)
                except Exception, e:
                    pass
        result = func(*args, **kwargs)
        if hasattr(itself, "node"):
            if mc.objExists(itself.node):
                try:
                    mc.lockNode(itself.node, lock=True)
                except Exception, e:
                    pass
        return result
    return wrapped
}}}
! {{{with}}} Context Manager:
{{{
class UnlockRelock(object):
    """
    Context Manager for unlocking and relocking the provided node. Note, the node doesn't
    yet have to exist at time of context manager creation.
    """
    def __init__(self, node):
        """
        Parameters:
        node : string : The node to unlock/relock.
        """
        self.node = node
    def __enter__(self):
        if self.node:
            if mc.objExists(self.node):
                try:
                    mc.lockNode(self.node, lock=False)
                except Exception, e:
                    pass
    def __exit__(self, exc_type, exc_value, traceback):
        if self.node:
            if mc.objExists(self.node):
                try:
                    mc.lockNode(self.node, lock=True)
                except Exception, e:
                    pass
        # Raise any exceptions:
        return False
}}}
! Both In Action:
{{{
class Spam(object):
    def __init__(self, node):
        self.node = node

    @unlockRelock
    def usingDecorator(self):
        mc.setAttr('%s.translateX'%self.node, 10)

    def usingContextManager(self):
        with UnlockRelock(self.node):
            mc.setAttr('%s.translateX'%self.node, 10)
}}}
You can find a link to a pdf for Maya certified hardware here:
https://knowledge.autodesk.com/support/maya/troubleshooting/caas/simplecontent/content/maya-certified-hardware.html
That PDF will tell you the certified drivers per graphics card per version.
Common ones:
| Maya Version | Card | Driver | Link |
| 2020 | ~NVidia |  441.28 | [[Here|https://www.nvidia.com/download/driverResults.aspx/154345/en-us]] |
| 2019.1 & .2 | ~NVidia |  430.86 | [[Here|https://www.nvidia.com/Download/driverResults.aspx/147971/en-us]] |
| 2019 | ~NVidia |  411.95 |  |
Maya has no built-in way to average a matrix.  However, if you have {{{numpy}}} installed, this is actually surprisingly easy.
The below example works if the array was a single list of numbers, or the list of list of numbers.  Maya matrices are 16 long, or 4 groups of 4.  For simplicity, 2 groups of 2:
{{{
from numpy import array

a1 = array( [[2.0, 2.0], [2.0, 2.0]] )
a2 = array( [[3.0 ,3.0], [3.0 ,3.0]] )
arrList = [a1, a2]
avg = sum(arrList)/len(arrList)

print avg
[[ 2.5  2.5]
 [ 2.5  2.5]]
}}}
If you don't have numpy, you'd need to write a function that would, for each value in each array, add it to the corresponding indices in the other arrays and  then divide by total arrays (basic average math) and plug that into a new array.
Had issues where shifting large amounts of keys coud hang Maya for a //very long time//.  Reported this with repro to Autodesk, and they said the below code should resolve:
{{{
import maya.cmds as mc
for dagObject in mc.ls(dagObjects=True):
    mc.setAttr('%s.ghosting'%dagObject, 0)
}}}
Presuming you have a selected hierarchy like below, how can you get back all the 'parent' nodes.  Meaning, any transform node that has a child?
*group1
**some poly mesh A
**some poly mesh B
***some poly mesh C
*group2
**some poly meshD
{{{
string $selObjects[] = `ls -sl`;
string $parents[] = `listRelatives -p $selObjects`;
$parents= `stringArrayRemoveDuplicates $parents`;
print $parents;
//group1
//group2
//some poly mesh B
}}}
*Thanks to a post from Johan Ramstrom
Also see:
[[Based on a selection of nodes, how can I return only the 'groups'?]]
Presuming you have a selected hierarchy like below, how can you get back just the "group" nodes.  Meaning, transforms with no shapes? (just the group# nodes)
*group1
**some poly mesh A
**some poly mesh B
***some poly mesh C
*group2
**some poly meshD
{{{
string $selObjects[] = `ls -sl`;
string $groups[]; clear $groups;
for($i=0;$i<size($selObjects);$i++)
    {
    string $shapes[] = `listRelatives -s $selObjects[$i]`;
    if(size($shapes)==0)
        $groups[size($groups)] = $selObjects[$i];
    }
print $groups;
// group1
// group2
}}}
Also see:
[[Based on a selection of nodes, how can I return back all the 'parents'?]]
{{{lsThroughFilter}}}
For example, find all the default shading groups and materials in a new scene:
{{{
string $nodes[] = `lsThroughFilter DefaultMaterialsFilter`;
// Result: lambert1 particleCloud1 //
}}}
Also see:
*[[How can I query all the default itemFilters in Maya?]]
*[[How can I use itemFilter or itemFilterAttr?]]
This subject is about batching Maya files in a session of Maya //other than the one that is currently open//, via Python.  It's one thing to (via the //currently opened Maya//) loop over a list of files opening them in succession and executing code.  But if you want to launch //external// Maya's in batch mode (so as to not disturb your current Maya session), open a file, and execute code on that file (all in Python), it's a different story.

To batch file via Maya with //mel//, it's pretty straightforward via {{{maya.exe}}}:
{{{
> maya -batch -file someMayaFile.ma -command someMelProc
}}}
There is {{{mayapy.exe}}} as well, but this doesn't launch Maya:  It launches Maya's version of Python.  It is a Maya mirror of {{{python.exe}}}.  However, once it has been launched, Maya itself can be imported in as a Python module, and then batching can take place.  Method A explains this below.
----

Different methods below.  I came up with Method C first, got suggested Method B, then sort of combined the two to come up with Method A.
* Method A & C launches a new Maya in batch mode from an //existing// Maya.
* Method B launches a new Maya in batch from from a .bat file on disk.
!Method A
The example below will batch over all the passed in Maya files, and saves a new group node in them as proof it worked.  Some of the magic is using {{{subprocess.call}}} to call to {{{mayapy.exe}}}, and passing in an additional argument that is the name of the current file to work on.  Another bit of trickery is having the module do double-duty:  When executed in //GUI Maya// the user runs its main() function to start it up, but when it is called to via {{{mayapy.exe}}} putting it in 'batch mode', the top & bottom section '{{{if __name__ == "__main__":}}}' are executed as well which both imports {{{maya.standalone}}} (thus now physically has Maya running inside of {{{mayapy.exe}}}) and calls to the {{{batchOperation()}}} function, which can then query, via {{{sys.argv}}} the command-line arguments passed in (which are the names of the files to batch over).  I have a feeling that's not terribly clear.  I hope the code makes more sense ;)

I'll label each thing has it's executed so you can see the data flow.
GUI mode starts with GUI A and ends with GUI D, which then starts BATCH A and ends with BATCH E.
{{{
# batchTest.py
import imp
import subprocess

if __name__ == "__main__":
    # If we're in 'batch' mode, it means that mayapy.exe is running,
    # so load maya.standalone into it:
    import maya.standalone
    maya.standalone.initialize(name='python')

    # If you have a userSetup.py that is needed for happy Maya execution, call
    # to it here:
    # Optional: Starting sometime around Maya 2016, I ran into issues where Maya's MASH
    # package's userSetup.py would be sourced before mine:  This solution allows you to
    # forcefully inject yours on top if it:
    # imp.load_source('userSetup', 'c:/path/to/my/userSetup.py')
    import userSetup
    userSetup.main()

import maya.cmds as mc

def main(paths):
    """
    Launched in the gui version of Maya, manually, or via some other script.

    Parameters :
    paths : string\list : Path(s) to Maya files to batch over.
    """
    if not isinstance(paths, list):
        paths = [paths]
    args = ['mayapy', __file__, '-']
    args.extend(paths)
    # This fires off another version of Maya in 'batch' mode, calling back to
    # this code, executing the 'batch only' parts.  Luckily, the current version
    # of Maya calling it waits for the other version to complete doing it's thing
    # before continuing on in this code...
    subprocess.call(args)

def batchOperation():
    """
    The work done by the batch:  This has been called to while Maya is in batch
    mode, via the subprocess.call() func in the main() function:  Because of this,
    we capture the incoming args via sys.argv.
    A single instance of Maya is launched, and then all the files are batched over
    in that instance, and processed
    """
    # The sys.argv vals match the subprocess call above
    # sys.argv[0] : The path to this module ( __file__ )
    # sys.argv[1] : '-'
    # sys.argv[2] + : Path(s) to the files to batch over
    paths = sys.argv[2:]

    # If there is an error, the shell will just disappear.  So we wrap it in a
    # try\except, allowing it to stay open to help track down bugs.
    try:
        mc.file(prompt=False)
        for fil in paths:
            mc.file(fil, force=True, open=True)
            mc.group(empty=True, name='I_was_made_in_batch_mode', world=True)
            mc.file(force=True, save=True)
    except Exception, e:
        print "Exception:", e
        raw_input("Encountered an exception, see above.  Press Enter to exit...")
    finally:
        mc.file(prompt=True)

# When called to via batch mode, run this code.  This line must come after the
# function it's calling.
if __name__ == "__main__":
    batchOperation()
}}}
To run in Maya:
{{{
import batchTest
batchTest.main(['c:/temp/myFileA.ma', 'c:/temp/myFileB.ma'])
}}}
You should see a shell pop up for the new instance of Maya that is launched (via mayapy.exe), and then each path is opened and batch processed in that shell.  The gui version of Maya will wait until completion of the batch to continue on with its code.
!Method B
This snippet will launch a myModule.py file living in the same directory (per the special {{{%~dp0}}} variable) as the bat script, using a pre-made env-var storing the path to the mayapy.exe file.  It will also pass the name of the item dragged&dropped onto it via the {{{"%1"}}}.
Note that either of these examples could then call to the code in Example A, above.
{{{
"%MAYA_PY_EXECUTABLE%" "%~dp0\myModule.py" "%1"
}}}
This (modified) example was given to me by Keir Rice.  Thanks Keir!
Make a batch script: {{{batchTest.bat}}} .  It runs {{{mayapy.exe}}}, which is //Maya's// version of Python.
{{{
REM Launcher batch file: batchTest.bat
"%ProgramFiles%\Autodesk\Maya2010\bin\mayapy.exe" "c:\temp\test.py"
pause
}}}
*Add {{{pause}}} to the end of the batch script if you want the shell to stay open upon completion.
Note that if mayapy is part of your Windows path, you can leave off the directory before it, and the .exe on the end of it.

Then make the python module it calls to:
{{{
# python script to run: c:\temp\test.py
import maya.cmds

def TestFunction():
    print maya.cmds.about(version=True)

if __name__ == "__main__":
    # Init Maya:
    import maya.standalone
    maya.standalone.initialize(name='python')
    # If you have one:
    import userSetup
    userSetup.main()

    # Run your code:
    TestFunction()
}}}
Finally, run the bat script.  Results should be printed in the shell.

!Method C:
This is one I came up with before I knew about Method A or B, above.
Key Components:  (the file-names aren't important, just illustrating the example)
*{{{mainModule.py}}} :  This is the Python module that orchestrates the whole thing.  It can be executed from Maya, or the command line.
*{{{melScript.mel}}} : This is a 'wrapper' script that our batch operation will call to.  It in turn, via the mel {{{python}}} command, will execute the Python code we want to have effect the scene files.
*{{{pythonModule.py}}} : This is the Python module that our mel script calls to.  In this example, it executes the {{{main()}}} function inside.
----
Here is a bit of code from {{{mainModule.py}}}.  By calling to {{{subprocess.call()}}}, we're able to launch Maya in batch mode on a specific file, executing our wrapper mel command on it:
http://docs.python.org/library/subprocess.html  
{{{
# mainModule.py
import subprocess
subprocess.call(['maya', '-batch', '-file', 'c:\\myFile.ma', '-command', 'melScript'])
}}}
----
This is the guts of our wrapper mel script.  All it does is call to the Python code we care about:
{{{
// melScript.mel

global proc melScript(){
    python("import pythonModule");
    python("pythonModule.main()");
}
}}}
----
{{{pythonModule.py}}} and its {{{main()}}} function can prettymuch do whatever you'd like to that scene file:
{{{
# pythonModule.py
import maya.cmds as mc

def main():
    # do stuff!
}}}
https://github.com/danbradham/manymaya

"A fast and light-weight wrapper for Autodesk Maya's standalone extension. ~ManyMaya leverages the multiprocessing module to run maya.standalone instances concurrently."
Can access via:
* Windows -> Bifrost Graph Editor
* Create -> Bifrost Graph : Creates a new graph, and opens the editor.
Once inside...
* Press tab to open the 'create node by name' options menu
* input node : What attrs are exposed to Maya as inputs for this graph.
** You can RMB on any attr to rename it.
* output node : What attrs are exposed to Maya as outputs for this graph.
* Compound : A collection of nodes that have been 'compressed' into a single node.
* You can MMB drag&drop nodes from the Outliner into the graph.
** This will create a 'bif' node in the outliner (Maya transform), with a child {{{bifShape}}} shape node, which has an input from a {{{bifrostBoard}}} node (the Bifrost graph).
** This is a //duplicate// of the original node you ~MMB-dragged into the graph.
* The 'Info' tab in the editor will explain what each of the properties on a node are.
* Selecting nodes and pressing ctrl+g creates a 'compound' node.
** The {{{l}}} hotkey will 'layout' the graph nicely.
* You can RMB on any wire (attr connection) and add a 'watch point', which you can mouse over to get info
* Some nodes you import, like {{{basic_particles_graph}}} work better if you RMB->Explode them, to work on them, which can be easier than working on a compounds inputs and outputs.  You can always regroup them later.
!Scripting
https://help.autodesk.com/view/BIFROST/ENU/?guid=__CommandsPython_index_html
{{{
# Create a new node in the graph:
vnnCompound "bifrostGraph1" "/" -addNode "BifrostGraph,Modeling::Converters,convert_to_volume";
}}}
{{{
# Create a new iniput port:
vnnNode "bifrostGraph1" "/convert_to_volume" -createInputPort "geometry.mesh" "Amino::Object";
}}}
{{{
# Connect attrs in the graph
vnnConnect "bifrostGraph1" ".mesh" "/convert_to_volume.geometry.mesh";
}}}
The Bifrost Graph Editor has two options for 'publishing' it's data:
* Publishing (aka saving/exporting) Compounds (see: [[Bifrost : Save and Load Compounds]] for more details).
* Publishing (aka saving/exporting) Graphs.
But at a high level, what is the difference between them?
* A {{{Compound}}} is a collection of multiple nodes 'grouped' together.  When multiple Bifrost nodes are grouped, that group is called a Compound.
** When you add Bifrost nodes to your graph, some may be Compounds, and others could be the 'raw nodes' that make up other compounds.
** Compounds are accessed via the Bifrost Graph 'Tab' menu.
* A {{{Graph}}} is a collection of multiple Compounds, with Input and Output nodes.  
** A graph (and all its nodes & compounds) is are contained in a {{{bifrostGraphShape}}} node in Maya
** Each graph has its own {{{bifrostGraphShape}}} node, and is represented by a separate tab in the Bifrost Graph Editor.
** Graphs are 'imported' from the Bifrost Browser.  This import could pull in Maya data with them, if included as part of the Publish process.  The import process creates a new {{{bifrostGraphShape}}} node in the scene containing all the node and Compound data for that graph.
** Their extra data (thumbnail, docs, Maya file) are called 'sidecar files'.
Armed with that, what is the difference between 'publishing a Compound' and 'Publishing a Graph'?
! Publishing A Graph
* [[Bifrost Docs|https://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_build_a_graph_publish_graphs_html]]
* Makes the graph available for view in both the Bifrost Browser (which lets your browse Bifrost Graphs), but not the Bifrost Graph (the graph lets you browse for nodes and Compounds).
* The publish includes everything in the graph:  Input node(s), output node, + all other nodes & compounds.
* Allows you to provide documentation file, thumbnail imagery, example Maya file. 
* By default, it's saved as a subdir here, with a single file in it: {{{C:\Users\<USERNAME>\Autodesk\Bifrost\Comounds\graphName\graphName.json}}}
! Publishing A Compound
* [[Bifrost Docs|https://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_build_a_graph_create_and_edit_compounds_publish_compounds_html]]
* Makes the Compound available in the Bifrost Graph (only) : Not part of the Bifrost Browser (Bifrost Browser shows Graphs, not compounds). 
* Only publishes that specific Compound:  Not any other connecting nodes (other compounds, input/output nodes, etc).
* After publish, you can manually add documentation. 
* By default, it's saved as a single file here: {{{C:\Users\<USERNAME>\Autodesk\Bifrost\Comounds\compoundName.json}}}

In either case, see [[Bifrost : Save and Load Compounds]] for more info on where they save, how to make the documentation, and how to update Maya with additional paths.
! Installer
The main download installer page is here:
https://area.autodesk.com/bifrost/downloads/
And the release notes here:
https://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_ReleaseNotes_release_notes_html

This is a separate plugin, that is added as a new module in Maya. See [[Understanding Maya Modules and Pacakges]]
! Plugins
Once installed, to load the plugins, see: [[Maya 2020 docs|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2019/ENU/Bifrost-MayaPlugin/files/Bifrost-MayaPlugin-load-bifrost-plugin-html-html.html]].

Note, loading the plugins (starting with {{{bifrostGraph.mll}}} can take 30 sec to a minute:  This is a pretty big speed hit when opening Maya, so I'd only load them on demand.
* {{{bifrostGraph.mll}}} : Main plugin
* {{{bifmeshio.mll}}}, {{{bifrostshellnode.mll}}}, {{{bifrostvisplugin.mll}}}: required for Bifrost Fluids in Maya's FX menu set.
* {{{Boss.mll}}}: required for BOSS (Bifrost Ocean Simulation System) in Maya's FX menu set.
Other:
* {{{mayaVnnPlugin.mll}}}: required for interacting with the node-based environment (automatically loaded by {{{bifrostGraph.mll}}}).
Shelf button to load them:
{{{
# Load all the bifrost plugins
import maya.cmds as mc
bifPlugins = ["bifrostGraph", "bifmeshio", "bifrostshellnode", "bifrostvisplugin", "Boss", "mayaVnnPlugin"]
for plugIn in bifPlugins:
    if not mc.pluginInfo(plugIn, query=True, loaded=True):
        mc.loadPlugin(plugIn, quiet=True)
        print("Loaded plugin: %s"%plugIn)
    else:
        print("Plugin already loaded: %s"%plugIn)
}}}
! Docs
* (Maya 2020) [[root level documentation|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=GUID-2E26338E-4433-4B6D-A776-AA2B60931407]] for it.
* Python scripting docs [[here|https://help.autodesk.com/view/BIFROST/ENU/?guid=__CommandsPython_index_html]]
* [[Bifrost Node Reference|https://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_html]]
* [[Python Commands|https://help.autodesk.com/view/BIFROST/ENU/?guid=__CommandsPython_index_html]]
* [[FAQ page|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2019/ENU/Bifrost-MayaPlugin/files/Bifrost-MayaPlugin-bifrost-faq-html-html.html]]
* [[Autodesk 'Area' for Bifrost|https://area.autodesk.com/bifrost/]]
* [[Bifrost Autodesk Blog posts|https://area.autodesk.com/all/blogs/bifrost/]]
* [[Bifrost history and overview|https://beforesandafters.com/2019/11/18/a-bifrost-journey/]]
! Tutorials
!! Autodesk
* [[Bifrost Basics Part 1: Node Graph Intro|https://www.youtube.com/watch?v=_3-bKGZwdbQ&list=PLk_DbHjVI_iDz4hagdDtwUxysL42muPiw]]
* [[Bifrost Quick Compounds - Episode 1: Shape Deformer|https://www.youtube.com/watch?v=SPQ5vB4qKQQ]]
* [[Instancing Sample Scenes|https://forums.autodesk.com/t5/bifrost-forum/instancing-sample-scenes/td-p/8951825]]
* [[Bifrost: Strands, Instancing and Visual Programming|https://area.autodesk.com/blogs/the-maya-blog/bifrost-strands-instancing-and-visual-programming/]]
* [[Getting Started with Bifrost|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/]] : 10 Tutorials on the Autodesk Area.
**[[Getting Started with the Bifrost Graph|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/getting-started-with-the-bifrost-graph/]]
**[[Creating a smoke trail in the Bifrost Graph|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/creating-a-smoke-trail-in-the-bifrost-graph/]]
**[[Caching and REndering a Bifrost effect|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/caching-and-rendering-a-bifrost-effect/]]
**[[Understanding Scale in the Bifrost Graph|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/understanding-scale-in-the-bifrost-graph/]]
**[[Export / Import Bifrost compounds with Info tab documentation|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/export-import-bifrost-compounds-with-info-tab-documentation/]]
**[[Bifrost Quick Compounds - Episode 1: Bulge Deformer|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/quick-compounds-episode-1-bulge-deformer/]]
**[[Bifrost Quick Compounds - Episode 2: Transfer Closest CPV Data|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/bifrost-quick-compounds-episode-2-transfer-closest-cpv-data/]]
**[[Bifrost Quick Compounds - Episode 3: Generate points on objects|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/bifrost-quick-compounds-episode-3-generate-points-on-objects/]]
**[[Bifrost Quick Compounds - Episode 4: Generate fuzz using strands|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/bifrost-quick-compounds-episode-4-generate-fuzz-using-strands/]]
**[[Getting Started with MPM Cloth in Bifrost|https://area.autodesk.com/tutorials/series/getting-started-in-bifrost/getting-started-with-mpm-cloth-in-bifrost/]]
* [[Bifrost + Maya remesh and retopo tutorial|https://www.youtube.com/watch?v=p6EuH5o_CuQ]] (youtube)
* [[Getting Started with the Bifrost Graph|https://www.youtube.com/watch?v=J1qQNYhmkaE&feature=emb_logo]] (youtube)
* [[Masterclass|https://area.autodesk.com/tutorials/series/bifrost-visual-programming-masterclass/]] (2 tutorials)
* [[Bifrost Quick Tip: Exporting / importing compounds with info tab documentation|https://www.youtube.com/watch?time_continue=2&v=FuPB9exDa6A]]
* Introduction to Bifrost
** [[Lesson 1 - Generating a mesh using noise|https://youtu.be/l3DtVsoLE4o]]
** [[Lesson 2 - Mesh Manipulation with fields|https://youtu.be/PE2MsoqpCeg]]
** [[Lesson 3 - Point scattering and instances|https://youtu.be/1yCKcmOk254]]
* [[Bifrost Scatter Series: 6 videos|https://www.youtube.com/playlist?list=PL8hZ6hQCGHMWe0v4aTTI7hYo9KQPDU02D]]
!! Others
* [[This very long thread on TechArtists.org|https://tech-artists.org/t/maya-bifrost-node-graph/11529]]
* [[Bezier curve bifrost graph|https://forums.autodesk.com/t5/bifrost-forum/bezier-curve-bifrost-graph/td-p/8974649]]
* [[MJCG Compounds (utilities, strands, procedural modeling, etc.)|https://forums.autodesk.com/t5/bifrost-forum/mjcg-compounds/td-p/8920114]]
* [[Make Custom Noise Deformer|https://hub.gametools.dev/display/2SV/Bifrost+In+Maya]]
* [[Make Custom Peak/Push Deformer|https://vimeo.com/351304213]]
* [[Maya Bifrost node editor Instancing and Scattering|https://www.youtube.com/watch?v=I1AsLhKwdeA]]
*[[Maya: VDB Clouds in the Bifröst Graph Editor|https://www.youtube.com/watch?v=-w8GPvw75rg&feature=youtu.be]]
! Menus & Shelves
Once all the plugins are loaded, 
* You should see a 'Bifrost Fluids' menu in Maya's 'FX' section.
* You'll find the 'Bifrost Browser' and the 'Bifrost Graph Editor' in Maya's main 'Windows' menu.
* Under the 'Create' main menu, there is a new 'Bifrost Graph' option.     
* There will be a 'Bifrost' shelf
! Running from the commandline
{{{
\bifrost\bin\bifcmd.exe

> bifcmd /path/to/graph.json --frames 1 30 --fps 24
}}}
See notes:
* [[Setting up system level env vars|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_bifcmd_Windows_setting_up_env_html]]
* https://area.autodesk.com/blogs/the-maya-blog/bifcmd-bifrost-engine-and-bifrost-compute-jobs-explained/
* http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_bifcmd_html
[[Node docs|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_html]]
!?
* {{{value}}} : Can create just about any value type.
!aminio
*[[and|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_and_html]] : Returns true if both inputs are true. If at least one or both inputs are false, it returns false. Can combine the outputs of {{{less}}} and {{{greater}}}.
*[[divide|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_divide_html]] : Divides values.
*[[evaluate_fcurve|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_evaluate_fcurve_html]] : Remaps the input x to the output y based on a editable function curve. It is useful for modulating effects using a custom profile, such as for falloffs and more.
*[[greater|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_greater_html]] : Returns true if the first input comes after the second one using the native ordering of the type. 
*[[if|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_if_html]] : This control flow node selects between two subgraphs based on the value of the condition input.
*[[length|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_length_html]] : Returns the length of a vector.
*[[less|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_less_html]] : Returns true if the first input comes before the second one using the native ordering of the type.
*[[multiply|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_multiply_html]] : Multiplies values.
*[[subtract|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_amino_subtract_html]] : One or more values to subtract in sequence. The second connected value is subtracted from the first, then the third value is subtracted from the result, and so on.

!bif
*[[create_object_from_schema|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_create_object_from_schema_html]] : Create a Bifrost object from an schema defined by a JSON file.  They can be found here: {{{resources/bif/schema/mesh.json}}} (for example).  Others include: levelset, mesh, mesh_geo_location, point_geo_location, points, strand, volume volume_geo_to_location (more).
*[[generate_sample_locations|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_generate_sample_locations_html]] : Generates random locations on a geometric object.
*[[get_closest_locations|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_get_closest_locations_html]] : Find the closest locations of points on a target mesh.
*[[get_geo_property|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_get_geo_property_html]] : Get the property from some geo.
*[[get_point_position|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_get_point_position_html]] : Read all the points of a mesh into an array.
*[[get_point_normal|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_get_point_normal_html]] : Returns the array of point normals for a mesh.
*[[is_a|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_is_a_html]] : Checks whether the input object conforms to a given prototype. For geometry types like meshes and volumes, this also checks whether the required properties have the type specified by the geometry schema.
*[[sample_property|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_sample_property_html]] : Get the average of a property around a point.
*[[remove_from_array|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_remove_from_array_html]] : Removes the elements with the specified indices from the input array.
*[[set_geo_property|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_materials_set_geo_property_reference_html]] : Drives a property on some geo.
*[[set_point_position|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_set_point_position_html]] : Sets the point position values of the given array.
* [[set_point_shape|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_bif_set_point_shape_html]] : Sets the shape used for displaying and rendering points, such as from a particle system.

!geometries
* [[construct_points|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_geometries_construct_points_html]] : Constructs a points geometry - creating a points from raw data in the graph
* [[contour_dual_marching_cubes|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_geometries_contour_dual_marching_cubes_html]] : Converts a volume (like what {{{convert_to_volume}}} generates into Bifrost geometry.
* [[convert_to_volume|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_geometries_convert_to_volume_html]] : Converts mesh into a volumetric representaton as either a 'fog' or 'level set' (mesh-ish).
* [[find_all_in_array|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_geometries_find_all_in_array_html]] : Returns all indices in an array that are equal to value.


!particles
*[[basic_particle_graph|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_particles_basic_particles_graph_html]] : sets up a simple default graph that will emits particles which fall under gravity and die after one second.
!simulation
*[[collider|http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_reference_simulation_collider_html]] : Obstacles to the flow of a Bifrost simulation. You can use them to form a barrier around which smoke must flow, or create a basin that prevents particles from falling away under gravity, or animate them to create various effects.
From this great video: https://www.youtube.com/watch?v=p6EuH5o_CuQ
The idea is, use bifrost to voxelize some 'bad' poly mesh, then use Maya's remesh & retopo to give it nice clean geo.

This is my take on that video tutorial, slightly modified
! In The Bifrost Graph Editor
* Create a new Bifrost graph if one isn't already made, default name is {{{bifrostGraph1}}}, a {{{bifrostBoard}}} node type.
* MMB drag in the //shape// node of your mesh. This will create a new Input node pointing to the path of your mesh:  You can delete the old one.
* Create {{{convert_to_volume}}}
** Uncheck 'Store Fog Density'
** Check 'Store Level Set' : This is what makes the 'geo'.
** You can change the 'resolution' of the upcoming tessellation by changing the 'Detail Size' on this node.
* Create {{{contour_dual_marching_cubes }}}
* Connect your {{{meshShape.mesh}}} to {{{conver_to_volume.geometry}}}
* Connect {{{conevrt_to_volume.volume}}} to {{{contour_dual_marching_cubes.volume}}}
* Connect {{{contour_dual_marching_cubes.mesh}}} to a new plug on the {{{output}}} node, which creates {{{output.mesh1}}} : This {{{mesh1}}} attr is now an 'output' of your bifrost graph node.
**  In old versions of Bifrost this would immediately create a {{{bif1}}} node ({{{transform}}}) and {{{bifShape1}}} node ({{{bifShape}}}) in Maya, which you can drag off to the side so it doesn't overlap the original.
** New versions don't create this extra node:  The {{{bifrostGraphShape}}} node displays this directly.
! In the Node Editor
* MMB drag the {{{bifrostGraph1}}} node into the Node Editor.
* Create a {{{bifrostGeoToMaya}}} node.
* Create a {{{mesh}}} node (which will auto-create a parental {{{transform}}} for it).
* Connect the {{{bifrostGeoToMaya.mayaMesh}}} to the {{{mesh.inMesh}}} : Immediately a 'green' version will spring to live, since it is unshaded:  You can drag a material onto it from the Hypershade.  You can drag it off to the side as well.
! In The Menues
* With the new poly mesh selected:
* Mesh -> Remesh : Adjust values as needed
* Mesh -> Retopo : Adjust the target face count : Then wait for it to compute.
* Done! Great new retopo'd mesh!
Also see:
* [[Bifrost : Difference between publishing Graphs and Compounds]]
----
! Publish / Save
* [[Bifrost Docs|https://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_build_a_graph_create_and_edit_compounds_publish_compounds_html]]
* [[Autodesk YouTube Example|https://youtu.be/FuPB9exDa6A]]
If you want to ''save'' an individual compound you've made, and you can either:
* Select it, and in the Bifrost graph -> Create -> Publish "my compound name".
* RMB on it -> Publish "my compound name".
* Select it and {{{Ctrl + p}}}.
By default, it will save here:
{{{
C:\Users\<USERNAME>\Autodesk\Bifrost\Comounds\compoundName.json
}}}
The {{{\Users\...\Compounds\}}} dir is the default location Maya looks at.
But Maya gives you a browser.  If you want to save it somewhere else, if you want Maya to see it, see the below notes.

You have to specify a 'Compound Namespace' : This is used to organize it in the {{{Tab}}} menu in the Bifrost Graph.  Must use double colon {{{::}}} to specify lower namespace levels.  The default namespace is {{{User::Compounds}}}.
Once saved, if you make a {{{compoundName.md}}} file next to it, following the 'Markdown' [[file syntax|https://www.markdownguide.org/cheat-sheet/]], Maya will auto-use that for the in-editor documentation.
----
! Load
To ''load'' it once published: You can browse to it via the Tab button based on the namespace you set above.  Or type in the name.

There appears to be no 'import' option:  you can't just point a browser to a saved .json file.  During Maya start, it must live in a dir that Maya can 'see', based on the next section.

NOTE: If you do copy in a new Compound to a 'location visible to Maya' since you opened Maya, you'll need to either close & reopen Maya to have Bifrost see it, or, you'll need to close all the Bifrost window, unload the {{{bifrostgraph.mll}}} plugin, copy in the graph, then reload the plugin.
----
! Define Other Directories For View

This doc shows how to configure other directories:
http://help.autodesk.com/view/BIFROST/ENU/?guid=Bifrost_Common_build_a_graph_create_and_edit_compounds_set_up_additional_compound_locations_html
Here, we'll setup this folder as a new location to store bifrost data:
{{{
C:\Users\Eric\Documents\maya\bifrost
}}}
You need to point your Windows env var {{{BIFROST_LIB_CONFIG_FILES}}} to this location.
Then, add a {{{bifrost_lib_config.json}}} (I think any name works) to that same folder, and update it per the below example.  
Then restart Maya/reload the Bifrost plugin.

See the doc for more info on this:
{{{
{
  "AminoConfigurations": [
    {
      "libraryName": "My_Compounds",
      "libraryVersion": "3.1.4",
      "jsonLibs": [
        {
          "path": "compounds",
          "files": [ ]
        },
        {
          "path": "experiments",
          "files": [ ]
        }
      ]
    }
  ]
}
}}}
Ran across this post describing how to code in-between blendshapes,... and the absolute chaos that is needed to pull it off:

http://forums.cgsociety.org/showthread.php?t=1079399
* https://www.braverabbit.com/shapes/
<<gradient horiz #ffffff  #ddddff #8888ff >>
The mel wiki blog.
*[[2010 01 20]] - Added the HARDWARE category.
*[[2010 01 13]] - Added the INFO category.
*[[2009 05 13]] - Added API category
*[[2008 11 19]] - Updated Python tagging
*[[2008 08 14]] - Created the Mel Wiki Guest Book
*[[2008 07 05]] - A new mel tiddlywiki online!
*[[2008 06 08]] - Blog about my new blog (blog blog blog)
*[[2008 05 21]] - tiddlywiki upgraded
*[[2008 05 20]] - Python Wiki!
*[[2008 03 31]] - More Processing.
*[[2008 03 11]] - Added 'Other Links'
*[[2008 03 07]] - Added the TROUBLESHOOTING category.
*[[2008 02 18]] - Processing!
*[[2008 01 29]] - Added [[All Subjects]] tiddler.
*[[2008 01 08]] - New feature: [[Visual Guide of UI Controls]]
*[[2008 01 07]] - Tiddlyspot back up.  And... robots!
*[[2008 01 04]] - rain!  Tiddlyspot down...
*[[2008 01 02]] - New [[PYTHON]] category, update wiki UI formatting.
*[[2007 04 27]] - mel wiki is born
Where can you download Maya's 'bonus tools'?
http://area.autodesk.com/bonus_tools
The below code was originally on [[Brian Ewerts 'match' web page|http://ewertb.soundlinker.com/mel/mel.094.htm]], which went down for a while. It appears to be back up!  But, I've left this info in here.  
http://ewertb.soundlinker.com/maya.htm
*Strip component
{{{
string $node = "pCube1.f[2]";
string $no_component = `match "^[^\.]*" $node`;
// Result: "pCube1" //
}}}
*Extract component or attribute, with '.'
{{{
string $node = "pCube1.f[2]";
string $component = `match "\\..*" $node`;
// Result: ".f[2]" //

string $nodeAttr = "blinn1.color";
string $attrName = `match "\\..*" $nodeAttr`;
// Result: ".color" //
}}}
*Extract attribute name, without '.'
{{{
string $node = "pCube1.f[2]";
string $component = `substitute "^[^.]*\\." $node ""`;
// Result: "f[2]" //

string $nodeAttr = "blinn1.color";
string $attrName = `substitute "^[^.]*\\." $nodeAttr ""`;
// Result: "color" //
}}}
*Extract parent UI control from full path
{{{
string $uiControl = "OptionBoxWindow|formLayout52|formLayout55|button6";
string $uiParent = `substitute "|[^|]*$" $uiControl ""`;
// Result: OptionBoxWindow|formLayout52|formLayout55 //
}}}
*Strip trailing CR, if any. This is useful when processing text input read from a file using 'fgetline'. (or you could just use the 'strip' command, but the concept is important)
{{{
string $input = "line\n";
$string $line = `match "^[^(\r\n)]*" $input`;
// Result: "line" //
}}}
*Extract directory from path Note that this leaves a trailing slash. This is typically desired, as it makes it easy to simply append a filename, and the slash is required for the ‘getFileList?’ command to return anything useful.
{{{
string $path = "C:/AW/Maya5.0/bin/maya.exe";
string $dir = `match "^.*/" $path`;
// Result: "C:/AW/Maya5.0/bin/"
}}}
*Extract file from path
{{{
string $path = "C:/AW/Maya5.0/bin/maya.exe";
string $filepart = `match "[^/\\]*$" $path`;
// Result: "maya.exe"
}}}
*Strip numeric suffix
{{{
string $node = "pCube1|pCubeShape223";
string $noSuffix = `match ".*[^0-9]" $node`;
// Result: "pCube1|pCubeShape"
}}}
*Extract numeric suffix
{{{
string $node = "pCube1|pCubeShape223";
string $suffix = `match "[0-9]+$" $node`;
// Result: "223" //
}}}
*Extract short name of DAG or control path
{{{
string $dagPath = "pCube1|pCubeShape223";
string $shortName = `match "[^|]*$" $dagPath`;
// Result: pCubeShape223 //
}}}
Ran into an issue in Maya 2019 around mocap, humanIK, and animation caching:
* If you have mocap assets that have been characterized via HIK, //and// you have animation caching turned on, if you start importing multiple mocap assets into your scene, Maya's performance will grind to a halt, and memory will skyrocket.
* If you disable animation caching, and import in multiple mocap files with HIK, the memory stays low, and performance stays good.
* If you delete the HIK data in the mocap, and reimport it with caching on, the performance drops, but it's not as bad as with HIK there.

Basically, if you're using mocap with or without HIK, you need to disable animation caching.

Furthermore, I can't get the memory used by animation caching to clear out:
After you get the slowdown \ memory hit: Even if I disable caching, clear the cache, and open a new Maya scene, Maya's memory can still be very,very high.  The only way to fix is to restart Maya.
This does it all, and all written in Python:
* https://github.com/chrisdevito/OBB
* https://obb.readthedocs.io
Pi is the ratio of the circumference of a circle to its diameter, approximately {{{3.14159}}}. 
So if you wanted to calculate this yourself:
{{{
# Python code:

# diameter is circumference / pi:
diameter = 5.0

# circumference is pi * diameter:
circumference = 15.7079632679

# so:
pi = circumference/diameter
# 3.14159265359
}}}
Of course, you can always just:
{{{
from math import pi
print pi
3.14159265359
}}}
But it's nice to actually see how it's done ;)
This post gives a nice solution;
https://stackoverflow.com/questions/24438720/arbitrary-barycentric-coordinates-on-a-polygon-3d-maya-api
{{{
def baryInterp(vecA, vecB, vecC, vecP):
    '''
    Calculates barycentric Interpolation of a point in a triangle.

    :param vecA - OpenMaya.MVector of a vertex point.
    :param vecB - OpenMaya.MVector of a vertex point.
    :param vecC - OpenMaya.MVector of a vertex point.
    :param vecP - OpenMaya.MVector of a point to be interpolated.

    Returns list of 3 floats representing weight values per each point.
    '''
    v0 = vecB - vecA
    v1 = vecC - vecA
    v2 = vecP - vecA

    d00 = v0 * v0
    d01 = v0 * v1
    d11 = v1 * v1
    d20 = v2 * v0
    d21 = v2 * v1

    denom = d00 * d11 - d01 * d01
    v = (d11 * d20 - d01 * d21) / denom
    w = (d00 * d21 - d01 * d20) / denom
    u = 1.0 - v - w

    return [u, v, w]
}}}
As of Maya 2008, when you hit {{{tab}}} in the Python Script Editor, it adds an 'actual tab' ({{{\t}}}), that is '8 spaces' long.  Python's [[PEP8 Style Guide|http://www.python.org/dev/peps/pep-0008/]] says that tabs in general should be avoided (but if you have to use them, they should be '4 spaces' in length).  You should be using '4 spaces' in place of tabs (most external IDE's have the ability to insert spaces instead of tabs when the tab button is pressed).  So the Script Editor has this //completely wrong//.

After searching online, and pinging various user groups, this appears to be a fix value.  In fact, this was reported as a bug to Autodesk in Maya8.5, and I'm told it's //still// not fixed in Maya2009.  :-(  Another reason to use an external IDE.
When you execute a playblast via its UI, you can open a 'Video Compression' sub-ui allowing you to adjust the compression settings.  The {{{playblast}}} command has {{{compression}}} arg you can pass a string value to that, will, from the docs:
>Specify the compression to use for the movie file. On Unix, valid values are "mvc1", "mvc2", "jpeg", "rle", "none". (Default is "mvc2".)  On Windows, if the argument is an empty string ("") a dialog comes up allowing the user to set the codec, data rate, keyframing, compression quality, and codec settings. (This dialog will also come up with `playblast -options`.) Use of any other string will utilize the codec that was previously set using the dialog.
So on Unix, you can specify values.  On Windows, if you pass it an empty string, it will pop up a dialog box letting you set values.  And, if you "use any other string" will "use the previous setting".

So this tells me that ''NO, you can NOT specify compression options via mel''.  Which kind of sucks.  

Searching the internets, it appears that all the other brains have come to the same conclusion.  There are {{{optionVar}}}s that store certain playblast values, but changing them seems to have no effect on the final product, or causes errors.
This tool has actually been around for some time, and while I've never used it, my friends have and vouched for it's abilities:

[[JoystickServer|http://www.geocities.com/jlimperials/realtime.html]] by Jorge Imperial
Download from Highend3d.com [[here|http://www.highend3d.com/f/1086.html?sheet=bg4]]
It basically turns your joystick into a mocap device, which Maya can then interface with.
[img[http://static.highend3d.com/downloadsimages/1086/screen1.jpg]]
Copy this file:
{{{
C:\Program Files\Autodesk\Maya20XX\icons\MayaStartupImage.png
}}}
To somewhere like your desktop, edit it, and copy it back.  You can't edit it directly in that folder based on Window's permissions.
In Maya 2019, when certain custom attrs were keyed on one of our rigs, after you saved the scene, quit Maya, and reopened the scene, once you changed the frame, Maya would crash.

After a *lot* of research, it came down to a weird evaluation graph bug.  I found that if I opened Maya, set it to DG mode, then opened the file, the file would work.  But if I put it back in EG mode, it would crash again.

After a lot more research, I found out that if I turned off (in Maya's prefs) 'GPU Override' & 'Include controllers in evaluation graph', and then turned off GPU Overide, after I reopened the scene, if I turned EG back on, refreshed the scene (set the frame to itself and {{{refresh -force}}}), then enabled GPU Override (& refresh) and then turned the 'Include controllers...' back on, the crash went away.

To resolve this, I made pre-and post scene open callbacks via :
{{{
om2.MSceneMessage.addCheckFileCallback(om2.MSceneMessage.kBeforeOpenCheck, callback(manageBeforeOpen))
# and
om2.MSceneMessage.addCallback(om2.MSceneMessage.kAfterOpen, callback(manageAfterOpen))
}}}
That managed all this, and it solved the crash.
----
Figured out what was actually crashing it though:
I had a {{{scriptJob}}} that was triggering on {{{timeChange}}}, that would update the visibility state of our rigs based on what state they were in (IK, FK): By wrappering the function the {{{scriptJob}}} called to in an {{{evalDeferred -lowestPriority}}}, it made the bug go away.  :-S

Update:  I figured out why //that// code was crashing:  The visibility code was querying all attrs with a certain name in the scene that control the IK/FK state ("ikFk"), then iterating over them.  The issue was, those were //proxy// (aka, instance) attrs,  So if each limb had an IK & FK controller setup, each with three controllers, each with a proxy attr, that woudl be 6 attrs per limb that are all proxy.  You don't want to iterate over all 6, you only need to use 1, since... they're instanced.  Once I instead filtered by the root proxy attr and only updated it, this resolve the remaining bug.  Something about iterating over proxy attrs in the Evaluation Graph causes instability.  See: [[Working with proxy attrs]]
As of Maya 2016, there's an annoying bug:
* If you open a scene, and change its file type (from ma to mb or vice versa), it will lose the scene name.
* If you open a scene, save the scene, then change the filetype, the bug doesn't show up.
Example of it acting broken:
{{{
import maya.cmds as mc

mc.file(r"C:/path/to/my/file.mb", open=True, force=True)
fileType1 = mc.file(query=True, type=True)
fileName1 = mc.file(query=True, sceneName=True)
print fileType1, fileName1
['mayaBinary'] C:/path/to/my/file.mb

# Change the file type:
mc.file(type='mayaAscii')

fileType2 = mc.file(query=True, type=True)
fileName2 = mc.file(query=True, sceneName=True)
print fileType2, fileName2
[u'mayaAscii']  # !!!! no more filename, it is an empty string !!!!!!!
}}}
Here is it not broken:
{{{
import maya.cmds as mc

mc.file(r"C:/path/to/my/file.mb", open=True, force=True)
fileType1 = mc.file(query=True, type=True)
fileName1 = mc.file(query=True, sceneName=True)
print fileType1, fileName1
['mayaBinary'] C:/path/to/my/file.mb

# Save the file
mc.file(save=True, force=True)

# Change the file type:
mc.file(type='mayaAscii')

fileType2 = mc.file(query=True, type=True)
fileName2 = mc.file(query=True, sceneName=True)
print fileType2, fileName2
[u'mayaAscii'] C:/path/to/my/file.ma
}}}
Use for finding attribute information based on a node class. Meaning, can find attributes on "transform" nodes, on "joint" nodes, etc. Can't be used to query info on nodes that you specify by name, like attributeQuery does.
*list internal attrs
*list hidden attrs
*list multi attrs
*list leaf attrs
*list writeable attrs
*list boolean attrs
*list enumerated attrs
*list "UI friendly" attr names rather than Maya Ascii names.
On an individual attribute on an individual node, use to find:
*If the attribute exists
*If it is a mulit-attribute
*find the range of its value
*find any "sibling" attributes
*find its parent attribute
*find any children attributes
*and several more....
{{{
// mel
int $exists = `attributeQuery -ex -n nodeName attrName`;
}}}
{{{
# Python
import maya.cmds
exists = mc.attributeQuery('attrName', node='nodeName', exists=True)
}}}
Find\set the current LINEAR (meters, feet, etc.) units, ANGULAR (degrees, radians) units, or FRAME RATE (film, video, etc.) that Maya uses.
Given an object, will return lists of:
*Readable attrs
*Writable attrs
*Array attrs
*Visible attrs
*Connectable attrs
*Keyable attrs
*Locked\unlocked attrs
*User defined attrs
*And many others....
*List both "source" and "destination" connections:
{{{
// this will only return object names thaat connect to your object:
listConnections "object";     
//  this will list object.attr namess that connect to your object:
listConnections -p 1 "object";  
// this will list in pairs, your object.attr's and the connecting object.attr's:
listConnections -p 1 -c 1 "object"; 
// this will list in pairs, your specifieed  object.attr, and it's connecting object.attr's:
listConnections -p 1 -c 1 "object.tx"; 
}}}
*List "source" or upstream connections only:
{{{
// this will only return object names:
listConnections -s 1 -d 0 "object";      
// this will list object.attr names of soource connections to your object:
listConnections -s 1 -d 0 -p 1 "object"; 
// this will list in pairs, your object.attr's connecting to the source's object.attr's:
listConnections -s 1 -d 0 -p 1 -c 1 "object";  
//  this will list in pairs, your specified  object.attr, and it's connecting source object.attr's:
listConnections -s 1 -d 0 -p 1 -c 1 "object.tx";
}}}
*List "destination" or downstream connections only:
{{{
// this will only return object names:
listConnections -s 0 -d 1 "object";      
// this will list object.attr names of deestination connections to your object:
listConnections -s 0 -d 1 -p 1 "object"; 
// this will list in pairs, your object.attr's and connecting destination object.attr's:
listConnections -s 0 -d 1 -p 1 -c 1 "object";  
//  this will list in pairs, your specified  object.attr, and its connecting destination object.attr's:
 listConnections -s 0 -d 1 -p 1 -c 1 "object.tx";
}}}
*Important:  Adding an attribute value to any of the queries filters the query to only check only on that object.attr.
*The "-t" "type" flag can also be used to query connections to only certain types of nodes. Very handy.
Query or set the current frame range.
Also: Query\set: playback speed, frames rate, and loopability.
Maya8 and later: Set the max playback speed
{{{polyEvaluate}}} is a really versatile command.  Things it can do:
*For a whole polygonal object, or just a component selection, return the number of:
**vertex
**edge
**face
**uv coordinate
**triangle
**shell
*Bounding box data:
**For the given object in 3d space, or it's uv's in 2d space.
**For the selected components in 3d space, or for the selected uv's in 2d space.
**The surface area of the faces in worldspace, or local space
----
Also see:
*[[Command: polyInfo]]




{{{polyInfo}}} aids in finding dependencies between polygonal components:
*How can I find the specific poly components connected to another poly component?
**faces that share the specified edge
**faces that share the specified vertex
**edges defining a face
**vertices defining a face
**vertices defining an edge
**Edges connected to a vertex
*Find non-manifold vertices, edges, and lamina faces? 
*How can I find the "normal" value of face?
Example, get a face's normal value:
{{{
polyInfo -fn; 
// however this will return a string that will need to be tokenized.
}}}
----
Also see:
*[[Command: polyEvaluate]]

''Commands'':  (WIP)
*[[commands|Commands]]
**__Can accept__ (when executed, depending on the command):
***command [[flags|Flags]] (& flag arguments), command [[arguments|Arguments]]
**__Can provide__ (when executed, depending on the command):
***[[return values|Return Values]]
----
*A command is one of the fundamental building blocks that make up mel.  
*Many commands can have flags listed after them, or arguments, or both, or neither (it depends on the individual command):  
**Arguments accept variables you pass into them, and expose those variables to the inner-workings of the command.  They are like doors that allow external data into a command.
**Flags have several different behaviors: Some flags, if present, modify behavior  of the command (their presence acts like a boolean switch, on|off), but take no arguments.  Other flags accept arguments.
**//Usually//, command arguments and flags are optional.  Sometimes certain commands require certain arguments.  However, if a flag is used, and takes its own arguments, they are required as well.  This is one way commands differ from scripted procedures:  If a procedure defines an argument, then the argument is always required.  And procedures can't be authored with flags.
**Command flags are listed first, then command arguments.  It is the way of things.  Of course if a flag has its own arguments, they're entered immediately after the flag.
*Using the below command, it will open a web browser to list all of the Maya commands.  {{{-doc}}} is a flag (which takes no arguments), and {{{"index.html?MEL"}}} is an argument to that //command// (it's //not// an argument to the flag).
{{{
showHelp -docs "index.html?MEL";
}}}
*In the html help, some of the "commands" will have a "''M''" after them.  This signifies that in fact they are //not// commands, but mel [[scripts|Scripts]] that ship with Maya.  But they lump them in with the commands, since they provide similar functionality.
*These [[scripts|Scripts]] behave like commands in the sense they can usually take [[arguments]] (not [[flags]]), and provide [[return values]].  They provide this functionality by having a global procedure defined //inside// the script, with the //same name// as the script (see 'scripts' above).
*What is the difference between a command and a script?
**A command (to the best of my knowledge) is a 'programmed' bit of code, probably in c++.  A //script// on the other hand, is a bunch of commands working together for some other goal.  Custom user-made plugins can provide new commands.  
**{{{xform}}} for example, is a command, and will move objects about.  {{{randomMoveAllObjectsInScene.mel}}} could be a script, that mostly likely calls to {{{xform}}} and many other commands, to have a much broader effect on the scene.
*Commands can be entered in two different modes: 'command syntax', and 'function syntax'.
<<<
*__Command Syntax__:  In command syntax you can leave off quotation marks around single-word strings and separate arguments & flags with spaces instead of commas:
{{{
setAttr mySphere1.translateX 10;
move -absolute 1 1 1;
// '-absolute' is a flag, '1' is an argument.
}}}
*__Function Syntax__:  In function syntax, commands are treated like functions (procedures):  Strings must be enclosed in quotes, and all arguments must have commas between them.  Furthermore, any flags must be encased in quotes, turning them into strings, any arguments must be separated with commas.  Finally, all the arguments and flags need to be surrounded by parenthesis:
{{{
setAttr("mySphere1.translateX", 10);
move("-absolute", 1, 1, 1);
}}}
<<<
''Returning Values:''
*Many commands provide [[return values]].  How you capture the return value of a script, and pass it into a [[variable]] depends on which syntax you're using:
**When in __command syntax__, values aren't returned unless you surround the statement with back-ticks \ back-quotes.
**When in __function syntax__, values are returned automatically.
A back-tick \ back-quote is the key to the left of ''1'':     {{{`}}}
{{{
//Command syntax capturing return values:
string $all[] = `ls`;
string $selected[] = `ls -selection`;
string $onlyJoints[] = `ls -type "joint"`;
}}}
{{{
//Function syntax capturing return values:
string $all[] = ls();
string $selected[] = ls("-selection");
string $onlyJoints[] = ls("-type", "joint");
}}}
By using function syntax, it allows you to 'nest' your commands in ways that you can't using command syntax.  This isn't to say you //should//, it just means you //can//. :-)  See notes [[here|How can I nest my mel commands?]].
Here is a growing list of commands for the new coder that I consider super useful.

Data Query:
*[[ls|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/ls.html]] :  Used for querying nodes in Maya.  By selection, by type, by name, wildcard (*) by any number of different rules.  
*[[optionVar|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/optionVar.html]] : Store and query data that is persistent between Maya scenes.
*[[objExists|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/objExists.html]] : Query a nodes existence based on its name.
*[[objectType|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/objectType.html]] : Query what type of object something is, based on its name.
*[[polyListComponentConversion|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/polyListComponentConversion.html]] : Great for converting between poly data:  Faces to verts, mesh to faces, etc.
* {{{type(variable)}}} : Python builtin, tells you what type of data your variable is as a string name.
* {{{dir(variable)}}} : Python builtin to return the attributes on a Python variable, as a list of strings.
* {{{print "spam"}}} : Python 2 statement to print the given data. In Python 3 it's a function: {{{print("spam")}}}.
Node Transformation:
*[[move|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/move.html]] : Translate the node.
*[[rotate|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/rotate.html]] : Rotate the node.
*[[scale|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/scale.html]] : Scale the node.
*[[xform|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/xform.html]] : All sorts of translate,rotate, and scale operations can be performed, including matrix application.
*[[makeIdentity|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/makeIdentity.html]] : 'Freeze' a nodes transforms.
*[[pointPosition|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/pointPosition.html]] : Query component level (vertex, cv) or transform-level pivot information, in local or worldspace.
Attribute Manipulation:
*[[getAttr|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/getAttr.html]] : Query the value of an attribute on a node.
*[[setAttr|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/setAttr.html]] : Set the value of the attr on a node.
*[[addAttr|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/addAttr.html]] : Add a custom attribute to a node.
*[[deleteAttr|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/deleteAttr.html]] : Delete a custom attr from a node.
*[[attributeQuery|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/attributeQuery.html]] : Query an attributes existence on a node, and many other things about it.
* [[listAttr|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/listAttr.html]] : like {{{ls}}} but for attributes on a node; query by different things: User defined, keyable, locked, etc.
Connection Info:
* [[connectAttr|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/connectAttr.html]] : Connect one attribute to another.
* [[disconnectAttr|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/disconnectAttr.html]] : Disconnect one attr from another.
*[[listConnections|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/listConnections.html]] : Find incoming and outgoing connections to nodes.
Family Tree:
*[[listRelatives|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/listRelatives.html]] : Find parent and children (DAG) nodes.
*[[listHistory|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/listHistory.html]] Find the nodes //connected// upstream (history)  & downstream (future) from this one.
Node Modification:
*[[select|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/select.html]] : Select (or deselect) one or more nodes by name.
*[[delete|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/delete.html]] : Delete nodes
*[[parent|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/parent.html]] : Parent nodes together.
*[[rename|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/rename.html]] : Change the name of given node.
Node Creation:
*[[createNode|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/createNode.html]] : Create a node of a given type.
* [[shadingNode|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/shadingNode.html]] : Much like {{{createNode}}}, but can flag nodes as a certain type (for example {{{asUtility=True}}}) so Maya will make them display in different editors correctly (like the Hypershade).
*[[duplicate|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/duplicate.html]] : Duplicate the given nodes.
*[[group|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/group.html]] : Create a new empty {{{transform}}} node (aka, 'group'), optionally 'grouping' (adding as children) other nodes in the process.
*[[joint|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/joint.html]] : Create a new {{{joint}}} node.
*[[spaceLocator|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/spaceLocator.html]] : Create a new {{{locator}}} node.
*[[sets|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/sets.html]] : Create or modify sets of nodes.
Animation:
*[[keyframe|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/keyframe.html]] : Keyframe query and modification.
*[[setKeyframe|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/setKeyframe.html]] : Create keyframe data.
*[[findKeyframe|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/findKeyframe.html]] : Tools to find keyframe data.
*[[currentTime|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/currentTime.html]] : Query & set the current frame.
*[[playbackOptions|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/playbackOptions.html]] : Set/query the framerange, framerate, etc.
Scene Access:
*[[file|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/file.html]] : Query scene name, open/import/reference a file, many other file-related options.
*[[referenceQuery|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/referenceQuery.html]] & [[referenceEdit|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/referenceEdit.html]] : Dealing with references in the scene.
*[[namespace|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/namespace.html]] & [[namespaceInfo|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/namespaceInfo.html]] : For dealing with namespaces.
Just comparing what the Output Window spits out on Maya 2016 startup, for different graphics cards.
!NVIDIA ~GeForce GTX TITAN
{{{
Initialized VP2.0 renderer {
  Version : 6.3.16.0. Feature Level 5.
  Adapter : NVIDIA GeForce GTX TITAN
  Vendor ID: 4318. Device ID : 4101
  Driver : nvwgf2umx.dll:10.18.13.5560.
  API : DirectX V.11.
  Max texture size : 16384 * 16384.
  Max tex coords : 32
  Shader versions supported (Vertex: 5, Geometry: 5, Pixel 5).
  Active stereo support available : 0
  GPU Memory Limit : 6144 MB.
  CPU Memory Limit: 31089.4 MB.
}
OpenCL evaluator is attempting to initialize OpenCL.
Detected 1 OpenCL Platforms: 
 0: NVIDIA Corporation. NVIDIA CUDA. OpenCL 1.2 CUDA 7.5.0.
 Supported extensions: cl_khr_byte_addressable_store cl_khr_icd cl_khr_gl_sharing cl_nv_compiler_options cl_nv_device_attribute_query cl_nv_pragma_unroll cl_nv_d3d9_sharing cl_nv_d3d10_sharing cl_khr_d3d10_sharing cl_nv_d3d11_sharing cl_nv_copy_opts 
OpenCL evaluator choosing OpenCL platform NVIDIA Corporation.
Choosing OpenCL Device GeForce GTX TITAN.  Device Type: GPU  Device is available.
}}}
!AMD Radeon HD 7900 Series
{{{
Initialized VP2.0 renderer {
  Version : 6.3.16.0. Feature Level 5.
  Adapter : AMD Radeon HD 7900 Series
  Vendor ID: 4098. Device ID : 26520
  Driver : aticfx64.dll:13.152.1.8000.
  API : DirectX V.11.
  Max texture size : 16384 * 16384.
  Max tex coords : 32
  Shader versions supported (Vertex: 5, Geometry: 5, Pixel 5).
  Active stereo support available : 0
  GPU Memory Limit : 3072 MB.
  CPU Memory Limit: 31089.4 MB.
}
OpenCL evaluator is attempting to initialize OpenCL.
Detected 1 OpenCL Platforms: 
 0: Advanced Micro Devices, Inc.. AMD Accelerated Parallel Processing. OpenCL 1.2 AMD-APP (1268.1).
Supported extensions: cl_khr_icd cl_amd_event_callback cl_amd_offline_devices cl_khr_d3d10_sharing cl_khr_d3d11_sharing cl_khr_dx9_media_sharing 
OpenCL evaluator choosing OpenCL platform Advanced Micro Devices, Inc..
Choosing OpenCL Device Tahiti.  Device Type: GPU  Device is available.
OpenCL device does not support out of order execution
}}}
This article has a good short overview:  https://3deeplearner.com/binary-comp/  : Under 'Solving binary compatibility'.  But for prosperity:
<<<
''Solving binary compatibility''
So, if you want to run any of these libraries, you will probably need to build many of their dependencies from source using the appropriate version of Visual Studio for your Maya installation. Here is a list:

Maya 2013-2014 – Visual Studio 2010 (MSC v.1600)
Maya 2015-2017 – Visual Studio 2012 (MSC v.1700)
Maya 2018 – Visual Studio 2015 (MSC v.1900)
After installing the appropriate version of Visual Studio, you’ll need to download the libraries from their ~GitHub repos. Just Google for Github repo and the name of the library. Here is Numpy’s repo as an example (https://github.com/numpy/numpy).

After that, you will need to run the repo’s {{{setup.py}}} in build mode, so it builds a version of the repo using the proper compiler. There are some tricks involved in doing this. I recommend you read this article for a thorough explanation: http://around-the-corner.typepad.com/adn/2017/05/how-to-build-pillow-on-windows-with-maya-2017.html.  Apart from using the adequate compiler you want setup.py to run on the proper Python interpreter (Maya’s). You might bump into additional complications in each library, which, for the most part, can be solved by reading and interpreting the error messages.

''Share''
Finally, if you want to save other people’s time you can then distribute this Maya friendly Python extensions building a Wheel, that can be installed using PIP. For that, I recommend reading this article: https://packaging.python.org/tutorials/distributing-packages/#id74.
<<<
Also see:
* [[Getting pywin32 working in Maya]]
* [[API : How to compile a plugin]]
* [[Python & Qt versions in Maya]]
* [[Maya compiler versions]]
Starting in Maya 2022, at a high level, component tags allow you to make sets of components with names.  It's much like a regular selection set, except it can be used by deformers.

! Questions
* How usable in games?  Generally the only deformer we can use / export to the game engine is the {{{skinCluster}}} node.  This whole process is based around the interaction of multiple different deformer types on a single mesh, the bulk of which can't be used in-engine.  You can decompos the deformation effects back into skinning, but of course that can be pretty lossy.
* I've brought this up a few times with the autodesk devs, and at the time they had no good answer:  In a rebuildable asset pipeline, where you have artists working on a 'static mesh' that you can then rebuild into an existing 'skeletal mesh' : What happens if the vert ~IDs (count, topology) change?  How can you 'export/import' this type of data for reapplication? As of Dec 2021, there are no built-in tools to do this.
* Would be nice if you could use the falloff objects or other implicit geometry types to surround your asset, and then create the tags based on their volumes, or keep that live in the scene.

! Maya Docs
* [[Edit deformer membership with Component Tags|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-6692AD3F-F7BE-450A-8DF3-C4099A412550]]
** [[Component Tags table|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-1D7D6E5A-2E20-42F7-91D2-8C18A878919A]]
** [[Component Tag expressions|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-237E61B3-CA0A-4C35-A3E7-E0D2004A898E]]
** [[Assign Component Tags to geometry|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-788B1C8B-7F45-4862-BC42-0A334DFECFD3]]
** [[Original Geometry|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-EEF323A6-8692-4D74-8EAA-5481BF437D42]]
* [[Deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-9924DC3A-A65B-4FC3-8AF6-53C914D5B5D6]]
** [[Create deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-2E227658-3F8B-424F-9B54-F9841FFDFC9E]]
** [[Deformer Attributes widget|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-08A2A603-926F-41A9-90CC-EF2F21486053]]
** [[Create vert lookup tables with the componentMatch node|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-5FD75B48-CA22-440B-B8DC-C4A2E664F98F]]

! Enable

As a feature, they can be enabled via `Preferences-> Settings -> Animation -> Rigging -> 
* Use component tags for deformation component subsets (on by default)
* Create component tags on deformer creation (on by default, turning this on re-enables the 'legacy' way of doing things)
* Tweaks: Add tweak nodes on deformer creation (off by default, the 'legacy' way of deforming things)

! Mesh Access
You can access them via the (mesh) shape node in the Attribute Editor -> Component Tags:

!! Creation

Press the {{{+}}} button based on the selected components.  The below [[Component Tags table|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-1D7D6E5A-2E20-42F7-91D2-8C18A878919A]] updates:

* Name : The string name that defines this CT.
* Type : The component type assigned to this CT.  Supports vertex, edge, and face.
* Injection Location : The original position of the deformer.  Note this is ually the {{{shapeOrig}}} (intermediate object) node at the from the chain. 
* Type Count : How many of the total number of that component type is assigned.
* Order : The sequence of the CT position in the deformation chain of the injection node, with the lowest number indicating the furthest upstream.

They can be RMB'd on for additional features\component type conversion, etc.

! Deformer Access

Commonly, you can add a certain CT for a subset of the geometry on the asset, then apply a deformer to only that region.

Open the deformer node in the Attribute Editor -> Deformer Attributes : This exposes the [[Deformer Attributes widget|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-08A2A603-926F-41A9-90CC-EF2F21486053]] in the Attribute Editor.

* Index : ?
* Component Tag (Expression)
** You can use expressions to search the [[Component Tags table|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-1D7D6E5A-2E20-42F7-91D2-8C18A878919A]] or the [[Deformer Attributes Widget|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-08A2A603-926F-41A9-90CC-EF2F21486053]]
** This is where interesting logic can come into play: See [[Component Tag expressions|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-237E61B3-CA0A-4C35-A3E7-E0D2004A898E]]
** The default is {{{*}}} : All CT's
** But once you start typing in that field, it'll display all the CT's used on that shape node for selection.
** You can do Boolean operations with other CT's via operators like {{{+ -}}}
* Falloff : Allows the addition of multiple 'falloff objects', see [[Deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-9924DC3A-A65B-4FC3-8AF6-53C914D5B5D6]].
* Geometry : The (mesh) shape node this is associated with.

! Deformer Falloffs
[[Deformer Falloff|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-9924DC3A-A65B-4FC3-8AF6-53C914D5B5D6]] nodes can be created  via the 'Falloff' channel on the 'Deformer Attributes' section of that deformer's Attribute Editor.  Based on the overall filtering provided by a CT, they can additionally limit where the deformer has effect.

There are multiple falloff types (see [[Create deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-2E227658-3F8B-424F-9B54-F9841FFDFC9E]])
* [[Primitive|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-503F5223-8179-476E-BF2E-B4AC0BB0D7C5]] : Can be sphere or plane: These are the only falloff nodes that have transforms in 3d space.
* Uniform : There are no Uniform falloff attributes. See 'Create consistent falloff effects' in the [[Create deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-2E227658-3F8B-424F-9B54-F9841FFDFC9E]] topic.
* [[Blend|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-E67D99D4-9035-4A18-886E-603EDC25E796]] : The Blend Falloff Attribute Editor widget contains a table where you can add various Falloff effects and combine them together to make complex results. Can add/subtract/multiply/divide them together, with weights.
* [[Proximity|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-5809A178-023F-42C3-9B5A-ACE46A4AF24F]] : ?
* [[Subset|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-6E174682-C677-40FC-93B3-0E61ACB343F9]]
* Component : There are no Component falloff attributes. See 'Component falloff: Paint deformer weights' in the [[Create deformer falloffs|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-2E227658-3F8B-424F-9B54-F9841FFDFC9E]] topic.
* [[Transfer|https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-9915CC6C-F425-4E03-B2F6-B952521C1E88]] : contains a table where you can specify how to apply falloff weighting from one deformer to another.
Subclass of DAG node types:
* [[primitiveFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/primitiveFalloff.html]]
Subclass of DG node types:
* [[uniformFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/uniformFalloff.html]]
* [[blendFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/blendFalloff.html]]
* [[proximityFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/proximityFalloff.html]]
* [[subsetFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/subsetFalloff.html]]
* [[componentFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/componentFalloff.html]
* [[transferFalloff|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/Nodes/transferFalloff.html]]

! Node Attributes

On the (mesh) shape (any subclass of {{{geometryShape}}}):
* {{{componentTags[x]}}} / {{{gtag[x]}}} : type {{{compound array}}} : Represents the index of that CT.
** {{{componentTagName}}} / {{{gtagnm}}} :  type {{{string}}} : The name of the assigned CT
** {{{componentTagContents }}} / {{{gtagcmp}}} : type {{{componentList}}} : strings defining the added components, like : {{{"vtx[200:379]" "vtx[381]"}}}
On a deformer (any subclass of {{{geometryFilter}}}):
* {{{input[x]}}} / {{{ip[x]}}} : type {{{compound array}}} :  Represents the index of that CT.
** {{{componentTagExpression }}} / {{{gtg}}} :  type {{{string}}} : The string that defines logic in the 'Component Tag' column, like {{{*}}} (the default).

! Commands and Scripts

Working in the Attribute Editor to create new CT's and other modifications don't report any work done to the Script Editor, even if 'Echo All Commands' is turned on.  However, if you undo with Echo All Commands turned on, it'll report stuff like this, which I'm presuming are {{{RunTimeCommand}}}s...
{{{
create component tag
rename component tag
}}}

Can find a lot of the Attribute Editor related code, specifically the 'Component Tag Widget' in here:
{{{
c:\Program Files\Autodesk\Maya2022\Python37\Lib\site-packages\maya\internal\nodes\componenttags\ae_template.py
}}}

Doing a bit more digging, looks like this module has the bulk of the work:
{{{
c:\Program Files\Autodesk\Maya2022\Python37\Lib\site-packages\maya\internal\common\utils\componenttag.py
c:\Program Files\Autodesk\Maya2022\Python37\Lib\site-packages\maya\internal\common\utils\falloff.py
}}}
With functions like {{{createNewTag}}} : Looks like Maya is finally replacing mel with Python for core stuff like this ;) 
{{{
from maya.internal.common.utils import componenttag
from maya.internal.common.utils import falloff
}}}
The below ~PyMel code will generate a completely new local axis matrix by providing a single vector.  The code is very simple, and only works a fixed way:  You provide a vector that specifies the 'x leg' of the new local axis, and it will compute the corresponding vectors for the y & z legs:  The z leg will attempt to match the world z, and the new y leg will be the cross product of the new x & z.  This is basically what an {{{aimConstraint}}} is doing, and the code could be expanded to be far more robust, emulating similar features of an {{{animConstraint}}}.
{{{
import pymel.core as pm

def computeLocalAxis(newX):
    """
    By providing a newX Vector leg for the new local axis, this will compute the Y & Z legs.
    The Z leg will be most closely aligned with the worldZ, Y leg will be a cross of the X & Z legs.

    Return is a PyMel Matrix of the new local axis, with the position zeroed:
    [newX,
     computedY,
     computedZ,
     [0,0,0,1]]
    """
    worldZ = pm.dt.Vector.zAxis
    newY = newX.cross(worldZ).normal()
    newZ = newX.cross(newY).normal()
    # Make sure Z is aligned correctly:
    if worldZ.dot(newZ) < 0:
        newZ *= -1
        newY *= -1
    return pm.dt.Matrix(newX, newY, newZ)
}}}
Here's an example of it in action:  Two cones are created, A and B.  B is translated and rotated differently from A.  The localX axis vector from A is assigned to B:
{{{
import pymel.core as pm

# Get PyNode Transforms for our two nodes:
nodeA = pm.PyNode("coneA")
nodeB = pm.PyNode("coneB")

# Get the worldspace matrix for nodeA
m_world_nodeA = nodeA.getMatrix(worldSpace=True)
# Extract its localX vector:
newX = pm.dt.Vector(m_world_nodeA[0][:3])

# Compute the new local axis:
newMtx = computeLocalAxis(newX)
# Get the position of nodeB
v_nodeB_rotPiv = pm.dt.Vector(nodeB.getRotatePivot(worldSpace=True).cartesian())
# Insert that position into our matrix:
newMtx[3] = v_nodeB_rotPiv
# Set nodeB to this new matrix:
nodeB.setTransformation(newMtx)
}}}
~PyMel Docs (2015):
*[[Vector|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Vector.htm]]
*[[Matrix|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Matrix.html]]
*[[Transform|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Transform.html]]
I had a situation where I needed a small polygonal ball to stay affixed inside a large polygonal ball, by connecting springs between specific verts on each.  This is the solution I found:
Via the 'FX' Maya menu...
# Select the outside sphere and 'nCloth -> Create Passive Collider'.
## Disable its collision for performance.
# Select the inside sphere and 'nCloth -> Create nCloth'.
## Set it's 'Rigidity' (under 'Dynamic Properties') to something large, like 1000:  This will keep it from deforming.
## Set it's 'collide' and 'selfCollide' off for performance.
# Find the nucleus node that was created, and turn off its gravity (unless you want gravity effecting the inside sphere).
# Then, picking pairs of inside/outside sphere verts:
## 'nConstraint -> Component to Component' : This will create the constraint, which is a 'spring' by default.
## Under the 'Connection Density Range', you can start changing the 'Rest Length Method' to something like 'Constant', and supplying values (always in cm, regardless of what you're UI unit is) to have some springs pull more than others.
(This is all based on a Windows system)
First, you need to get some type of com object solution working in Python for Maya:
*See my notes here: [[Getting pywin32 working in Maya]]
Once you have that package (or something similar) up and running, you can start having Maya talk to Photoshop.  Here's a simple example:
{{{
# Presuming you have the package installed....
# Need to import in order
import pythoncom
import win32com.client

app = win32com.client.Dispatch("Photoshop.Application") # Application
psdFile = 'c:/temp/myPrettyPic.psd'
document = app.Open(psdFile) # Document

# For each layer in the psd:
for layerSet in document.LayerSets: # LayerSet in LayerSets, which PS calls 'groups'
    print layerSet.Name
    layerSet.Resize(50, 50)
    layerSet.Translate(128,128)

# Some optional fun:
#document.ResizeImage(64, 64)
#document.Save()
}}}
How about some docs?  The root for all related scripting docs for multiple versions of Photoshop is here:
http://www.adobe.com/devnet/photoshop/scripting.html

I would recomend taking a look at the "''~VBScript Reference''" for your version of Photoshop, compared to the ~JavaScript or ~AppleScript:  The VB seems to more closely match the Python bindings\syntax.
'Skin -> Bake Deformers to Skin Weights' opens the 'Bake Deformer Tool'.

I commonly use this when placing blocked weights on an asset, then applying a deltaMush deformer to it, the converting that back to smooth (linear) skinning.  Does not support dualQuat skinning.

The UI for it is a bit clunky:  While they let you bake this from one skeleton/mesh to another, I never use it this way.  Plus. you need to manually enter the name of the mesh and root joint (or the 'most root joint' effecting that mesh) into both the 'Source' and 'Destination' sections.
For the 'Skeleton', it's really important you only pick the 'root most joint' that influences your mesh.  For example, if you were applying this operation to a head, you'd probably use a neck or spine joint as the root.  The tool puts every joint through a range of motion to determine the deformation effect it has on the mesh, and the more joints in the root hierarchy, the more complex/time consuming it becomes.

Official docs for the tool, [[HERE|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-CharacterAnimation/files/GUID-DD430C9B-95E7-4EBB-8D2B-A566018B4AC4-htm.html]]
And the [[bakeDeformer|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/bakeDeformer.html]] command
Example script usage:
{{{
bakeDeformer -srcMeshName pCylinder1 -srcSkeletonName joint1 -dstMeshName pCylinder1 -dstSkeletonName joint1 -maxInfluences 4;
}}}
Note, I've see this error pop up often:
{{{
# Error: Failed to calculate the weights: points (4,5,47,53,159,164,349,1015)
}}}
This seems somehow related to the mesh topology, even though doing a cleanup on it has no effect.  Not sure yet how to resolve.
~PyMel has a nice utility to convert from mel straight to Python:
{{{
import pymel.tools.mel2py as mel2py
print mel2py.mel2pyStr('aimConstraint -mo -weight 1 -aimVector 0 -1 0 -upVector 0 0 1 -worldUpType "vector" -worldUpVector 0 0 1 -skip y;')
}}}
{{{
aimConstraint(weight=1,upVector=(0, 0, 1),skip='y',mo=1,worldUpType="vector",aimVector=(0, -1, 0),worldUpVector=(0, 0, 1))
}}}
Tip from Mason Sheffield.
By default, Maya normalizes all skin weights so they always add to {{{1.0}}}.  But in a version back in the 'teens they introduced a new normalization method called 'post', that just adds up all the weights, so nothing is normalized.  Personally not a big fan, find it confusing trying to get accurate values.

How can one convert 'post' normalization to 'interactive' (the default 'normalized' mode)?
{{{
mySkinCluster = "skinnerCluster92"

# Set it to be 'interactive", this will make it look horrible.
mc.setAttr(f"{mySkinCluster}.normalizeWeights", 1) # 0 = none, 1 = interactive, 2 = post

# Then normalize the weights:
mc.skinPercent(mySkinCluster, normalize=True)
}}}

Or, with the mesh picked:
* 'Skin Menu -> Normalize Weights' menu
** Enable Weight Normalization
** Normalize Weights
Much of this I have learned from others, we all ride on shoulders of giants.  But for what I present as my own work, it's under the below licence:
----
All information on this wiki is under the Apache Licence, v 2.0:
{{{
Copyright 2015 Eric Pavey

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
}}}
To understand how this licence works see [[this overview|http://developer.kde.org/documentation/licensing/licenses_summary.html]].
But it pretty much means you can use the information on this wiki however you want, but I always appreciate credit where applicable.
Good overview here:
http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_to_wrap_GUID_3F96AF53_A47E_4351_A86A_396E7BFD6665_htm
Example file lives here:
{{{
C:\Program Files\Autodesk\Maya2018\devkit\devkit\pythonScripts\widgetHierarchy.py
}}}
This example also provides you data on every window attached to the main Maya window, so it's a bit interesting on its own.
This whole thing is very non-trivial.  Below is some boilerplate that makes it work though.  
The only major change is once this is saved in a module, you need to update teh TB.uiScript string, to properly import and execute it.
To launch the window, execute {{{TB()}}}
{{{
# thisModuleName.py

from __future__ import print_function

import maya.cmds as mc
import maya.OpenMayaUI as omui
from maya.app.general.mayaMixin import MayaQWidgetDockableMixin

from PySide2 import QtWidgets, QtCore

#-----------------------

# This stores an instance to the Dockable class when first instanced.
# The downside is reloading this module will totally break this system and you'll
# need to resart Maya ever time you want to see a change to your widget.
gDockableWin = None

class Dockable(MayaQWidgetDockableMixin, QtWidgets.QWidget):
    """
    Create a dockable window.
    """
    name = "aDockableWindow"
    title = "MrDock"
    # Will need to update this with the import for this module correctly:
    uiScript = 'import thisModuleName as tmn; tmn.Dockable.dockableScript(restore=True)'
    
    @staticmethod
    def dockableScript(restore=False):
        '''
        To launch the dockable window, this staticmethod is called to by the calling code.
         Boilerplate code.
        '''
        # --------------
        # All boilerplate code
        global gDockableWin
        parentControl = None
        if restore == True:
            parentControl = omui.MQtUtil.getCurrentParent()
        if gDockableWin is None:
            gDockableWin = Dockable()
            # Set it to be docked by default:
            # Or not.  This worked at one point, but now errors because it says
            # the workspaceCtrl doesn't work.  Leaving in to troubleshoot later...
            #workspaceCtrl = self.getWorkspaceControl()
            #mc.workspaceControl(workspaceCtrl, edit=True, floating=False)
        if restore == True:
            mixinPtr = omui.MQtUtil.findControl(Dockable.name)
            omui.MQtUtil.addWidgetToMayaLayout(long(mixinPtr), long(parentControl))
        else:
            # Show is a method on the MayaQWidgetDockableMixin superclass
            gDockableWin.show(dockable=True, uiScript=Dockable.uiScript)
        # --------------
        return gDockableWin

    def __init__(self):
        """
        Init our widget.  Boilerplate code.
        """
        super(Dockable, self).__init__()
        self.setObjectName(Dockable.name)
        self.setWindowTitle(Dockable.title)
        self.setWindowFlags(QtCore.Qt.Window)
        self.setProperty("saveWindowPref", True) # Save prefs on exit.
        self.populate()
        self.show()

    def getWorkspaceControl(self):
        """
        Return the string name of the workspaceControl this widget is associated with.
        Maya automatically appends the "WorkspaceControl" string to the workspace control child object name.
        """
        return "%sWorkspaceControl"%self.objectName()

    def populate(self):
        """
        Create the primary contents of our window.
        """
        self.layout_main = QtWidgets.QVBoxLayout()
        self.setLayout(self.layout_main)
        if self.layout_main:
            widget_but = QtWidgets.QPushButton("A Button")
            self.layout_main.addWidget(widget_but)
}}}
I didn't discover this until Maya 2020, not sure how long it's been around. BTW, there's a 'twist', so be sure to read to the end...

I went to make a cluster defomer form Maya's menu, and Maya wasn't printing the command to the Script Editor.
So I put the button command on my shelf to introspect it:
{{{
CreateCluster;
}}}
Was the mel it was running.  But that's a {{{runTimeCommand}}}, that really calls to this mel:
{{{
pythonRunTimeCommand cluster.cmd_create 0
}}}
{{{pythonRunTimeCommand}}} is a mel global proc living here:
{{{
C:/Program Files/Autodesk/Maya202X/scripts/others/pythonRunTimeCommand.mel
}}}
That is effectively doing this:
{{{
import maya.internal.common.cmd.base
# Run the command immediately
maya.internal.common.cmd.base.executeCommand("cluster.cmd_create") # if the mel arg is 0
# Or, open the options to the tool:
maya.internal.common.cmd.base.openCommandDialog("cluster.cmd_create") # if the mel arg is 1
}}}
The {{{base.py}}} module lives here:
{{{
c:\Program Files\Autodesk\Maya202X\Python\Lib\site-packages\maya\internal\common\cmd\base.py
}}}
And the {{{executeCommand}}} function has these internals:
{{{
def executeCommand(nodeTypeCmd):
    # f.e. nodeTypeCmd = 'tension.cmd_create'
    met = getCommandClass(nodeTypeCmd)
    if met is not None:
        met().executeWithPreferences()
        return True
    else:
        return False
}}}
And as you can see, it takes a string arg, that looks a lot like a Python package path.
In fact it is, they live here:
{{{
C:\Program Files\Autodesk\Maya2020\Python\Lib\site-packages\maya\internal\nodes
}}}
So, from the above example, the {{{executeCommand()}}} function is actually executing this module:
{{{
c:\Program Files\Autodesk\Maya2020\Python\Lib\site-packages\maya\internal\nodes\cluster\cmd_create.py
}}}
Which has a {{{Command}}} class inside, which is instanced, then executed via its {{{executeWithPreferences()}}} method.

My presumption is this is a way that Autodesk is slowly moving away from it's old mel bones, and authoring Python classes to create these deformers based on the {{{optionVar}}} prefs stored via the option windows.

But it sucks that {{{executeCommand}}} doesn't return anything other than bool, so to get something more functional, plus you can pass in args to it, but you need to introspect the source to understand what they are:
{{{
clustClass = maya.internal.common.cmd.base.getCommandClass("cluster.cmd_create")
clustNode = clustClass.command(relative=relative, envelope=envelope)
}}}
Furthermore, @@in some cases@@ there is another module/class combo that acts as a Maya-side OOP wrapper around the node once it's been created.  Here is an example of this for the {{{proximityWrap}}} deformer:
{{{
C:\Program Files\Autodesk\Maya2020\Python\Lib\site-packages\maya\internal\nodes\proximitywrap\node_interface.py
}}}
So, to wrapper up your proximity wrap node and then get access to those class methods:
{{{
from maya.internal.nodes.proximitywrap.node_interface import NodeInterface

wrapNode = NodeInterface("proximityWrap1")
wrapNode.addDriver("driverMeshShape")
}}}
But for some reason, not all nodes have the {{{node_interface.py}}} module
----
The Twist
As it turns out, for {{{cluster}}} nodes at least, the {{{cluster}}} command it self actually does everything you need, plus you can pass in your own list of components, instead of basing it on selection.  Of course, my example uses selection to get that list, but I'm sure you understand:
{{{
import maya.cmds as mc
selComp = mc.ls(selection=True, flatten=True)
clusterNode, clusterTransform = mc.cluster(selComp)
}}}
Other deformer types definitely need higher-level help, but ironically, I went on this journey, learned a lot, then realized I should just call to the core command directly...
Maya has no built-in curvature shader which I find surprising.  Some resources below:
* https://github.com/Byron/bsuite/tree/master/src/shaders
* http://www.stepanjirka.com/maya-api-curvature-shader/
** https://github.com/sjirka/CurvatureShader
* http://www.tomcowland.com/mentalray/tc_curvature/
* http://wiki.polycount.com/wiki/Curvature_map#Maya
** https://polycount.com/discussion/comment/1162740#Comment_1162740

* The old school one by Duncan Brinsmead.  Only works on NURBS:
** https://www.highend3d.com/maya/script/curvaturetexture-for-maya
{{{
//
//  Alias|Wavefront Script File
//  MODIFY THIS AT YOUR OWN RISK
//
//
//  Creation Date:  April 1999
//  Author:         Duncan Brinsmead
//
//  Procedure Name:
//      curvatureTexture
//
//  Description:
//      Creates an expression that uses 3 pointOnSurface nodes
//		to determine local curvature in u and v and then uses
//		this curvature to lookup the u values in a ramp texture.
//		Extra attributes are added to the ramp texture to
//		control the curvature range and method of combining
//		the u and v curvature.
//		The resulting ramp texture should be assigned to
//		some component of the shader for the surface
//		to have an effect.
//
//  Input Arguments:
//      None.
//
//  Return Value:
//      None.
//

global proc curvatureTexture( )
{
    int $i;
    string $pickedNurbs[] = `ls -dag -sl -lf -ap -typ nurbsSurface`;

    if( !size( $pickedNurbs ))
    {
        print("// No nurbs surfaces are selected! Can't create curveTexture.//");
        return;
    }
    string $samplerNode = "curvatureTextureSamplerUtility";
    if( !objExists( $samplerNode ) )
    {
        $samplerNode = `shadingNode -asUtility samplerInfo -name $samplerNode`;
    }

    for( $i = 0; $i < size( $pickedNurbs ); $i++ )
    {
        string $p  = `pointOnSurface -top on -ch on $pickedNurbs[$i]`;
        string $pU = `pointOnSurface -top on -ch on $pickedNurbs[$i]`;
        string $pV = `pointOnSurface -top on -ch on $pickedNurbs[$i]`;
        if( size($p) && size($pU) && size($pV) )
        {
            string $rampNode = `shadingNode -asTexture ramp -name ($pickedNurbs[$i] + "Curvature")`;
            if( size( $rampNode ))
            {
                addAttr -sn cmn  -ln curvatureMin  -dv -0.05 -min -1 -max 1 $rampNode;
                addAttr -sn cmx  -ln curvatureMax  -dv  0.05 -min -1 -max 1 $rampNode;
                addAttr -sn csw  -ln curveSampleWidth -dv 0.01 -min 0.00001 -max 0.2 $rampNode;
                addAttr -sn muv  -ln useMaxOfUorV  -dv 0 -at bool $rampNode;
                addAttr -sn wrc  -ln wrapCurvature -dv 0 -at bool $rampNode;
                expression -s ( "float $u = " + $samplerNode + ".u;\n" +
                    "float $v = " + $samplerNode + ".v;\n" +
                    $p + ".u = $u;\n" +
                    $p + ".v = $v;\n" +
                    $pU + ".u = $u + " + $rampNode + ".curveSampleWidth;\n" +
                    $pU + ".v = $v;\n" +
                    $pV + ".u = $u;\n" +
                    $pV + ".v = $v + " + $rampNode + ".curveSampleWidth;\n" );

                expression -s ( "float $c,$dotU,$dotV;\n" +
                    "$dotU = " +$p+".tux * "+$pU+".nnx + " +
                        $p+".tuy * "+$pU+".nny + " +
                        $p+".tuz * "+$pU+".nnz;\n" +
                    "$dotV = " +$p+".tvx * "+$pV+".nnx + " +
                        $p+".tvy * "+$pV+".nny + " +
                        $p+".tvz * "+$pV+".nnz;\n" +
                    "if( " + $rampNode + ".useMaxOfUorV ){\n" +
                        "if( abs($dotU) > abs($dotV) ) \n"+
                            "$c = $dotU;\n"+
                        "else\n"+
                            "$c = $dotV;\n"+
                    "}else{\n" +
                        "$c = ($dotU + $dotV) * 0.5;\n" +
                    "}\n"+
                    "$c = ($c - " + $rampNode + ".cmn) / (" +
                        $rampNode + ".cmx - " +$rampNode+ ".cmn);\n" +
                    "if( !" + $rampNode + ".wrapCurvature ) { \n" +
                        "if( $c > 1.0 ) \n"+
                            "$c = 1.0;\n"+
                        "else if( $c < 0 )\n"+
                            "$c = 0;\n"+
                    "}\n"+
                    $rampNode + ".u = -$c;\n" +
                    $rampNode + ".v = $c;\n" );
                print( "\n// Result texture: " + $rampNode + " (map to shader to view) //");
            }
        }
    }
}
}}}
Great reference showing interactive curve easing functions:
!Reference 1
https://www.desmos.com/calculator/2rnqgoa6a4
This loads up a generic function.  Click on the menu in the top left to get others.
!Reference 2
http://www.gizma.com/easing/
!Reference 3
http://easings.net/
And the code for it all (~JavaScript) is here:
https://github.com/gdsmith/jquery.easing/blob/master/jquery.easing.1.3.js

Here's some Python conversions:

~JavaScript:
{{{
easeInOutCubic: function (x, t, b, c, d) {
    if ((t/=d/2) < 1) return c/2*t*t*t + b;
    return c/2*((t-=2)*t*t + 2) + b;
    },

easeInQuad: function (x, t, b, c, d) {
    return c*(t/=d)*t + b;
    },
}}}
Python:  (note I left out the 'x', since it isn't needed in these funcs)
{{{
"""
t: current time
b: beginning value
c: change In value
d: duration
"""

def easeInQuad(t, b, c, d):
    T = t/d
    return c*T*T+b

def easeInOutCubic(t, b, c, d):
    T = t / (d / 2)
    if T < 1:
        return c/2*T*T*T + b
    else:
        TT = T - 2
        return c / 2 * ( TT*TT*TT + 2) + b
}}}
For example, here we have a curve 'ten units' long, that has a min/max value of 0 -> 1.0, and we query the frame in the middle (5), giving us a value of .5 (the middle of the curve, the one spot not being modified from a linear curve):
{{{
currentTime = 5
beginningValue = 0.0
changeInValue = 1.0
duration = 10.0
print easeInOutCubic(t=currentTime, b=beginningValue, c=changeInValue, d=duration)   
.5
}}}
Note with the Python, you can't modify values in-place like they do in ~JavaScript: {{{ if ((t/=d/2) < 1)}}}.  While this is legal in Python:
{{{
a = 4
a += 3
}}}
This would fail:
{{{
a = 4
b = a += 3
}}}
Because of that, I broke them out into their own lines, an it works just fine, and is slightly less confusing ;)
[[Introduced in Maya 2018|http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=GUID-94DA2210-6FF1-4993-8EB9-9A6D87562D35]]
----
To activate the Dash Script Editor
#Select one or more attributes in the Channel Box.
#Alt + right-click anywhere in the Channel Box.
##Maya displays the Dash Script Editor where you can enter Dash commands.
| Command |Effect |
| l(number) |Distributes the selected objects linearly over the total distance denoted by 'number' (requires 2 or more objects to be selected). |
| r(number) |Assigns a random number between 0 and 'number'. |
| r(low, high) |Assigns a random number between 'low' and 'high'. |
| e() |Auto ease animation channel. |
| e(number) |Eases acceleration / deceleration of an animation channel based on a number between -1 and 1. A value of -1 represents slow acceleration and rapid deceleration, while a value of o1 produces the opposite. |
| ts(number) |Time offset any keyframes for the selected attributes by 'number' of frames. |
In addition to these, anything from the Python Random module is also available for use.

Customized Dash commands
You can create your own Dash commands by editing the Dash.json file, located in 
Windows:
{{{
C:/Program Files/Autodesk/Maya2018/plug-ins/MASH/scripts
}}}
Mac:
{{{
/Applications/Autodesk/Maya2018/plug-ins/MASH/scripts
}}}

This is in Maya 2008:
Still trying to figure out what the top history layout is called.
*{{{scriptEditorPanel1Window}}}  -- window
**{{{TearOffPane}}} -- paneLayout
***{{{scriptEditorPanel1}}} -- panel -- This and the next one are a bit confusing, being by the same name...
****{{{scriptEditorPanel1}}}  -- scriptedPanel
*****{{{formLayout52}}}  -- formLayout
******{{{frameLayout10}}} -- frameLayout
*******{{{formLayout53}}} -- formLayout
********All the buttons across the top of the Script Editor
******{{{formLayout54}}}
*******{{{paneLayout1}}} -- paneLayout
********{{{cmdScrollFieldReporter1}}}  -- cmdScrollFieldReporter (the bottom half of the Script Editor.  This value is also held in {{{global string $gCommandReporter}}}
********{{{formLayout55}}}
*********{{{tabLayout2}}} -- tabLayout
**********All the current mel and python tabs in the Script Editor
*********{{{tabLayout3}}} -- tabLayout
**********{{{formLayout66}}} -- formLayout
***********{{{textField5}}}  -- textField
***********{{{textScrollList1}}}  -- textScrollList
You can use the {{{cmdScrollFieldExecuter}}} command to create the bottom half of the Script Editor, in a new window.
Mel:
{{{C:/Program Files/Autodesk/Maya2008/scripts/startup/scriptEditorPanel.mel}}}
[[Welcome]]
* https://www.ea.com/seed/news/open-source-dem-bones
* https://github.com/electronicarts/dem-bones
"An automated algorithm to extract the linear blend skinning (LBS) from a set of example poses".
* Converting any animated mesh sequence, e.g. geometry cache, to LBS, which can be replayed in popular game engines,
* Solving skinning weights from shapes and skeleton poses, e.g. converting blendshapes to LBS,
* Solving bone transformations for a mesh animation given skinning weights.
"In software engineering, a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern isn't a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations."

Great overview & examples can be found here:
https://sourcemaking.com/design_patterns
! Multi
Multi isn't an attr type, but a modifier you can put on an attr during creation.  If you create a new attr with the {{{multi}}} flag set, you can consider it an 'array' attr:  You can access ({{{getAttr}}}/{{{setAttr}}}) them via {{{myNode.myMulti[0]}}}, {{{myNode.myMulti[3]}}} etc.
Taken from the {{{addAttr}}} docs:
{{{
# Add a multi attribute named ff/forcefield of type double3.
#
cmds.addAttr( shortName='ff', longName='forcefield', dataType='double3', multi=True )
}}}
Its usage is very similar to how you create an array attr in //mel//:  You define the //type// of variable as say 'float'.  But then by putting {{{[]}}} after the variable name, you define it as an array attr:  This is just like passing the {{{multi}}} flag during attr creation:
{{{
float $var = 3.3;
float $arrayVar[] = {2.2, 3.3, 4.4};
}}}
! Compound
Compounds are actual attr types, and are very similar to the concept of {{{structs}}} in programming languages like {{{c++}}} : Really, they're just 'containers'/'collections' of various different attribute types.  So a single compound could have multiple different child attr types.  The {{{translate}}} attr is a compound type, that has three double child attrs for the {{{translateX}}}, {{{translateY}}}, and {{{translateZ}}} values.  

it's worth noting that:
* The attrs created as a compound's children could be multi.  
* A compound itself could be multi, generating an array of copies of all the children that could be set differently per index.
Taken from the {{{addAttr}}} docs:
{{{
# Add a compound attribute named sampson with children home, midge,
# damien, elizabeth, and sweetpea of varying types
#
cmds.addAttr( longName='sampson', numberOfChildren=5, attributeType='compound' )
cmds.addAttr( longName='home', attributeType='matrix', parent='sampson' )
cmds.addAttr( longName='midge', attributeType='message', parent='sampson' )
cmds.addAttr( longName='damien', attributeType='double', parent='sampson' )
cmds.addAttr( longName='elizabeth', attributeType='double', parent='sampson' )
cmds.addAttr( longName='sweetpea', attributeType='double', parent='sampson' )
}}}
----
Also see:
* [[attributeType vs dataType]]
The more I learn about matrices in Maya, the more ways I find to access, manipulate, and apply them.  Need to write it down to keep my head straight ;)
The below doc is split into these sections:
*Gotcha's
*Docs
*Access
*Manipulation
*Application
!Matrix gotcha's:
*Internally, API matrix translational data is saved in cm in Maya, regardless of what you have the UI set to (inches, etc).  So if you're //not// working in cm, you'll need to convert the cm data to your 'ui units' if using application methods such as {{{setAttr}}}, or if you're extracting and modifying the data before reapplication.
*"Freezing transforms" on a node will screw up where Maya thinks the world matrix position is:  It will no longer return the correct positional values after that.  
**If you freeze transforms on a node, it will think the new world matrix is at the location defined by the 'world rotate pivot' minus the 'local rotate pivot'.  Really, it's just the 'world rotate pivot'.
**Often, I need to rebuild the world matrix with correct worldspace positional information, acquired form the {{{pointPosition}}} command querying the world {{{roatePivot}}} of the given node, and re-inserting it back into the matrix (converting to cm first if need be).
** For more info: [[What happens when you 'freeze transforms' behind the scenes?]]
* Trying to get the {{{worldInverseMatrix}}} with the above two issues present can be a pain.  To do it, you first need to fixup your worldMatrix with the correct positional values, then execute the {{{inverse()}}} method on it (presuming this is through the API), rather than querying the {{{worldInverseMatrix}}} directly.
*Matrix attributes on nodes like transforms and joints (unless user-created) are output\read only:  You can't use {{{setAttr}}} to change them.
To help keep the code clean, the below examples //aren't going to handle any of that//.  It's your job to modify them and fix-up where you see fit.
!Matrix-related Docs:
(not all of these are discussed below)  There are additional matrix related nodes and commands here: [[Matrix info]]
!!!Python ~OpenMaya API
*Query\Access matrix data:
**[[OpenMaya.MFnDagNode|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dag_node.html]] : Can be used to return an //local// (not worldspace) {{{MMatrix}}} for the given {{{MDagPath}}}.
***[[OpenMaya.MFnTransform|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_transform.html]] : Can be used to return a {{{MTransformationMatrix}}} for the given {{{MObject}}} or {{{MDagPath}}}.  Note this will be the //local// matrix of the node, not the worldspace matrix.
**[[OpenMaya.MDagPath|http://download.autodesk.com/us/maya/2010help/API/class_m_dag_path.html]] : Can be used to query a world-space (inclusive) or parent-space (exclusive) {{{MMatrix}}}.
**[[OpenMayaUI.M3dView|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m3d_view.html]] : It has a variety of methods ({{{viewToObjectSpace}}}, {{{projectionMatrix}}}, {{{modelViewMatrix}}}) used for getting and transforming matrices (and vectors) based on the camra, between camera-space, object-space,and world-space.
*Matrix data types:
**[[OpenMaya.MMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix.html]] : Pure matrix data type.
**[[OpenMaya.MFloatMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_float_matrix.html]] : This is like a {{{MMatrix}}} but stores the internal data as {{{floats}}}, rather than {{{doubles}}}.
**[[OpenMaya.MTransformationMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_transformation_matrix.html]] : This provides many helper methods for transforming matrix data.  Note you can't access matrix indices individually or do math-operator (like multiplying two {{{MTransformationMatrix}}}'s together).  In both cases need to be converted to {{{MMatrix}}} first.
*Matrix containers:
**[[OpenMaya.MMatrixArray|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix_array.html]] : Not covered here.  An array of {{{MMatrix}}} data.
*Scripted Plugin related matrix data:
**[[OpenMayaMPx.MPxTransformationMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_px_transformation_matrix.html]] : Not covered here, can be used to create your own custom scripted plugin based {{{matrix}}} nodes.
**[[OpenMaya.MFnMatrixAttribute|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_matrix_attribute.html]] : Not covered here, used to add matrix attributes on custom nodes authored via scripted plugins.  Used inside the 'initializer' function of the scripted plugin.
**[[OpenMaya.MFnMatrixData|http://download.autodesk.com/us/maya/2010help/API/class_m_fn_matrix_data.html]] : Not covered here, used to access matrix data passed to, or generated by a custom node authored via a scripted plugin.  Code used inside the scripted plugin itself.
!!!Python (& mel)  commands:
*[[xform|http://download.autodesk.com/us/maya/2010help/CommandsPython/xform.html]] : Used to query or set matrix data (in a variety of spaces) by returning or passing a list of 16 floats.
*[[getAttr|http://download.autodesk.com/us/maya/2010help/CommandsPython/getAttr.html]] : Used to query the variety of matrix attributes on a node.  Usually returns a list of 16 floats.
*[[dagPose|http://download.autodesk.com/us/maya/2010help/CommandsPython/dagPose.html]] : Used to store and query matrix data in a {{{dagPose}}} //node//. Most commonly used during skinning operations.
*{{{pointMatrixMult}}}   (Mel only)
**This Mel proc returns the multiplication of a point and a matrix as an array of 3 doubles.  It is a wrapper around the {{{pointMatrixMult}}} node, which the script actually creates, modifies, gathers the return value, and then deletes.
**Located: {{{C:/Program Files/Autodesk/Maya<version>>/scripts/others/pointMatrixMult.mel}}}
!!!Mel variables
*[[matrix|http://download.autodesk.com/us/maya/2010help/index.html?url=Arrays_vectors_and_matrices_Matrices.htm,topicNumber=d0e158454]]
!!!Nodes:
*[[dagNode|http://download.autodesk.com/us/maya/2010help/Nodes/dagNode.html]] : Base node type that stores these attributes: {{{matrix}}} (local), {{{inverseMatrix}}}, {{{worldMatrix}}}, {{{worldInverseMatrix}}}, {{{parentMatrix}}}, {{{inverseParentMatrix}}}, (starting in Maya 2020) {{{offsetParentMatrix}}}.
**[[transform|http://download.autodesk.com/us/maya/2010help/Nodes/transform.html]] : Base transform node (that inherits from {{{dagNode}}}).  Adds the attribute {{{xformMatrix}}} which is the same as the parental {{{matrix}}} (local) attribute.
*[[dagPose|http://download.autodesk.com/us/maya/2010help/Nodes/dagPose.html]] : Created via the {{{dagPose}}} command, used to store matrix data as a pose. Most often used during skinning operations.
*[[addMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/addMatrix.html]] : Add a list of matrices together.
*[[fourByFourMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/fourByFourMatrix.html]] : This node outputs a 4x4 matrix based on 16 input values. This output matrix attribute can be connected to any attribute that is type "matrix".
*[[holdMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/holdMatrix.html]] : Cache a matrix.
*[[multMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/multMatrix.html]] : Multiply a list of matrices together.
*[[passMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/passMatrix.html]] : Multiply a matrix by a constant without caching anything.
*[[pointMatrixMult|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/pointMatrixMult.html]] : The dependency graph node to multiply a point by a matrix.  Pairs with the {{{pointMatrixMult}}} mel proc (above).
*[[wtAddMatrix|http://download.autodesk.com/global/docs/maya2013/en_us/Nodes/wtAddMatrix.html]] : Add a weighted list of matrices together.
* {{{matrixNodes.mll}}} plugin : Ships with Maya 2013.  Includes these nodes:
**{{{decomposeMatrix}}} : Previous to Maya 2013, created via the {{{decomposeMatrix.mll}}} plugin.  Exposes the matrix data for a given transform.
**{{{composeMatrix}}}
**{{{inverseMatrix}}}
**{{{transposeMatrix}}}
* New in Maya 2020, based on the {{{offsetParentMatrix}}} updates
** [[pickMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/pickMatrix.html]]
** [[blendMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/blendMatrix.html]]
** [[aimMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/aimMatrix.html]]
!Accessing Matrix Data:
!!!API Only:
Here, we have functions that return either {{{MMatrix}}} or {{{MTransformationMatrix}}} objects.
----
^^Calls used in the below //API// examples:^^
{{{
import maya.OpenMaya as om
}}}
{{{
def getMDagPath(node):
    """
    Convenience function to make getting MDagPaths easier:
    Get a MDagPath for a node.  node is a string name.
    """
    selList = om.MSelectionList()
    selList.add(node)
    mDagPath = om.MDagPath()
    selList.getDagPath(0, mDagPath)
    return mDagPath
}}}
----
{{{
def get_MDagPath_MMatrix(node):
    """
    This will return the worldspace MMatrix of a node. node is a string name.
    """
    mDagPath = getMDagPath(node)
    mMatrix = mDagPath.inclusiveMatrix()
    return mMatrix
}}}
{{{
def get_MFnTransform_MTransformationMatrix(node):
    """
    Using a MFnTransform function, return a MTransformationMatrix for the node.
    This is the *local* matrix of the node (not worldspace).  node is a string name.
    """
    mDagPath = getMDagPath(node)
    transformFunc = om.MFnTransform(mDagPath)
    mTransformMtx = transformFunc.transformation()
    return mTransformMtx
}}}
!!!API and getAttr / xform:
The below to functions are almost the same.  The {{{getAttr}}} method however gives you a few more matrix querying options.
----
^^Calls used in the below //API// examples:^^
{{{
import maya.cmds as mc
}}}
----
{{{
def get_getAttr_MMatrix(node, matrixType):
    """
    Access the matrix attr data directly on a node.
    node : string name of node to query.
    matrixType : "matrix", "inverseMatrix", "worldMatrix", "worldInverseMatrix",
        "parentMatrix", "parentInverseMatrix", "xformMatrix"   
    return a MMatrix
    """
    mList = mc.getAttr(node+"."+matrixType)
    mMatrix = om.MMatrix()
    om.MScriptUtil.createMatrixFromList(mList, mMatrix )
    return mMatrix
}}}
{{{
def get_xform_MMatrix(node):
    """
    Access the matrix attr data directly on a node.
    This will return the worldspace MMatrix for the node.
    node : string name of node to query.
    return a MMatrix
    """
    mList = mc.xform(node, query=True, matrix=True)
    mMatrix = om.MMatrix()
    om.MScriptUtil.createMatrixFromList(mList, mMatrix )
    return mMatrix
}}}
!!!Mel only:
Python has no built-in matrix [[type|http://docs.python.org/library/stdtypes.html]], that's why above we use the Maya API's version.  Here we do it all in mel:
{{{
global proc matrix get_getAttr_matrix(string $node, string $matrixType){
    // Access the matrix attr data directly on a node.
    // string $node : Name of node to query.
    // string $ matrixType : "matrix", "inverseMatrix", "worldMatrix",
    //     "worldInverseMatrix", "parentMatrix", "parentInverseMatrix", 
    //     "xformMatrix"
    float $m[] =`getAttr ($node+"."+$matrixType)`;
    matrix $mtx[4][4] = <<$m[0],  $m[1],  $m[2],  $m[3];
                          $m[4],  $m[5],  $m[6],  $m[7];
                          $m[8],  $m[9],  $m[10], $m[11];  
                          $m[12], $m[13], $m[14], $m[15]>>; 
    return $mtx;
    }

// example:
matrix $myMatrix[4][4] = get_getAttr_matrix("pCube1", "worldMatrix")
// Result: << 1 0 0 0;  0 1 0 0;  0 0 1 0;  0 0 0 1 >> // 
}}}
{{{
global proc matrix get_xform_matrix(string $node){
    // returns a matrix type for the given node's world matrix.
    float $m[] = `xform -query -matrix  $node`;
    matrix $mtx[4][4] = <<$m[0],  $m[1],  $m[2],  $m[3];
                          $m[4],  $m[5],  $m[6],  $m[7];
                          $m[8],  $m[9],  $m[10], $m[11];  
                          $m[12], $m[13], $m[14], $m[15]>>; 
    return $mtx;
    }

// example:
matrix $myMatrix[4][4] = get_xform_matrix("pCube1")
// Result: << 1 0 0 0;  0 1 0 0;  0 0 1 0;  0 0 5.667197 1 >> // 
}}}
!Manipulating matrix data:
Once you have API {{{MMatrix}}} data, you can manipulate it like standard types.  //Usually// this is matrix multiplication, and it creates a new matrix:
{{{
cubeMtx = get_MDagPath_MMatrix('pCube1')
sphereMtx = get_MDagPath_MMatrix('pSphere1')
newMtx = cubeMtx * sphereMtx
}}}
Same thing goes for the mel {{{matrix}}} type:
{{{
matrix $cubeMtx[4][4] = getAttr_matrix("pCube1", "worldMatrix");
matrix $sphereMtx[4][4] = getAttr_matrix("pSphere1", "worldMatrix");
matrix $newMtx[4][4] = $cubeMtx * $sphereMtx;
}}}
It should be noted that {{{MTransformationMatrix}}} has no operator operations, so you can't do multiplication like above.  If you wanted to, you'd need to convert it to a {{{MMatrix}}}, do the multiplication, then convert the new {{{MMatrix}}} back to a {{{MTransformationMatrix}}}, see examples of this below.  It should be noted however that the {{{MTransformationMatrix}}} class has many methods for setting its values, check the docs.
----
If you want to query the individual elements of a {{{MMatrix}}}, there are a couple different ways:
{{{
# Note how you call ( ) to the matrix, rather than access the index directly [ ] ?
mtxItem = someMMatrix(2,2) 
mtxItem = om.MScriptUtil.getDoubleArrayItem(someMMatrix[2], 2)
}}}
Which is similar for mel {{{matrix}}} types:
{{{
float $mtxItem = $newMtx[2][2];
}}}
The {{{MTransformationMatrix}}} provides no index access to its internal elements.  You first need to convert it to a {{{MMatrix}}} type, then query the values you're after via the above methods:
{{{
newMMatrix = myMTransformationMatrix.asMatrix()
}}}
----
If you want to set an individual element of a {{{MMatrix}}} you need to use a {{{MScriptUtil}}} convenience function.  Here, we set the 2rd row (index 1), 3rd column (index 2) to 3.1:
{{{
om.MScriptUtil.setDoubleArray(someMMatrix[1], 2, 3.1) 
}}}
This is pretty easy in mel:
{{{
$newMtx[2][2] = 3.1;
}}}
Like above you can't directly access the elements of a {{{MTransformationMatrix}}}.  So if you want to set one of them, based on the above solution you'd need to convert it to a {{{MMatrix}}}, modify the element, then re-create the {{{MTransformationMatrix}}} based on the new {{{MMatrix}}} data.  This is lossy however, and you could loose other local transformation data the original {{{MTransformationMatrix}}} was storing:
{{{
myMTransformationMatrix = om.MTransformationMatrix(someMMatrix)
}}}
!Apply matrix data:
Once you have updated\new matrix data, how do you get it back on your nodes?
!!!API Only
It's interesting that I can't find anything via the API to apply a {{{MMatrix}}} back to a node, it only has functions for applying {{{MTransformationMatrix}}} data.  So in theory you could create a new {{{MTransformationMatrix}}} based around a given {{{MMatrix}}}, and use this code:
{{{
def set_MFnTransform_MTransformationMatrix(node, mtf_mtx):
    """
    Using a MFnTransform function, apply a MTransformationMatrix to a node.
    node is a string name.
    """
    mDagPath = getMDagPath(node)
    transformFunc = om.MFnTransform(mDagPath)
    transformFunc.set(mtf_mtx)

# For example:
sphereMtx = get_MFnTransform_MTransformationMatrix('pSphere1')    
set_MFnTransform_MTransformationMatrix('pCube1', sphereMtx)
}}}
!!!API & xform:
{{{
def set_xform_MMatrix(node, mMatrix):
    """
    Apply the given MMatrix to the given node in worldspace
    """
    mList = [mMatrix(i,j) for i in range(4) for j in range(4)]
    mc.xform(node, worldSpace=True, matrix=mList)

# For example:
pCubeMtx = get_MDagPath_MMatrix('pCube1')
set_xform_MMatrix('pSphere1', pCubeMtx, 'worldSpace')
}}}
!!!Mel Only:
{{{
global proc set_xform_matrix(string $node, matrix $matrix){
    // Apply the given matrix to the given node in worldspace
    // First we need to convert the matrix to a float array:
    float $ml[] = {};
    for($i=0;$i<4;$i++){
        for($j=0;$j<4;$j++){
            $ml[size($ml)] = $matrix[$i][$j];
            }
        }
    xform -matrix $ml[0] $ml[1] $ml[2] $ml[3] $ml[4] $ml[5] $ml[6] $ml[7] $ml[8] 
                  $ml[9] $ml[10] $ml[11] $ml[12] $ml[13] $ml[14] $ml[5]
          -worldSpace $node;
}
// For example:
set_xform_matrix("pCube1",$matrix);
}}}
I wanted a way to reference a node by a persistent ID, that would remain the same between Maya sessions, //even if the name of the node changed//.  Meaning, reference a node by a value other than its string name.

Short answer is yes, but //they're only persistent for the given Maya session//:  Restarting Maya rests the id's... :(

And to clarify, I'm not referring to the {{{MTypeId}}} class, which provides access to the unique id provided to a given type\classification of node:  I was looking for a persistent id for a specific instance of a node, like it's location in memory, or a representative hash.
----
What I found id this:  Given an {{{MObject}}} for a node, you can access the secretive {{{myMObject.__hash__()}}} method, which will return a unique hash for that instance of an {{{MObject}}} for the given node.  If you create another {{{MObject}}}, it will have it's own unique hash.  AND, obviously, if you quit Maya, reopen it, and generate a new {{{MObject}}}, it too will have its own unique hash.  There's also the {{{MObjectHandle}}} class that has a {{{hashCode()}}} method, which I presume references the one on the {{{MObject}}}.

~PyMel's {{{PyNode}}} has a similar {{{__hash__()}}} method, which has the same limitations.

So basically, in a given Maya session, you can create a {{{MObject}}} for a node, and then (via the authoring of new tools) access the node by it's 'hash name'.  But creating a new {{{MObject}}} or reloading the scene will break this by generating all new hash values.
----
I brought this up with Autodesk support, and after much troubleshooting\research they said 'no such thing exists'.
----
The current solution I'm working on is to leverage Python's  [[uuid|http://docs.python.org/2/library/uuid.html]] module ([[Universally Unique Identifier|http://en.wikipedia.org/wiki/UUID]]) into a tool that will emulate this functionality by adding\querying custom 'uuid' attrs applied to nodes.
I've pretty-much made the switch from mel (Maya's scripting language) to Python. And what I describe below is standard in Python (implemented differently, but similar visually). But I just recently learned that you can include dots (periods) '.' in mel procedure names. While this doesn't in any way change their behavior, it does let you organize your procs in a more intelligent way based on their names.

Background: In Maya, if you have a global procedure in a mel script, and the global proc and the script share the same name, when you call to the name (via some other proc\script), Maya will search in its 'script path' for the script with the name, then search in that script for the global proc with that name, and (if it finds it) execute the proc. Furthermore, any other global procs in that script are now sourced and can be executed outside of the script by other procedures. Nothing new here. The 'dot in name' convention though lets you associate the script name with the other global procs in the script. When calling them in other procedures/scripts, you'll have a visual reminder of where they came from via their name. To explain this a bit better, an example:
{{{
// foo.mel

global proc foo.boo()
	{
	print "doing .boo() work\n";
	}

global proc foo.shoe()
	{
	print "doing .shoe() work\n";
	}


global proc foo()
	{
	print "foo.mel  -- I do nothing.\n";
	print "My procs:\n";
	print "\t.boo()\n";
	print "\t.shoe()\n";
	}
}}}
The above code resides in 'foo.mel'. Inside, there is a global proc called 'foo()', that doesn't really do anything other than print some info. But its there so when the user calls to 'foo()', the other global proces will become sourced. There are other global procs in the script that do the actual work. When you execute 'foo()', the other global procs 'foo.boo()' and 'foo.shoe()' are sourced, and can be called to inside of that script, or other scripts\procs. If you're calling to them inside that script, it's fairly obvious where they're coming from. But say you have some other script called 'goo.mel':
{{{
// goo.mel

// Execute foo(), making its other global
// procs available  in this script.
// Could have optionally executed 'source foo;'
foo;

// Execute foo's global procs:
foo.boo();
foo.shoe();
}}}
'goo.mel' executes (or optionally sources) 'foo.mel', thus giving 'goo.mel' access to 'foo.mel's' global procs, and then can call to each of them. By having 'foo.' in front of each proc, it's plainly clear to the user where they originally came from, which is a great aid in troubleshooting. This is a simple example, but imagine if you have goo executing procs from foo, moo, hoo, doo, etc, and you can see where this can come in handy.

So again, the 'dot in proc' is by no means required, but it's another tool in your organizational toolkit. 
It's not too often I run across quality skinning tools, but if I do, //I put them here//:
*http://www.ngskintools.com/
**player.vimeo.com/video/95829347 - video overview
*https://github.com/chrisevans3d/skinWrangler
**https://www.youtube.com/watch?v=b__ABAKjDRI
Playblast seems super clunky these days.  Questionable codecs, just sort of a pain.  

Enter OBS: https://obsproject.com/
This will let you easily capture video with audio right from the Maya window at at good quality and framerate.  Here's a short tutorial:
* After installing OBS, follow along with the [[quickstart guide|https://obsproject.com/wiki/OBS-Studio-Quickstart]] (seriously, it's short, easy, explains the basics), plus these notes:
* In the auto-configuration wizard:
**  Choose 'Optimize just for recording, I will not be streaming'
** Choose what resolution and framerate you deem appropriate.
* Be sure to set your 'Settings → Output → Recording → Recording Format to ~MP4, and Encoder to h264.
* Per the quickstart guide:
** Set your audio source correctly:  Settings -> Audio -> Desktop Audio : Set it to whatever is playing sound out of Maya.  If this is working, as Maya is playing, you'll see the 'audio mixer' levels change.  Set any other audio to 'disabled'. 
* You need to add Maya as a 'Source' to record it:
** In the Sources' section, press the "+" button and choose 'Window Capture' : Set it to point to your Maya application.
** You can use the red outline to make it fit correctly into the resolution of the capture window.
** You can press Alt, and drag the corners of the window to crop it better, to just fit what you want to record, then resize that again to fit your resolution.
* For recording, I generally give my animations a bit of pre & post roll and hit play: Then, in OBS, hit the 'Start Recording' and 'End Recording' as appropriate.
----
Also see:
* [[How can I playblast using h.264?]] : To configure that to playblast better, as an option.
The Maya [[connectControl|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/connectControl.html]] command can setup a bi-directional link between an object.attribute, and a UI control.  Like linking the rotateZ of something to a slider:  Move the slider, the object rotates.  Rotate the object, and the slider updates.

How is this achievable in Qt?  I'm just learning Qt myself, and there could quite possible be a less convoluted solution, but what I've come up with behaves just fine.

Here's an overview of how it works:
* Window is created.  A signal/slot combo is setup so that whenever the dial is changed, it will modify the {{{rotateZ}}} on the defined object.
* User picks some object, and presses the "Define Object" button.
* This executes the {{{butCmd}}} method, which:
** Sets the {{{self.transform}}} attr with the current selected node.
** Deletes any pre-existing attr-changed callbacks.
** Fires the {{{createAttrChangedCallback}}} method, which creates a callback that will execute the {{{attrChangedCallbackCmd}}} method whenever the nodes {{{rotateZ}}} is changed.
* When the node is rotated by the user and the {{{attrChangedCallbackCmd}}} method executes:
** The first thing it does is to {{{blockSignals}}} on the {{{QDial}}}:  It then sets the new dial value, then stops blocking the signals.  
** If that signal blocking //didn't// happen, Maya will effectively go into a cyclic loop:  The user rotates the node, which updates the dial, which updates the node, which updates the dial, etc.
Code:
{{{
# dialer.py

from math import degrees

import pymel.core as pm
import maya.OpenMayaUI as omui
import maya.OpenMaya as om

from PySide import QtCore
from PySide import QtGui

from shiboken import wrapInstance


def maya_main_window():
    # Get a pointer to Maya's main menu qt widget:
    main_window_ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(main_window_ptr), QtGui.QWidget)

def getMObject(node):
    """
    # Return a MObject for the passed in node name
    """
    selList = om.MSelectionList()
    selList.add(str(node))
    mObject = om.MObject()
    selList.getDependNode(0, mObject)
    return mObject

#-------------------------------------------------------------------------------

class App(QtGui.QDialog):

    def __init__(self):
        """
        Create the window.
        """
        super(App, self).__init__(maya_main_window())

        # Delete the window if it already exists:
        self.title = "Dialatron"
        for widget in QtGui.QApplication.topLevelWidgets():
            if self.title == widget.windowTitle():
                widget.close()

        # This tracks the ID for our attr changed callback, so it can be later
        # removed when the window is closed, or a new node is picked:
        self.cid = None
        # This tracks the selected object:
        self.transform = None

        self.setWindowTitle(self.title)
        self.setWindowFlags(QtCore.Qt.Tool)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.createLayout()
        self.createSignals()
        self.show()

    def closeEvent(self, event):
        """
        Called when a Qt window is closed.  We use it to remove the attr changed
        callback.
        """
        if self.cid:
            om.MMessage.removeCallback(self.cid)

    def createLayout(self):
        """
        Create the main ui layout and widgets
        """
        self.button = QtGui.QPushButton("Define Object")
        self.dial = QtGui.QDial(minimum=0, maximum=360)
        self.dial.setNotchesVisible(1)
        self.dial.setWrapping(1)

        # Acts like a columnLayout in Maya:
        mainLayout = QtGui.QVBoxLayout()
        mainLayout.setContentsMargins(10,10,10,10)
        mainLayout.setSpacing(10)
        mainLayout.addWidget(self.button)
        mainLayout.addWidget(self.dial)
        self.setLayout(mainLayout)

    #---------
    # Signals:

    def createSignals(self):
        """
        These create the *signals* based on ui interaction, and connects them
        to *slots*.
        """
        self.button.clicked.connect(self.butCmd)
        self.dial.valueChanged.connect(self.dialCmd)

    #----------
    # Slots:

    def butCmd(self):
        """
        Slot for the button signal.  It both defines the object to 'dial', and
        creates the attr changed callback.
        """
        sel = pm.ls(selection=True)
        if not len(sel):
            pm.displayWarning("Nothing selected")
            return
        self.transform = sel[0]
        self.createAttrChangedCallback()
        print "Defined '%s' for dialation"%self.transform

    def dialCmd(self):
        """
        Slot for the dial signal:  When happens when the user turns the dial.
        """
        sender = self.sender()
        val = sender.value()
        if self.transform and pm.objExists(self.transform):
            pm.PyNode(self.transform).rotateZ.set(val)

    #---------
    # Callbacks:

    def createAttrChangedCallback(self):
        """
        Create the callback that will be executed when an attribute changes.  It
        calls to attrChangedCallbackCmd.
        """
        if self.cid:
            om.MMessage.removeCallback(self.cid)
        if pm.objExists(self.transform):
            self.cid = om.MNodeMessage.addAttributeChangedCallback(getMObject(self.transform),
                                                                   self.attrChangedCallbackCmd, self.dial)

    def attrChangedCallbackCmd(self, msg, plug, otherPlug, *args):
        """
        Function executed when an attr is changed.
        """
        if 'rotateZ' in plug.name():
            qdial = args[0]
            val = degrees(plug.asDouble()) # Rotations are returned as radians.
            # If we don't block the signal emitted from the dial, we'll get into
            # a cyclic loop.
            qdial.blockSignals(1)
            qdial.setValue(val)
            qdial.blockSignals(0)
}}}
To execute:
{{{
import dialer
dialer.App()
}}}
Got this when trying to do a very simple skinning operation:
{{{
// Error: line 0: Problems occurred with dependency graph setup. // 
# Error: file: C:/Program Files/Autodesk/Maya2022/scripts/others/pythonRunTimeCommand.mel line 15: RuntimeError: file C:\Program Files\Autodesk\Maya2022\Python37\lib\site-packages\maya\internal\nodes\skincluster\cmd_create.py line 202: Error occurred during execution of MEL script
line 0: Problems occurred with dependency graph setup. # 
}}}
Tracing the mel, I ended up on this line causing that error:
{{{
skinCluster -toSelectedBones -bindMethod 0  -normalizeWeights 1 -weightDistribution 0 -mi 2  -dr 4 -rui false jointName meshShape;
}}}
What was causing it?  Bad mesh:  I found that if I exported the mesh as FBX, deleted it, then reimported it back into Maya, it skinned just fine. 

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.
When running the evaluation graph in parallel mode, there are certain node types that will stop it in its tracks, and push it back into serial mode.  These nodes include:
!!!Expressions
Avoid them, they are horrible for the EG.  Try to re-write as nodal networks.  They are flagged as "untrusted".
//However//, if all the outputs of an expression are directly related to the inputs, they can be set to "globally serial".
For example, this is bad, since it's using {{{getAttr}}} : Never use {{{getAttr}}} in an expression this way, if you want it to work in the EG.  Plus I'm not sure if {{{sin}}} is ok either...
{{{
float $val = `getAttr foo.tx` * sin(6);
spam.ty = $val;
}}}
However, this would work:
{{{
spam.ty = foo.tx*6;
}}}
Since the output is directly related to the input, with no external calls.
!!!Python Scripted Plugins
Since Python Scripted Plugins use Python, and Python is inherently single-threaded, any node it hits will throw the EG back into serial mode.  Re-write as c++ plugin.  If they are used, they're evaluated as "globally serial".
!!!Cycle Clusters
While a 'cycle cluster' isn't a node, it's either a node, or collection of nodes that has a cycle.  This is different from things that cause 'cycleCheck' warnings:  A simple constraint can cause a cycle cluster.  Sometimes they're unavoidable, but if you can, they should be, since they'll force the EG back to a serial mode during their eval.

''Note'':  
<<<
I have a newer subject listed here:
[[Positional args in Python authored UI's]]
It doesn't invalidate anything listed below, but puts another spin on it.  When I authored the below section, I didn't quite understand 'positional arguments', and thought the data I was seeing was a bug.  So I put a bit of a negative spin on it.  I have re-written bits of below to reflect the new knowledge, but you should keep that in mind when reading it.
<<<
----
----
Python has a similar issues to mel when it comes to building a UI that calls to an external command:
*In //mel//, say you have a button that references a procedure in the same script as the UI (or it could be in a different script).  If that procedure isn't a //global procedure//, the UI will never be able to find it after UI creation.
*Python has the same issue:  In a Python module you author a Maya UI.  In that UI you create a button that tries to execute a Python function.  But when you run the UI, it can't find the function even though the function was authored in the same module as the UI.  How can you make it work?
I've found --two-- six different solutions.  Part of this ''update'' are three new solutions, now listed first.
*The first example shows a clean way of having a function-authored UI call to a function in the same module, via hard-coded UI control names.
*The second example shows how to author a UI in a class, and call to methods in that class via hard-coded UI control names.
*The third example shows how to author a UI in a class, and call to methods in that class via control object name pointers, rather than hard-coded names.
*The fourth example illustrates the {{{button}}}-argument bug (also see 'one last bug' below), and some solutions around it.
*The fifth (one of the original) example shows how you can create UI's via classes, and pass object pointers back as the commands.  
*The sixth (last original) example uses more basic functions, and embeds the commands as strings.
I would recommend using methods 1-4, but I've kept the last two since they do show yet another way of approaching things.  
----
''{{{lambda}}}'':
I should call out how important the {{{lambda}}} expression (//Python// expression, not Maya expression) is to this system.  See {{{lambda}}} notes on my [[Python wiki|http://pythonwiki.tiddlyspot.com/#%5B%5BUnderstanding%20lambda%5D%5D]].  I'll touch on it briefly here:  A button command in a Maya UI expects to have either a //string//, or a //function// passed into it.  If you pass something else, you'll raise a {{{TypeError}}} exception like this:
{{{
# Passing in a 'None' object to a button command:
# TypeError: Invalid arguments for flag 'buttonCommand'.  Expected string or function, got NoneType # 
}}}
Why is this important?  
*{{{lambda}}}'s are 'Python expressions' that return //function objects//.  This means you can use a {{{lambda}}} to wrapper a command you're passing into a UI button command, without having to jump through the hoops of turning that command into a string to be evaluated later.  And because of this, the {{{lambda}}} remembers the 'scope' it was authored in, thus allowing you to continue to search through it's parental namespaces in the containing module.  
To put it another way:
*If you make the button command a string, when the command is executed, the string is evaluated in the 'most global scope' of Python.  Technically this is the {{{__builtin__}}} scope of Python, which is the parental scope of modules (which define global scopes) and functions (local scopes).  So that string really has //no// idea about the module, or function, it was authored in.  {{{lambda}}} however isn't an anonymous string: it creates a live function object, fully aware of its scope.  And because of that, makes you authoring life much easier.  If you want more info understanding Python's scope, see my Python wiki [[here|http://pythonwiki.tiddlyspot.com/#%5B%5BPython%20variable%20scope%5D%5D]].
----
''Positional args, what you need to know:''
<<<
I used to think this was a bug in the system, but I've finally tracked this issue down. I explain this in great detail here: [[Positional args in Python authored UI's]], but I will cover it briefly here:
When a UI control is executed, either by changing its value (via a floatSlider, textField, floatField, etc), or by "pressing" it (via a button), the control will return //positional arguments// to its command flags.  This means if you author the {{{-changeCommand}}} of a control to call to a function, the control will pass any positional arguments into that function.  For example, if you have a {{{floatFieldGrp}}}, with three fields, and you map a function into the {{{-changeCommand}}} of that control, any time you change one of the three arguments, it will return all three //positional arguments// to the function defined by the {{{-changeCommand}}}.  If you're not expecting this, it can seem like a bug.  But it's not, and actually, its quite handy.
It should be noted though that this behavior can vary between controls:  The {{{button}}} control always returns back an empty string {{{""}}} as its positional arg.  The 'button' of a {{{textFieldButtonGrp}}} returns back nothing at all as its positional arg.  That difference in behavior I //do// consider a bug.
<<<
----
!Example #1
We make a module called mayaWindowTest01.py.  In it we author two functions, one to build the window, and one that is executed when the button in the window is pressed.
Here, three important things are happening:
#When the UI creates itself, it hard-codes the name of the {{{textFieldButtonGrp}}} as '{{{TFBG_stuff}}}'.
#The {{{buttonCommand}}} of the control is calling to a {{{lambda}}} expression, that actually ends up executing our button command.  You can see an example above it (commented out) for the solution you'd need if you //weren't// using the {{{lambda}}} expression.
#Inside the {{{buttonCmd}}} function, it finds the control to query via the hard-coded name:
{{{
# mayaWindowTest01.py
import maya.cmds as mc

# Pass UI values by hard-coded UI names in functions outside a class:
def buttonCmd(word):
    text = mc.textFieldButtonGrp("TFBG_stuff", query=True, text=True)
    print text + "  " + str(word)

def mainWindow():
    if mc.window("pyWin", exists=True):
        mc.deleteUI("pyWin")
    mc.window("pyWin", title="Python Window",  resizeToFitChildren=True)
    mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
    # Example of passing our button-commad as a string, rather than lambda:
    # mc.textFieldButtonGrp("TFBG_stuff",  buttonCommand="mayaWindowTest01.buttonCmd('Success!')")
    mc.textFieldButtonGrp("TFBG_stuff",  buttonCommand=lambda:buttonCmd('Success!'))    
    mc.showWindow()

mainWindow()
}}}
Then to execute:
{{{
import mayaWindowTest01
}}}
The {{{buttonCmd}}} both queries the UI control value, and an incoming argument, just to illustrate possible usages.  When you hit the button, it queries the UI control text, adds the text passed in as an arg, and prints the result.
Notice the commented-out example above.  If you pass the command in as a string, you need to use the fully-qualified namespace of {{{buttonCmd}}}: '{{{mayaWindowTest01.buttonCmd}}}', since as a string, in a sense the command has no idea where to find the data its executing.  But by using {{{lambda}}}, it is no longer a string, but an actual function, and it finds {{{buttonCmd}}} in the global scope of the module automatically.
!Example #2
Here, we author the UI and the command it calls to inside of a class.  Like Example #1, we hard-code the UI control name for our {{{textFieldButtonGrp}}}.  The {{{buttonCmd}}} method has the same functionality as the {{{buttonCmd}}} function from Example #1.
We still use a {{{lambda}}} to assign our command to our {{{buttonCommand}}} argument.  But notice that the lambda points to '{{{self.buttonCmd}}}', since this is a method of the class:
{{{
# mayaWindowTest02.py
import maya.cmds as mc

# Pass UI values by hard-coded UI names inside a class:
class mainWindow(object):
    def __init__(self):
        self.showWindow()
        
    def buttonCmd(self, word):
        # pull UI control name from our hard-coded name:
        text = mc.textFieldButtonGrp("TFBG_stuff", query=True, text=True)
        print text + "  " + str(word)        
        
    def showWindow(self):
        if mc.window("pyWin", exists=True):
            mc.deleteUI("pyWin")
        mc.window("pyWin", title="Python Window",  resizeToFitChildren=True)
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        # Hard-code our UI name:
        mc.textFieldButtonGrp("TFBG_stuff", buttonCommand=lambda:self.buttonCmd('Success!'))
        mc.showWindow()   
}}}
Then to execute:
{{{
import mayaWindowTest02
win = mayaWindowTest02.mainWindow()
}}}
!Example #3
This is probably the most elegant example, since there is no hard-coding of UI control names.  While nearly identical to Example #2, here we create a class attribute called {{{self.tfbg}}} that stores the object reference created when we author the {{{textFieldButtonGrp}}}.  Inside of our {{{buttonCmd}}} method, we then call to that class attr for our control name.  Never have to wory about UI name clashing!
{{{
# mayaWindowTest03.py
import maya.cmds as mc

# Pass UI values by object pointers inside a class:
class mainWindow(object):
    def __init__(self):
        self.showWindow()
                
    def buttonCmd(self, word):
        # Pull in the control name via the class attr:
        text = mc.textFieldButtonGrp(self.tfbg, query=True, text=True)
        print text + "  " + str(word)
        
    def showWindow(self):
        if mc.window("pyWin", exists=True):
            mc.deleteUI("pyWin")
        mc.window("pyWin", title="Python Window",  resizeToFitChildren=True)
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        # Create a class attribute, to store the UI name
        self.tfbg = mc.textFieldButtonGrp(buttonCommand=lambda:self.buttonCmd('Success!'))
        mc.showWindow()
}}}
To execute:
{{{
import mayaWindowTest03
win = mayaWindowTest03.mainWindow()
}}}
!Example #4
(I authored this example when I didn't quite understand //positional arguments//, and though they were a bug.  While I've updated it to reflect that newer knowledge, it was still originally authored to show what I thought the problem was.)
When the {{{button}}} control is pressed, it passes a single empty string back as a //positional argument//.  When  it is pressed, and it tries to execute its {{{command}}}, it also passes and empty string as an argument to the command.  If the command isn't expecting this... bad things.
We can still use {{{lambda}}}, but as you can see, we need to implement the concept of {{{*args}}}, which will let {{{lambda}}} get any number of external args during its execution.
In the below example, a window is created with four buttons.  It shows how you can call to a method generated in an external object, and methods in the same object that created the UI, using {{{lambda}}} or not.  The class {{{MyCmd}}} and it's method {{{printer}}} are designed to illustrate how the {{{button}}} command will pass its empty arg, if not caught by {{{lambda *args}}} first.  To learn more about {{{*args}}}, see my Python wiki [[here|http://pythonwiki.tiddlyspot.com/#argument]].
{{{
# mayaWindowTest04.py
import maya.cmds as mc

class MyCmd(object):
    def printer(self, *args):
        self.args = args
        print "External object, length of positional args: " + str(len(self.args))
 
class mainWindow(object):
    def __init__(self):
        self.showWindow()
        
    def mainCmd(self):
        print "Internal object, success"
        
    def showWindow(self):
        if mc.window("pyWin", exists=True):
            mc.deleteUI("pyWin")
        mc.window("pyWin", title="Python Window",  resizeToFitChildren=True)
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        # make an object that will execute our external command:
        cmdObject = MyCmd()
        
        # Here we use the button command, with and without lambda.  The button
        # command always passes an empty string as an arg to the command, so we
        # need to catch it with lambda using *args:
        mc.button(command=lambda *args:cmdObject.printer(), label="Button, external object, lambda")
        mc.button(command=lambda *args:self.mainCmd(), label="Button, this object, lambda")   
        mc.button(command=cmdObject.printer, label="Button, external object, no lambda")
        mc.button(command=self.mainCmd, label="Button, this object, no lambda (will fail)")  
        
        # textFieldButtonGrp's button command doesn't return any positional args,
        # which I actually think is a bug.  But we can handle it differently because of that
        mc.textFieldButtonGrp(buttonCommand=lambda:cmdObject.printer(), buttonLabel="textFieldButtonGrp, external object, lambda" )
        mc.textFieldButtonGrp(buttonCommand=lambda:self.mainCmd(), buttonLabel="textFieldButtonGrp, this object, lambda" )                
        mc.textFieldButtonGrp(buttonCommand=cmdObject.printer, buttonLabel="textFieldButtonGrp, external object, no lambda" )        
        mc.textFieldButtonGrp(buttonCommand=self.mainCmd, buttonLabel="textFieldButtonGrp, this object, no lambda" )        

        mc.showWindow()
}}}
To execute:
{{{
import mayaWindowTest04
win = mayaWindowTest04.mainWindow()
}}}

----
''These are two older examples:''
----
!Example #5 
(big shout out to Doug Brooks for helping me figure this out...)
Make sure that this module is saved in part of your Maya Python path.
{{{
# myWin.py
import maya.cmds as mc

class MyCmd(object):
    # Create a object that will do the work the button requests
    def __init__(self, word):
        self.val = word
    def printer(self, *arg):
        print self.val

class MyWin(object):
    # Our window object
    def mainWindow(self):
        # Display the window:
        if mc.window("pyWin", exists=True):
            mc.deleteUI("pyWin")
        mc.window("pyWin", title="Python Window")
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
        # make our command object, with argument:
        cmdObject = MyCmd("success!")
        mc.button(label="super button!", command=cmdObject.printer)
        mc.showWindow()
}}}
In the above example, we create two objects, both existing in the same module, {{{myWin.py}}}.  Key things to note:
*Before we make our button, we first create an object that will do the work, including the arguments that should be used:
{{{
cmdObject = MyCmd("success!")
}}}
*Next, when we create the button, we pass a pointer to the object.method we want to execute.  Note that I have left //off// the trailing parenthesis '{{{()}}}' that one would normally include.
{{{
mc.button(label="super button!", command=cmdObject.printer)
}}}
*Now, inspect the {{{MyCmd}}} class:  It has a standard {{{__init__}}} method for turning the passed in variable into an object attribute.  But what is interesting, is the {{{printer}}} method:
{{{
def printer(self, *arg):
}}}
*See the 2nd argument, {{{*arg}}}?  This is a //positional argument//, and can capture arbitrary passed in variables as a {{{tupple}}} (a Python immutable list).  Even though the {{{printer}}} method doesn't call to the {{{*arg}}} inside of it, if you were to remove this argument, it would fail.  Why?  It //seems// that when Maya's {{{mc.button}}} command is ran, it passes an //empty string// as an argument //to the command//.  I have no idea //why// it's doing this, but you need to catch this rouge argument via {{{*args}}}, and then just ignore it.
Finally, you can execute the code:
{{{
import maya.cmds as mc
import myWin
win = myWin.MyWin()
win.mainWindow()
}}}
This will launch the UI.  When you hit the '{{{super button!}}}' button, it //should// print to Maya, {{{success!}}}.
!Example #6
Make sure that this module is saved in part of your Maya Python path:
{{{
# myWin2.py
import maya.cmds as mc

def printer(p):
    # our buttom command function
    print p

def mainWindow():
    if mc.window("pyWin", exists=True):
        mc.deleteUI("pyWin")
    mc.window("pyWin", title="Python Window")
    mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
    mc.button(label="super button!", command="import myWin2 as mw; mw.printer('success!')")
    mc.showWindow()
}}}
This example is a bit more simplistic that the first example.  Here, we have no class objects being created.  Really, the only important thing to note is on the button line:
{{{
mc.button(label="super button!", command="import myWin2 as mw; mw.printer('success!')")
}}}
*Note how for the command, there are actually two separate commands:  The first imports the module that the window was authored in ("{{{import myWin2 as mw}}}"), and the second executes the {{{printer}}} function defined there within ("{{{mw.printer('success!')}}}").  If the import didn't happen, when this command was executed, it'd have no knowledge of what '{{{printer}}}' was.
To execute the code:
{{{
import maya.cmds as mc
import myWin2
myWin2.mainWindow()
}}}
This will launch the UI.  When you hit the '{{{super button!}}}' button, it //should// print to Maya, {{{success!}}}.
Since you're embedding the command as a string in this example, it's not as flexible as the first (in case you wanted a command to run on arbitrarily changing values), but, it is easier to wrap your head around.
Special values can be used in the Expression Editor.  What are they?
*{{{frame}}} : Will return the current frame.
*{{{time}}} : Will return a float based on the current second value.  Frame 15 @ 30fps would return .5
Are there more?  That's a good question :)
How do you go about extracting/connecting an individual X, Y, or Z axis vector, or its position, from a nodes worldMatrix **attribute**?  There //seems// to be no built in node-type for this.
{{{
(right column ignored in 4x4 matrix in Maya)
1 0 0 0 <-- X axis vector
0 1 0 0 <-- Y axis vector
0 0 1 0 <-- Z axis vector
0 0 0 1 <-- Positional component
}}}
There is, it's just not obvious.  Thanks to this thread, all has become clear:
http://discourse.techart.online/t/maya-accessing-matrix-components-with-dg-nodes/4470
Below is a modified version of the info provided by user 'Theodox':
!Use a vectorProduct node 
Connect the output {{{worldMatrix}}} attr of a node to the {{{matrix}}} input of the {{{vectorProduct}}}. 
Then:
!! Extract A Vector
Set the {{{vectorProduct.operation}}} to {{{VectorMatrixProduct}}}. 
You can then extract the X, Y or Z vectors of the matrix by setting the {{{input1}}} to:
* (1,0,0) for the X vector component.
* (0,1,0) for the Y vector component.
* (0,0,1) for the Z vector component.
This is equivalent to a standard vector-matrix multiply, so it will give you orientation but not translations, or scales. 
There’s also a flag to normalize the output or not.
!! Extract The Position
Set the {{{vectorProduct.operation}}} to {{{PointMatrixProduct}}}, you’ll get the point-matrix multiply with scale and translation (but you have to watch out for {{{unitConversion}}} nodes sneaking in and screwing with your numbers). 
Then set this on {{{input1}}} (default values, but worth noting) :
* (0,0,0) : The positional component of the matrix.
@@IMPORTANT@@ : If the transform you're querying has ever had it's transforms frozen, the position this returns will be incorrect :S
!! Get the result
In either case, you can connect the {{{vectorProduct.output}}} attr to another node that takes a triple float input.
!Use a {{{decomposeMatrix}}} node
You can connect the {{{worldMatrix}}} of your node to a {{{decomposeMatrix}}} node, and get the {{{outputTranslate}}} attr from that.
@@IMPORTANT@@ :  Like above, if the transform you're querying has ever had it's transforms frozen, the position this returns will be incorrect :S
!The {{{pickMatrix}}} node
This was new in 2020:  It takes a matrix input, and provides a matrix output, but lets you 'turn off' things like translate, rotate, scale, and shear.
http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/pickMatrix.html
@@IMPORTANT@@  : If you're using version control (like P4) and storing your FBX files there, make sure that P4 and your FBX file format are both the same type.  Meaning, if you're saving binary FBX files, make sure P4 is storing them as binary files.  If you're saving ascii FBX files, make sure P4 is storing them as ascii files.  If there is any unalignment here, the FBX files will be corrupted on the server: The person that submitted them will have legit local copies, but anyone else who syncs them will get the corrupted versions.

!Export style comparison
Based on the {{{fbxmaya.mll}}} plugin.

''mel'':
{{{
FBXExport -f "path to file" -s;
}}}
''Python'':
{{{
import maya.mel as mm
mm.eval('FBXExport -f "path to file" -s;')
}}}
''~PyMel'':
{{{
import pymel.core as pm
pm.mel.FBXExport(f="path to file", s=True)
}}}
!Examples
This is a great example taken from [[this thread|https://groups.google.com/forum/?fromgroups#!topic/python_inside_maya/cLkaSo361oE]] by [[Geordie Martinez|https://plus.google.com/u/0/100001095454321399948/posts]]
{{{
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=takeNumber)
}}}
Take numbers are:
* {{{-1}}} :The last take in the array
* {{{0}}} : Import no animation
* {{{Any other number}}} : That take.  Presumably 1 is a good starting point...
! Commands vs Properties
To perform an FBX export (or import), you call to a number of commands, or set a number of properties, then execute the import/export.  Because of that, it's a good idea to reset them all to defaults each time so no old values screw up a future export (shown below).

There are many FBX plugin-based commands available (see below docs).  However, they don't appear to cover all the bases, based on what the export/import UI expose.  For example, let's say you want to export mesh, but no animation.   In the UI, you can uncheck 'animation', but there's no //command// for that.  How to resolve?
{{{
import maya.mel as mm
mm.eval('FBXResetExport') # Reset all previous export settings via a command.
mm.eval('FBXExportSmoothingGroups -v true')  # Set mesh to export via a command.
mm.eval('FBXProperty Export|IncludeGrp|Animation -v false;') # Don't export animation via a property.
mm.eval('FBXExport -f "path to my file" -s') # Do the export via a command.
}}}
While the commands can be found in the below docs, or by looking at 'Plug-in information' provided by the {{{fbxmaya.mll}}} via the 'Plugin Manager', how you can you get a list of properties?
Executing this mel:
{{{
FBXProperties;
}}}
Will print them all, and their state to the Script Editor.  From there, you can find the ones you want, their current values, which give you a good idea on how to set them.
!Docs
Maya 2019 docs:
* [[Maya FBX Plugin|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-18A2CDD7-3334-4FC1-A1B3-A308AD331BB2]]
** [[FBX Plugin UI|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-A525A2DF-AEBE-48FB-8783-C712C877BB62]]
*** [[FBX Export|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-BD85FA4C-4D40-457C-BE66-47BC08B82FC3]]
*** [[FBX Import|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-A31311AB-6FEB-4408-9937-48621CE24EA0]]
**[[FBX Mel Scripting|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-F48E3B78-3E56-4869-9914-CE0FAB6E3116]]
*** [[FBX Export Mel Commands|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-6CCE943A-2ED4-4CEE-96D4-9CB19C28F4E0]]
*** [[FBX Import Mel Commands|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-699CDF74-3D64-44B0-967E-7427DF800290]]
*** [[FBX General Mel Commands|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=GUID-335F2172-DD4D-4D79-B969-55238C08F2EF]]
* [[FCheck Reference|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-8BADA377-5D2A-48A7-96E5-2A88B3FF0702-htm.html]]
** [[FCheck options list | https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-AB9246E8-D483-4605-80AF-7123AF7ED1D1-htm.html]]
** [[FCheck supported file formats|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-53999B62-1164-494E-8404-46F88BD8C858-htm.html]]
* [[Working With FCheck|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-98E00C0A-12F9-452B-B0F9-E7706BF1CCF6-htm.html]]
**[[Frame Numbering And Padding|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-6379FC90-954B-4530-AB36-998B6F1E0315-htm.html]]
** [[Using FCheck options | https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-3B731248-678E-4F75-8F96-F877288637F6-htm.html]]
You can also find local docs here based on your Maya install:
{{{
file:///C:/Program%20Files/Autodesk/Maya2022/docs/ext/FCheck/FCheck20.html
}}}
These can be auto opened by launching the {{{fcheck.exe}}} -> Help -> Command Line...

From that doc:
{{{
fcheck -<option> <spec> myimage.ext
}}}
{{{
<option>
}}}
The ~FCheck option letter. See [[FCheck options list | https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Maya-Rendering/files/GUID-AB9246E8-D483-4605-80AF-7123AF7ED1D1-htm.html]]
{{{
<spec>
}}}
Option-specific parameters.
{{{
myimage.ext
}}}
The name of the file to load. You must be in the current directory in the command prompt to open the file. On Linux, you must include the filename to the command line. When using Windows, you can omit the filename from the command line.

For example:
{{{
fcheck -r 15 
}}}
Where r specifies the frame rate option and 15 is the number of frames per second.

You can combine some options together in one command line using the following convention:
{{{
fcheck -<option> <spec> -<option> <spec> ... myimage.ext
}}}
For example,
{{{
fcheck -B -G 1 -r 15 myimage.ext
}}}
Where:
* -B turns on border around the image.
* -G 1 sets gamma value to 1.
* -r 15 sets frame rate to 15 frames per second.
http://en.wikipedia.org/wiki/Facial_Action_Coding_System
From Wikipedia:
<<<
Using FACS, human coders can manually code nearly any anatomically possible facial expression, deconstructing it into the specific Action Units (AU) and their temporal segments that produced the expression. As AUs are independent of any interpretation, they can be used for any higher order decision making process including recognition of basic emotions, or pre-programmed commands for an ambient intelligent environment.
<<<
This is good for facial rigging, since there are (quoted from Jeremy Ernst's [[GDC paper|http://www.unrealengine.com/files/downloads/Jeremy_Ernst_FastAndEfficietFacialRigging2.pdf]] ) "32 action units of the face that can achieve the entirety of the range of (e)motion of the face.  An action unit is defined as a contraction or relaxation of 1 or more muscles in the face".

This allows the rigger a set of guidelines to go by when creating facial rigs, that can more closely match what the human face is actually doing.

Good resource:
* https://melindaozel.com/facs-cheat-sheet/
From here:
http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_to_wrap_GUID_3F96AF53_A47E_4351_A86A_396E7BFD6665_htm
{{{
from maya import OpenMayaUI as omui 
widgetStr = mel.eval( 'string $tempString = $gMainCreateMenu' )    
ptr = omui.MQtUtil.findControl( widgetStr )    
widget = wrapInstance(long(ptr), QWidget)
}}}
----
Also see:
* [[PySide : How can I search Maya for Qt created objects?]]
Given a matrix, how do you find the corresponding Euler rotations?  I've been working on this problem, and my "Third Solution, Method A" gives a good result.  But you can see a history of research below.
!Third Solution
After learning more from the API, I realized the differences between the [[MMatrix|http://download.autodesk.com/us/maya/2010help/api/class_m_matrix.html]] class and the [[MTransformationMatrix|http://download.autodesk.com/us/maya/2010help/api/class_m_transformation_matrix.html]] class:  {{{MMatrix}}} purely holds matrix data, not unlike a {{{MVector}}} storing vector data and nothing else.  the {{{MTransformationMatrix}}} does everything a {{{MMatrix}}} does, but it has knowledge of how Maya wants it, so provides many convenience methods for accessing the data and interaction with a Maya transform node..

The big takeaway I found is this:  {{{MTransformationMatrix}}} objects will keep track of rotations past 360 degrees & rotation order of the node, while {{{MMatrix}}} objects will not. It makes sense:  A matrix itself stores orientations as vectors, which have no concept how how far they've been 'rotated', so the {{{MMatrix}}} object doesn't track this data.  But behind the scenes, the {{{MTransformationMatrix}}} //does// track this info, which the below examples illustrate.

One more very important piece of data:  When extracting a {{{MTransformationMatrix}}} from a node via a {{{MFnTransform}}} function, what you're actually getting is the //local transformation matrix// of that node (since the {{{MTransformationMatrix}}} appears to track transformations //specific to that node//, relative to its parent).  So if you were expecting the world-space matrix, prepare to be disappointed.  You can build a {{{MTransformationMatrix}}} out of a worldspace-acquired {{{MMatrix}}}, but at that point 'rotations past 360' would be lost.
{{{
# Python code
import math
import maya.cmds as mc
import maya.OpenMaya as om

# Define a node to pull a matrix from.
node = 'pCube1'
# Set some rotation values for comparison later:
mc.setAttr('%s.rotate'%node, 15, -45, 1000)
mc.setAttr('%s.scale'%node, 3, 3, 3)
# Change the rot order, to make sure returned euler values are correct:
mc.setAttr('%s.rotateOrder'%node, 3)
}}}
''Method A:  Using {{{MTransformationMatrix}}}''
This, in my opinion, is the best solution.  Via the API, you get an {{{MDagPath}}} object for your node, wrapper it in a {{{MFnTransform}}} object, and then directly extract a {{{MTransformationMatrix}}} object from it.  This object tracks the rotation order (so you don't have to), and rotation values past 360
{{{
#-------------------------------------------
# Part 1: Get a MTransformationMatrix from an object for the sake of the example.
# You can use your own MTransformationMatrix if it already exists of course.

# get a MDagPath for our node:
selList = om.MSelectionList() # make a sel list # MSelectionList
selList.add(node) # add our node by name
mDagPath = om.MDagPath() # create an empty dag path # MDagPath
selList.getDagPath(0, mDagPath) # fill the dag path with our node

# Create a MFnTransform object for our MDagPath,
# and extract a MTransformationMatrix from it:
transformFunc = om.MFnTransform(mDagPath) # MFnTransform
mTransformMtx = transformFunc.transformation() # MTransformationMatrix

#-------------------------------------------
# Part 2, get the euler values
# Get an MEulerRotation object
eulerRot = mTransformMtx.eulerRotation() # MEulerRotation
# note, we *don't* have to set the rot order here...

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (eulerRot.x, eulerRot.y, eulerRot.z)]
print angles, "MTransformationMatrix"
}}}
''Method B: Using {{{MMatrix}}}''
While this method works well, it doesn't track rotation values past 360:  We create a {{{MMatrix}}} object directly from the {{{worldMatrix}}} attr queried on our node.  We then convert that to a {{{MTransformationMatrix}}} object so we can extract the {{{MEulerRotation}}} object.  However, the {{{MMatrix}}} doesn't pass along the rotation order of the node, and has no knowledge of rotations past 360, so the resultant {{{MTransformationMatrix}}} won't know that stuff either.  To get accurate rotation values, we have to save the rotation order value ahead of time, and then apply it back before retrieving values from our {{{MEulreRotation}}} object.
{{{
#-------------------------------------------
# Part 1:  Get a MMatrix from an object for the sake of the example.
# You can use your own MMatrix if it already exists of course.

# Get the node's rotate order value:
rotOrder = mc.getAttr('%s.rotateOrder'%node)
# Get the world matrix as a list
matrixList = mc.getAttr('%s.worldMatrix'%node) # len(matrixList) = 16
# Create an empty MMatrix:
mMatrix = om.MMatrix() # MMatrix
# And populate the MMatrix object with the matrix list data:
om.MScriptUtil.createMatrixFromList(matrixList, mMatrix)

#-------------------------------------------
# Part 2, get the euler values
# Convert to MTransformationMatrix to extract rotations:
mTransformMtx = om.MTransformationMatrix(mMatrix)
# Get an MEulerRotation object
eulerRot = mTransformMtx.eulerRotation() # MEulerRotation
# Update rotate order to match original object, since the orig MMatrix has
# no knoweldge of it:
eulerRot.reorderIt(rotOrder)

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (eulerRot.x, eulerRot.y, eulerRot.z)]
print angles, "MMatrix"
}}}
Running the above code prints (and I reformatted the float precision issues, just so it's easier to compare to the above values):
{{{
[15, -45, 1000] MTransformationMatrix
[15, -45, -80] MMatrix
}}}
As you can see, the {{{MTransformationMatrix}}} stores the rotation values past 360 deg, while the {{{MMatrix}}} doesn't.
!Second Solution
When I came up with this solution I thought it was pretty slick.  But solution 3 above is even better since it skips the need to create the {{{MQuaternion}}} objects.
{{{
import math
import maya.cmds as mc
import maya.OpenMaya as om

# Define a node to pull a matrix from.  No reason you
# can't use your own matrix of course.
node = 'pCube1'
# Get this nodes rotate order:
rotOrder = mc.getAttr('%s.rotateOrder'%node)

# Get the world matrix as a list
matrixList = mc.getAttr('%s.worldMatrix'%node)
# create an MMatrix to hold this data:
mMatrix = om.MMatrix()
om.MScriptUtil.createMatrixFromList(matrixList, mMatrix)

# Convert MMatrix to MTransformationMatrix object:
mTransformationMatrix = om.MTransformationMatrix(mMatrix)

# Get rotation as MQuaternion:
mQuaternion = mTransformationMatrix.rotation()

# Convert to MEulerRotation:
mEulerRotation = mQuaternion.asEulerRotation()

# Update rotate order to match original object:
mEulerRotation.reorderIt(rotOrder)

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (mEulerRotation.x, 
                                            mEulerRotation.y, 
                                            mEulerRotation.z)]
print angles
# [-26.706627346943492, -79.553892761509957, -58.387916674342371]
}}}
Matrices store rotations as vectors, and because of this (and due to conversion to quaternions) they have no concept of rotations past 360 degrees.  So if you have a source rotation value of 400, this will convert it to 40 (400 - 360 = 40).

While reading the API docs, I saw that the {{{OpenMaya.MTransformationMatrix}}} has a {{{getRotation()}}} method.  I thought it would be better using this method  so I could skip the {{{MQuaternion}}} \ {{{MEulerRotation}}} conversion.  However, I wasn't ever able to get it to work.  Doing some research, I ran across [[this post|http://www.rtrowbridge.com/blog/2009/02/python-api-mtransformationmatrixgetrotation-bug/]] that confirms that it __is a bug__.
!First Solution
[[This post|http://forums.cgsociety.org/archive/index.php/t-617014.html]] gave me the knowledge of how to tap into Maya's API (via Python) to find a solution.
Below is the //first// solution I came up with:  It just uses {{{MMatrix}}} , no {{{MTransformationMatrix}}} is involved.  The problem was that if you //scaled// the node being queried, it would throw off the ~MQuaternion ahead of time.  The above solution fixes this issue.
{{{
# Python code

import math
import maya.cmds as mc
import maya.OpenMaya as om

# Define a node to pull a matrix from.  No reason you 
# can't use your own matrix of course.
node = 'pCube1'

# Get the world matrix as a list
matrixList = mc.getAttr('%s.worldMatrix'%node)

# Convert list to MMatrix object
mMatrix = om.MMatrix()
om.MScriptUtil.createMatrixFromList(matrixList, mMatrix)

# Convert matrix to quaternion (MQuaternion object)
quatRotate = om.MQuaternion()
quatRotate.assign(mMatrix)

# Pull Euler rotation values from quaternion (MEulerRotation object)
eulers = quatRotate.asEulerRotation()

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (eulers.x, eulers.y, eulers.z)]
print angles

# [-26.706627346943492, -79.553892761509957, -58.387916674342371]
}}}
Presume you have line (vector) defined by the points {{{vStart}}}, {{{vEnd}}}.  You have a point {{{somePoint}}} living off in space.  You want to find the closest point on the line to the point off in space.  As it turns out, this point defines a vector from {{{somePoint}}} to the line which is perpendicular to the line.
http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html
{{{
# Python code
from maya.OpenMaya import MVector

# point off in space
somePoint = MVector(5,7,0)

# start and end of our line:
vStart = MVector(0,0,0)
vEnd = MVector(10,10,0)

# Vector of our line from start to end
vStartEnd = vEnd - vStart
# Find our line length:
vStartEndLen = vStartEnd.length()
# Normalize our line vector length:
vStartEnd.normalize()
# Vector from the start of our line to the point in space:
vStartToPoint = somePoint - vStart

# * = dot product.  This is the distance along our vStartEnd
# where the new point will live.
projectLen = vStartToPoint * vStartEnd

# Find the new point position.  Here, * is multiply,
# since projectLen is a float, not a MVector.
newPoint = vStartEnd * projectLen + vStart

# Bonus:  Track if this point lies within the bounds of our line.  Picture a cylinder
# with infinite radius surrounding our line.  Does the point fall in that?
if projectLen < 0 or projectLen > vStartEndLen:
    print "Out Of Bounds:", newPoint.x, newPoint.y, newPoint.z
else:
    print "In Bounds:", newPoint.x, newPoint.y, newPoint.z
}}}
{{{
In Bounds: 6.0 6.0 0.0
}}}
For compound attrs like translate, rotate, scale (etc), find their child attrs:
{{{
print mc.attributeQuery("translate", node="null1", listChildren=True)
# [u'translateX', u'translateY', u'translateZ']
}}}
----
Also see:
* [[API: Find all child attributes under a compound attribute]]
You can use....
*The API class method {{{MFnNurbsCurve.closestPoint}}}
*The node [[nearestPointOnCurve|http://download.autodesk.com/global/docs/maya2012/en_us/Nodes/nearestPointOnCurve.html]]
*The node [[pointOnCurveInfo|http://download.autodesk.com/global/docs/maya2012/en_us/Nodes/pointOnCurveInfo.html]]
**This node can be created via the [[pointOnCurve|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/pointOnCurve.html]] command.
This is a nice little implementation in pure Python:
https://github.com/Vectorized/Python-KD-Tree
https://en.wikipedia.org/wiki/K-d_tree

Easy to use in Maya:
{{{
from random import randrange
import maya.api.OpenMaya as om2
import kdtree

def distBetween(ptA, ptB):
    # Return the distance between two MPoint instances.
    return ptA.distanceTo(ptB)

numPoints = 10000
points = [om2.MPoint(randrange(-100,100), randrange(-100,100), randrange(-100,100)) for i in range(numPoints)]
testPoint = om2.MPoint(0,0,0)
kd_tree = kdtree.make_kd_tree(points, dim=3)

# Get the closest neighbor:
nearest = kdtree.get_nearest(kd_tree, testPoint, 3, distBetween, return_distances=True)
print nearest
# [5.477225575051661, maya.api.OpenMaya.MPoint(-2, 5, 1, 1)]

# Get the closest neighbors:
numNeighbors = 3
nearNeighbors = kdtree.get_knn(kd_tree, testPoint, k=numNeighbors, dim=3, dist_func=distBetween)
for nn in nearNeighbors:
    print nn
# (5.477225575051661, maya.api.OpenMaya.MPoint(-2, 5, 1, 1))
# (8.06225774829855, maya.api.OpenMaya.MPoint(4, 0, 7, 1))
# (8.774964387392123, maya.api.OpenMaya.MPoint(8, -2, -3, 1))
}}}
I recently ran into this problem:  I had code written that would snap one object to another via their matrices quite successfully.  I was using this in a rigging 'snap' tool.  Then, the design of the rig changed, and there became an offset needed between the source and target nodes (in my case some rotational values).  Since matrices are such nice things to use for transformational snapping, I wanted to keep using this method, but needed a way to calculate the difference of two matrices, store that value, then later re-apply it after the snap to make my target have the same relative transformations to the source based on their initial state.  This is hard to describe, so here's the proof of concept example I came up with:
*Two nodes (cubes) in Maya.  
*Transform them both to some new location in space (both in the same location).  
*Now apply an //additional// transformation to the target node... maybe a rotational offset?  
Our goal is to allow us to transform the target to any new value, and have the source match the new transformation with relative offset previously assigned.  Meaning, if you'd rotated the source cube 90 deg's off from the target, wherever we transform the target, we want the source to match, rotated the same 90 deg's off.

----
This is all implemented through Python, and the code for the imported {{{matrixutils}}} module can be found in my subject [[here|Working with matrices with Python]].  It accesses the API's (~OpenMaya) {{{MMatrix}}} object and provides wrapper code for using it in Python.
{{{
# Python code
import maya.cmds as mc
import matrixutils as matrix

# Setup names for our source node, and our target node.
source = 'src'
target = 'dest'
The ''source'' node is the one being later trasformed.  The ''target'' node is what the difference matrix is applied relative to, to move the source.
}}}
As described above, transform both nodes to some location in space.  Then apply an additional transformation to the source.

Find the worldspace inverse matrix of our target object, and the worldspace matrix of our source:
{{{
srcWorldMatrix= matrix.getMMatrix(source, 'worldMatrix')
targetWorldInverseMatrix= matrix.getMMatrix(target, 'worldInverseMatrix')
}}}
Create the difference matrix.  This is done by multiplying the source world matrix by the target inverse world matrix ... sort of like subtracting the target matrix from the source to find the difference.
Maya matrix application order is <- right to left -
{{{
differenceMatrix = sourceWorldMatrix * targetWorldInverseMatrix
}}}
Set target to the difference matrix, just for visualization:  Should snap it to (or near, depending on the initial difference between the nodes) the origin, showing the difference in trans, rot, scale, and shear vals that were stored:
{{{
matrix.matrixXform(source, differenceMatrix, 'worldSpace')
}}}
Now move the source node somewhere new in the Maya scene.
Execute this code to find the //new// source worldspace matrix:
{{{
newTargetWorldMatrix = matrix.getMMatrix(target, 'worldMatrix')
}}}
Finally move our source to the new location, with the {{{differenceMatrix}}} applied.
{{{
offsetMatrix = differenceMatrix * newTargetWorldMatrix 
matrix.matrixXform(source, offsetMatrix, 'worldSpace')
}}}
It should snap to the new location of the target node, but have the additional difference matrix applied, putting it into the same relative state that was originally captured.
I'd been looking for a LONG time, trying to find an IDE for Python that mirrored //one// important feature from Maya's Script Editor:  ''Evaluate Selection'', which is the ability to @@highlight blocks of code@@, and execute just that code block interactively.  This is also known as [[REPL|http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop]].

//Maya's// implementation of Python in the Script Editor can do this (since version 8.5), but what if you want to author Python //outside// of Maya, but still have this functionality?

After asking a lot of people, and doing a lot of searching, I finally found Wing:
http://www.wingware.com/
''Wings docs on integrating with Maya'' [[here|http://www.wingware.com/doc/howtos/maya]] (which actually links back to this wiki :-) )
It has three versions:
*Wing IDE 101 (free, but //doesn't// have 'Evaluate Selection')
**Update:  It appears you can hack it:  I saw this post in a forum, from Wingware itself:
<<<
It looks like the keyboard config pref is omitted in 101.  You could
hack it by editing the keyboard.normal file in the Wing installation
and adding:
{{{'Ctrl-F5': 'evaluate-file-in-shell'}}}
Or to evaluate the current selection:
{{{'Ctrl-Shift-F5': 'evaluate-sel-in-shell'}}}
You'll need to restart Wing to read those and you can of course alter
the bindings.  Also, if you're using one of the other keyboard
personalities you should edit the associated keyboard.* file instead.
<<<
*Wing IDE Personal $
*Wing IDE Professional ($ - Lets you access the Wing API via Python, to write your own plugins for the tool + other features)
----
Compare version features:
http://www.wingware.com/wingide/features
----
@@Interaction With Maya@@:
*I've come up with three different ways that allow Wing to interact with Maya and vice-versa (Maya Script Editor replacement, Remote debugging Maya data in Wing, Run Maya in Wings Python shell).  See that subject here:
**[[Interaction between Wing and Maya]]
----
Notes:
*Wing itself runs a different version of Python than you may have installed. This is only really important if you get the "Pro" version, and start making your own 'plugin scripts':  The plugins you author will be running Wings version of Python, not your installed version of Python.  Furthermore, the Wing distribution isn't the full install of Python, so not all modules are available.  This isn't a huge deal, but it's important to be aware of if your plugins don't work.  For example, I had one that called to the {{{webbrowser}}} module:  That module isn't distributed with Wing.  Luckily, they have their own function ({{{wingapi.gApplication.OpenURL(url)}}}) that could be used in its place.
Pretend you have 1000 separate leaf mesh.  They're all UV'd the same, but have been polycombined.  You need to split them into separate mesh, and have their pivots placed at the root of the leaf (which is a consistent UV point).  How to do?  

After separation, select all the mesh and open the UV Texture Editor.  Drag-select over the UV you want to snap the pivot too:  Since all the mesh have the same uv's, you should now have 1000 uv's picked.  Run the below code:   It will find the vert for each UV, the mesh for the vert, then snap its transform-level pivot to that UV's position.
{{{
import pymel.core as pm

def snapPivToSelUv():
    sel = pm.ls(selection=True)
    if any([item for item in sel if not isinstance(item, pm.MeshUV)]):
        pm.displayError("Only UVs can be selected")
        return
    pm.undoInfo(openChunk=True)
    pm.waitCursor(state=True)
    try:
        for uv in sel:
            # Convert from UV to vert.
            # polyListComponentConversion returns a strings, rather than PyNodes
            vert = pm.PyNode(pm.polyListComponentConversion(uv, toVertex=True)[0])
            pos = pm.pointPosition(vert, world=True)
            vert.node().getParent().setPivots(pos, worldSpace=True)
        pm.displayInfo("Done: Snapped pivots to UVs on %s nodes!"%len(sel))
    finally:
        pm.undoInfo(closeChunk=True)
        pm.waitCursor(state=False)

snapPivToSelUv()
}}}
Presuming you know the name of your camera, and the name of the panel:
{{{
modelPanel -e -cam $camera $panel;
}}}
And if you need to know the name of the currently active panel:
{{{
string  $panel = `getPanel -withFocus`;
}}}
or:
{{{
string  $panel = `playblast -activeEditor`;
}}}
{{{playblast}}} has the advantage of always returning back a {{{modelPanel}}}: The {{{getPanel}}} command can return back other editor types that may be active.
Also see:
*[[How do I query the name of the camera in the active panel?]]
I needed to get a list of faces sorted by their surface area.  Came up with a nifty one-line solution (not counting the function definition) using ~PyMel:
{{{
from operator import itemgetter
import pymel.core as pm

def getSortedFaceArea(mesh):
    return sorted( [[face, face.getArea(space='world')] for face in mesh.f], key=itemgetter(1) )
}}}
Sorts from smallest to largest:
{{{
mesh = pm.ls(selection=True)
sortFace = getSortedFaceArea(mesh[0])
for data in sortFace:
    print data

[MeshFace(u'myMeshShape.f[280]'), 0.21133190393447876]
[MeshFace(u'myMeshShape.f[293]'), 0.21990084648132324]
[MeshFace(u'myMeshShape.f[456]'), 0.2264084368944168]
[MeshFace(u'myMeshShape.f[611]'), 0.23773804306983948]
# etc...
}}}
Also see: [[Compiling Python Libraries for Maya]]

Had to do some hoop-jumping to get "[[pywin32|http://sourceforge.net/projects/pywin32/]]" working in Maya.  Here's what started the whole shebang:
*Wanted to have Maya control Photoshop.  You can do this via the aforementioned package.
*I'm using Maya2010 64-bit (Win7), with the 64-bit cut of Python 2.6.
*My system-level Python is 32-bit, 2.6.
*When you install {{{pywin32}}}, it installs into the root Python {{{\site-packages}}} folder, which is entirely different from the one Maya's cut of Python uses.
*Furthermore, it doesn't install under a single package dir:  It creates a lot of dirs and loose files, pretty messy.
These were the steps I went through to get it working in Maya:
#Download the latest cut:  This is the 64-bit cut of the package that will match up with Maya's 64-bit cut of Python.
##https://github.com/mhammond/pywin32/releases
#Presuming you already have other pre-existing site-packages installed, rename Python's default {{{C:\Python26\Lib\site-packages}}} dir to {{{C:\Python26\Lib\site-packages-old}}} before the install:  When then install runs, it will create a new {{{\site_package}}} dir.
#Run the downloaded package executable.  When complete, the new {{{C:\Python26\Lib\site-packages}}} dir will be populated with the new (messy) package contents.
#Copy all of that new stuff to Maya's site-package dir here: {{{C:\Program Files\Autodesk\Maya20XX\Python\Lib\site-packages}}}
#Delete the new {{{C:\Python26\Lib\site-packages}}} dir.
#Rename the old dir {{{C:\Python26\Lib\site-packages-old}}} back to {{{C:\Python26\Lib\site-packages}}}.
#Launch Maya, and in the script editor, execute:
{{{
# Need to import in order
import pythoncom
import win32com.client
}}}
It should work without a hitch.
And continuing our Photoshop example, launch Photoshop from Maya:
{{{
photoshop = win32com.client.Dispatch("Photoshop.Application")  
}}}
Blam.
----
Other notes
* If you get an error like this:
{{{
# ImportError: No system module 'pywintypes' (pywintypes27.dll) # 
}}}
It means you need to add this directory to your Windows PATH env var:
{{{
c:\path\to\pywin32_system32
}}}
----
*It appears that you //must// put the {{{pywin32}}} package contents in Maya's {{{/site-packages}}} dir:  I tried other package paths I had defined, but they didn't work.  This package comes with a {{{pywin32.pth}}} file.  According to the docs for the Python [[site|http://docs.python.org/library/site.html]] module, {{{.pth}}} files are only recognized when they live in one of four special directories.
----
Nice tutorial on how to compile ~PyWin32 here:
http://around-the-corner.typepad.com/adn/2014/10/building-pywin32-for-motionbuilder-2015.html
----
Also see:
*[[Control Photoshop from Maya]]
{{{basename}}}
{{{dirname}}}
Say you have a selection of verts, and it's important you process the odd and even numbered verts differently.  How can you generate those two lists?

{{{
// get our vert list:
string $sel[] = `ls -fl -sl`;

// make our buckets
string $odd[];  clear $odd;
string $even[];  clear $even;

for($i=0;$i<size($sel);$i++)  // print $sel[$i]
	{
// get just the END number, inside of brackets:
	string $endNum = `match "\\[[0-9]+\\]$" $sel[$i]`;

// just num without brackets:
	int $justNum = `match "[0-9]+" $endNum`;

// if we divide in half, do we have a remainder?  Magic with the modulus operator:
	float $remainder = $justNum%2;
	if($remainder)
		$odd[size($odd)] = $sel[$i];
	else
		$even[size($even)] = $sel[$i];
	}

print $even;
print $odd;
}}}
would result something like:
{{{
...
pSphere1.vtx[240]
pSphere1.vtx[260]

pSphere1.vtx[143]
pSphere1.vtx[163]
...
}}}


Thanks to Eric Vignola
*Multiply the unit-vector by the distance, and add that to your original point:
{{{
vector $point = <<1, 1, 0>>;
vector $vector = <<1, 1, 0>>;
float $distance = 2;
vector $result = ($point + (unit($vector)* $distance));
// Result: <<2.414214, 2.414214, 0>>  // 
}}}
{{{filterExpand}}}
*Example, given a selection of polygon and nurbs surfaces, generate a list of only the polygon surfaces:
{{{
filterExpand -sm 12;
}}}
{{{
string $inf[] = `listConnections "skinCluster1.matrix"`;
}}}
Check out the replaceNode.mel script. Located here: 
{{{
.../MayaX.X/scripts/others/replaceNode.mel
}}}
There's also the {{{nodeCast}}} command.
''The Mel Wiki Guest Book''
Since this wiki isn't opening to public editing (got too much spam), please email me your guest book entry, and I'll post it!  -- {{{warpcat}}} (at) {{{sbcglobal}}} (dot) {{{net}}} --
Please use this format in your mail:
{{{
Name\Handle:  Joe Bobson
Email (optional): joebob@bob.com
Comment:  Wow, what a great wiki!
}}}
----
----
----
Date: Feb 08, 2010
Name: Zhi Min Chen
Comment:  Great website.  I use it on a regular basis to help me on most my projects at work.  Your wiki's question format is very well thought out and it makes finding answers extremely quick.  Keep up the great work!
----
Date: Aug 15, 2008
Name: Ben Brac
Email: brac@sonic.net
Comment: Awesome wiki ~WarpCat! I use it all the time when the usual documentation leaves much to be desired. Keep the updates coming!
----
Date:  Aug 06, 2008
Name:  Bob Aunum
"Thank you sincerely.  The subject says it all. since i decided to teach myself mel,  i've been looking for a good web resource, but i really didn't expect to find anything as elegantly functional as your wiki.  It's great. Really."
----
Date:  May 19th, 2008
Name:  Fridi Kühn
I am a student from Germany and have recently started helping a games developer to automate their process of animating.  Since I am a beginner to MEL I wanted to state that your mel wiki helped me alot to overcome the mysteries of that language.  I just wanted to thank you for your website and hope you will keep it alive! Please know that I will frequently return and see what's new...
----
<<timeline>>
Had a really weird bug where whenever the hotbox was shown, it will take a 'screen capture' of the previous window inside it.

Come to find out, on Windows 7, if you have it's theme set to 'classic mode', it causes this problem. If you set it back to 'Areo', it seems to resolve.
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
Ran across an animators file where their ~animCurves were getting templated, thus making them un-editable.  I wrote a little script to unlock them:
{{{
import maya.cmds as mc
nodeAttrs = mc.ls("*.keyTimeValue")
for na in nodeAttrs:
    mc.setAttr("%s[0]"%na, lock=False)
}}}
But it didn't explain why this was happening.

As it turns out, there's an option in the Anim Layers -> Options -> 'Template Curves Of Locked Layers' that is on by default, that causes this.  Neither of us realized this, but turning that off fixed it.
{{{
lockNode -lock
}}}
Note:  This is a different kind of "lock"than a "reference" node, which is read-only, or certain system nodes (like lightLinkers) that are read-only.
You can also make connected attributes 'non-deletable' as well via:
{{{
lockNode -lockUnpublished;
}}}
If you don't do that, and run a {{{delete -ch}}} on a node with inputs, it's history (both connections and node) can be deleted as well.
Requested\suggestion from Matti Grüner:

You can use the {{{addRecentFile.mel}}} script that comes with Maya.  Lives here by default:
{{{
C:\Program Files\Autodesk\Maya<VERSION>\scripts\others\addRecentFile.mel
}}}
Easy to call to via mel:
{{{
addRecentFile(string $file, "mayaAscii"); // or "mayaBinary"
}}}
If that succeeded, and you go to {{{File -> Recent Files ->  }}}You should see the newly added entry.
----
If you want to do this in Python:
{{{
import maya.cmds as mc
import maya.mel as mm

# Open the file!
openMe = r'c:\some\awesome\file.ma'
mc.file(openMe, open=True, force=True)

# Then update the menu!
fileType = ''
if openMe.endswith('.ma'):
    fileType = 'mayaAscii'
elif openMe.endswith('.mb'):
    fileType = 'mayaBinary'
# Must convert slashes before calling to the mel:
mm.eval('addRecentFile("%s", "%s")'%(openMe.replace('\\', '/'), fileType))
}}}
Ultimately, that menu item calls to something like this:
{{{
mc.bakePartialHistory("MyNodeName", prePostDeformers=True )
}}}
You can lock nodes and attrs, how do you lock an animCurve so it  can't be modified?
{{{
mc.setAttr('myCurveName.keyTimeValue', lock=True)
}}}
Insert on a PC appears to be 'function+left arrow' on a Mac.  :-S
If the Outliner is active, you can use the //mel procedure//:
{{{
fitPanel -selected;
}}}
Which lives here:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\fitPanel.mel
}}}
I knew this worked for cameras, but didn't know it worked in an {{{outlinerPanel}}} \ {{{outlinerEditor}}} as well.
It appears this is the default '{{{f}}}' hotkey... went for many years without knowing this...
----
If the Outliner isn't active, and you want to force this via code through say a custom hotkey:
{{{
import maya.cmds as mc
# Name of the default Outliner:
outliner = "outlinerPanel1"
mc.setFocus(outliner)
mc.outlinerEditor(outliner, edit=True, showSelected=True)
}}}
{{{setFocus}}} isn't needed, but it doesn't hurt either.
In Python, this concept is handled by the {{{reload}}} command.  The first time you {{{import}}} a module (Python's equivalent of a mel script), its loaded in memory.  No amount of re-importing will change it in memory.  If you make changes to your module //after// its imported, you need to use {{{reload}}} to update it in memory:
{{{
import myModule

# do work

reload(myModule)
}}}
(Suggested addition by Chris Mills)
Maya's 'Freeze Transforms' menu option calls to the {{{makeIdentity}}} command.  What this effectively does (for translation) is take the {{{translate}}} values of the {{{transform}}} node, and puts them in the {{{rotatePivot}}} attr, and then zeros out the {{{translate}}} channels.  So the node doesn't move in space, but it's {{{translate}}} values are all zeroed out.

How can you get those {{{translate}}} values back?  How can you 'unfreeze' it.  The below code will handle that for you.  Note, this only works on {{{transform}}} nodes, not {{{joint}}}, since joints always store their translation relative to their parent.

{{{
import maya.cmds as mc

node = "myMeshName"

# See if it has child mesh shape node:
hasMesh = mc.listRelatives(node, shapes=True, type='mesh')
if hasMesh:
    # If so. store the worldspace position of all the verts:
    vertPositions = [mc.pointPosition(vert, world=True) for vert in mc.ls('%s.vtx[*]'%node, flatten=True)]

# Reset the rotate pivot, and move the node back to where it was.
wsPos = mc.pointPosition('%s.rotatePivot'%node, world=True)
mc.setAttr('%s.rotatePivot'%node, 0,0,0)
mc.move(wsPos[0], wsPos[1], wsPos[2], node, absolute=True, worldSpace=True, rotatePivotRelative=False)

if hasMesh:
    # Update all the vert positions to be where they were before
    for i,vertPos in enumerate(vertPositions):
        mc.move(vertPos[0], vertPos[1], vertPos[2], '%s.vtx[%s]'%(node, i),  absolute=True, worldSpace=True, rotatePivotRelative=False)

# Unfrozen!
}}}
----
Also see:
* [[What happens behind the scenes when you 'freeze transforms'?]]
* [[How can I test if a transform is frozen or not?]]
>See [[How can I find all instanced nodes in the scene?]] for an updated version.
----
Update 2:
Some ~PyMel to uninstance all the mesh in the scene.  Note, this will destroy any children of any of the mesh.
{{{
import pymel.core as pm

mesh = pm.ls(type='mesh')
for m in mesh:
    instances = m.getInstances()
    if len(instances) < 2:
        continue
    for instMesh in instances:
        dupeT = pm.duplicate(instMesh)[0]
        dupeM = dupeT.getChildren(type='mesh')[0]
        parentT = instMesh.firstParent()
        pm.delete(parentT)
        unique = parentT.nextUniqueName()
        dupeT.rename(parentT)
        unique = dupeM.nextUniqueName()
        dupeM.rename(unique)
}}}
----
Update 1:
As of Maya 2010, there is a Run Time Command called:
{{{
ConvertInstanceToObject
}}}
...that will do this.  I'm not too sure when this was introduced.  In in turn calls to the global procedure: 
{{{
convertInstanceToObject()
}}}
...which is found in the mel script: 
{{{
C:\Program Files\Autodesk\Maya2010\scripts\others\convertInstanceToObject.mel
}}}
It should be noted that running this is destructive:  If the instance has any children with any kind of connections, those connections will be lost as part of the procs duplication\deletion operation.  The code I have below works around this issue, in a non-destructive way:
----
Maya seems to provide no easy way to 'uninstance' a shape node, once instanced.  This has caused a lot of frustration.  The common solution I've seen is to simply duplicate the instance, delete the old one, and rename the duplicate.  This is fine, unless there is something parented to the instanced node.  The below code jumps through a lot of hoops, but it does so as to not delete the original transform the instance shape node is associated with.  In a nutshell:
*Find the shape node
*Make a dummy shape (mesh) and associated transform
*Parent the dummy shape to the same parental transform as the instance.  This is silly, and important, so we can get rid of the instance, and not have Maya also delete the transform.
*Parent the instance shape to the dummy transform
*Duplicate the dummy transform (and thus duplicating the instance shape)
*Parent the new uninstanced shape back to original transform
*Delete all the dummy stuff.
It's still a lot more steps than it should be...
{{{
// To start:  Select the instanced objects
// Get a list of all of our objects
string $sel[] = `ls -l -sl`;
for($i=0;$i<size($sel);$i++)
	{
// get a list of the current children of our selection:	
	string $kids[] = `listRelatives -f -s $sel[$i]`;

	for($j=0;$j<size($kids);$j++)
		{	
	// create a dummy mesh: a dummy shape node, and find its parent name		
		string $dum = `createNode mesh -name instanceDummy`;
		string $dumPare[] = `listRelatives -p $dum`;		
	// parent that dummy shape to our current object.  This lets us later
	// unparent the instance, and not have the object delete itself.
		parent -s -r $dum $sel[$i];
		
	// Now parent the instance under our dummy node, and delete it.	
		string $reParent[] = `parent -s -r $kids[$j] $dumPare[0]`;  // print $reParent[0]
		delete $dum;
		
	// duplicate the instance shape node (that was just reparented a moment ago),
	// and then parent the new, uninstaned shape, back to the original parent.
		string $newShapeParent[] = `duplicate -rc $reParent[0]`;
		string $newUninstancedShape[] = `listRelatives -s -f $newShapeParent[0]`;
		parent -s -r $newUninstancedShape[0] $sel[$i];

	// finally, clean up by deleting the dummy parent node, and the transform left over
	// from the duplication of our shape node
		delete $dumPare;
		delete $newShapeParent;
		}		
	}
}}}
In the Maya Menu, you can execute:
*Modify -> Evaluate Nodes -> Evaluate All \ Ignore All
To enable\disable certain types of node evaluations.  Those node types include:
*constraints, expressions, fluids, globalstitch, iksolvers, particles, rigidbodies, & snapshots.
But what code does that UI actually call to?
The UI calls to two maya {{{runTimeCommand}}}s:
{{{
EnableAll;
DisableAll;
}}}
{{{runTimeCommand}}}s are simply small wrappers for other bits of mel.  Those {{{runTimeCommand}}}s actually execute:
{{{
// DisableAll:
doEnableNodeItems false all;
// EnableAll:
doEnableNodeItems true all;
}}}
{{{doEnableNodeItems}}} is a "startup" mel script found below.  That means (to my understanding) that it is executed every time Maya starts up.
*{{{../Maya7.0/scripts/startup/doEnableNodeItems.mel}}}
But this mel script is mainly just a wrapper for this one:
*{{{../MayaX.X/scripts/others/setState.mel}}}
So while you could call to any of the mel listed above to do this, the cleanest way is to call to {{{setState}}}. It's arguements are:
{{{
setState(string $type, int $state);
}}}
The acceptable $types are (as listed above):
*{{{"all"}}}
*{{{"constraint"}}}
*{{{"expression"}}}
*{{{"fluid"}}}
*{{{"globalstitch"}}}
*{{{"iksolver"}}}
*{{{"particle"}}}
*{{{"rigidbody"}}} 
*{{{"snapshot"}}}
Note that I have found that if the code tries to set the state on something that is "locked" it will fail.  This will also fail at Maya startup, which is bad news.  For example, one time I had locked a particle shape node's ".isDynamic" attribute.  Whenever I'd later open that rig file, none of the constraints would evaluate, since the code to enable their evaulation at startup would crash.  Crazy.
If it's not coming up in your Playblast options, this may be the answer:
https://knowledge.autodesk.com/support/maya/troubleshooting/caas/sfdcarticles/sfdcarticles/Maya-2015-and-2016-with-qt-playblast-on-Windows-absent.html 
Basically, add this:
{{{
C:\Program Files (x86)\QuickTime\QTSystem\ 
}}}
To you windows {{{PATH}}} system variable, presuming you have that dir to begin with.
----
Also see:
* [[How can I playblast using h.264?]]
There is a handy command for this:
{{{toolPropertyWindow}}}
----
Also see:
*[[I want to add a UI to the tool settings window, how do I do that?]]
Simple tool to add Python code as a button in the active shelf.
{{{
import pymel.core as pm

def addPyToShelf(label, pyCode, annotation=""):
    """
    Add the provided Python code as a shelf button, to the current shelf.

    Parameters:
    label : The 5-character label to display on the shelf icon.
    pyCode : string : The code to add to the shelf button.
    annotation : string : Default empty string : Any additional annotation to use
        as a tooltip popup.
    """
    gShelfTopLevel = pm.language.melGlobals['gShelfTopLevel']
    if pm.tabLayout(gShelfTopLevel, query=True, exists=True):
        currentShelf = pm.tabLayout(gShelfTopLevel, query=True, selectTab=True)
        pm.setParent(currentShelf)
        sBut = pm.shelfButton(label=label,
                              imageOverlayLabel=label,
                              command=pyCode,
                              image1='commandButton.png',
                              sourceType='python',
                              ann=annotation,
                              style=pm.shelfLayout(currentShelf, query=True, style=True),
                              width=pm.shelfLayout(currentShelf, query=True, width=True),
                              height=pm.shelfLayout(currentShelf, query=True, height=True))
    else:
        pm.displayWarning("The shelf doesn't appear to exist")
}}}
{{{
addPyToShelf("test", "import pymel.core as pm\nprint pm.ls(type='transform')",
             annotation="Print all the transforms in the scene")
}}}
----
Also see:
*[[How can I make a shelf button via mel?]]
Maya 2011(?) introduced the {{{fileDialog2}}} command that supplants the {{{fileBrowserDialog}}}.  It has a option on the left for 'folder bookmarks', and for the longest time I couldn't figure out how to update that field.

As it turns out, you just drag-and-drop a folder into that field to make a bookmark.  That's it.
{{{
# Python code
import maya.cmds as mc

node = 'myNode'
colorAttr = 'color'
defaultColor = [.9,.9,.9]

mc.addAttr(node, longName=colorAttr, attributeType='float3', usedAsColor=True)
mc.addAttr(node, longName='%sR'%colorAttr, attributeType='float',
           parent=colorAttr, defaultValue=defaultColor[0])
mc.addAttr(node, longName='%sG'%colorAttr, attributeType='float',
           parent=colorAttr, defaultValue=defaultColor[1])
mc.addAttr(node, longName='%sB'%colorAttr, attributeType='float',
           parent=colorAttr, defaultValue=defaultColor[2])
}}}
Some control types, like {{{iconTextButton}}} and {{{shelfButton}}} have a {{{commandRepeatable}}} arg that can be set True.
{{{menuItem}}} has a {{{enableCommandRepeat}}}.
Strange that the {{{button}}} command doesn't support this? :S
----
Also, Maya has a {{{repeatLast}}} command, that appears to be undocumented (as of Maya 2016).  Here's the interactive help:
{{{
help repeatLast;
// Result: 

Synopsis: repeatLast [flags]
Flags:
   -e -edit
   -q -query
  -ac -addCommand            String
 -acl -addCommandLabel       String
  -cl -commandList           Int
 -cnl -commandNameList       Int
  -hl -historyLimit          Int
   -i -item                  Int
 -nhi -numberOfHistoryItems 

Command Type: Command
 // 
}}}
You can pass a string to the {{{addComand}}} flag.  //However// it only parses ''mel'', not Python.  To executePython, you need to jump through some hoops:
{{{
import pymel.core as pm

def doStuff():
    print "doing stuff"
    
pm.repeatLast(addCommand="python(\"doStuff()\")", addCommandLabel="doStuff")
}}}
{{{
import maya.cmds as mc
mc.text(label='<a href="http://lmgtfy.com/?q=HTML+link">hey click here.</a>', hyperlink=True)
}}}
Thanks to this post:
https://groups.google.com/forum/?fromgroups#!topic/python_inside_maya/BvwoSWBnu4E
{{{
mayaWin = mm.eval("string $__convert__ = $gMainWindow;")
mc.setParent(mayaWin)
mc.menu(label="My Main Menu!")
}}}
In this example, I'll add a multi attr of type 'message' to a {{{null1}}} node, and then start connecting the message attrs of other nodes to it:
{{{
# Python code
import maya.cmds as mc

mc.addAttr('null1', longName='myMulti',  attributeType='message', multi=True )
mc.connectAttr('object1.message', 'null1.myMulti[0]')
mc.connectAttr('object1.message', 'null1.myMulti[1]')
}}}
To get a list of which attr indices are currently in use, and their values:
{{{
# indices are in the form of ["myMulti[0]", "myMulti[1]", etc...]
indices = mc.listAttr('null1.myMulti', multi=True)
for i in indices:
    print "null1."+i, " --> ", mc.listConnections('null1.'+i)[0]
}}}
{{{
null1.myMulti[0]  -->  object1
null1.myMulti[1]  -->  object2
}}}
Notice how you use {{{listConnections}}}, not {{{getAttr}}} to query {{{message}}} attributes?
----
Also see:
*[[How can I get a list of all the child multi-attrs, of a given attr name?]]
First, you need to get the existing names:
{{{
string $enumAttrs = `addAttr -q -enumName "myNode.myEnumAttr"`;
"enumA:enumB:enumC"
}}}
Then you can append your new attr to those names, and add:
{{{
string $newEnumName = "enumD";
addAttr -e -enumName ( $enumAttrs + ":" + $newEnumName)  "myNode.myEnumAttr";
}}}
----
Same thing in Python:
{{{
import maya.cmds as mc
enumAttrs = mc.addAttr("myNode.myEnumAttr", query=True, enumName=True)

newEnumName = "enumD"
mc.addAttr("myNode.myEnumAttr", edit=True, enumName='%s:%s'%(enumAttrs,newEnumName))
}}}
----
Also see:
* [[PyMel : enum attr access]]
Adding a new index to a {{{layeredTexture}}} node takes a lot of work: Multiple nodes are created and connected together.  I found the mel global proc {{{createRenderNodeCB}}} living in the {{{createRenderNode.mel}}} script, that actually does all the heaving lifting for you.  You just need to pass in the {{{layeredTexture}}} node to update, and which index you're adding to.
You'll note in the string formatting that a {{{'%node'}}} is being passed in:  That's actually part of the //mel// syntax, but since it includes a percent sight, //Python// thinks its string formatting, if left in the main string.  So, we actually use Python's string formatting to add it in, to avoid the error.  Wacky.

If you check the print in the Script Editor after you execute this, you'll see all the work it did.
If you want it to add a 2d texture projected into 3d space, check this out:  [[How can I project a 2d texture in 3d space]] : And run that code first.
{{{
import pymel.core as pm

layeredTex = "layeredTexture1"
index = 0
mel = 'source createRenderNode; createRenderNodeCB -as2DTexture "" file "defaultNavigation -force true -connectToExisting -source %s -destination %s.inputs[%s].color; ";'%('%node', layeredTex, index)
pm.mel.eval(mel)
}}}
Presuming you have a head mesh called {{{head}}}, that already has a blendshape node input called {{{blendShape1}}}, add a new mesh called {{{headShape3}}} as the next target in the list:
{{{
import pymel.core as pm

bsHeadMesh = pm.PyNode("head")
bsNode = pm.PyNode("blendShape1")
newTargMesh = pm.PyNode("headShape3")
weightCount = pm.blendShape(bsNode, query=True, weightCount=True)

pm.blendShape(bsNode, edit=True, target=[bsHeadMesh, weightCount, newTargMesh, 1])
}}}
The {{{weightCount}}} is the number of current targets, which also gives you the 0-based index to add the next new target.  The trailing {{{1}}} is how much influence should be used for the new target.  Shoud probably always be {{{1}}}.

Here's a tool that will add a new blendshape target mesh based on selection:
{{{
import pymel.core as pm

def addBsTarget():
    """
    Select two mesh in order: A new target mesh, and the mesh with blendshape to add it to.
    """
    sel = pm.ls(selection=True)
    if not len(sel) == 2:
        pm.displayError("Please select exactly two mesh, the target, and the base head.")
        return
    newTargetMesh = sel[0]
    bsMeshNode = sel[1]
    bsMeshShape = pm.listRelatives(bsMeshNode, shapes=True, type='mesh')[0]

    bsNode = pm.listConnections(bsMeshShape, source=True, destination=True, type='blendShape')
    if not bsNode:
        pm.displayError("Could find no blendShape node on mesh: %s"%sel[1])
        return

    weightCount = pm.blendShape(bsNode, query=True, weightCount=True)
    pm.blendShape(bsNode, edit=True, target=[bsMeshNode, weightCount, newTargetMesh, 1])
    pm.displayInfo("Added new blendshape target: %s"%sel[0])
}}}
http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/blendShape.html
{{{scriptedPanel}}}s seem like magical things:  Popping between UI's, docking in the UI and floating on top.  How can you easily add one to your own UI?
If they're already authored by Maya, it's a simple act of finding the name, then adding it to your own UI with the {{{scriptedPanel}}} command.  Below, I create a window that has both a graph editor and the dope sheet, with a time-slider below them.  And, it's actually really easy.  

How can you find the names of the existing scriptedPanels?  In the Script Editor, turn on "echo all commands".  Then pop off a window you want to use in your own ui.  It should print a bunch of stuff, including a line like this:
{{{
addDopeSheetPanel dopeSheetPanel1;
}}}
In this case, "{{{dopeSheetPanel1}}}" is the name of the scriptedPanel we care about, and can now embed into our own UI:
{{{
# Python code:
import maya.cmds as mc

class App(object):
    """
    Create the Maya UI object.
    """
    def __init__(self):
        self.name = "scriptedPanelTest"
        self.title = "scriptedPanel test"

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

        mc.window(self.name, title=self.title, resizeToFitChildren=True)
        form = mc.formLayout()
        self.paneLayout = mc.paneLayout( configuration='horizontal2' )
        mc.scriptedPanel( 'graphEditor1', e=True, parent=self.paneLayout)
        mc.scriptedPanel( 'dopeSheetPanel1', e=True, parent=self.paneLayout)
        mc.setParent(form)
        self.timePort = mc.timePort()
        mc.formLayout(form, edit=True, attachForm=[(self.paneLayout, "top", 1),
                                                   (self.paneLayout, "left", 1),
                                                   (self.paneLayout, "right", 1),
                                                   (self.timePort, "left", 1),
                                                   (self.timePort, "right", 1),
                                                   (self.timePort, "bottom", 1)],
                      attachControl=[(self.paneLayout, "bottom", 0, self.timePort)])

        mc.showWindow()

App()
}}}
The core command is the [[editorTemplate|https://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/editorTemplate.html]]
Generally this is called to when making a some new plugin based {{{MPxNode}}} subclass.
----
One way to support a custom node is to make a custom {{{AE<nodeType>Template.mel}}} script that lives in your {{{MAYA_SCRIPT_PATH}}}.  You can find an example of that here, for the custom {{{transCircle}}} node:
{{{
C:\Program Files\Autodesk\Maya2016\devkit\plug-ins\transCircleNode\AEtransCircleTemplate.mel
}}}
----
Optionally, if authoring a scripted plugin, here is an example setup I've seen done that embeds this data directly into the authoring class.  Only the necessary code has been included.
{{{
import maya.OpenMaya as om
import maya.OpenMayaMPx as ompx

class MyCustomNode(ompx.MPxNode):

    kPluginNodeName = "myCustomNode"

    # Bunch of code...

    @classmethod
    def nodeInitializer( cls ):
        # Bunch of code...

        # Then add the template
        cls.init_AETemplate()

    @classmethod
    def init_AETemplate( cls  ):
        melCmd= '''
  global proc AE%sTemplate( string $nodeName )
  {
  editorTemplate -beginScrollLayout;
   editorTemplate -beginLayout "Main Attributes" -collapse 0;
    editorTemplate -addControl "AttrA";
    editorTemplate -addControl  "AttrB" ;
    editorTemplate -addSeparator ;
    editorTemplate -addControl "AttrC";
    editorTemplate -addControl "AttrD";
   editorTemplate -endLayout;
   editorTemplate -addExtraControls;
  editorTemplate -endScrollLayout;
  }
  '''.%(cls.kPluginNodeName )
        om.MGlobal.executeCommand( melCmd )
}}}
The "{{{AttrA}}}" , "{{{AttrB}}}", etc would be any custom attr name created on this node:  My guess is behind the scenes Maya uses the {{{connectControl}}} control type to hook into the attrs, appropriately displaying them in the Attribute Editor.

The idea is a custom classmethod called {{{init_AETemplate}}} is created, that defines the template for the given custom node.  
It is called to inside the {{{nodeInitializer}}} classmethod, that is ultimately called to via the {{{initializePlugin}}} function inside that module, via the {{{OpenMayaMPx.MFnPlugin}}}'s {{{registerNode}}} method.
Just like anything else:
{{{
sets -add setName object.attribute;
}}}
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.
----
*Also see [[How can I have a script return where it lives on disk?]]
*{{{skinCluster}}} : name of a given {{{skinCluster}}} node.
*{{{joints}}}  :  List of joints
{{{
# Python code
import maya.cmds as mc

mc.skinCluster(skinCluster, edit=True, addInfluence=joints)
}}}
Say you have a set hierarchy in the Outliner, and you want to quickly pick one or more sets, add select one or more objects, and add the selected objects to the selected sets.  Maya has the 'Sets Relationship Editor', but I find it's implementation very clunky.
{{{
# Python code
import maya.cmds as mc

sel = mc.ls(selection=True)
nodes = []
sets = []
for item in sel:
    if mc.objectType(item) == "objectSet":
        sets.append(item)
    else:
        nodes.append(item)
        
for s in sets:
    mc.sets(nodes, addElement=s)
}}}
A slightly different way:  Add selected items to the single selected set.  If more than one set is picked, error.  Done in ~PyMel:
{{{
# Python / PyMel
import maya.OpenMaya as om
import pymel.core as pm

sel = pm.ls(selection=True)

allsets = []
nonset = []
for item in sel:
    if isinstance(item, pm.nt.ObjectSet):
        allsets.append(item)
    else:
        nonset.append(item)

if not len(allsets)==1:
    om.MGlobal.displayError("Please select exactly one set node")
else:
    pm.sets(allsets[0], forceElement=nonset) 
    print "Added items to set '%s':"%theset, [item.name() for item in nonset]
}}}
I find it really handy to organize sets inside of sets.  However, my brain can never seem to wrap itself around how to add a 'child set' to a 'parent set' via code:  The syntax to me is... a bit backwards:

Given a parental set '{{{parentSet}}}', and a set (or any other node) you want to add as a subset called '{{{subset}}}':
@@Mel:@@
{{{
sets -addElement parentSet subSet;
}}}
@@Python:@@
{{{
import maya.cmds as mc
mc.sets("subSet", addElement="parentSet")
}}}
In both instances of the command, to me, it looks like you're trying to add {{{parentSet}}} to {{{subSet}}}, but that's just not the case.

@@~PyMel:@@
~PyMel flipped it around, so it makes a lot more sense:
{{{
import pymel.core as pm
pm.sets("parentSet", addElement="subSet")
}}}
Regardless of the method use, in the Outliner this would give you the effect:
{{{
- parentSet
|
---- subSet
}}}
{{{select}}}
Example:  Using a wildcard:
{{{
select "foo*";
}}}
In the Attribute Editor for materials, there is a field for "Hardware Texturing".  In that field, there is an option to adjust the "Texture resolution".  It makes it //look// like this is a //material// property, but in fact, this is done on the '//texture// node level'.  
When you adjust that UI drop-down, Maya is actually calling to this proc: 
*{{{AEhardwareTextureQualityCB}}}, which lives in this script:
*{{{../MayaX.X/scripts/AETemplates/AEhardwareTextureTemplate.mel}}}
That code does all the magic to make this happen.  

It appears that at one point the below work was needed to be done on the //material//.  But as of Maya 2016, the work needs to be done on the node feeding //into// the material.

To do this on your own, you'd need to:
<<<
*Find all the "texture" nodes that are connected to a material.  This could be file, ramp, noise, etc. ({{{ls -tex}}})
*For each node, you'd need to add a new {{{.resolution}}} attr, and then update it's value.  Like below (how the actuall ~MayaAscii stores it).  
<<<
What's interesting, is that as soon as that attr exists, you'll see the "resolution" of the texture change in the viewport.  Maya must have some scriptJob running that detects for it(?)
{{{
addAttr -ci true -sn "resolution" -ln "resolution" -dv 128 -at "long";
}}}
The {{{-dv}}} (default value) is what the new resolution should be.  If you set the resolution to "default" in the Attribute Editor, it seems to actually delete the attr completely.  The other values are powers of two:
*Low: 32
*Med: 64
*High: 128
*Highest: 256
I wonder if you can enter in higher vals... (haven't tried yet)

What is interesting:  Open the Attribute Editor for a given texture node, and "copy tab" (at the bottom of the UI) to rip off a dupe of it, expand the "Extra Attributes" field.  If you then go back to the material's Attribute Editor, and start changing the Hardware Texturing values, you'll see the new attributes get built on the fly on the texture node.  Nifty.
The {{{controlPoint}}} node (a parental node type of {{{nurbsCurve}}}) has a {{{.weight[]}}} array attr.  Each index of that array corresponds to a CV on the curve.  The default weights are 1.0 I believe.
{{{
setAttr curveShape1.wt[0] .5;
}}}
I've been told if there is history on the curve, it can reset the values, FYI.
After you create a {{{parentConstraint}}}, Maya gives you no easy way to adjust their offset values per the attribute editor.  But you still can of course:

{{{
setAttr ($pareCon + ".tg[0].tot") -type "double3" 0 2 0;
setAttr ($pareCon + ".tg[0].tor") -type "double3" 0 0 45;
}}}
{{{.tg}}} is the target list.  In the above example, I'm adjusting the offset of the first target.
{{{.tot}}} is for offseting translation, and {{{.tor}}} for offsetting rotation.
The {{{animView}}} command will let you adjust the start\end time (x-axis), and the min\max value (y-axis) of an {{{animCurveEditor}}} (Graph Editor).

The docs say these values can be queried as well, but via mel it does nothing, and via Python I get a {{{RuntimeError}}}.
You can manually enabled this feature via 'Windows -> Settings/Preferences ->  Preferences -> Settings -> File References -> Allow locking and unlocking edits on referenced attributes'.
Or you can set this {{{optionVar}}} (requires Maya restart):
{{{
optionVar -intValue "refLockEditable" 1;
}}}
See: [[Why does adding an item to a Maya main menu remove all the items?]]
When you RMB in the Graph Editor, a context sensitive menu pops up.  How can you append your own items to this?
Actually, I have yet to figure out how, and here's why:
The script
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\graphEditorPanel.mel
}}}
Holds all the code the creating these menus.  The proc {{{buildGraphEditorPopupMenuGeneral()}}} specifically is what creates the menu items, and the parental  menu's name is {{{graphEditor1GraphEdanimCurveEditorMenu}}}.  The issue is, the proc {{{buildGraphEditorPopupMenu()}}} is called whenever the RMB is clicked in the Graph Editor, and it firsts deletes all the menu items, then rebuilds them.
So even if you write some code like this to add your own menu item:
{{{
setParent -menu graphEditor1GraphEdanimCurveEditorMenu;
menuItem -divider true;
menuItem -label "AWESOME";
}}}
You'll never actually see it, since it will be deleted before the menu is rebuilt for display.

The proc {{{defineGraphEditorPopupMenu()}}} physically creates the actual {{{popupMenu}}} that houses the various menu items.  In //theory// this proc could be overridden to point to your own {{{popupMenu}}} creation code.
The time slider menu's name is {{{TimeSliderMenu}}}.  However, if you start appending items to it before the user has first clicked on it, all the standard menu items will be replaced with yours.  This issue is described here: [[Why does adding an item to a Maya main menu remove all the items?]]

The workaround for the time slider is a bit different than the main menu.
There is a script (and global proc) called {{{
C:\Program Files\Autodesk\Maya20xx\scripts\others\TimeSliderMenu.mel
}}}
When you execute the {{{TimeSliderMenu}}} global proc, it in turn calls to another global proc in that file called {{{updateTimeSliderMenu}}} which actually builds the menu.  You need to do these steps first before you add your own items:
{{{
source TimeSliderMenu;
updateTimeSliderMenu TimeSliderMenu;
setParent -menu TimeSliderMenu;
menuItem -l "My Awesome Menu";
}}}
Presuming you know the {{{shadingEngine}}} for the given material (use {{{listSets}}} to track down the {{{shadingEngine}}} for a given material), it's pretty easy.  In the below example we force a cube to be shaded by the default material:
Mel:
{{{
string $node = "pCube1";
string $shadingEngine = "initialShadingGroup";
sets -forceElement $shadingEngine $node;
}}}
~PyMel:
{{{
import pymel.core as pm
node = "pCube1"
shadingEngine = "initialShadingGroup"
pm.sets(shadingEngine, forceElement=node)
}}}
Note how ~PyMel flips the arguments around (compared to mel) so it makes a bit more sense.
Maya is a //statically typed// language (like c++, Java), meaning, when you declare a variable, you have to define what //type// of data the variable is. {{{string $s}}}, {{{int $i}}}, {{{float $nums[]}}} etc.  Python (like Ruby) is an example of a language that is //dynamically typed//:  The type of the data is a part of the stored object, and has nothing to do with the variable name.  The variable name is simply a pointer to a memory location which defines what type of data it is.

You can run into instances in mel where you may need to capture data, but you're not too sure what //type// of data the source is.  An example of this is accessing Maya's {{{optionVar}}} system, which makes a good example.

{{{optionVar}}}s are variables stored on disk, usually for UI preferences.  But you can use them for anything else you need to stay persistent between Maya sessions.  I use them... a lot.  But you can run into instances in your code where you may make an optionVar of 'type string', and then later turn it into a 'string array'.  This is a blessing, an a curse: While {{{optionVar}}}s //are// typed, you can change their type at any point - they're just text stored on disk that can be modified at any point.  But if you want to pull that data into your mel scripts, and the type ihas changed, you can obviously run into problems.

In //Python//, this is a non-issue (see example at bottom).  But in mel this is a big issue.  The below example explains it:

Make a optionVar of type string, called 'ovString', and filled with the text 'firstItem':
{{{
optionVar -stringValue ovString firstItem;
string $str = `optionVar -q ovString`;
// Result: firstItem // 
}}}
Remove it from disk:
{{{
optionVar -remove ovString;
}}}
Remake ovString, but this time make it an array by default:
{{{
optionVar -stringValueAppend ovString firstItem;
string $str = `optionVar -q ovString`;
// Error: line 1: Cannot convert data of type string[] to type string. // 
}}}
Now our code can't capture its value, since its a string array, and not type string.

To address this, we can make a procedure that can {{{catch}}} problems, and based on the result handle them.  In this case, the code tries to pass a {{{optionVar}}} into a //string array//.  If that fails, it knows it must be of type //string//, and acts accordingly.
{{{
global proc string varTest(string $optionVar){
	// define our return value
	string $stringVar;

	// try to turn our optionvar into a string array, catch if fail:
	int $bad = catch(eval("string $temp_array[] = `optionVar -q "+$optionVar+"`"));
	if($bad){
		// failed, this must be a string then
		$stringVar = `optionVar -q $optionVar`;
	}
	else{
		// didn't fail, this must be a list
		string $local_ov[] = `optionVar -q $optionVar`;
		$stringVar = $local_ov[0];
	}
	
	// return our single string
	return $stringVar;
}
}}}
Lets see if it works:
{{{
string $str = varTest("ovString");
// Result: firstItem // 
}}}
Even though the {{{optionVar}}} ovString is of type string array, our code catches the error, and processes the data correctly.

I should also note that Maya has the {{{whatIs}}} command, to tell you what type a variable is:
{{{
whatIs("$str")
// Result: string variable // 
}}}
----
For comparison, here's the same thing in Python, showing the dynamic typing at work:
{{{
# Python code
import maya.cmds as mc

mc.optionVar(stringValue=('ovString', 'firstItem'))
str = mc.optionVar(query='ovString')
# firstItem 
}}}
Above, variable {{{str}}} points to a {{{string}}} object, with value "firstItem"
{{{
# delete optionVar
mc.optionVar(remove='ovString')
}}}
Now remake our {{{optionVar}}} as a string array (Python list object):
{{{
mc.optionVar(stringValueAppend=('ovString', 'firstItem'))
str = mc.optionVar(query='ovString')
# [u'firstItem']
}}}
Now, based on Python dynamic typing system, the {{{str}}} variable points to a {{{list}}} object (emulating Maya's array type).  The {{{list}}} object holds one {{{string}}} object at index {{{[0]}}}, which value is "firstItem".
So much easier to deal with than mel ;)

Like in mel, Python also has a command to tell you what type of object a variable points to:
{{{
type(str)
# Result: <type 'list'> # 

# query the string inside of the list:
type(str[0])
# Result: <type 'unicode'> # 
}}}

Whenever you ctrl+shift ~LMB-click on a menu item in one of Maya's main menus, it'll add that command as an {{{shelfButton}}} in the active shelf (via the mel proc {{{menuItemToShelf}}}).  You can add your own menus to Maya's main menu as well, for easy user access.  If you add these menus via Python, and if the commands they call to are via Python, depending on how the commands are authored they won't later work when assigned to the shelf via ctrl+shift ~LMB-click.  The below code examples explain how to author them so they //will// work after Maya restarts.

In the below example, three files are authored:  One that holds a command to execute, one that builds the Maya menu, and another that then creates the menu.  Make sure they're saved in a directory that is part of Maya's Python path.
----
This creates a command to execute, that needs to be passed multiple arguments:
{{{
# someModule.py
import maya.cmds as mc
def someCommand(*args):
    # A function that can take multiple arguments.
    print "Executing someCommand():"
    print "\t", args
}}}
This code creates the menu, and menuItems that passe multiple arguments to our command:
{{{
# awesomeMenu.py
import someModule
def makeMenu():
    if mc.menu("myAwesomeMenu", query=True, exists=True):
        mc.deleteUI("myAwesomeMenu")
    mc.menu("myAwesomeMenu", parent="MayaWindow", label="My Awesome Menu", tearOff=True)
    mc.menuItem(label="Bad command to shelf", command=lambda *args:someModule.someCommand("A","B","C"))
    mc.menuItem(label="Good command to shelf", command='import someModule; someModule.someCommand("A","B","C")')
}}}
This code actually builds the menu.  Could be added to the {{{userSetup.py}}} so the menu is built every time Maya launches:
{{{
# userSetup.py (for example)
import awesomeMenu
awesomeMenu.makeMenu()
}}}
----
*Execute the bottom code in whatever way you see fit:  A new menu will appear in Maya's main menubar.
*Make sure you have a shelf open: ctrl+shift ~LMB-click on both menu-items to create shelf buttons.  If you press both shelf buttons they should both work.
*Restart Maya, press the shelf buttons again:  The one created via "Bad command to shelf" should give you some type of error like this:
{{{
<function <lambda> at 0x0000000022BEC2E8>
# Error: invalid syntax
#   File "<maya console>", line 2
#     <function <lambda> at 0x0000000022BEC2E8>
#     ^
# SyntaxError: invalid syntax # 
}}}
*It's often very convenient to wrapper command callbacks via a Python {{{lambda}}}, since it allows you to embed arguments into it on the fly, without having to hard-code them as a string.  Problem is, the anonymous function it generates to wrapper the command is only valid during the current Maya session:  When Maya restarts, that memory has been reclaimed, the function no longer exists.
*Pressing the shelf item authored via "Good command to shelf" should work correctly, printing out the given text assigned to it at creation:
{{{
	('A', 'B', 'C')
}}}
*It works because of a few specific things:
**The command is entirely embedded as a string:  It can be re-evaluated as a string successfully, no anonymous {{{lambda}}} function floating around in memory.
**It embeds the module import directly in the command-string:  If that import wasn't in there, there's a good chance the module wouldn't have yet been imported into proper scope, and it wouldn't know how to execute the given function.
*The downside to the command-string is that it looses the capacity to have dynamically changing variables assigned to it (like the {{{lambda}}} allows)... but in my experience this isn't very common, and in most cases probably worth the trade-off if the users want easy access to the commands.
[img[http://farm3.static.flickr.com/2067/2197352149_10f912b166.jpg?v=0]]
{{{gradientControl}}}'s are cool, since they're a small little UI showing you a ramp (or curve) that can be manipulated by the user and ties into some object.attr.  Problem though, the docs on it leave a //lot// to be desired.

(I pulled most of this info from a post by 'ewerybody' on highend3d.com [[here|http://www.highend3d.com/boards/lofiversion/index.php/t214296.html]])

There's two ways I've found to interact with this type of control:  Via a ramp, and via a 'ui curve', both described below.  The 'curve' method is really similar in process and look as a [[gradientControllNoAttr|How can I author a 'gradientControlNoAttr' into my UI?]].  However, the curve method stores its values as attributes on an object, while the {{{gradientControllNoAttr}}} stores its values in an [[optionVar|How can I store and query variables in Maya, that are stored on the hard drive?]].

''RAMP:''
{{{
// first we need any object to add the attribute to:
string $obj[] = `polyCube`;
string $attrName = "gradientControl";

// add the attrs to our object that the gradientControl will interact with:
string $attrName = "gradientControl";
addAttr -ln ($attrName) -numberOfChildren 3 -at compound -multi $obj[0];
    addAttr -sn ($attrName + "Pos") -at "float" -parent $attrName $obj[0];
    addAttr -sn ($attrName + "Value") -at "float3" -parent $attrName -storable 1 $obj[0];
    addAttr -sn ($attrName + "ValueR") -at "float" -parent ($attrName + "Value") $obj[0];
    addAttr -sn ($attrName + "ValueG") -at "float" -parent ($attrName + "Value") $obj[0];
    addAttr -sn ($attrName + "ValueB") -at "float" -parent ($attrName + "Value") $obj[0];
    addAttr -sn ($attrName + "Interpolation") -at "enum" -enumName "0:1:2:3" 
        -parent $attrName $obj[0];

//give it some default vals, or we'll get an error when the UI tries to build below: 		
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Value") -type double3 1 0 0;
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Pos") 0;
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Interpolation") 3;
	
    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Value") -type double3 0 1 0;
    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Pos") .5;
    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Interpolation") 3;
	
    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Value") -type double3 0 0 1;
    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Pos") 1;
    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Interpolation") 3;

// now we can open a simple window that contains a control for this attributes:
if (`window -ex testWin`) 
    deleteUI testWin;
window testWin;
    columnLayout;
        string $colorControl = `attrColorSliderGrp -label "color"`;
        gradientControl -at ($obj[0] + "." + $attrName) -selectedColorControl $colorControl;
showWindow testWin;
}}}
Now we have an attrColorSliderGrp that will control the color of whatever part of the gradientControl you have selected.  You could make another one to control their position as well.

Of course the big problem is you can't query the value of the gradientControl itself, which is bothersome:
You can query the attr values on your object, but you really can't find the //interpolated// values that you'd be after.  For that, you'd need to write a another proc, probably using the {{{hermite}}} function, to find the interpolated values.  If I ever get that code authored, I'll update here.

For something similar, check out the [[rampColorPort|How can I make a UI control to edit my ramp?]] command.

----

''CURVE:''
[img[http://farm3.static.flickr.com/2270/2198047046_1b6111924d.jpg?v=0]]
(above image says 'gradientControlNoAttr', but this 'curve' method of the gradientControl looks the same)
{{{
// first we need any object to add the attribute to:
string $obj[] = `polyCube`;
string $attrName = "gradientControl";

// add the attrs to our object that the gradientControl will interact with:
// The enumName stands for the type of curve interpolation provided: 
//    flat, linear, spline plateau, and spline.

addAttr -ln ($attrName) -numberOfChildren 3 -at compound -multi $obj[0];
    addAttr -sn ($attrName + "Pos") -at "float" -parent $attrName $obj[0];
    addAttr -sn ($attrName + "Value") -at "float" -parent $attrName $obj[0];
    addAttr -sn ($attrName + "Interpolation") -at "enum" -enumName "0:1:2:3" 
        -parent $attrName $obj[0];

// and if you want, you can add some default values:
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Value") 1;
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Pos") 0;
    setAttr ($obj[0] + "." + $attrName + "[0]." + $attrName + "Interpolation") 3;

    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Value") .5;
    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Pos") .5;
    setAttr ($obj[0] + "." + $attrName + "[1]." + $attrName + "Interpolation") 3;

    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Value") 0;
    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Pos") 1;
    setAttr ($obj[0] + "." + $attrName + "[2]." + $attrName + "Interpolation") 3;

// now we can open a simple window that contains a control for this attributes:
if (`window -ex testWin`) 
    deleteUI testWin;

window testWin;
    columnLayout;
        gradientControl -at ($obj[0] + "." + $attrName) -h 75 testRamp;
showWindow testWin;
}}}
If you open the attribute editor for your object, under extra attributes you can see them change, as you add\remove\modify the gradientControl
[img[http://farm3.static.flickr.com/2270/2198047046_1b6111924d.jpg?v=0]]
{{{gradientControlNoAttr}}}'s are cool, since they're a small little UI showing you a curve that can be manipulated by the user.  Problem though, the docs on it leave a //lot// to be desired.  There is also the [[gradientControll|How can I author a 'gradientControl' into my UI?]] which pretty much does the same thing, but ties into an object's attributes.

Here's how to get started:
{{{
// To get more docs on it than the html page, run this:
help gradientControlNoAttr;
// standard docs:
help -doc gradientControlNoAttr;
}}}
To first set it up, you'll need to create an [[optionVar|How can I store and query variables in Maya, that are stored on the hard drive?]] to store\set up it's values.  We'll call our {{{optionVar}}} '{{{GCNA_test}}}':
{{{
// remove the optionVar if it already exists:
optionVar -rm "GCNA_test";
// add some default values:
optionVar -stringValueAppend "GCNA_test" "1,0,3";
optionVar -stringValueAppend "GCNA_test" ".5,.5,3";
optionVar -stringValueAppend "GCNA_test" "0,1,3";
}}}
The data format stored in the optionVars is:
*yPos, xPos, curveType
The curveType is:
*0 = flat
*1 = linear
*2 = spline plateau
*3 = spline
*4+ = weird
Once the optionVar is in existance, you can build you UI, to take control of it:
{{{
global proc tempWin()	
    {
    if ( `window -exists tempWin` )
        deleteUI tempWin;
    window -rtf 1 tempWin;
        columnLayout -adj 1 -co both 5;
            gradientControlNoAttr -optionVar "GCNA_test" GC_test;
    showWindow;
    }
tempWin;
}}}
Now you can adjust and add points to the {{{gradientControlNoAttr}}}.  To query the values:
{{{
string $vals[] = `optionVar -q GCNA_test`;
print $vals;
}}}
Of course, this just returns back the values you set either in the initial {{{optionVar}}} declaration, or based on updating the {{{gradientControlNoAttr}}}, not the //interpolated// values at any random point.  For that, you'd need to write a another proc, probably using the {{{hermite}}} function, to find the interpolated values.  If I ever get that code authored, I'll update here.

For something similar, check out the {{{rampColorPort}}} command.
For more info on the {{{gradientControl}}}, see this other posting [[here|http://www.highend3d.com/boards/lofiversion/index.php/t214296.html]]

Credit to 'Vlad Dubovoy' on highend3d.com for his posting on this subject that I was able to pull from.
Great tutorial here:
https://bindpose.com/custom-marking-menu-maya-python/
The code for it is here:
https://gist.github.com/vshotarov/cdf79835a0e30a9cbdeaaf2eb4151077
Starting with Python 2.5, they introduced the new {{{with}}} statement, which is known as a 'context manager'.  See official docs:
*http://docs.python.org/reference/compound_stmts.html#with
*http://docs.python.org/reference/datamodel.html#context-managers
*http://docs.python.org/library/stdtypes.html#context-manager-types
*http://www.python.org/dev/peps/pep-0343/
And notes on my Python Wiki:
*http://pythonwiki.tiddlyspot.com/#%5B%5Bwith%20statement%5D%5D
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.

Maya has {{{progressWindow}}}'s, which provide a nice graphical representation of how long an operation is taking on screen, and allows you to exit out during the operation via the {{{Esc}}} key.  However, they're a bit clunky to author.  I have a subject on setting them up here:
[[How do I create a "Status Window" that shows the graphical progression of some operation?]]

It dawned on me, a Python 'context manager' would be a perfect place to author a {{{progressWindow}}}, since you can wrapper all the ugliness of the {{{progressWindow}}} syntax in the context manager, making a much cleaner interface for the user.  Contrast this solution with the above link:  While it's more code, the bulk of it (the {{{ProgressWindow}}} class) can be hidden from the user.

Author the {{{progressWindow}}} context manager.  Code has been updated to support an 'enable' arg upon creation:  It's True by default, but by setting this to False, it allows calling code to suppress the progress window... in the chance that they themselves have a progress window (there can only be one at a time).
{{{
# Python code
import maya.cmds as mc

class ProgressWindow(object):
    """
    A context manager wrappering Maya's progressWindow functionality.
    """

    def __init__(self, minF, maxF, enable=True):
        self.progressSteps = 100.0 / (maxF-minF)
        self.progress = 0.0
        self.enable = enable

    def __enter__(self):
        """
        Enter the context manager, setup the progress window:
        """
        if self.enable:
            mc.progressWindow(title='Progress Window!',
                              progress=0, minValue=0, maxValue=100,
                              status='Not started yet...',
                              isInterruptable=True )
        return self

    def update(self):
        """
        Call this every loop once the context has been entered.  It detects for
        progress window canceling, and updates the current progress.
        """
        ret = True
        if self.enable:
            if mc.progressWindow( query=True, isCancelled=True ):
                ret = False
            else:
                mc.progressWindow(edit=True, progress=int(self.progress), status='%.1f%%'%self.progress)
                self.progress += self.progressSteps
        return ret

    def __exit__(self, exc_type, exc_value, traceback):
        """
        Called when the context manager is exited, exits the progress window.
        """
        if self.enable:
            mc.progressWindow(endProgress=True)
        if exc_type:
            print "Exiting ProgressWindow, caught exceptions:", exc_type, exc_value
        return True
}}}
So how is this used?  Here is a simple example:
{{{
# Define the frame range the progressWindow should act in:
minF = 1
maxF = 500

# We wrapper our loop with the Progress Window context manager:
with ProgressWindow(minF, maxF) as progress:
    for i in range(minF,maxF+1):
        # call to our update() every frame, and exit out if a cancel event is detected:
        if not progress.update():
            break

        # Do any work on the current frame (print it in this case), then advance it:
        print i
        mc.currentTime(i, edit=True)
}}}
In the above example, we want to loop over a certain frame range, from frames 1 to 500.  In the loop, we have some work to do, and in this case it's just printing the frame number.  But where the print statements goes, any amount of other code could go.

As you can see, contrasting the 'for loop' code of this example against the above link, it's //much// easier to read:
*We wrapper our for loop with our {{{ProgressWindow}}} context manager.
*At the start of each looop we {{{update()}}} the progress window, detecting for cancel events, and if so, breaking out of the loop.
*When the loop is exited, the {{{__exit__()}}} method of the context manager is automatically called, closing the {{{progressWindow}}}, and handling any exceptions.
nice!
*This is a general concept I came up with which I'm still roughing out.
*A script can hold multiple procedures. Procedures can take arguments. But if a user doesn't define the proper arguments when executing a procedure, they'll get an error. Or, if you change the procedure to have additional arguments, or remove some arguments, any scripts that call to it will need to be updated to pass the correct arguments, or error.
*Current Maya Example:
**Here is my procedure, with arguments. Let's pretend it's saved in a file called foo.mel
{{{
global proc foo(string $a, string $b, string $c)
    {
    print ($a + "\n");
    print ($b + "\n");
    print ($c + "\n");
    } 
}}}
**And here is our code that calls to it:
{{{
foo("please", "don't", "break");
// result:
please
don't
break
}}}
**Now, pretend you change the arguments of foo to this:
{{{
global proc foo(string $a, string $c)
    {
    print ($a + "\n");
    print ($c + "\n");
    } 
}}}
**When executing foo based on the old args, (hoping to now see "please break"), instead Maya will error:
{{{
// Error: foo("please", "don't", "break"); //
// Error: Line 1.31: Wrong number of arguments on call to foo. //
}}}
*Other scripting languages like Python allow the authoring of procedures with arguments, but you can author them such that you can assign default values to the arguments, or if you leave an argument out, it won't freak. Maya is much more rigid. Here is the concept I have working to sidestep this limitation. I'm sure it has some bugs, but this is just a prototype. First I'll show the scripts, and then describe what they do:
**'Arguemnt-less' script example:
{{{
// this file is saved as "argLess.mel"  It is our script that has no hard-coded 
// argument definitions, but can still take passed-in arguments:
    {
// this defines our list of arguments that the script cares about:
    global string $arglist[];

// now, if it finds an argument it cares about, do something:
    if(stringArrayCount("$printme", $arglist))
        print ($printme + "\n");
    if(stringArrayCount("$addme", $arglist))
        {
        float $val = $addme + $addme;
        print ("Added vals = " + $val + "\n");
        } 
    } 
}}}
{{{
// this is an example script that would make a call to our "argument-less" script above:
// it is saved as a script called "call_argLess.mel"
    {
// Define our arguments:
    global string $printme = "printing!!!!!";
    global float $addme = 2.5;
    global string $arglist[]; 
// here we define our global list of argurments. Note we've added one that argLess.mel isn't expecting
    $arglist = {"$printme", "$addme", "$notused"};
// and execute our "argumentless" script        
    source argless; 
    } 
}}}
***To execute call_argLess.mel, you have to source it, since there is no internal procedure definition:
{{{
source call_argLess;
// and the result in the Script Editor:
printing!!!!!
Added vals = 5
}}}
**So what's going on here? We have a script called argLess.mel that has no actual arguemnt definitions (since no procedures have been defined inside of it), yet you can call to it and pass in pre-defined arguments. It's common sense but it had just never occured to me before:
**In call_argLess.mel we setup a global string array called 'global string $arglist[]'. Into this we pass the names of the other global variables we want to send to our argLess.mel script.
**In argLess.mel we "pull-in" the global $arglist[] so it's seen inside of the script. Then we stimply check to see if it has data that it can deal with.
**In the case of the example, it knows to print something called $printme, and to add something call $addme. Even though we've 'passed' in something it isn't expecting ($notused), it doesn't matter, since argLess.mel isn't looking for it.
**Furthermore, if say we removed an argument from our $arglist[], the script could handle that as well, since it only executes on code that it can find.
*I hope this makes some sense! In a nutshell you have your "arg-less" proc that looks to a global list of arguments to work on. If they exist, then it does stuff. When you "call" to your "arg-less" script, first you define what the args values are, and then point your "global list of arguemnts" to their variable names. Since all the data is "global", they are seen between scripts.
*The only downside I've see so far is the way you need to author your "arg-less" script. You basically have a bunch of "if" statements querying if a variable they care about exists in the 'global var list', and if so, executes on it's contents. In most script\procedure writing you can use your variables all over the code, but using this method it's a bit more compartmentalized. Still, it may have some benefits.
{{{
import pymel.core as pm

class Timer(object):

    def __init__(self, name="Default", precision=2):
        self.name = name
        self.precision = precision

    def __enter__(self):
        pm.timer( startTimer=True, name=self.name)

    def __exit__(self, exc_type, exc_value, traceback):
        fullTime = pm.timer( endTimer=True, name=self.name )
        print "# Timer '%s' : Ran for %s seconds"%(self.name, round(fullTime, self.precision))
        return False
}}}
{{{
with Timer(name="Time my thing"):
    # all your code goes here

# Timer 'Time my thing' : Ran for 0.89 seconds
}}}
Starting with Python 2.5, they introduced the new {{{with}}} statement, which is known as a 'context manager'.  See official docs:
*http://docs.python.org/reference/compound_stmts.html#with
*http://docs.python.org/reference/datamodel.html#context-managers
*http://docs.python.org/library/stdtypes.html#context-manager-types
*http://www.python.org/dev/peps/pep-0343/
And notes on my Python Wiki:
*http://pythonwiki.tiddlyspot.com/#%5B%5Bwith%20statement%5D%5D
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!
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|http://pythonwiki.tiddlyspot.com/#decorator]].

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.
----
Updated code here.  This works fine decorating functions and methods.  While the below function-based decorator works, it doesn't pass out the return of the wrappered function:  This one does.
{{{
import pymel.core as pm
def undoable(f):
    def wrapper(*args, **kwargs):
        pm.undoInfo(openChunk=True)
        try:
            result = f(*args, **kwargs)
        finally:
            pm.undoInfo(closeChunk=True)
        return result
    return wrapper
}}}
----
Other examples:

{{{
# 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 author an undo context manager?]]
*This will open a new Command Prompt:
{{{
>> maya -batch -file someMayaFile.ma -command "file -save"
}}}
*Same as "maya -batch" but this runs in the same Command Prompt as it was executed in:
{{{
>> mayaBatch -file someMayaFile.ma -command "file -save"
}}}
{{{fileBrowserDialog}}}
*Notes: The docs on this suck, so this is what you need to do:
----
This subject shows how to save the last directory accessed, so you can consistently open your dialogs to the same dir:
[[How can I define a start location for my fileBrowserDialog?]]
----
Example with Python, for picking directories.  Since you can nest functions in Python, it makes it easier to wrapper the functionality of this command together into one cohesive unit.
{{{
import maya.cmds as mc
def fbd():
    def fileCommand(directory, ignore):
        # This is called by the fileBrowserDialog command after a dir is picked.
        print directory
    mc.fileBrowserDialog(mode=4, fileCommand=fileCommand, fileType='directory',
                         actionName='Choose a directory:')
fbd()   
}}}
----
*Mel Examples:
**First, built the file brower dialog: -mode is telling it what to do:
{{{
fileBrowserDialog -mode 2 -fileCommand "procName" 
    -actionName "whatYouAreDoing";
}}}
**-mode 0 is for reading files, so if you type in a name that dosn't exist, the browers will give an error
**-mode 1 & 2 are pretty much the same and are for for writing to file, so you can speficy your own filename in the browers.
**-mode 4 is for selecting directories only.
**-fileCommand is the name of the below proc, that the fileBrowserDialog? passes its info to.
**-actionName is simply what is printed in the UI, telling the user what is going on
**If you're using mode 1 or 2, and you want to specify your own "file types" for filtering, you can use the below code snippet (thanks to Mason Sheffield).  
***The {{{-filterList}}} can be called to multiple times to define different file types.  The {{{-filterType}}} is simply the default UI filterList choice.  Also, you //must// set {{{-dialogStyle 1}}} for some reason, or the filtering won't work :(
{{{
-filterList "My file type(*.mft),*.mft" 
-filterList "otherFileType(*.oft),*.oft"
-fileType "My file type(*.mft)" 
-dialogStyle 1
}}}
*Next, you need to build a proc, that can take the return from the FBD, and then do something with it. It needs to be in the below format, but the proc name, and var names can change.
{{{
global proc procName(string $result, string $type){
    textFieldButtonGrp -e -tx $result controlName;} 
}}}
**So, the {{{fileBrowserDialog}}} passes its info, via the {{{$result}}} argument, into the "procName" proc.  Then procName will update controlName with $result.
*Another issue: By default you can't specify WHERE the dialog will open too. It appears that it bases where it opens on where Maya's current workspace is. SO, to tell it where to go, you'd need to do something like this:
{{{
// query Maya's current workspace:  
string $mayaWorkspace = `workspace -q -dir`;
// set the workspace to where you want the dialog to open:
workspace -dir $myCustomPath;
// run the dialog code:
fileBrowserDialog ....
// and set the workspace back again:
workspace -dir $mayaWorkspace;
}}}
*Annoying!
----
----

I got an emailed suggestion from Nicolas Combecave.  Thanks Nicolas!
*After scrutining into the fileBrowser mel //command//, I found that the //script// {{{fileBrowserWithFilter}}}, does both in one single command, plus adds an items filtering mechanism. Here is its syntax from the procedure declaration:
{{{
global proc int fileBrowserWithFilter(
    string $callBack, 
    string $action, 
    string $title, 
    string $type, 
    int $mode, 
    string $filters[], 
    string $dir)
}}}
*We can translate it into a mode understandable text:
**{{{string $callBack}}} > your procedure to launch upon validation
**{{{string $action}}} > text label on the validation button
**{{{string $title}}} > browser title
**{{{string $type}}} > a filetype to select in the dropdown list. Even custom ones (see below).
**{{{int $mode}}} > choose for read/write/listDirs... (see description above)
**{{{string $filters[]}}} > fileTypes in the dropdown menu. You can define yours here: {"myFileType,*.mft":"yourFileType,*.yft":"theirFileType,*.tft":etc...}
**{{{string $dir}}}  > the starting directory
*So you can use it when you want to specify a file that doesn't exists, into a specified destination, filtering only special file types, even custom ones:
{{{
string $startDir  ="c:/tmp/";
fileBrowserWithFilter(
    "procName",
    "myLabel",
    "myTitle",
    "afileType",
    1,
    {"aFileType,*.aft","anotherFileType,*.anft"},
    $startDir);
}}}
Given the 'Focal Length' and 'Horizontal Film Aperature'? (since it isn't an actual attribute)
*Code pulled from Maya's {{{AEcameraTemplate.mel}}} script:
{{{
float $focal = `getAttr "cameraShape1.focalLength"`;
float $aperture = `getAttr "cameraShape1.horizontalFilmAperture"`; 
float $fov = (0.5 * $aperture) / ($focal * 0.03937);
$fov = 2.0 * atan ($fov);
$fov = 57.29578 * $fov;
}}}
Given the 'Horizontal Film Aperature' and 'Angle of View' (Field of View)?
*Code pulled from Maya's {{{AEcameraTemplate.mel}}} script. In this example we query the camera's Attribute Editor floatSliderGrp for the FOV, since the FOV isn't an actual camera attribute.
{{{
float $fov = `floatSliderGrp -q -value fovGrp`; 
float $aperture = `getAttr "cameraShape1.horizontalFilmAperture")`;
float $focal = tan (0.00872665 * $fov);
$focal = (0.5 * $aperture) / ($focal * 0.03937);
}}}
Given the 'Focal Length' and 'Angle of View'?
* I don't know... someone please tell me....
With the Python API:
http://docs.python.org/library/math.html#math.acos
http://docs.python.org/library/math.html#math.degrees
http://download.autodesk.com/us/maya/2010help/API/class_m_vector.html
{{{
# Python code
from math import degrees, acos
from maya.OpenMaya import MVector

# Define two vectors:
vA = MVector(1,0,0)
vB = MVector(0,0,1)

# Get the normalized versions of the vectors:
vA_norm = vA.normal()
vB_norm = vB.normal()

# Find the dot product:
dot = vA_norm * vB_norm

# Get the angle in degrees from the acos of the dot:
angle = degrees(acos(dot))
}}}
{{{
print angle
# 90.0
}}}
----
Via mel's vector types.  In this example, we calculate our two vectors based on three 3d points.
{{{
vector $vA = <<0,0,0>>;
vector $vB = <<1,0,0>>;
vector $vC = <<0,.5,.5>>;

// Define two vectors:
vector $vAB = $vB - $vA;
vector $vAC = $vC - $vA;

// Get the normalized versions of the vectors:
vector $vAB_norm = unit($vAB);
vector $vAC_norm = unit($vAC);

// Find the dot product:
float $dot = dot($vAB_norm, $vAC_norm);

// Get the angle in degrees from the acos of the dot:
float $angle = rad_to_deg(acos($dot));
// Result: 90.000001 // 
}}}
----
You can also use, in Maya:
{{{angleBetween}}} //command//.
OR
{{{angleBetween}}} //node//.
Not a code snippet, but a description of what you need to do.
You can use this to reverse-engineer where a ik handle's pole vector should live, based on the shape of the joint chain itself.
*Get a nice vector library.
*multVal = some val :  This is used to move the location of the pole vector.
*startPoint = vector(first joint worldspace position)
*midPoint = vector(average of all worldspace positions of all joints between start and end)
*endPoint = vector(end joint worldspace position)
*endVector = endPoint - startPoint
*midVector = midPoint - startPoint
*perpendicularVec = crossProduct(endVector ,midVector)
*poleVector = startPoint + (unit( crossProduct(perpendicularVector, endVector ) ) * multVal)

You can then use multVal to 'slide' the pole vector towards and away from the start joint.
Scripting languages like Python allow a procedure to be authored such that if it is expecting an argument, but none is passed in, it can use a default value.  Mel doesn't allow that.  :-(

But, you can use a technique to //simulate// the functionality.  In a nutshell, rather than defining a default argument in the //procedure//, you define it when you pass in an //argument//.

To explain, a simple proc:
{{{
global proc string foofoo(string $arg)
    {
    return $arg;
    }
}}}
So, whatever you pass in as $arg will be returned.  But what if you don't want to pass anything in as $arg, but you still want it to return something by default?  You can use a {{{conditional statement}}} //as// the argument:
{{{
// passing in an arg:
string $arg = "MY ARG";
foofoo((size($arg) > 0 ? $arg : "NO ARG"));
// Result: MY ARG // 

// no arg passed in, using default value:
string $arg = "";
foofoo((size($arg) > 0 ? $arg : "NO ARG"));
// Result: NO ARG //
}}}
So either way, you have to pass in $arg, there's no way around that.  But, it allows the user to write code to pass in a //different// default value if the argument (in this case) is empty.  The conditional statement can be changed to support int values, etc.

Also see:
*[[How can I use operators outside of conditional statements?]]
*[[How can I define a variable outside 'if statement', but change its value within the statment?]]

Was recently working with Autodesk to capture crash data on a reproable bug, where when a condition was met, Maya would 'just disappear'.
The recommended some Microsoft tools for this: "Debug Diagnostic Tool" (v2 at this time)
https://www.microsoft.com/en-us/download/details.aspx?id=49924
When it first launches, it will (should, did for me) open a configuration wizard to make a new 'rule', and from what I remember, you should do this:
* It will ask what process to attach itself too:  You can just enter "maya.exe"
* It will ask where to save it's logs, and provides a default location.  I moved mine to an easier to find dir like c:/temp/maya/crashlogs
Next, make sure the tool is up and running:
{{{
C:\Program Files\DebugDiag\DebugDiag.Collection.exe
}}}
Then open Maya, and repro your error.

When Maya crashes, it will store out two files into the dir you set:
* {{{maya__PID__31976__Date__12_05_2018__Time_03_38_48PM__657__Log.txt}}}   < not too big usually, few hundred k
* {{{maya__PID__31976__Date__12_05_2018__Time_03_39_53PM__242__Second_Chance_Exception_C0000005.dmp}}} < about 2.5 //gigs//
To view usable info form this data, launc this app:
{{{
C:\Program Files\DebugDiag\DebugDiag.Analysis.exe
}}}
When it opens:
*Check the '~CrashHangAnalysis' checkbox
*Add Data Files: Browse to the big .dmp file.
*Start Analysis  : This can take a while the first time you run it.
When complete, it will open Internet Explorer window with the results.  
* There will be for colored boxes (Error, Warning, Information, Notification), that may all be '0'. If you click on the 'Error' box, IW may ask you to 'allow blocked content', if so, say yes, and then the boxes should populate.
* You can then save this webpage if needed, and provide to Autodesk, or read through it yourself if you know what to look for ;)
When I'm rigging, I will often create an initial 'rig-skeleton' that fits inside the mesh to be deformed.  To speed things along, I would first just rough in the joint positions.  But later I like to get them centered into a selection of verts.

The below code will center a joint based on an edge loop.  To use the code, pick a joint, then add-select an edge from your mesh (RMB on the mesh -> Edge).  The code will (try to) find the edge loop, and center the joint in it.
{{{
// Center joint in edge loop.
// Select one joint, and one edge.

// first, find edge loop:
string $sel[] = `ls -fl -sl`;

string $joints[] = `ls -type "joint" $sel`;
string $edges[] = `polyListComponentConversion -te`;
int $edgeNum=0;

if(size($sel)!=2)
	error("Please select exactly one edge, and one joint");

string $buffer[];
tokenize $edges[0] ".[]" $buffer;
$edgeNum = $buffer[size($buffer)-1];

if(size($joints)==0)
	error "You didn't pick a joint";
if(size($edges)==0)
	error "You didn't pick an edge";
	
polySelect -el $edgeNum;

// convert that to a list of verts:
string $verts[] = `polyListComponentConversion -tv`;
$verts = `ls -fl $verts`;

// Find center of vert positions:
float $xyz[] = {0,0,0};
float $x=0;
float $y=0;
float $z=0;

for($i=0;$i<size($verts);$i++)
	{
// for some reason, no matter what is picked, or nothing is picked,
// pointPosition likes to toss a warning and say that more than one
// think is picked.  So we just hide that, since it shouldn't be
// warning at all... :-S
	scriptEditorInfo -e -sw 1;
	float $pos[] = `pointPosition -w $verts[$i]`;
	scriptEditorInfo -e -sw 0;
	$x = $x + $pos[0];
	$y = $y + $pos[1];
	$z = $z + $pos[2];
	}
$xyz = {($x / size($verts)), ($y / size($verts)), ($z / size($verts))};

// Move joint pivot to new location:
move -a -ws -rpr $xyz[0] $xyz[1] $xyz[2] ($joints[0] + ".rotatePivot") ($joints[0] + ".scalePivot");
print ("Centered " + $joints[0] + " in edge loop\n");

// Finally pick our edge loop, and joint:
select -r $verts;
select -add $joints[0];
}}}
{{{
import maya.cmds as mc

def average(*args):
    return sum(args) / len(args)

def centerToPoints():
    """
    Select components first, the node to center last, and exceute.
    """
    sel = mc.ls(selection=True, flatten=True)
    comps = sel[:-1]
    node = sel[-1]
    verts = mc.ls(mc.polyListComponentConversion(comps, toVertex=True), flatten=True)
    positions = [mc.pointPosition(vert, world=True) for vert in verts]
    avg = list(map(average, *positions))
    mc.move(avg[0], avg[1], avg[2], node, absolute=True, worldSpace=True)
    
centerToPoints()    
}}}
There is a mel script that lives here, that will do the switching for you (very handy):
{{{
../MayaX.X/scripts/startup/setNamedPanelLayout.mel
}}}
Which has this argument structure:
{{{
setNamedPanelLayout( string $whichNamedLayout );
}}}
So you can call to it like this:
{{{
setNamedPanelLayout "Single Perspective View";
}}}
And it will switch to that view.  You can get a list of these by accessing the Menu: {{{'Panels -> Saved Layouts -> (list of layouts)'}}}

The little bit of code that is interesting in that script is this line:  (presuming:  {{{string $whichNamedLayout = "Single Perspective View";}}})
{{{
string $configName = `getPanel -cwl $whichNamedLayout`;
// Result: panelConfiguration2 //
}}}
Which actually returns something useful for the code to work with.
__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
}}}
That can be handled through the:
*{{{defaultRenderGlobals}}} node (node type: {{{renderGlobals}}})
**Image format, Renderable Objects, Per camera, what to render (Image, Mask, Depth, or renderable at all), renderable animation range, etc.
It has two sibling nodes that help it control your renders.  The:
*{{{defaultRenderQuality}}} node (type: {{{renderQuality}}}) 
**Setting Anti-aliasing, particle sampling, motion blur stuff, etc.
*and {{{defaultResolution}}} node (type: {{{resolution}}})
**Resolution, aspect ration, field-rendering, etc.
For example, set our output render format to .jpg:
{{{
setAttr "defaultRenderGlobals.imageFormat" 8;
}}}
For Hardware rendering you have the:
*{{{hardwareRenderGlobals}}} (node type: {{{renderGlobals}}})
*and {{{defaultHardwareRenderGlobals}}} (node type: {{{hwRenderGlobals}}})
**I wonder why it's special and gets two nodes?
Other renderers would have their own nodes.  But since I don't have any loaded right now, this is all I'm listing ;)
Also see:
*[[How can I get a list of the available renderers?]]
*[[How can I change the current renderer through mel?]]
*Edit the Maya script here:
{{{
C:/Program Files/Alias/MayaX.X/scripts/AETemplates/AEshaderTypeNew.mel
}}}
*And in the {{{global proc AEshaderTypeCB}}} inside that script,
*After this line:
**{{{ delete $shaderNode;}}}
*Add this:
**{{{rename $replaceNode $shaderNode;}}}
*Save, re-{{{source}}} {{{AEshaderTypeNew}}} in Maya, it should do just what you want when changing the "Type" in the Attribute editor, when a shader is selected. If you want to some thing more custom, the guts of that script will tell you how to do it. I wonder why this isn't the default option?
The {{{defaultRenderGlobals}}} node (see notes [[here|How can I change my Render Settings?]]), who's node type is {{{renderGlobals}}}, allows you to modify it to do this.
To change the file format, you modify the {{{.imageFormat}}} attr:
{{{
// set to .tga:
setAttr defaultRenderGlobals.imageFormat 19;
}}}
From the docs, these are the current choices:
<<<
GIF (0), SI (1), RLA (2), Tiff (3), Tiff16 (4), SGI (5), Alias (6) IFF (7) JPEG (8) EPS (9) ~IFF16 (10) Cineon (11) Quantel (12) ~SGI16 (13) TARGA (19) BMP (20) SGIMV (21) QT (22) AVI (23) MACPAINT (30) PHOTOSHOP (31) PNG (32) QUICKDRAW (33) QTIMAGE (34) DDS (35) DDS (36) ~IMFplugin (50) Custom (51) SWF (60) AI (61) SVG (62) SWFT (63)
<<<
Thanks to a tip from [[Seith|http://seithcg.com/wordpress/]], based on the {{{IMFplugin}}} setting (#50), you can load in formats //other// than what is listed.  For example, to render out as {{{.xpm}}}:
{{{
// set to IMFplugin format:
setAttr defaultRenderGlobals.imageFormat 50;
// set the imfPluginKey to .xpm:
setAttr defaultRenderGlobals.imfPluginKey -type "string" "xpm";
}}}
Nice!  If you're wondering, you can visually see the {{{imfPluginKey }}} data in the 'Extra Attributes' section of the Attribute Editor on the {{{defaultRenderGlobals}}}.
The easiest way is to just call to the {{{CycleBackgroundColor}}} runtimeCommand over and over to run through the presets.  It in turns calls to the {{{cycleBackgroundColor.mel}}} script, which:
Queries the background color via:
{{{
float $rgb[3] = `displayRGBColor -query background`;
}}}
Queries if the gradient is on via:
{{{
int $gradOn = `displayPref -q -displayGradient`;
}}}
Turns the gradient on/off via:
{{{
displayPref -displayGradient 1; // or 0
}}}
And changes the colors via presets with:
{{{
displayRGBColor background 0.36 0.36 0.36;//default color
}}}
{{{
setCurrentRenderer "rendererName";
}}}
*Note that {{{setCurrentRenderer}}} is a sub-proc living in {{{.../scripts/others/supportRenderers.mel}}}
*This has the same effect as changing the render type in the Render Globals top dropdown menu. It turns out that if you open a scene that's in "Maya Software" mode, and you want to change values on the Mental Ray renderer, you need to "set" the renderer to mental ray first, because by default, the mental ray nodes don't exist in the scene. By setting the renderer to mental ray, the appropriate nodes are created.
{{{polyOptions}}}
http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/polyOptions.html
*Example, show the UV map border:
{{{
polyOptions -dmb 1;
}}}
*Example, enable the vertex color of a poly object in shaded mode:
{{{
polyOptions -cs 1;
}}}
Double the height:
{{{
layout -e -height 80 ShelfLayout;
}}}
Or if you have the tabs visible:
{{{
layout -e -height 101 ShelfLayout;
}}}
It  appears the default height is 40 pixels with no tabs, and 61 pixels with tabs visible: the tab takes up 21 pixels.
So if you have tabs visible, multiply the number of rows you want by 40, then add 21.
Maya {{{window}}}s have an {{{iconName}}} property that can be set and queried.  However, I've never got it to actually change the icon that's in the top-left corner of the window.
Since Maya has adopted ~PySide, even if you create a window via the Maya {{{window}}} command, the end result is a Qt object.  And because of that, we can use ~PySide to change the icon.  Surprisingly easy actually:
{{{
import maya.OpenMayaUI as omui
from shiboken import wrapInstance
from PySide.QtGui import QWidget, QIcon

def setWindowIcon(winName, iconPath):
    """
    Convert from a Maya window string name to the QWidget window instance.
    Then set its icon.

    winName : string : The name of the maya 'window' control
    iconPath : string : Full path to icon.png file.
    """
    ptr = omui.MQtUtil.findWindow(winName)
    qwin = wrapInstance(long(ptr), QWidget)
    qwin.setWindowIcon(QIcon(iconPath))
}}}
{{{
winName = "myAwesomeWindowName"
iconPath = "C:/path/to/some/icon.png"
setWindowIcon(winName, iconPath)
}}}
I personally dislike the view-cube.  Just yet one more thing to clutter the UI.  But maybe now I'll give it a second chance :)
Based on this post (by Shawn ~McClelland):
http://forums.cgsociety.org/showpost.php?p=6009050&postcount=216
It says:
<<<
In your install location ({{{C:\Program Files\Autodesk\Maya2009\icons\AutoCam\}}}) there is a file called {{{VCfacemap.png}}} which is basically a simple cube map that the ~ViewCube uses to texture itself.
...
Save the {{{VCfacemap.png}}} file and reload Maya, voila you now have a textured viewcube
<<<
To turn on\off:
Window > Settings/Preferences > Preferences -> Interface -> ~ViewCube

Some examples :)
http://www.flickr.com/photos/7875859@N03/3772730145
When you set Maya to the 'move' tool (w hotkey), you hold w+LMB, you can bring up a marking menu that lets you translate in different spaces: Object, World, Component.  Plus, their are //other// spaces the marking menu doesn't expose, that the Tools Settings window does, like Parent (Local), Normal, and Custom.  How can you change what mode you're in via code?  

{{{
import maya.cmds as mc
mc.setToolTo("moveSuperContext")
mc.manipMoveContext("Move", edit=True, mode=1) # Set to 'Parent/Local'
}}}


~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)
}}}
{{{jointDisplayScale}}}
{{{setStartupMessage}}}
*Notes: You'll need to edit the {{{\MayaX.X\scripts\startup\initialLayout.mel}}} script to see this work.
Starting with Maya 2018 (maybe 2017), if you like to run your hotbox with a single toolset in it (like 'just animation', or 'just rigging') there was a bug introduced that every time you'd restart Maya, Maya would add 'another' menu to it.  I've logged this as a defect in 2018 & 2019, but Autodesk has yet to take any action on it.  Having used the hotbox since Maya v1, and Alias Poweranimator before it, I find this incredibly annoying.  Update:  Persists in Maya 2022.

New code:
If you put the below code in your userSetup.py, you can have Maya auto-set the Hotbox back to a single menu on startup to work around this.
{{{
import maya.cmds as mc
import maya.mel as mm

def fixHotbox():
    # Turn off what we don't want to see by default
    mc.hotBox(animationToggleMenus=False)
    mc.hotBox(modelingToggleMenus=False)
    mc.hotBox(polygonsToggleMenus=False)
    mc.hotBox(renderingToggleMenus=False)
    # Turn on what we do want to see:
    mc.hotBox(riggingToggleMenus=True)
    # If we don't do this, there'll be a mismatch with the hotbox & Maya's menus.
    mm.eval('ShowRiggingUI')
}}}
----
Old code:  I felt like sometimes this could possibly cause a start on crash error in Dec 2020, because of this {{{optionVar}}}:
{{{
optionVar -iv "isHotBoxShowAll" 0;
}}}
FYI, this is basically a Python rewrite of the 'echo-all commands' that spit out when opening the hotbox, and enabling a specific menu mode:
{{{
import maya.cmds as mc
import maya.mel as mm

def fixHotbox():
    mm.eval('ShowHotbox; hotBox;')
    if mc.menu("MayaWindow|HotBoxControlsMenu", query=True, numberOfItems=True) == 0:
        mc.setParent("MayaWindow|HotBoxControlsMenu", menu=True)
        mm.eval("source HotboxControlsMenu")
    mc.hotBox(riggingOnlyMenus=True)
    mc.setMenuMode("riggingMenuSet")
    mm.eval("updateHotboxOptionVarSettings()")
    mm.eval("updateMenuModeUI; HideHotbox; hotBox -release;")
}}}
{{{setToolTo}}}
*Example: Set the current tool to the "select tool"
{{{
setToolTo moverSuperContext;
}}}
{{{connectionInfo}}} only requires a single object.attr.  {{{listConnections}}} also works, but this is a bit more simple:
{{{
// Is the object.attr a source of a connection?
connectoinInfo -isSource object.tx;
}}}
{{{
// Is the object.attr the destination of a connection?
connectionInfo -isDestination object.tx;
}}}
{{{
// List an object.attr's source connection, meaning, list the input connection.  
// This will return a single object.attr.
connectionInfo -sourceFromDestination object.tx
}}}
{{{
// List an object.attr's destination connection, meaning, list the all output connections:
// This could return multiple object.attrs.
connectionInfo -destinationFromSource object.tx;
}}}

If you have a shortcut on your desktop, it's as easy as specifying it as a startup arg:
{{{
"C:\Program Files\Autodesk\Maya2022\bin\maya.exe" -pythonver 2
}}}
Sometimes when running a {{{getAttr}}} on an float node attribute, Maya will return back some very tiny value:
{{{
# Python code:
print mc.getAttr('myNode.myFloat')
2.7912680472872789e-017
}}}
The {{{print}}} statement turns the {{{float}}} into a {{{string}}} for display to the screen.  And in the process, does some shorthand so it's not an incredibly long number displayed before you.

If you see {{{e-}}} in the number (exponent), it basically means the value is zero for all intensive purposes.
But how can you really make that value zero?  At least so it prints nicer?

If you're using Python (and you should be), you can use Python's {{{round()}}} function to take care of this.  Just pass in how many places to the right of the decimal you want it rounded to:
{{{
val = 2.7912680472872789e-017
print round(val, 4)
0.0
}}}
Matt Estela pointed me to this posting over on tokeru.com:
http://www.tokeru.com/t/bin/view/Maya/MayaMelAndExpressions#Strip_spaces_and_punctuation_mak
Here's the source from the site (thanks Matt!):
{{{
string $comment = "this, is a line with numbers like 0.25 and: stuff!";
string $regex = "[^a-zA-Z0-9_]";
string $replace = "_";

print ($comment+"\n");
int $i = 0;
string $match;
int $totalChars = `size($comment)`;

for ($i = 0; $i < $totalChars; $i++) {
  $match  = `match $regex $comment`;
  $comment = `substitute $regex $comment $replace`;
  if ($match == "") break;
} 

print $comment;
print "\n";

// returns "this__is_a_line_with_numbers_like_0_25_and__stuff_"
}}}
I thought I'd see how I could author this in Python, and posted an alternative over at my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#%5B%5BHow%20can%20I%20strip%20illegal%20characters%20from%20a%20string%3F%5D%5D]]
By default, when a UI is created, Maya will remember its size. If later in your script you change it's size, those changes may not be reflected on screen.
{{{
windowPref -remove "UI_nameGoesHere";
}}}
Old example:
{{{
// compute current position:
float $posNowX = obj.translateX;
float $posNowY = obj.translateY;
float $posNowZ = obj.translateZ;

// compute previous position:
float $posThenX = `getAttr -t (frame - 1) obj.translateX`;
float $posThenY = `getAttr -t (frame - 1) obj.translateY`;
float $posThenZ = `getAttr -t (frame - 1) obj.translateZ`;

// compute distance traveled per frame:
float $distance = sqrt( pow(($posNowX - $posThenX), 2) +
pow(($posNowY - $posThenY), 2) +
pow(($posNowZ - $posThenZ), 2) );

print ($distance + "\n");
}}}
Newer example:  (thanks Jamie ~McCarter)
{{{
float $t = `currentTime -q`;
vector $currentP = `getAttr object.translate`;
vector $previousP = `getAttr -t ($t-1) object.translate`;
float $dist = mag( $currentP - $previousP );
print( "distance traveled " + $dist + "\n");
}}}
Newer, with matrices! ;) Probably the most accurate way of doing it?
>Note:  It appears that this line "{{{float $prevM[] = `getAttr -t ($t-1) object.worldMatrix`;}}}" causes a cycleCheck warning in Maya.  Not sure why?
{{{
float $t = `currentTime -q`;

float $curM[] = `getAttr object.worldMatrix`;
float $prevM[] = `getAttr -t ($t-1) object.worldMatrix`;
vector $currentP = <<$curM[12], $curM[13],$curM[14]>>;
vector $previousP = <<$prevM[12], $prevM[13],$prevM[14]>>;
float $dist = mag( $currentP - $previousP );

print( "distance traveled " + $dist + "\n");
}}}
Example pulled from [[here|https://stackoverflow.com/questions/31462276/maya-python-how-to-calculate-the-reflection-angle-vector-of-an-given-face-to-ca]], and modified from ~PyMel to the ~API2.0 ~MVector:

Here, we'll reflect our vector across the Y axis:
{{{
import maya.api.OpenMaya as om2
ray = om2.MVector(-2, -3, 1)
nrm = om2.MVector(0,1, 0).normal()
reflection = ray - (2 * (ray*nrm)  * nrm)
print reflection
# (-2, 3, 1)
}}}
From the above link:
<<<
Where {{{ray}}} is the ray from the eyepoint to the surface and {{{nrm}}} is the (normalized) surface normal at the intersection point.
<<<
Sure would be nice if this was a //built in method//... ;)
{{{connectControl}}}
----
Also see:
* [[Emulating connectControl in Qt]]
* [[How can I create a UI control for a given attribute type?]]
Always have trouble trying to connect to the next index in an array attr.
The {{{connectAttr}}} command has a '{{{nextAvailable}}}' arg, but I've never been able to get it to work.
So instead, query the size of the array attr, then connect into that index.
{{{
import pymel.core as pm

nodeArrayAttr = pm.PyNode("myNode.someArrayAttr") # PyMel Attribute
arraySize = len(pm.listAttr(nodeArrayAttr, multi=True))
pm.connectAttr(someOtherNodeAttr, nodeArrayAttr[arraySize])
}}}
Maybe there's a better way?  I'd like to hear it...

It should be noted that while undoing this operation will disconnect the attrs, the new index created will be persistent.  So if you re-execute the code, it will connect to a new index, leaving a gap.  Bug?
The {{{curveInfo}}} node lets you do this.  I havn't actually tried all of this yet, but here's the idea:
Make the {{{curveInfo}}}, and connect your curve to it:
{{{
# Python code
import maya.cmds as mc

lenCalc = mc.createNode("curveInfo")
mc.connectAttr("myCurveShape.worldSpace[0]", lenCalc+".inputCurve")
}}}
The in the //Attribute Editor//, with the new {{{curveInfo}}} selected, go to the "Control Points" drop down, and "Add New Item" for each of the CV's.
Then, those attributes will be available to connect to, either through the 'Connection Editor', or via scripting.

I need to research how to do all of this in mel\Python	
I've made several rigs that could for all intensive purposes be envisioned as suspension bridges:  There is a chain of joints stretched over a certain distance.  There is a controller at the start and end of the chain.  Transforming either the start or the end controller will in-turn transform the chain, but the transformations have a falloff over the distance of the chain.

This is done by constraining all the joints in the chain to both controllers, and then modifying the weights of each constraint to give priority to the closer controller.

Presuming that "target1" is at the same location as "joint1", and "target2" is at the same location as "joint5", here are the resulting weights that would be applied to the joints (based on the below example code):
| | target1 | target 2|
| joint1 | 1.0 | 0 |
| joint2 | .75 | .25 |
| joint3 | .5 | .5 |
| joint4 | .25 | .75 |
| joint5 | 0 | 1.0 |
{{{
# Python code
import maya.cmds as mc

# define our source list of nodes, plus the two targets they'll follow
nodes = ["joint1", "joint2", "joint3", "joint4", "joint5"]
target1 = "locator1"
target2 = "locator2"

# define the "step" a weight will increase or decrease for each node:
weightStep = 1.0/(len(nodes)-1)

for i in range(len(nodes)):
    # create the constraint:
    pc = mc.parentConstraint(target1, target2, nodes[i], maintainOffset=True)
    
    # query the constraint target attrs just created:
    targets = mc.parentConstraint(pc, query=True, targetList=True) 
    
    # calculate the weights to be applied to the two target attrs:
    w1 = abs(1 - i * weightStep) 
    w2 = 1 - w1 
    
    # apply weights:
    mc.setAttr(pc[0]+"."+targets[0]+"W0", w1)  
    mc.setAttr(pc[0]+"."+targets[1]+"W1", w2)
}}}
The above example gives a linear falloff between the targets.  Multiplying the weights based on a (Python) {{{math.sin()}}} function could start to give you a more ease-in \ ease-out weighting solution.
The {{{animView}}} command give access.
Look at the area between frames 0 and 30, and the value range -10 - 100:
{{{
import maya.cmds as mc
mc.animView( 'graphEditor1GraphEd', startTime=0, endTime=30, minValue=0, maxValue=100 )
}}}
{{{graphEditor1GraphEd}}} is the name of the default Graph Editor.
I wanted to build a rig animation controller that would change its wireframe color depending on which 'state' it was in, via a {{{.localGlobal}}} attr (going from 0-1).  If it was in a "local" state, it would be green, in "global" state, red.

FYI, Maya has 32 available override colors:  0 = default color, and 1-31 are other colors.  Red happens to be 13, and green 14.  The {{{colorIndex}}} command will return back the RGB or HSV vals of each index.
{{{
// Define the 'state' node name (a transform) that has our switch attr (.localGlobal), 
// and the shape node name of our animation controller that will change color:
    string $stateNode = ("someTransform");
    string $controllerShape = ("someShape");

// define our color indices.
    int $green = 14;
    int $red = 13;	

// enable our override colors onr our shape:		
    setAttr ($controllerShape + ".overrideEnabled") 1;

// make our condition node, to define what the colors should be:	
    string $con = `createNode condition`;	

// we want values less than .5 to be green, and greater than .5 to be red:	
    setAttr ($con + ".secondTerm") 0.5;

// set our condition to 'greater than'
    setAttr ($con + ".operation") 2;

// define our condition colors:
    setAttr ($con + ".colorIfTrueR") $red;
    setAttr ($con + ".colorIfFalseR") $green;	

// hook our switch attr into our condition:
    connectAttr -f ($stateNode + ".localGlobal") ($con + ".firstTerm");

// and hook the output of the condition into our controllers color:
    connectAttr -f ($con + ".outColorR") ($controllerShape + ".overrideColor");
}}}
Now, when the .localGlobal attr goes from 0-1, it will change the wireframe color from green to red.
Got pointed to the blog post [[here|http://www.185vfx.com/2003/03/convert-a-3d-point-to-2d-screen-space-in-maya/]] by Rob Bredow.  Take a gander.
As I figured, Maya actually does most the heavy lifting for you, if you know what to call.  [[This blog post|http://around-the-corner.typepad.com/adn/2012/08/convert-maya-world-coordinates-to-screen-coordinates.html]] by Cyrille Fauvel describes another way to do it.  

I've condensed those notes down into my own method here:
{{{
import maya.cmds as mc
import maya.OpenMaya as om
import maya.OpenMayaUI as omui

def getScreenSpace(node):
    """
    Return the screen-space position of the node.
    0,0 is the bottom left.  +X goes to the right, +Y goes up.
    """
    activeView = omui.M3dView.active3dView()

    # Can't use 'pointPosition', since that will return values in the 'ui units',
    # and we need them returned in 'internal units', which is what the matrix
    # attr stores:
    mtxList = mc.getAttr("%s.worldMatrix"%node)
    # Extract just the position:
    pt = om.MPoint(mtxList[12], mtxList[13], mtxList[14])

    # Pointer hoop-jumping...
    xPtrInit = om.MScriptUtil()
    yPtrInit = om.MScriptUtil()
    xPtr = xPtrInit.asShortPtr()
    yPtr = yPtrInit.asShortPtr()

    activeView.worldToView(pt, xPtr, yPtr)

    x = xPtrInit.getShort(xPtr)
    y = yPtrInit.getShort(yPtr)

    return x,y
}}}
Docs for ~OpenMayaUI's [[M3dView|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m3d_view.html]]
Via the mel script {{{polyConvertToShell.mel}}}
{{{
\Maya20XX\scripts\others\polyConvertToShell.mel
}}}
The {{{convertSolidTx}}} command.
Also see this script: {{{copyConvertSolidTx.mel}}}

This command can be execute from the Hypershade by selecting a material + a mesh it's assigned to, and 'Edit -> Convert To File Texture []'

Notes:
* I've learned that if you  set {{{antiAlias=True}}}, if you're trying to compute the alpha channel, the command will set it entirely black (usually very wrong).  My guess this is a bug in the tool.  By default it is {{{False}}} and should be left that way.
* If you want to project and convert multiple materials //with alpha//, use a {{{layeredTexture}}} node, not a {{{layeredShader}}} : {{{layeredShader}}} nodes //don't// pass alpha out to the convert to solid command.
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
}}}
I wanted a way to turn mesh wireframe into a solid volume, so I could 3d-print it.  Sort of like turning a sphere into a solid geodetic dome.  This would be like extruding a circle down every edge of a polygonal mesh, and welding up all the spots they touch.  One of the modelers were I work (Sean Marino) tipped me off to this technique:
*Pick all the faces
*Turn off "Keep faces together"
*Extrude Face : Set Divisions to 1, Increase offset to set desired edge width.
*Delete new faces.
*Re-select all faces.
*Turn on "Keep faces together"
*Extrude Face : Set divisions to 1.  Increase thickness.  Done
Based on that workflow, here's a simple script to do it:
{{{
import maya.cmds as mc

def wireframeToSolid(offset):
    """
    Based on the given selection of a single polygonal object, convert it's wireframe
    to a solid volume.  Will create a 'thickness' attribute on the mesh object, for adjustment
    of the size of the wireframe volume after creation.
    
    Parameters:
    offset : float : The thickness of the wires.
    """
    sel = mc.ls(selection=True)
    assert len(sel)==1, "Please select a single polygonal object"
    # Create the custom attr:
    if not mc.objExists("%s.thickness"%sel[0]):
        mc.addAttr(sel[0], longName="thickness", defaultValue=offset, keyable=True)
    mc.setAttr("%s.thickness"%sel[0], offset)

    mc.select(sel)
    origFaces = mc.polyListComponentConversion(toFace=True)
    mc.select(origFaces)
    extrudeA = mc.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=0, divisions=1, twist=0, taper=1, off=offset, thickness=0)[0]
    mc.delete() # Delete the interior faces creating holes.
    mc.select(sel) # Reselect the whole object
    newFaces = mc.polyListComponentConversion(toFace=True)
    mc.select(newFaces)
    extrudeB = mc.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=1, divisions=1, twist=0, taper=1, off=0, thickness=offset)[0]

    # Hook up the custom attr:
    mc.connectAttr('%s.thickness'%sel[0], '%s.offset'%extrudeA)
    mc.connectAttr('%s.thickness'%sel[0], '%s.thickness'%extrudeB)
    print "wireframeToSolid: Complete!"
}}}
To use, pick a single poly object and execute:
{{{
wireframeToSolid(.05)
}}}
----
There also appears to be a tool that does this too on Creative Crash:
http://www.creativecrash.com/maya/downloads/scripts-plugins/modeling/poly-tools/c/convert-to-polywire
The {{{vectorProduct}}} node can aid with this, concepts below, using a joint.
*Create a new {{{vectorProduct}}} node, and set it's 'operation' to 'Vector Matrix Product'.
*Connect the output {{{myJoint.matrix}}} attr to the {{{myVectorProduct.matrix}}} input attr.
*Set the 'input1' section of the {{{vectorProduct}}} node to which local axis of the joint you'd like to track.
*The 'output' XYZ values of the {{{vectorProduct}}} node should now reflect the position in space that local axis is, one unit away from origin.
http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/Nodes/vectorProduct.html 
{{{polyClipboard}}}
This will copy\paste data in the top half (the 'reporter') of the Script Editor:
{{{
cmdScrollFieldReporter -e -copySelection "cmdScrollFieldReporter1";
cmdScrollFieldReporter -e -pasteSelection "cmdScrollFieldReporter1";
}}}
This will copy\paste data in the bottom half (the 'executer') of the Script Editor:
{{{
cmdScrollFieldExecuter -e -copySelection "cmdScrollFieldExecuter1";
cmdScrollFieldExecuter -e -pasteSelection "cmdScrollFieldExecuter1"
}}}
Maya has no built-in way to do this, but it does ship with the Image Magick {{{imconvert.exe}}}, for doing stuff like this along the commandline.

I had to come up with this solution since Maya's {{{surfaceSampler}}} tool can generate color diffuse maps, and alpha transparency maps, but //not in the same map// :S  I need a 32 bit tga //with alpha//, so this is the solution I came up with.
{{{
import os
import sys
import subprocess

def getImconvertExe():
    binDir = os.path.dirname(sys.executable)
    exe = '%s\\imconvert.exe'%binDir
    if not os.path.isfile(exe):
        raise Exception("Missing this file from disk: %s"%exe)
    return exe

def copyAlpha(sourceTex, alphaTex, outputTex):
    imconvert = getImconvertExe()
    commandCalls = [imconvert, sourceTex, alphaTex, "-alpha", "off", "-compose", "copy_opacity", "-composite", outputTex ]
    # Need to use communicate, to halt Maya from continuing in execution until this is over.
    fail = subprocess.Popen(commandCalls, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    return fail

colTex =  r"C:\temp\maya\transferMaps\sampledDiffuseColor.tga"
alphaTex = r"C:\temp\maya\transferMaps\sampledAlpha.tga"
targetTex = r"C:\temp\maya\transferMaps\sampledDiffuseColorAlpha.tga"

copyAlpha(colTex, alphaTex, targetTex)
}}}
{{{popupMenu}}}
See the docs on these subjects:
* [[attrControlGrp|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/attrControlGrp.html]]
** This command creates a control of the type most appropriate for the specified attribute, and associates the control with the attribute. Any change to the control will cause a change in the attribute value, and any change to the attribute value will be reflected in the control. Not all attribute types are supported.
* [[attrFieldGrp|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/attrFieldGrp.html]]
** This command creates a pre-packaged collection of label text, plus two to four float fields. These fields will be attached to the specified vector attribute, so that changes in either will be reflected in the other.
* [[attrColorSliderGrp|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/attrColorSliderGrp.html]]
** Create a color slider group consisting of a label, a color canvas, a slider and a button. Clicking on the canvas will bring up the color editor. If the button is visible, it will allow you to map a texture to the attribute.
* [[attrNavigationControlGrp|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/attrNavigationControlGrp.html]]
** This command creates a pre-packaged label navigation button. The group is used to help the user manage connections to a particular attribute.
----
Also, see:
* [[connectControl|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/connectControl.html]]
* [[Emulating connectControl in Qt]]
Maya comes with a script to do this, which lives here (Maya 2008):
{{{
<drive>:\Program Files\Autodesk\Maya<version>\scripts\others\clusterCurve.mel 
}}}
However, it doesn't return anything, just does work.  Here is a modified snippet that can provide the cluster info:
{{{
// $curve is the shape of some curve
string $clusters[];
int $numCVs = `getAttr -size ($curve + ".controlPoints")`;
int $i = 0;
for ($i; $i < $numCVs; $i++)
	{
	//make CV into a cluster
	string $c[] = `cluster -relative ($curve + ".cv[" + $i + "]")`;
	$clusters[$i] = $c[0];
	}
}}}
----
Also see:
*[[How can I get a list of all the CV's on a curve?]]
Check out the docs on the {{{draggerContext}}} command.
I seem to end up doing this a lot.  Using Python and 'list comprehensions', it's really, really easy:

In this example, we'll base it on a selection of verts:
{{{
# Python code
import maya.cmds as mc

verts = mc.ls(selection=True, flatten=True)
pos = [mc.pointPosition(v, world=True) for v in verts]
myCurve = mc.curve(degree=1, point=pos)
}}}
That's it.  If you were basing it of a selection of transforms, you'd need to change your {{{pointPosition}}} arg to '{{{v+".rotatePivot}}}' to pull the proper location, fyi.
A combo of the mel {{{copyAEWindow}}} & {{{showEditor}}} global procs, which lives in this script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\showEditor.mel
}}}
{{{
string $sel[] = `ls -sl`;
showEditor $sel[0];
copyAEWindow;
}}}
This will display an Attribute Editor docked in the main panel, then pop a dupe off.
----
Also see:
*[[I want to open an Attribute Editor for a certain object. How to do so?]]
Say you have a hierarchy of joints, and you want to walk over each joint in the hierarchy, and do something to that joint, and its children?  This simple Python generator has you covered:  For each item in the hierarchy it will return the current item, and any children it has.  It will keep doing this until the hierarchy fully walked.
Given this example joint hierarchy:
*grandparent
**grandchildA
***greatGrandchildA1
***greatGrandchildA2
**grandchildB
***greatGrandchildB1
***greatGrandchildB2
Run the below code:
{{{
import pymel.core as pm

def jointWalk(root):
    kids = [pm.PyNode(root)]
    for k in kids:
        children = pm.listRelatives(k, type='joint')
        kids.extend(children)
        yield (k, children)
}}}
{{{
root = 'grandparent'
for parent, children in jointWalk(root):
    print parent, children

nt.Joint(u'grandparent') [nt.Joint(u'grandchildA'), nt.Joint(u'grandchildB')]
nt.Joint(u'grandchildA') [nt.Joint(u'greatGrandchildA1'), nt.Joint(u'greatGrandchildA2')]
nt.Joint(u'grandchildB') [nt.Joint(u'greatGrandchildB1'), nt.Joint(u'greatGrandchildB2')]
nt.Joint(u'greatGrandchildA1') []
nt.Joint(u'greatGrandchildA2') []
nt.Joint(u'greatGrandchildB1') []
nt.Joint(u'greatGrandchildB2') []
}}}
Simple function to build the material & shading engine, and connect them together.
{{{
# Python code
import maya.cmds as mc

def makeMatAndShader(matName, matType):
    material = mc.shadingNode(matType, asShader=True, name=matName)
    # The material will be selected when created:  Need to de-select before
    # Creating our shadingEngine (since it's a set).
    mc.select(clear=True)
    # Nope, can't do it this way, won't shade properly later for some reason:
    # shader = mc.createNode('shadingEngine', name='%sSG'%material)
    # Need to do it this way:
    shader = mc.sets(renderable=True, name='%sSG'%material) 
    # Can use this rather than a call to connectAttr:
    mc.defaultNavigation(connectToExisting=True, source=material, destination=shader)
    return (material, shader)
}}}
Or ~PyMel Code:
{{{
import pymel.core as pm

def makeMatAndShader(matName, matType):
    material = pm.shadingNode(matType, asShader=True, name=matName)
    shader = pm.sets(renderable=True, noSurfaceShader=True, empty=True,
                     name='%sSG'%material.nodeName())
    material.outColor >> shader.surfaceShader
    return (material, shader)
}}}
{{{
matName = 'myPhong'
matType = 'phong'
print makeMatAndShader(matName, matType)
}}}
{{{
(u'myPhong', u'myPhongSG')
}}}
http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/layoutDialog.html

Similar to both {{{confirmDialog}}} & {{{promptDialog}}}, the  {{{layoutDialog}}} allows for a more robust modal-dialog solution by embedding a {{{formLayout}}} into the dialog itself.

The main problem I've found with more complex {{{layoutDialog}}}s is the retrieval of any internal data defined within:  The below two examples show ways to capture this info either through a global dictionary, or via optionVars:

----
In this example, a simple window is craeted that lets the user enter two values.  The values are later retireved via a ''global dictionary'':
{{{
from functools import partial as callback
import maya.cmds as mc

# This dict will hold values created\changed inside the layoutDialog:
modalData = {}

def updateModealData(key, *args):
    # Callback used to update the dict from inside the layoutDialog
    modalData[key]=args[0]

def myModalWindow():
    """
    Create a modal dialog allowing for the user to set certain values.
    """
    # Get the formLayout created via the layoutDialog:
    form = mc.setParent(query=True)
    # layoutDialog's are not resizable, so hard code a size here,
    # to make sure all UI elements are visible.
    mc.formLayout(form, edit=True, width=300)
    col = mc.columnLayout(adjustableColumn = True)

    mc.text(align='left', label="Insert some interesting text here!")
    mc.separator(height=20)

    defaultValA = mc.currentTime(query=True)
    defaultValB = 0
    # update these with the default values, in case the user doesn't change anything
    modalData["valA"]=defaultValA
    modalData["valB"]=defaultValB

    mc.intFieldGrp(label="Value A:", value1=defaultValA, changeCommand=callback(updateModealData, "valA"))
    mc.intFieldGrp(label="Value B:", value1=defaultValB, changeCommand=callback(updateModealData, "valB"))
    mc.separator(height=10, style='none')
    mc.button(label="Do Something!", backgroundColor=[.2,.9,.2], command="import maya.cmds as mc; mc.layoutDialog(dismiss='doit')")

    mc.formLayout(form, edit=True, attachForm=[(col, 'top', 5),
                                               (col, 'bottom', 5),
                                               (col, 'left', 5),
                                               (col, 'right', 5)])

def main():
    result = mc.layoutDialog(ui=myModalWindow, title="My Modal Window")
    if result == "doit":
        print modalData

main()

}}}
----
In this example, a simple window is created that lets the user enter two values. the values are later retrieved via ''optionVars''
{{{
import maya.cmds as mc

ovA = "ov_startFrame"
ovB = "ov_offset"

def myModalWindow():
    """
    Create a modal dialog allowing for the user to set certain values.
    """
    # Get the formLayout created via the layoutDialog:
    form = mc.setParent(query=True)
    # layoutDialog's are not resizable, so hard code a size here,
    # to make sure all UI elements are visible.
    mc.formLayout(form, edit=True, width=300)
    col = mc.columnLayout(adjustableColumn = True)

    mc.text(align='left', label="Insert some interesting text here!")
    mc.separator(height=20)

    defaultValA = mc.currentTime(query=True)
    defaultValB = 0
    mc.optionVar(intValue=[ovA, defaultValA])
    mc.optionVar(intValue=[ovB, defaultValB])

    mc.intFieldGrp(label="Value A:", value1=defaultValA, changeCommand=lambda *args: mc.optionVar(intValue=[ovA, args[0]]) )
    mc.intFieldGrp(label="Value B:", value1=defaultValB, changeCommand=lambda *args: mc.optionVar(intValue=[ovB, args[0]]) )
    mc.separator(height=10, style='none')
    mc.button(label="Do Something!", backgroundColor=[.2,.9,.2], command="import maya.cmds as mc; mc.layoutDialog(dismiss='doit')")

    mc.formLayout(form, edit=True, attachForm=[(col, 'top', 5),
                                               (col, 'bottom', 5),
                                               (col, 'left', 5),
                                               (col, 'right', 5)])

def main():
    result = mc.layoutDialog(ui=myModalWindow, title="My Modal Window")
    if result == "doit":
        valA = mc.optionVar(query=ovA)
        valB = mc.optionVar(query=ovB)
        print valA, valB

main()
}}}
The [[polyCreateFacet|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/CommandsPython/polyCreateFacet.html]] command.

It works by you passing in vert positions, which it creates one at a time, and auto-generates the triangles that go along with them.
Often when making UI's you want a {{{fileBrowserDialog}}} to consistently open in the same location.  Or, remember the last place it was opened from.  The below example shows how to store a setting  that remembers the last dir that was accessed, and repoint the browser there on later browsing expeditions.

The magic is the the {{{workspace}}} command's {{{directory}}} arg:  Need to set that sucker to your saved values.  Took me a while to figure that out...

I've labeled each section in the order it is executed so it makes more sense ;)
{{{
# Python code:
import os
import maya.cmds as mc

# Section 1
# Query the current workspace, since we'll end up changing it while we
# open our browsers, and need to set it back to normal later.
currentWorkspace = mc.workspace(query=True, active=True)

# Section 4
# What is called to via the fileBrowserDialog.  This is where the 'action code'
# would take place once a file from the browser was selected:
def fbdCommand(fileName, fileType):
    # Do stuff:
    #    stuff, stuff, stuff....

    # Then store the path preset:
    path = os.path.split(fileName)[0]
    mc.optionVar(stringValue=['testPath', path])

# Section 2
# Load our directory preset (if it exists) before launching the dialog:
if mc.optionVar(exists='testPath'):
    savedPath = mc.optionVar(query='testPath')
    if os.path.isdir(savedPath):
        # This is the magic that lets us configure where the browser opens:
        mc.workspace(directory=savedPath)

# Make a file-save dialog (for example).  We wrap its execution in a try\finally
# clause to make sure that even if it fails, we can set our workspace back.
try:
    # Section 3
    mc.fileBrowserDialog(mode=1, fileCommand=fbdCommand, fileType='mayaAscii',
                          operationMode='SaveAs', actionName="Save_as")
finally:
    # Section 5
    # Now set our workspace back to normal:
    if os.path.isdir(currentWorkspace):
        mc.workspace(directory=currentWorkspace)
}}}
If you're trying to open the dialog in 'directory mode', which is mode 4, then this subject has additional notes:
[[How can I modify the current project \ workspace?]]
*Use an 'if statement' with correct variable scope: 
*Example 1 (keep scope within the variable):
{{{
string $foo = "abcd";
if(1)
    $foo = "dcba";
print $foo;
// result: "dcba" //
}}}
**This works for two reasons:  {{{$foo}}} is defined first, outside the scope of the 'if statement'.  Second, when {{{$foo}}} is updaetd inside the 'if statement', since it's being updated by '{{{$foo = ...}}}', //rather// than '{{{string $foo = ...}}}', then it keeps the original scope of {{{$foo}}}.  If we had //instead//  updated it with '{{{string $foo = ...}}}', then the print statement would have printed the original value, since we would have broken out of its original scope.  In a nutshell, if you put the variable descriptor before variable name (like {{{string}}}), then that defines the start of a new scope for that variable name.  Every time after that, if you call direcltly to the variable name without the descriptor, then you're still acting within the scope of that original variable.  I really hope this makes some sense.

*Example 2 (//loose// original variable scope due to re-declaring the variable inside the 'if statement'):
{{{
string $foo = "abcd";
if(1)
    string $foo = "dcba";
print $foo;
// result: "abcd" //
}}}

*Example 3:  //Or//, use a 'conditional statement':
{{{
string $bob = "asdf";
string $lary = $bob == "asdf" ? "happy" : "sad";
}}}
**so, //if// $bob = asdf, then $lary is "happy". Otherwise, $lary is "sad".
**The conditional statements can be nested, to provide different return values.
There is the //mel// procedure
{{{
source cleanUpScene;
deleteUnknownNodes();
}}}
Which lives in this script:
{{{
C:\Program Files\Autodesk\Maya<VERSION>\scripts\startup\cleanUpScene.mel
}}}
Optionally you can roll your own.  But I've found the above method to be a bit more robust.
{{{
import maya.cmds as mc
unknown = mc.ls(type='unknown')
if unknown:
    mc.delete(unknown)
}}}
{{{
camera -e -startupCamera 0 $camName;
delete $camName;
}}}
Default camera's ({{{persp}}}, {{{top}}}, {{{front}}}, {{{side}}}) have a special flag that is set to let Maya know they are "startup cameras".  This keeps the cameras persistent between different Maya files when you're opening them, importing, etc.  Have you even wondered why when you import multiple maya files the cameras don't start piling up?  

There have been issues when external tools start to monkey with the cameras, renaming them, or evening creating duplicates, that can't be deleted.  Using this method, you can now delete them.  FYI, there always needs to be at least one cam in the scene listed as a "startup camera".
!!!mel:
I'm not sure what version of Maya introduced it, but //at least// by Maya 2009 the {{{sysFile}}} command has a {{{-removeEmptyDir}}} arg (thanks for the tip from Matti Grüner)
{{{
sysFile -removeEmptyDir "c:/temp";
}}}
http://download.autodesk.com/us/maya/2009help/Commands/sysFile.html
!!!Python:
Of course if you have Maya 8.5 or newer you can just use Python:
This only works if the dir is empty:
{{{
import os
os.rmdir("c:/temp")
}}}
if you want to remove a directory, and all the stuff in it:
{{{
import shutil
shutil.rmtree("c:/temp")
}}}
!!!Direct Windows system call:
The DOS {{{rmdir}}} command.
Example: Delete a directory and all files in it ({{{/s}}}), without prompt ({{{/q}}}):
{{{
system("rmdir /s /q c:/temp");
}}}

{{{deleteUI "buttonName";}}}
And then be sure to save the shelfs\prefs
Here is some code to figure out the members of what the current shelf is.
{{{
string $buttons[]; clear $buttons;
global string $gShelfTopLevel;
if (`tabLayout -exists $gShelfTopLevel`) 
    {
    string $currentShelf = `tabLayout -query -selectTab $gShelfTopLevel`;
    $buttons = `shelfLayout -q -ca $currentShelf`;
    }
print $buttons;
}}}
While you're working on contexts, it's often handy to delete them and remake them (not unlike windows) to see your updates:
{{{
if(`contextInfo -exists "myContextName` == 1){
    deleteUI -toolContext "myContextName";
}
// remake context here:
}}}
The below code will search the scene for all materials with the given string in their name, then delete anything assigned to them, and finally delete the material and shader as well.
{{{
import pymel.core as pm

def delDumbMat(matString):
    mats = pm.ls("*%s*"%matString, materials=True)
    for m in mats:
        shader = pm.listConnections(m, source=False, destination=True, type='shadingEngine')
        stuff = pm.sets(shader[0], query=True)
        if stuff:
            pm.delete(stuff)
        pm.delete([m, shader])
}}}
Often times I want to cleanup a scene by deleting all leaf nodes:  Nodes that don't have children.  Or, hierarchies of the same kind of node that don't have any meaningful children.
{{{
# Python code
import maya.cmds as mc

def delLeafByType(types):
    """
    types : list : List of given DAG node types to delete, presuming
        they're leaf of a hierarchy.
    """
    totalDel = 0
    triedToDel = 0
    leaf = mc.ls(dag=True, leaf=True)
    noDel = []

    while len(leaf) > 0 and len(leaf) >= len(noDel):
        for i in leaf:
            try:
                if mc.objectType(i) in types:
                    mc.delete(i)
                    totalDel += 1
                else:
                    noDel.append(i)
            except Exception, e:
                noDel.append(i)
                triedToDel += 1
                print str(e).strip(), i
        leaf = mc.ls(dag=True, leaf=True)

    if triedToDel:
        print "Deleted %s leaf nodes, tried to delete %s others but couldn't.  Check Script Editor."%(totalDel, triedToDel)
    else:
        print "Deleted %s leaf nodes"%totalDel
}}}
{{{
delLeafByType(["transform", "locator"])
}}}
{{{
Deleted 14 leaf nodes
}}}
Often, when I'm cleaning a scene for export to game, I want to make sure only nodes of a certain type live in it.  Here's one way to simply remove everything from the scene that isn't part of the list of "acceptable nodes" you want.
{{{
# Python code
import maya.cmds as mc

def delAllByTypes(exceptThese, dag=False):
    """
    Delete all nodes in the scene, except the ones provided by the user.
    If anything is flagged for deletion and can't be, print exception results.

    exceptThese : list : node types that *shouldn't* be deleted.
    dag : bool : Optional.  If True, will limit node types to only
        DAG nodes (transforms & shapes and their associates).  Otherwise,
        look to ALL node types.
    """
    nodes = []
    if dag:
        nodes = mc.ls(dag=True)
    else:
        nodes = mc.ls()
    nodes.reverse()

    removal = []
    for d in nodes:
        if mc.objectType(d) not in exceptThese:
            removal.append(d)

    for r in removal:
        try:
            mc.delete(r)
        except Exception, e:
            print str(e).strip(), r
}}}
{{{
# delete all DAG nodes in the scene that aren't of type 'good':
good = ["joint", "transform", "mesh"]
delAllByTypes(good, dag=True)
}}}
Execute the proc:
{{{
MLdeleteUnused;
}}}
Which lives in the script here:  {{{../MayaX.X/scripts/others/MLdeleteUnused.mel}}}
"ML" stands for "~MultiLister" the old version of the Hypershade (which was brought into Maya from Alias).

This code is also called to when you use "Optimize Scene Size" from the File menu, or in the '~HyperShade -> Edit -> Delete Unused Nodes'.
Had an animator request a way to deselect any highlighted channels in the channel box:  I could find no way via the {{{channelBox}}} command to do this.  The docs list it having a {{{-select}}} flag, but when you try to run it, the command says it's unsupported.

I came up with the below hack:  You can't simply deselect\reselect the current nodes via code to clear the channel box highlighting (but it works doing it by hand):  Maya will keep the channels highlighted.  But by running an {{{evalDeferred}}} on the reselect, it'll clear the highlighting.
{{{
# Deselect the highlighted channels in the Channel Box
import maya.cmds as mc

reselectMe = []

def doReselect():
    global reselectMe
    if reselectMe:
        mc.select(reselectMe, replace=True)

def deselChannelBox():
    global reselectMe
    reselectMe = mc.ls(selection=True)
    mc.select(clear=True)
    mc.evalDeferred(doReselect)
    
deselChannelBox()
}}}
[[pointCurveConstraint|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/pointCurveConstraint.html]]
This command will constraint a point on a curve to //something else//.  It does not constrain things //to// a curve.
It implements this via a [[leastSquaresModifier|http://download.autodesk.com/global/docs/maya2012/en_us/Nodes/leastSquaresModifier.html]] node.
In Maya 2015-ish you can change viewport gamma.  Based on our production, this caused problems.  To disable:
{{{
import maya.cmds as mc	
mc.colorManagementPrefs(edit=True, cmEnabled=False)
}}}
This doesn't just 'turn it off', but physically disables it, graying out the button in the viewport menubar.

If you don't want to physically disable the //whole system//, and just want to turn it off in a given viewport:
{{{
import maya.cmds as mc
mc.modelEditor("modelPanel4", edit=True, cmEnabled=False)
}}}
Below code will find all the incoming and outgoing connections to a node, and //DESTROY// them.
{{{
# Python code
import maya.cmds as mc

node = 'myNode'
incoming = mc.listConnections(node, source=True, destination=False, plugs=True, connections=True)
outgoing = mc.listConnections(node, source=False, destination=True, plugs=True, connections=True)
if incoming:
    for i in range(0,len(incoming),2):
        mc.disconnectAttr(incoming[i+1], incoming[i])
if outgoing:
    for i in range(0,len(outgoing),2):
        mc.disconnectAttr(outgoing[i], outgoing[i+1])
}}}
----
Also see:
*[[How can I disconnect an attribute?]]
{{{disconnectAttr}}}
''Some examples'':
''mel'':
{{{
// Disconnect only keyable attrs.  
string $obj = "null1";
string $keyable[] = `listAttr -k $obj`;

for($i=0;$i<size($keyable);$i++)
	{
	string $incoming[] = `listConnections -s 1 -d 0 -p 1 -c 1 ($obj+"."+$keyable[$i])`;
	if (size($incoming) )
		disconnectAttr $incoming[1] $incoming[0];
	}
}}}
{{{
// Disconnect ALL incoming connections.
string $obj = "null1";
string $incoming[] = `listConnections -s 1 -d 0 -p 1 -c 1 $obj`;

for($i=0;$i<size($incoming);$i=$i+2)
	disconnectAttr $incoming[$i+1] $incoming[$i];
}}}
''Python'':
{{{
import maya.cmds as mc

string obj = "null1"
string keyable = mc.listAttr(obj, keyable=True)
for k in keyable:
    incoming = mc.listConnections('%s.%s'%(obj,k), source=True,
                                  destination=False, plugs=True, connections=True)
    if incoming is not None:
	mc.disconnectAttr(incoming[1], incoming[0])
}}}
In both examples we have {{{listConnections}}} return back both plugs and connections, which gives us a list of source\destination objec.attr pairs, making them easy to feed directly into {{{disconnectAttr}}}.
----
Here's another Python example using {{{connectionInfo}}}, which simplifies things a bit:
{{{
objAttr = 'muNode.rotateX'
con = mc.connectionInfo(objAttr, sourceFromDestination=True)
if con:
    mc.disconnectAttr(con, objAttr)
}}}
----
Also see:
*[[How can I disconnect all connections to a node?]]
In years past I've had success with {{{polyGeoSampler}}}... but not lately.
----
You can also use this method:
*Map a texture to the displacement channel of the mesh's materials shading engine (via the ui, so it creates the appropriate {{{displacementShader}}} node and connections in the process.
*On the mesh's shape node, under the {{{Displacement Map}}} dropdown, set the {{{initialSampleRate}}} and {{{extraSampleRate}}} attrs to 1.
*Modify -> Convert -> Displacement to polygons.
This will triangulate the mesh in the process, but shouldn't add any extra verts.
{{{waitCursor}}}
Best to make the border size bigger too in the process:
{{{
polyOptions -activeObjects -displayBorder true -global -sizeBorder 10;
}}}
You can also add the {{{-relative}}} flag, which will turn it into a toggle.

To turn it back off:
{{{
polyOptions -activeObjects -displayBorder false;
}}}
Around Maya 2010 (maybe earlier) they have a wiz-bang feature that displays a soothing gradient color in the background of the viewports.  To turn it on, the default hotkey is {{{ALT+B}}}.  Or, you can use code (after hacking apart {{{cycleBackgroundColor.mel}}}):
{{{
# Python code
import maya.cmds as mc

mc.displayPref(displayGradient=True)
mc.optionVar(intValue=["displayGradient", 1])
}}}
I know WAY more stuff in mel than in dos.  But if you operate in windows, and need to do stuff in the system, then you'll need to know how to do it in dos.  Here are some links I've found that are useful for explaining things like batch scripts, redirection, variables, etc.  The list will grow... 
*http://www.febooti.com/products/command-line-email/batch-files/
*http://www.computerhope.com/msdos.htm
Python has no built-in vector math calls :(  You can see my notes on this [[here|What mel commands are 'missing' from the Python integration?]].  In Maya however, you have a few solutions to continue to do vector math:
----
''Method A'':  Write your own wrappers around the @@mel@@ vector commands.   You could do it, but that would be silly:
{{{
import maya.mel as mm
def mvCross(vec1, vec2):
    mVec1 = vec1.__str__().replace("[", "<<").replace("]",">>")
    mVec2 = vec2.__str__().replace("[", "<<").replace("]",">>")
    res = mm.eval("cross(" + mVec1 + ", " + mVec2 + ")")
    return list(res)

vec1 = [0,1,0]
vec2 = [1,0,0]
cross = mvCross(vec1,vec2)
print cross
# [0.0, 0.0, -1.0]
}}}
----
''Method B'':  Call to {{{maya.OpenMaya}}}, and use API {{{MVector}}} ([[docs|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_vector.html]]) goodness.  ''This is the recommended way.''
{{{
import maya.OpenMaya as om
vec1 = om.MVector(0,1,0)
vec2 = om.MVector(1,0,0)
vec3 = vec1^vec2  # calculate cross product
print vec3.x, vec3.y, vec3.z
# 0.0 0.0 -1.0
}}}
*{{{*}}} operator: The dot product of two {{{MVector}}}s.
*{{{^}}} operator: The cross product of two {{{MVector}}}s.
----
''Method C'':  Write your own Python vector module, or get someone else's.  But why do that when the API gives you all you need?
*I document the math behind some of the basic vector functions [[here|Vector Math]]
*[[Vector|http://code.activestate.com/recipes/578006-vector/]] : A fairly robust looking 2d vector solution.
*The 2d and 3d vector classes in the [[gameobjects|http://code.google.com/p/gameobjects/]] package (over at Google Code).
*~PyGame have contributed [[Vec2d|http://pygame.org/wiki/2DVectorClass]] and [[Vec3d|http://pygame.org/wiki/3DVectorClass]] classes.
----
''Method D'': Get {{{NumPy}}}
http://numpy.scipy.org
I've not tested it, but it's possible you could get a speed boost based on Numpy's ndarray objects.
You'll need to make sure that the lib is installed where Maya can see it, like here:
{{{
C:\Program Files\Autodesk\Maya<version>\Python\lib\site-packages
}}}
Example:
{{{
import numpy
v1 = (1,2,3)
v2 = (3,2,1)
v3 = numpy.cross (u, v)
}}}
Based on the example in [[Query which standard Maya dockControl is visible]] : you can learn the default control names for the Channel Box, Attribute Editor, etc.  Based on that, how can you dock \ undock them via code?
{{{
import pymel.core as pm

# To dock the Channel Box:
pm.dockControl("MayaWindow|dockControl1", edit=True, floating=False) 

# To undock the Attribute Editor:
pm.dockControl("MayaWindow|dockControl4", edit=True, floating=True) 
}}}
Also see:
*[[Creating a dockable window via PySide and workspaceControl]]
*[[Understanding window docking and 'workspaceControls']]
{{{inViewMessage}}} creates a great little bubble-shaped message that can be different colors and fade out, etc.
http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/inViewMessage.html
{{{
import maya.cmds as mc
mc.inViewMessage(assistMessage='In-view message <hl>test</hl>.', position='midCenter', fade=True, backColor=0x00990000)
}}}
----
You can use the {{{headsUpMessage}}} command to do this.  It will disappear on the next screen refresh (by default).  It has arguments to have it display for a fixed amount of time, or be centered on a particular object \ object selection.

You could do something similar with a {{{confirmDialog}}} or {{{promptDialog}}}, but they are modal dialog boxes that stop the user from interacting until their condition is met.  {{{headsUpMessage}}} doesn't stop the user from interaction, and automatically goes away.

From the docs:
{{{
headsUpMessage "Ouch!";
headsUpMessage -object circle1 "This is Circle 1";
headsUpMessage -selection "These objects are selected";
headsUpMessage -time 5.0 "Text appears for minimum of 5 seconds.";
headsUpMessage -verticalOffset 20 "Text appears 20 pixels above point.";
headsUpMessage -horizontalOffset -20 "Text appears 20 pixels to the left of the point.";
}}}
For the {{{backColor}}}, it's hexadecimal:
* Sets the background color for the message using the format 0xAARRGGBB, alpha is not taken into account.
https://www.hexcolortool.com 
{{{createDrawCtx}}}
I often end up with long chains of joints, like so:
*tentacleA1
**tentacleA2
***tentacleA3
****etc...
Which I want to duplicate.   Maya has an option to duplicate a node and give all children unique names.  But what I want is to duplicate the hierarchy and rename the hierarchy in the process.  So I'd get something like this:
*tentacleB1
**tentacleB2
***tentacleB3
****etc...
And, code:
{{{
import pymel.core as pm

def dupeAndRename(replaceThis, withThis):
    oldNames = [node.nodeName() for node in sel]
    new = pm.duplicate(sel)
    for i,n in enumerate(new):
        newName = oldNames[i].replace(replaceThis, withThis)
        n.rename(newName)
        kids = pm.listRelatives(n, type='transform')
        for k in kids:
            newKName = k.nodeName().replace(replaceThis, withThis)
            k.rename(newKName)
    return new
}}}
To use it, I'd just select the {{{tentacleA1}}} root, and run the code like so:
{{{
sel = pm.ls(selection=True)
dupeAndRename("A", "B")
}}}
Presuming you have a node with children, how can you duplicate it, but not its children?
You can use the {{{parentOnly}}} argument of the {{{duplicate}}} command
{{{
# Python code
import maya.cmds as mc

sel = mc.ls(selection=True, long=True)
for s in sel:
    new = mc.duplicate(s, parentOnly=True)
}}}
----
Old data, incorrect concept (the execution still works, but obviously it's unneeded).
<<<
To my knowledge, you can't:  The children are always created during the duplication.  However, you can duplicate it, and then delete the new children:
{{{
# Python code
import maya.cmds as mc

sel = mc.ls(selection=True, long=True)
for s in sel:
    new = mc.duplicate(s, renameChildren=True,  returnRootsOnly=True)
    kids = mc.listRelatives(new, children=True,  fullPath=True)
    if kids is not None:
        mc.delete(kids)
}}}
<<<
If a node lives in a set, when you duplicate the node, the new duplicate well live in that set as well.  But, what if you don't want this behavior?
{{{
import maya.cmds as mc

def dupeNoSet(nodesInSet):
    """
    Duplicate the passed in nodes, and remove them from any sets the original
    nodes were in.

    Args:
    nodesInSet : ["node", ...] : List of node names to duplicate.
    
    return : ["node", ...] : List of duplicated node nams.
    """
    if len(nodesInSet) == 0:
        return []
    newNodes = mc.duplicate(nodesInSet, renameChildren=True)
    
    # Rename the new joints, and fill our jointMap dict:
    for i in range(len(newNodes)):
        outgoing = mc.listConnections(newNodes[i], source=False,
                                      destination=True, plugs=True,
                                      connections=True)
        if outgoing is not None:
            for i in range(0, len(outgoing), 2):
                if "instObjGroups" in outgoing[i]:
                    try:
                        mc.disconnectAttr(outgoing[i], outgoing[i+1])
                    except:
                        # there can be dupe reports in our outgoing list, so skip
                        # if its already been disconnected.
                        pass
    return newNodes
}}}
{{{
nodesInSet = mc.ls(selection=True, long=True)
dupes = dupeNoSet(nodesInSet)
}}}
{{{
objectName.f[*];
}}}
One of the most important things when painting smooth skin weights, is holding and unholding influence weights.  If an influence is 'unheld' (unlocked), its influence weights can be modified.  If it is held (locked), its influence weights can't be modified.  In general when painting weights, you want ALL the influences held except the current two you're working on.

In the 'Paint Skin Weights Tool', under the 'Influence' frame, it shows the influence list for everything effecting that geo, and a button at the bottom to 'Toggle Hold Weights on Selected'.  If you have 100 joints, then you need to pick each one in the list, and hit that button.  This can be, really time consuming.  What if you want to simply hold all the weights at once, then unhold just the two of your choosing?

Two simple shelf buttons can be written to do this, the code is below.  The key is to query all the nodes in the scene with a {{{.liw}}} attr.  {{{.liw}}} stands for "Lock Influence Weights", and any node that is added as a smooth skin influence will have this attr added to it.  If the value is 1, the weights are locked\held.  If the value is 0, the weights are unlocked\unheld.
{{{
// 1 = lock\hold, 0 = unlocked\unheld.  
//Change this for each shelf button
int $lockState = 1;

string $boundJoints[] = `ls "*.liw"`;
for($joint in $boundJoints)
	setAttr $joint $lockState;

if($lockState)
	print "Locked all influence weights in scene\n";
else
	print "Unlocked all influence weights in scene\n";

// optional:  Relaunch the 'Paint Skin Weights Tool' 
//to see the effect of the code:
ArtPaintSkinWeightsToolOptions;
}}}
Also see:
*[[How is a joint's ".liw" (Lock Influence Weights) attribute connected, with regards to a skinCluster?]]
Autodesk has a handy-dandy screencast utility:
https://screencast.autodesk.com/
----
Also see:
* [[Easily capture videos with audio from Maya without playblast via OBS]]
The {{{animCurveEditor}}} command lets you access a named Graph Editor.
It's confusing, since this is a UI element, you'd think in the Maya mel docs it'd be under the general "Windows" section, or maybe even "Panels \ Controls" sections.  But no, it's under "Animation".  Which makes sense too I suppose.  But I don't like it...

{{{graphEditor1GraphEd}}} is the name of the default Graph Editor.

From the Maya docs on {{{animCurveEditor}}}:
{{{
// Check to see if the "default" graph editor has been created
animCurveEditor -exists graphEditor1GraphEd;

// Show result curves
animCurveEditor -edit -showResults on graphEditor1GraphEd;

// Decrease the sampling rate for the result curves
animCurveEditor -edit -resultSamples 5 graphEditor1GraphEd;
}}}
What I'm still trying to figure out:  What kind of UI element //is// the 'graph-side' of a 'Graph Editor'?  According to the {{{animCurveEditor}}} docs, it gets created by a {{{scriptedPanel}}}.  The name of the panel that the default Graph Editor is associated with is {{{graphEditor1}}}:
{{{
animCurveEditor -q -pnl graphEditor1GraphEd;
// Result: graphEditor1 //
}}}
But that still dosn't answer the above question....
----
FYI, the Outliner to the left of the Graph Editor is an {{{outlinerEditor}}} called {{{graphEditor1OutlineEd}}}, and it's parent is a {{{scriptedPanel}}} called {{{graphEditor1}}}.  This appears to be the same {{{scriptedPanel}}} that is associted with the default Graph Editor (from above, which would make some sense).
{{{
scriptedPanel -q -ex graphEditor1;
    outlinerEditor -q -ex graphEditor1OutlineEd;
}}}
If you want to be able to figure out what you have highlighted in that {{{outlinerEditor}}}, see here:
[[How can I get a string array of the contents for a selectionConnection?]]
Also:
[[How can I highlight channels in the Graph Editor based on what's selected in the Channel Box?]]
*Every DG node in Maya has the ability to have a "note" added to it, which exposed in the bottom of the Attribute Editor. Not really a convention, but a feature they exposed in the Attribute Editor around Maya 5? How do you update that via mel?
*It would seem simple with a setAttr command. The issue is, that attr dosen't exist by default, so if you try to set it without creating it first, it won't work. When entering notes directly in the Attribute Editor, it creates the attr for you behind the scenes.
{{{
// mel
if(!`attributeQuery -n $node -ex "notes"`)
    addAttr -ln notes -dt "string" $node;
setAttr ($node + ".notes") -type "string" "My notes";
}}}
{{{
# Python
import maya.cmds as mc
if not mc.attributeQuery('notes', node=node, exists=True):
    mc.addAttr(node, longName='notes', dataType='string')
mc.setAttr('%s.notes'%node, "My notes", type='string')
}}}
There is a 'Gradient' dropdown in the 'Paint Skin Weights' tool, that allows you to "Use Color Ramp", then set that color.
But how can you set it through script?
The default values for the gradient (black on left, white on right) look like this:
{{{
artAttrCtx -e -colorRamp " 1,1,1,1,1,0,0,0,0,1 " artAttrSkinContext;
}}}
What do all those numbers mean?  It's interest that in fact it appears that Maya intersects a {{{1}}} between each chucnk of 4 values.  This is what it means:
{{{
r,g,b,p1, 1, r,g,b,p2, 1
}}}
Where rgb & p1 (position 1) go together as a unit, and rgb& p2 (position2) go together as a unit, with a {{{1}}} separating them, and an additional {{{1}}} at the end.  Weird\interesting.
So if you want to add more in, just append in new chunks of {{{"r, g, b, pX, 1 "}}} to the end of that string.

You can also set the special min/max colors with the {{{-rampMinColor r g b}}} and {{{-rampMaxColor r g b}}} flags.
{{{fileInfo}}}
*Note: This seems similar to the {{{optionVar}}} command, but stores the var's relative to the file, not wherever the optionVar's are stored.
*Example: store a "keyword" with a value in the scene:
{{{
fileInfo "secretData" "WarpCat was here...";
string $secretData[] = `fileInfo -q "secretData"`;
}}}
----
Also see:
* [[How can I pickle Python data to fileInfo?]]
* [[How can I pickle Python data to attributes?]]
Every camera-based panel has Show menu, with a bunch of checkboxes that define what is shown in view or not.
This appears to be saved as a pref with the scene, and even though in my Maya preferences I say "don't load scene preferences", whenever I open someone else's scene, I see how they had these options set last.
The below code will turn them 'all on':  You'd think the {{{allObjects=True}}} would do it, but in fact, they've added more options at the bottom of that menu, that require individual calls.
{{{
from __future__ import print_function
import maya.cmds as mc
import maya.mel as mm

def showAll():
    """
    Check on everything in the 'Show' menu
    """
    for i in range(1,5):
        try:
            mc.modelEditor("modelPanel%s"%i, edit=True, allObjects=True, 
                manipulators=True, grid=True, headsUpDisplay=True, holdOuts=True, selectionHiliteDisplay=True)
            mm.eval("updateModelPanelBar modelPanel%s"%1)
        except:
            pass
    print("Enabled all options in the 'Show' menu")

showAll()
}}}
Based on a new install of Maya, I believe the default frame rate is set to '{{{Film (24 fps)}}}'.  I work in games, where most of the time the frame rate should be set to '{{{NTSC (30 fps)}}}'.  Based on the tools we use, if an animator loads a scene up, and starts animating at 24 fps, then loads it into our animation sequencer that expects things to be at 30fps,... bad things will happen.

I've isolated two locations that need to be updated to ensure that whenever a scene is created \ opened, it will be a the right frame rate.  For this example, we'll be using '{{{NTSC (30 fps)}}}'.

!Location #1: Maya Preferences:
Maya has its default preferences (''Window -> Settings / Preferences -> Preferences -> Settings -> Working Units -> Time'') that set the overall time for the scene.  When you change the value, Maya calls to the {{{currentUnit}}} command to update the system.  What's interesting, is that it //doesn't// seem to modify any {{{optionVar}}}s to store these settings.  
Look in the {{{global proc prefWndUnitsChanged()}}} in the script:
{{{C:/Program Files/Autodesk/Maya<version>/scripts/startup/createPrefWndUI.mel}}}
...to see what's really going on.

However, this value can be changed by incoming files with different values.  So we need to do something else to ensue we have the correct settings when we open //any// scene...

!Location #2:  New Scene Options
When you make a //new// scene (''File -> New -> Options''), there are //new scene options// where you can set the framerate.  These will //override// anything set in your Maya preferences (see #1, above).  This is a main gotcha I see for most people:  They don't realize that these need updated as well.
Changing the 'time' in these options //does// store the change in an {{{optionVar}}}.  
Look in {{{global proc setDefaultUnits()}}} in the script:
{{{C:/Program Files/Autodesk/Maya<version>/scripts/startup/performNewScene.mel}}}}
... for more details.

!The Solution:
You need these options to be set every time you access a //new// scene, or open an //existing// scene.  The best way I've found to do this is add a {{{scriptJob}}} your {{{userSetup.mel}}} that executes on any file access:
{{{
// userSetup.mel

global proc setNTSC(){
	// Set our 'Maya preferences':
	currentUnit -t ntsc;
	// Set our 'new scene options':
	optionVar -sv workingUnitTimeDefault ntsc;
}

scriptJob -event SceneOpened setNTSC; 
scriptJob -event NewSceneOpened setNTSC;
}}}
Done:  Anytime you open access a file, you're sure to know that it's at the correct framerate.  With a bit more 'detection code', you could query what the values were before modification, and alert the user if any changes were taking place.
{{{
string $format = "mayaAscii";
optionVar -sv defaultFileOpenType "mayaAscii";
optionVar -sv "defaultFileSaveType" $format;
file -type $format;
}}}
Adding this to your {{{userSetup.mel}}} file will make sure that things are saved in that format by default, and the new scene that Maya opens with is also of that format.
When doing a "Save As", this will set the "File Type" under "General Options" to the defined {{{$format}}}.

Optionally, if you really wanted to make sure your users always saved as a given format no matter what:  Presuming you push a studio-wide {{{userSetup.mel}}}, in that file, you can set, via the {{{file}}} command, a {{{-preSaveScript}}} arg.  That points to a script that will be ran before save.  In that script, you can query for the file type, and if not the format you want, change the format, then save.
@@Note:@@  Wing can interact with Maya in three different ways.  This is one of the three.  See an overview here on the whole system:  [[Interaction between Wing and Maya]].  

See ''Wing Setup Notes'' below for how to setup Wing to use this information.
----
Maya comes with its own cut of Python (see [[Python & Qt versions in Maya]]).  The executable for that lives here on Windows:
{{{
c:\Program Files\Autodesk\Maya<version>\bin\mayapy.exe
}}}
Or here on Mac:
{{{
/Applications/Autodesk/maya<version>/Maya.app/Contents/bin/mayapy
}}}
<<<
Note, on the mac, you have to pass in the full path to the mayapy app to execute it, or the below initialize call will fail:
{{{
$bash /Applications/Autodesk/maya2017/Maya.app/Contents/bin/mayapy
}}}
<<<
You can execute this directly to launch an interactive prompt of Python (most commonly for batching purposes).  Or, if you're running a Python IDE external to Maya, but you want Maya's Python calls to exist in it, you can point your IDE To that exe.  There is often an option in your IDE to allow for this (Wing allows you to do this, for example).
Once that executable has been started, you still need load and initialize Maya into that session.  Per Maya's docs: "{{{These commands take a significant amount of time to execute as they are loading all of the Maya's libraries and initializing the scene.}}}"  This is the code I use:
{{{
try:
    import maya.standalone
    maya.standalone.initialize()
    print "Imported maya.standalone\n"
except:
    print "Couldn't find 'maya.standalone' for import\n"
}}}
If you want that code to execute //every time// you launch Maya's version of Python (I do this in Wing), you can author the above code as a module, and point Python's {{{PYTHONSTARTUP}}} environment variable to it:
{{{
PYTHONSTARTUP = <somePath>\<somePythonStartupModule>.py
}}}
I have [[Wing|For Python: Maya 'Script Editor' style IDE]] execute this code for my 'Maya Python coding sessions'.
----

The {{{maya.cmds()}}} library is actually empty on disk (go take a look for yourself) other than the {{{__init__.py}}} module that defines that dir structure //as// a library:
{{{
C:\Program Files\Autodesk\Maya<VER>\Python\lib\site-packages\maya\cmds\
}}}
Although you can fully access all Maya commands through Python like '{{{maya.cmds.ls()}}}', the Python {{{ls()}}} command wrapper has no physical location on disk:  Until you execute the above code example ({{{import maya.standalone}}} ...), Python won't see any of the commands. If you want to get a better understanding, see the subject [[How does Maya populate maya.cmds?]]. 
!Wing Setup Notes
*In Wing, create a new Project, and access the "Project Properties".
*In the Environment tab:
**Set "Python Executable" to "Custom", and browse to the path mentioned above with {{{mayapy.exe}}} in it.
**Set "Environment" to "Add to inherited environment", and paste in the {{{PYTHONSTARTUP}}} path you authored above.
**If you want Wing to be able to analyze your ~Maya-Python modules and give you functionality like "Go To Selected Symbol Definition" (ctrl+LMB on a symbol name, F4 hotkey on a symbol, etc), you can add the paths where they're stored to the "Python Path" section of the ui.  If you're using Python's packaging system, you only need to add the root dir of each package to this field.
**Hit OK
*Be sure to "Save Project" ;)
Maya has the mel command {{{python}}}, which lets you execute Python code inside of mel.  I will often author python modules outside of the defined 'Python PATH'.  If you're in the middle of a mel script, and want to successfully execute Python code that isn't part of the Python path, how to do?

Below is one method I have come up with.  Presuming you're trying to execute some module called {{{c:\some\path\myModule.py}}}:
{{{
// First, set the path to where the module lives, and import the
// module.  Note the use of Python single-quotes inside the
// Maya double-quotes:
python("import sys;" +
	"import os;" + 
	"sys.path.append(os.path.normpath(r'c:/some/path'));" + 
	"import myModule");

// Then, you can execute whatever function is in that module:
string $results[] = python("myModule.someFunction()");
}}}
In theory, you could do it all in the same {{{python}}} call.  However, if you want to capture the return value from your executed module, you need to execute it in its //own// {{{python}}} command (according to the docs).  As well, you could split each execution into a separate {{{python}}} call (rather than using '+' to catenate the strings), but I try to keep it as compact as possible.

Also, why am I calling {{{os.path.normpath}}}?  Why not just use double-backslashes in the path name, rather than using forward-slashes, and making the string //raw// (via the little {{{r}}} in front of it)?
*Let's say your path was:  {{{"c:\temp"}}}
*But, when the interpreter sees {{{\t}}}, it thinks 'tab', and inserts a tab into your path:  {{{"c:	emp"}}}  
*To author that with double-backslashes, so it would get read in properly: {{{"c:\\temp"}}}.  And in this example, it would work.  BUT:
*I have found mixing double-backslashs with //other// letter combinations creates all sorts of additional problems.  Plus, many Maya commands return forward-slash paths.
*So by passing a a //raw// ('{{{r}}}') string with forwardslashes, and then via {{{os.path.normpath}}} converting that to pythonic-backslashes, it circumvents this issue.

With the {{{python}}} mel command.
Example:  Presuming you have a python script called {{{returnRange.py}}}, with a {{{main}}} function, and that function returns a range of numbers based on two arguments:
{{{
// via mel:
python("import returnRange");
int $rangeInt[] = python("returnRange.main(1, 4)");
print $rangeInt;
1
2
3
}}}
See the Maya docs on how data type conversions are handled.
Also see:
*[[How can I execute mel code via Python?]]
It's possible you may want to execute a Python module, external from Maya, and not part of the system Python path, via mel.  Maybe it launches a custom window or something exciting like that.  Here's one solution:
{{{
string $module = "C:\\path\\to\\my\\module.py";
string $mName = basename($module, "");
string $dName = dirname($module);

string $dosCmd = ("chdir " + $dName + " & start " + $mName);
system($dosCmd);
}}}
There is a {{{python}}} mel command, that will take the given string, and execute it as Python code.  It's easy to do single line stuff like this:
{{{
// mel code
python("import sys; print sys.path");
}}}
You can call to Python builtins, your own modules, etc.  

But what if you want to call to a large block of Python code that way?  For example, marking menus only accept mel commands:  You have a bunch of Python you want to call to via a marking menu, but you don't want to have to make it it's own module.

Via the below technique, you can convert a block of Python code into a multiline Python string, then convert that string into a bunch of print statements that can in turn be used as mel.

For example, let's write a tool via ~PyMel that will convert the names of everything selected to capital letters.  Here is the pure Maya Python:
{{{
# Rename selected nodes to uppercase
import pymel.core as pm
sel = pm.ls(selection=True)
for s in sel:
    upper = s.nodeName().upper()
    if upper == s.nodeName():
        continue
    s.rename(upper)
}}}
Now that we know that works, re-write it as a multi-line string:
{{{
pyStr = """# Rename selected nodes to uppercase
import pymel.core as pm
sel = pm.ls(selection=True)
for s in sel:
    upper = s.nodeName().upper()
    if upper == s.nodeName():
        continue
    s.rename(upper)"""
}}}
Now, split that multiline string, and reformat it for use as a mel:
{{{
splitStr = pyStr.split("\n")
print "string $pyStr = ("
for line in splitStr[:-1]:
    print r'"%s\n"+'%line
print r'"%s\n");'%splitStr[-1]  
print "python($pyStr);"
}}}
Which ends up printing this //mel// code:
{{{
string $pyStr = (
"# Rename selected nodes to uppercase\n"+
"import pymel.core as pm\n"+
"sel = pm.ls(selection=True)\n"+
"for s in sel:\n"+
"    upper = s.nodeName().upper()\n"+
"    if upper == s.nodeName():\n"+
"        continue\n"+
"    s.rename(upper)\n");
python($pyStr);
}}}
Which you can copy and stick it into whatever mel script \ hotkey \ marking menu as needed.
Via Maya's [[scriptJob|http://download.autodesk.com/us/maya/2010help/CommandsPython/scriptJob.html]] command, you can easily execute callbacks.  And via Maya's [[scriptNode|http://download.autodesk.com/global/docs/maya2013/en_us/CommandsPython/scriptNode.html]] command, you can create 'script nodes' that can execute 'script jobs' when a scene opens.
Via this method, its fairly trivial to create a {{{sciptJob}}} that will cause a script to evaluate whenever a certain attribute on a certain node changes.  But this all breaks down if that file is referenced:  Since the name of the node has now changed (due to the namespace), the {{{scriptJob}}} no longer detects it.

Thanks to an undocumented tip from Mason Sheffield, you can actually put a wildcard ({{{*:objName.attr}}}) in front of the object name during the {{{scriptJob}}} authoring:  It will now detect the {{{namespace:obj.attr}}} name in the reference.  Awesome.

However, there are two problems with this system:
*Upon open of the un-referenced scene, you'll get a {{{RuntimeError}}} saying (for example):
<<<
{{{
# RuntimeError: Could not find attribute named "*:mynode.tx" # 
}}}
<<<
*If you reference the referenced scene (which can happen) :  The {{{scriptJob}}} will no longer evaluate, since it's only looking 'one level deep' for the namespace.  If you know this will be consistent, you can embed {{{*:*:}}} in front of your node, instead of just {{{*:}}}.
----
Here's an example of how to set it up:

Create this script, and save it as {{{attrCallback.py}}}.  Save it in Maya's script path.  What does this script do?  A:  It creates a {{{scriptNode}}} that lives in the current scene, that will execute a {{{scriptJob}}} when that scene is opened.  B:  Whenever the {{{translateX}}} attr on a node called {{{mynode}}} is modified (in a scene referenced one level deep), it will execute the {{{attrCallback}}} //function//, which simply prints some text.
{{{
# attrCallback.py

import maya.cmds as mc

def attrCallback():
    print "mynode.tx changed!"

def makeScriptNode():
    scriptStr = 'import maya.cmds as mc\n'+\
        'import attrCallback\n'+\
        'mc.scriptJob(attributeChange=["*:mynode.tx", attrCallback.attrCallback])'
    mc.scriptNode( scriptType=2, beforeScript=scriptStr, name='myScriptNode', stp='python')
}}}
Make a new scene, and in it create a new node.  Rename that node to {{{mynode}}}.
In the script editor, execute:
{{{
import attrCallback
attrCallback.makeScriptNode()
}}}
This will create the {{{scriptNode}}}.  Save your scene as {{{attrCallback.ma}}}.  If you modify {{{mynode.translateX}}} nothing will happen, since the {{{scriptNode}}} is searching for the node in a sub-namespace (and it currently lives in the root namespace).
Create a new scene.  Reference in {{{attrCallback.ma}}}.  Adjust the {{{mynode.translateX}}} : You should see the Script Editor starting printing the results from {{{attrCallback()}}}.
So simple, but I always seem to forget the syntax... :-S
{{{
import maya.cmds as mc
sc = mc.skinCluster(someListOfJoints, aMesh, dropoffRate=5.0, maximumInfluences=4, toSelectedBones=True)
print sc
# [u'skinCluster1']
}}}
note the {{{bindSkin}}} command is the (much) older system of setting up 'rigid skinning', using basically clusters.  yuk.
Often times I want to call a function when I close/delete a window.  Unfortunately there is no 'delete detection' for this via the {{{window}}} command and no way via the {{{window}}} command to assign callbacks.  However I've found two ways to implement callback systems that do detect for window deletion:
*Via the {{{OpenMayaUI}}} API, one can create a {{{MUiMessage_addUiDeletedCallback}}} object that will track when the given window is deleted, and execute the provided code when that happens.  It also allows for an optional {{{clientData}}} parameter allowing you to easily pass data to the function upon window delete.
**Note: It appears that this code runs just before, or possibly at the same time as the window is deleted:  It can be used to execute code to safely delete {{{modelPanel}}}s before the window is deleted, when that same code would fail in the {{{scriptJob}}} callback. 
*It's easy to make a {{{scriptJob}}} which attaches itself to the window, executes when the window is deleted, and for safety can be set to only 'run once'.  We have the {{{scriptJob}}} call to the a method on our {{{Window}}} object at time of death telling it what to do.  It should be noted that this will execute the code after the window is closed.
It should be noted that the {{{scriptJob}}} needs to be called before {{{showWindow}}} or it won't seem to attach correctly.

Finally, even though these callbacks are executed when the window is closed, they appear to execute after the window is deleted.  If you try to query part of the window ui in the callback, it will fail.  I've got around this by storing the values I need to query in optionVar's.
{{{
# Python code
import maya.cmds as mc
import maya.OpenMayaUI as omui

class App(object):
    def __init__(self):
        self.name = 'myWin'
        if mc.window(self.name, exists=True):
            mc.deleteUI(self.name)
        mc.window(self.name)

        # OpenMayaUI callback example:
        omui.MUiMessage_addUiDeletedCallback(self.name, self.MUiMessageCallback, "clientData")

        # scriptJob callback example:
        mc.scriptJob(uiDeleted=[self.name, self.scriptJobCallback], runOnce=True)

        mc.showWindow()

    def MUiMessageCallback(self, clientData):
        print "MUiMessage_addUiDeletedCallback", clientData

    def scriptJobCallback(self, *args):
        print "scriptJobCallback", args
}}}
To launch the window:
{{{
App()
}}}
When the window is closed, it will print:
{{{
MUiMessage_addUiDeletedCallback clientData
scriptJobCallback ()
}}}
Notice how the {{{MUiMessage_addUiDeletedCallback}}} is executed first?
{{{commandPort}}}
*Example on a local network: First, "Open a commandPort in Maya": 
**(Win2000 syntax is "computerName:portNumber")
{{{
commandPort -n "warpcat:8888";
}}}
**How do I find my IP address if need be on Windows?  At the Command Prompt (windows):
{{{
>> ipConfig
}}}
**On your external machine or command prompt, open an app to talk with your other machine:
***On Unix, {{{telnet}}}.
***On Linux, {{{socat}}}.
***On Windows I HIGHLY recomend using [[Putty|http://www.chiark.greenend.org.uk/~sgtatham/putty/]], since according to some online threads I've read, W2K telnet acts "funny": It won't accept more than a single character at a time. As a result, you'll need to download a new client. And I've found that Putty works very well.
**At the prompt:
**Unix (this is the windows format, but I'm 'guessing' it's the same?):
{{{
telnet warpcat 8888;
}}}
**Linux (tip from Hal Bertram):
{{{
socat readline tcp4:localhost:8888
}}}
***From Hal: You can use socat instead of telnet to get a maya prompt that feels more like a shell with history, filename completion, and other readline goodness.
**Windows:
{{{
insert Putty example here
}}}
**Now you can start typing commands and executing them in Maya.
----
*Notes: 
**I've only tested this on windows with telnet and putty, FYI. Irony that I don't have a Putty example isn't it?
The Python {{{maya}}} package has a {{{mel}}} module.  In that, it has the {{{eval}}} function that can evaluate mel directly.
Example:  Presume you have a mel script called {{{returnRange.mel}}}, that has a {{{global proc returnRange}}} which returns a range of numbers based on two arguments
{{{
# via python:
import maya.mel as mm
range = mm.eval("returnRange(1,10)")
print range
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}}}
See the Maya docs on how data type conversions are handled.
Also see:
*[[How can I execute Python code via mel?]]
*[[Understanding Python integration in Maya]]
Via pure Python, you can use {{{subprocess.Popen}}} to do this.  My Python Wiki has a [[subject covering this|http://pythonwiki.tiddlyspot.com/#%5B%5BHow%20can%20I%20execute%20a%20system%20command%2C%20and%20capture%20the%20return%3F%5D%5D]].

In Maya though, ~PyMel makes this even easier via its [[pymel.util.shellOutput|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.util/pymel.util.shellOutput.html?highlight=stdout]] 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.
Say you have some nodes in a namespace, either manually created, or because the nodes are referenced.
You want to export those new nodes to a new file, but have them *not* be in a namespace in that new file: How to do?
Maya's {{{file}}} command has a {{{relativeNamespace}}} parameter:  If you pass it the same namespace they're currently in, that namespace will be removed from them in the new file they've been exprorted too.  

Note, the namespace must be provided as an absolute namespace, so it must have a leading colon.  So this is vaild: {{{:myNamespace:}}}, but not this: {{{myNamespace:}}}.

In action:
{{{
mc.file(myExportFilePath, exportSelected=True, force=True, relativeNamespace=":myNamespace:", type="mayaBinary")
}}}
When you query the selection of vert via {{{ls()}}}, it will give you back data like so: {{{pPlane1.vtx[60]}}}.
How can you get just the vert id number {{{60}}} from that string?  Python's {{{re}}} module makes this pretty easy:
{{{
# Python code
import re

myStr = 'pPlane1.vtx[60]'
pattern = r'\d+'
# This will return a list ["1", "60"], so grab the last item, & turn it into an int:
vid = int(re.findall(pattern, myStr)[-1])
# 60
}}}
See my notes on {{{lerp()}}} [[here|How can I find a value some percentage between two other values?]].  This expands on the concept, but using 3d points rather than single values.

{{{p1}}} and {{{p2}}} are the two 3d points in space.  {{{amt}}} is the percentage (from 0-1) between them you want to find the new point.  If {{{amt}}} is {{{.5}}}, {{{lerp3D()}}} will return a point 50% between {{{p1}}} and {{{p2}}}:
{{{
# Python code

def lerp3D(p1, p2, amt):
    x = p1[0] + (p2[0] - p1[0]) * amt
    y = p1[1] + (p2[1] - p1[1]) * amt
    z = p1[2] + (p2[2] - p1[2]) * amt
    return [x, y, z]

print lerp3d([2,2,2], [10,20,10], .25)
# [4.0, 6.5, 4.0]
}}}
Since color values are also expressed (usually) as three floats, you could use this code to {{{lerp}}} colors too:
{{{
print lerp3d([0,0,0], [255, 255, 255], .75)
# [191.25, 191.25, 191.25]
}}}
But, you'd need to convert those to {{{int}}}'s to be usable.
----
For the opposite, see:
*[[How can I find what percentage between a start and end value a mid value is?]]
In other languages they have a {{{lerp()}}} command.  Mel and Python don't seem to.  

Here's how it works:
{{{val1}}} and {{{val2}}} are the two values.  {{{amt}}} is the percentage (from 0-1) between them you want to find the new value.  If {{{amt}}} is {{{.5}}}, {{{lerp()}}} will return the value 50% between {{{val1}}} and {{{val2}}}:
{{{
# Python code

def lerp(val1, val2, amt):
    return val1 + (val2 - val1) * amt 

print lerp(2, 16, .5)
# 9
}}}
{{{
// Mel code

global proc float lerp(float $val1, float $val2, float $amt)
    {
    return $val1 + ($val2 - $val1) * $amt;
    }
	
lerp(5, 10, .5);
// Result: 7.5 // 
}}}
See docs, and visual examples for {{{lerp()}}} over at [[processing.com|http://www.processing.org/reference/lerp_.html]]
----
Also see:
*[[How can I find a 3d point some percentage between two other 3d points?]]
For the opposite, see:
*[[How can I find what percentage between a start and end value a mid value is?]]
The default name for the Outliner is {{{outlinerPanel1}}}, but I've ran into intsances where there is either more than one, or it's somehow got renamed :S
{{{
import maya.cmds as mc
outliners = []
panels = mc.lsUI(panels=True)
for p in panels:
    if mc.outlinerPanel(p, query=True, exists=True):
        outliners.append(p)
print outliners
}}}
Thanks to my bud Demetrius Leal on the tip with {{{netstat}}}:

On a //Windows// system, you can use {{{subprocess.Popen}}} to run the system {{{netstat}}} command.  It returns a file-object which you can then parse.  It should be noted that this doesn't need to be executed //in Maya//, it could be in an external Python shell.  But Maya needs to be running of course.  And you can run it in Maya if you feel like it ;)
{{{
# Python code
import subprocess

pipe = subprocess.Popen(['netstat', '-ab'], stdout=subprocess.PIPE)
data = [line.strip() for line in pipe.stdout]
for i,e in enumerate(data):
    if 'maya.exe' in e and 'LISTENING' in data[i-1]:
        print e, data[i-1].strip()
}}}
{{{-a}}} : Displays all connections and listening ports.
{{{-b}}} : Displays the executable involved in creating each connection or listening port.

Will print something like this (presuming you have active commandPorts in Maya...):
{{{
[maya.exe] TCP    [::]:6000              userName:0               LISTENING
}}}
It removes other ports that aren't 'LISTENING', since they'd probably do you no good anyway ;)
I recently had to find all the {{{animCurves}}} that were controlling a node.  At the time, it turned out to be a bit more difficult that it seemed so I came up with the API solutions (B, C) shown below:  See, the curves were in animation layers, and I wasn't getting the result I wanted with the {{{listConnections}}} or {{{listHistory}}} commands.
!Solution A:
{{{
My latest solution that seems pretty rock solid:

def getAnimCurves(node):
    inputs = mc.listConnections(node, source=True)
    if not inputs:
        return []
    for inpt in inputs:
        inptType = mc.objectType(inpt)
        # Only pass types that related to keyframes or anim layers.  But leave out
        # actual 'animLayer' nodes, since they're connected to so much more that
        # will get pulled in.
        if "animBlend" in inptType or "animCurve" in inptType:
            theseInpts = mc.listConnections(inpt, source=True)
            if not theseInpts:
                continue
            for ti in theseInpts:
                if ti not in inputs:
                    inputs.append(ti)
    curves = mc.ls(inputs, type='animCurve')
    return curves
}}}
!Solution B:
Thanks to a tip from Han Jiang (that he found over on [[Stack Overflow|http://stackoverflow.com/questions/18738371/how-to-find-all-upstream-dg-nodes-with-maya-python-api]], this works well, but sometimes still returns too much.

This will get keys on the node, and dig through the anim layers to find them as well.
{{{
import maya.cmds as mc
curves = mc.ls(mc.listHistory('joint1'), type = 'animCurve')
# if that doesn't work, try this:
curves = mc.ls(mc.listHistory(mc.listConnections('joint1', source=True)), type = 'animCurve')
}}}
!Solution C: Use ~MAnimUtil
[[MAnimUtil|http://download.autodesk.com/us/maya/2010help/API/class_m_anim_util.html]] docs.
This is great, since it does all the heavy lifting for you:  Find all the attrs on the node that have incoming connections, and then for those connections, find the {{{animCurve}}}s controling them.
{{{
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

# Get a MDagPath for the given node name:
node = 'myNodeName'
selList = om.MSelectionList()
selList.add(node)
mDagPath = om.MDagPath()
selList.getDagPath(0, mDagPath)

# Find all the animated attrs:
mPlugArray = om.MPlugArray()
oma.MAnimUtil.findAnimatedPlugs(mDagPath, mPlugArray)
animCurves = []

# Find the curves ultimately connected to the attrs:
for i in range(mPlugArray.length()):
    mObjArray = om.MObjectArray()
    oma.MAnimUtil.findAnimation(mPlugArray[i], mObjArray)
    for j in range(mObjArray.length()):
        depNodeFunc = om.MFnDependencyNode(mObjArray[j])
        animCurves.append(depNodeFunc.name())
  
# See what we found:      
for ac in sorted(animCurves):
    print ac
}}}
!Solution D: Iterate the DG:
The below example will print a list of all the animation curves on the selected objects, in animation layers or not.
{{{
# Python code
import maya.OpenMaya as om

animCurves = []
# Create a MSelectionList with our selected items:
selList = om.MSelectionList()
om.MGlobal.getActiveSelectionList(selList)

# Create a selection list iterator for what we picked:
mItSelectionList = om.MItSelectionList(selList)
while not mItSelectionList.isDone():
    mObject = om.MObject()  # The current object
    mItSelectionList.getDependNode(mObject)
    # Create a dependency graph iterator for our current object:
    mItDependencyGraph = om.MItDependencyGraph(mObject,
                                               om.MItDependencyGraph.kUpstream,
                                               om.MItDependencyGraph.kPlugLevel)
    while not mItDependencyGraph.isDone():
        currentItem = mItDependencyGraph.currentItem()
        dependNodeFunc = om.MFnDependencyNode(currentItem)
        # See if the current item is an animCurve:
        if currentItem.hasFn(om.MFn.kAnimCurve):
            name = dependNodeFunc.name()
            animCurves.append(name)
        mItDependencyGraph.next()
    mItSelectionList.next()

# See what we found:
for ac in sorted(animCurves):
    print ac
}}}
!Solution E: findKeyframe
Well, honestly, this really isn't a solution, but I wanted to put it in here anyway:
{{{
import maya.cmds as mc
node = "pCube1"
curves = mc.findKeyframe(node, curve=True, attribute='rotateX')
}}}
The {{{findKeyframe}}} command will return back the {{{animCurve}}} for a specific attribute, based on the //current {{{animLayer}}}//.
{{{listHistory}}}
I found the source of this code below:
{{{
..\Autodesk\Maya2010\Python\Lib\site-packages\maya\app\general\zipScene.py
}}}
The version Maya provides does more error checking, but below I add the ability to //not// store the dir structure of the zipped files, if you don't want to.

Surprisingly easy.

{{{
# Python code
# zipScene.py

import os
import locale
import zipfile
import maya.cmds as mc

def main(storePaths=False):
	"""
	If storePaths == True, then full directory paths are saved to each file in
	    the zip.  If False, then all files are at root level of zip.
	"""
	theLocale = locale.getdefaultlocale()[1]
	files = mc.file(query=True, list=True, withoutCopyNumber=True)
	zipFileName = os.path.splitext(files[0])[0]+'.zip'
	zipper = zipfile.ZipFile(zipFileName, 'w', zipfile.ZIP_DEFLATED)

	for f in files:
		name = f.encode(theLocale)
		if storePaths:
			zipper.write(name)
		else:
			zipper.write(name, os.path.basename(name))
	zipper.close()

	for f in files:
		print "\t",f
	print "Added above files to zip:", zipFileName
}}}
I have a post on [[my blog|http://www.akeric.com/blog/?p=1087]] for this topic as well.

I'd tried a variety of methods using mel commands to try and trawl the scene looking for instances.  This worked well on shape nodes.  However, when it dawned on me you can instance transforms (or anything I suppose) just as easily as shapes, that code went out the window.  The Python ~OpenMaya API came to the rescue:
{{{
# Python code
import maya.OpenMaya as om

def getInstances():
    instances = []
    iterDag = om.MItDag()
    while not iterDag.isDone():
        instanced = om.MItDag.isInstanced(iterDag)
        if instanced:
            instances.append(iterDag.fullPathName())
        iterDag.next()
    return instances
}}}
This function will return a list of the full path to all instanced nodes in the scene.

The below function will then uninstance all those nodes:
{{{
import maya.cmds as mc
def uninstance():
    instances = getInstances()
    while len(instances):
        parent = mc.listRelatives(instances[0], parent=True)[0]
        mc.duplicate(parent, renameChildren=True)
        mc.delete(parent)
        instances = getInstances()
}}}
Note this is slightly destructive to any child nodes to those instances:  Any connections they have will be lost.  I have a mel workaround below (too lazy to port to Python):
----
~PyMel also makes it pretty easy:
{{{
sel = pm.ls(selection=True)
for s in sel:
    print s, s.isInstanced()
}}}
{{{isInstanced}}} is a method of the [[DagNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html]] class
----
Also see:
*[[How can I 'uninstance' a node?]] for other methods.
Say you have a polygonal mesh, with a bunch of joints bound to it.  How can you find, for a given joint, all the verts that have non-zero weights?
{{{
# Python code 

import maya.cmds as mc
def getWeightedVerts(joint, geo):
    geoShape = mc.listRelatives(geo, shapes=True, noIntermediate=True)
    skinCluster = mc.listConnections(geoShape, source=True, destination=False, 
				     type="skinCluster",  
				     skipConversionNodes=True)[0]
    vertCount = mc.polyEvaluate(geo, vertex=True)
    nonZero = []
    for i in range(vertCount):
	weightVal = mc.skinPercent(skinCluster, geo+".vtx["+str(i)+"]", 
				   transform=joint, query=True)
	if weightVal > 0:
	    nonZero.append(geo+".vtx["+str(i)+"]")
    return nonZero
}}}
Usage: (in Python)
{{{
joint = "l_knee"
geo = "legs"
print getWeightedVerts(joint, geo)
['legs.vtx[499]', 'legs.vtx[500]', 'legs.vtx[501]', etc...]
}}}
{{{ls}}}
{{{
// find all objects with an "outTime" attr in your scene:
string $attr = "outTime";
string $objs[] = `ls -o ("*." + $attr)`;
print $objs;
// time1
}}}
Notes:
*If you leave off the {{{-o}}} flag, it will return back the whole obj.attr name
*Sometimes, it will return back duplicate names for everything.  Weird.
*I can't believe it's taken me 8.5 versions of Maya to learn this... shame...  Thanks to Doug Brooks for showing me this :) 
!!!~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|http://mayastation.typepad.com/maya-station/2010/01/remove-reference-edits.html]] 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.
<<<
{{{file}}}
*Example:
{{{
string $alldependencies[] = `file -q -l`;
}}}
Also check out the {{{filePathEditor}}} command.

I should note that this tool only lists dependencies it can actually find.  For example, if the user types in a path to a texture that's not on disk, this tool won't return that path, since the texture can't be found.

----
Also see:
*[[How can I find all the references in the scene?]]
{{ls}}
Example A: Find all objects with the string "bob" in their name:
{{{
ls "*bob*";
}}}
Example B. Find all "transforms" with the string "larry" in their name:
{{{
ls -type transform "*larry*";
}}}
And by references, I mean file references.
!!!Commands:
This Python command would seem to do it:
{{{
import maya.cmds as mc
topRefs = mc.file(query=True, reference=True)
}}}
But in fact it will only return the top-level references.  Meaning, if you've referenced {{{C.ma}}} into your scene, but in fact it references {{{B.ma}}} and {{{A.ma}}}, only {{{C.ma}}} would be returned.

You can use //this// solution instead to get all the references, //and// subreferences.  However, it will return back everything 'referenced' in the scene, including textures, so we need to filter it by only maya files.  Plus we sort it, and remove any dupes:
{{{
allrefs = mc.file(query=True, list=True, withoutCopyNumber=True)
fileRefs = sorted(list(set([item for item in allrefs if item.endswith(".ma") or item.endswith(".mb")])))
}}}
You can optionally use this ~PyMel code.  It returns a dict of {"namespace":"filepath"}:
{{{
import pymel.core as pm
refs = pm.getReferences(recursive=True)
fileRefs = [str(refs[r]) for r in refs]
}}}
!!!API
(Starting in Maya 2011)
{{{
import maya.OpenMaya as om
topRefs = []    
om.MFileIO.getReferences(topRefs)
}}}
However, this suffers the same fate as the top {{{file}}} command example:  Only returns top-level references.

This emulates the second command example above, but via the API call, giving you all the subreferences as well:
{{{
allrefs = []
om.MFileIO.getFiles(allrefs)
fileRefs = sorted(list(set([item for item in allrefs if item.endswith(".ma") or item.endswith(".mb")])))
}}}
{{{listSets}}}
Let's you query convenient things like object sets, or rendering sets (shadingEngines. or shaders).
{{{
import maya.cmds as mc
topRefs = mc.file(query=True, reference=True)
unloaded = []
for ref in topRefs:
    if mc.file(ref, query=True, deferReference=True):
        unloaded.append(ref)
print unloaded
}}}
Given this hierarchy:
*top
**middle1
***bottom11
***bottom12
**middle2
{{{
string $kids[] = `ls "middle1|*"`;
// Result: bottom11 bottom12
string $kids[] = `ls "top|*"`;
// Result: middle1 middle2
}}}
Basically {{{ls}}} is finding those strings in the full path of the object names since we include the pipe in the search.
Tricky!
!!! Via ~PyMel:
Pretty darn easy.  This creates a dictionary where each key is an index on the mesh, and each value is a list of verts that connect to it by edge. However, ~PyMel can be much slower than the API.
{{{
import pymel.core as pm

mesh = pm.PyNode("myAwesomeMesh")
neighbors = {}
for vert in mesh.vtx:
    connected = vert.connectedVertices()
    neighbors[vert.currentItemIndex()] = [v.currentItemIndex() for v in connected]

print neighbors
# {0: [1, 11, 12], 1: [0, 2, 13], etc...
}}}

In below examples, you pass in a mesh\vertex ID, it returns back the names of all the verts connected to that one based on common edges.  The API version is a lot less code... half of it is just getting an ~MObject...
!!! Via the Maya API:
!!!! Newer Way:
{{{
import maya.api.OpenMaya as om2

def getConnectedVerts(verts):
    """
    Return a list of all connected verts based on the list of passed in verts.
    All verts are presumed to be on the same mesh.
    """
    if not isinstance(verts, (list, tuple)):
        verts = [verts]
    connectedVerts = []
    node = verts[0].split(".")[0]

    selList = om2.MSelectionList()
    verts = mc.ls(selection=True, flatten=True)
    for vert in verts:
        selList.add(vert)

    iterSelList = om2.MItSelectionList(selList)
    while not iterSelList.isDone():
        nodeDagPath, componentsObject = iterSelList.getComponent()
        if not componentsObject.isNull():
            iterGeo = om2.MItMeshVertex  (nodeDagPath, componentsObject)
            while not iterGeo.isDone():
                connectedVertIds = iterGeo.getConnectedVertices()
                for vid in connectedVertIds:
                    vertName = '%s.vtx[%s]'%(node, vid)
                    if vertName not in connectedVerts:
                        connectedVerts.append(vertName)
                iterGeo.next()
        iterSelList.next()

    return connectedVerts
}}}
!!!! Older Way:
{{{
import maya.OpenMaya as om

def getConnectedVerts(mesh, vtx):
    """
    Parameters:
    mesh : string : Name of the mesh to query.
    vtx : int :The specific vert index to query.

    return : list : List of full mesh.vtx[#] items that connect.
    """
    # Get an MObject for the passed in mesh:
    selList = om.MSelectionList()
    selList.add(mesh)
    mObject = om.MObject()
    selList.getDependNode(0, mObject)

    ret = []
    iterVert = om.MItMeshVertex(mObject)
    while not iterVert.isDone():
        if iterVert.index() == vtx:
            intArray = om.MIntArray()
            iterVert.getConnectedVertices(intArray)
            ret = ['%s.vtx[%s]'%(mesh, intArray[i]) for i in range(intArray.length())]
            break
        iterVert.next()

    return ret
}}}
{{{
mesh = 'pPlane1'
vtx = 61
print getConnectedVerts(mesh, vtx)

# ['pPlane1.vtx[62]', 'pPlane1.vtx[50]', 'pPlane1.vtx[60]', 'pPlane1.vtx[72]']
}}}
!!! Via Maya commands:
The {{{polyInfo}}} command returns back its info in a really weird way, so we have to chop it up to make it usable.
{{{
import re
import maya.cmds as mc

def getConnectedVerts(mesh, vtx):
    """
    Parameters:
    mesh : string : Name of the mesh to query.
    vtx : int :The specific vert index to query.

    return : list : List of full mesh.vtx[#] items that connect.
    """
    sourceV = '%s.vtx[%s]'%(mesh,vtx)
    eStr = mc.polyInfo(sourceV, vertexToEdge=True)[0].strip()
    edges = ['%s.e[%s]'%(mesh, item) for item in re.split('[: ]', eStr.strip().split(':')[-1])[1:] if item]
    vStrs = mc.polyInfo(edges, edgeToVertex=True)

    verts = []
    for vStr in vStrs:
        ids = []
        for item in re.split('[: ]', vStr.strip().split(':')[-1])[1:]:
            if not item:
                continue
            try:
                # I once saw a values passed in as 'Hard': only ints allowed:
                ids.append(int(item))
            except:
                continue
        # Remove any dupes:
        ids = list(set(ids))
        tempVs = ['%s.vtx[%s]'%(mesh, item) for item in ids]
        for item in tempVs:
            if item not in verts:
                verts.append(item)

    verts.remove(sourceV)
    return verts
}}}
{{{
mesh = 'pPlane1'
vtx = 61
print getConnectedVerts(mesh, vtx)

# ['pPlane1.vtx[72]', 'pPlane1.vtx[60]', 'pPlane1.vtx[50]', 'pPlane1.vtx[62]']
}}}
Via ~PyMel, this is pretty trivial:
{{{
import pymel.core as pm

myNs = "spam"
topFileRefs = pm.getReferences()
refFile = topFileRefs.get(myNs)
}}}
{{{topFileRefs}}} is a dict, with the key being a namespace.  
{{{refFile}}} will either be a ~PyMel [[FileReference|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.system/pymel.core.system.FileReference.html]] class instance (the string version of which is the path to the file) or {{{None}}} if it doesn't come from a reference.
{{{getPanel}}}
{{{fileExtension}}}
{{{currentTime}}}
*Example:
{{{
float $frame = ‘currentTime -q‘;
}}}
*At the Command Prompt
{{{
>> lmutil lmstat -c <licence file name> -a
}}}
*Notes:
**{{{lmutil}}} is usually found here: {{{c:/aw/com/bin}}}
***But since Autodesk bought Maya, it's probably in a new dir...
**<licence file name> is usually {{{c:/flexlm/aw.dat}}}
Quickly written snippet so an animator could easily know how fast a node was moving in Maya.  This is based on linear units set to inches (yuck) and framerate @ 30fps.  Adjust maths below as needed.
{{{
from math import sqrt
import pymel.core as pm

def printSpeed():
    """
    Print the speed in MPH for the selected node on the given frame.  Expects the
    units to be inches, and the framerate to be 30 fps.
    """
    sel = pm.ls(selection=True)
    if not len(sel) == 1:
        pm.displayError("Please pick exactly one node")
        return
    node = sel[0]

    thisFrame = node.translate.get()
    prevFrame = node.translate.get(time=pm.currentTime()-1)
    inchPerFrame = sqrt( sum( map( lambda x:pow(x[0]-x[1], 2), zip(thisFrame, prevFrame) ) ) )
    inchPerHour = inchPerFrame * 30 * 60 * 60
    mph = inchPerHour / 12 / 5280
    print "%s is traveling at %.1f MPH"%(node, mph)

printSpeed()
}}}
{{{
nurbsSphere1 is traveling at 5.3 MPH
}}}
{{{polyEditUV}}}

*Example: find a UV called object.map[21]
{{{
polyEditUV -q -uValue object.map[21];
}}}
* Example: Select a vertex, and find it's U value:
{{{
string $sel[] = `ls -fl -sl`;
string $UV[] = `polyListComponentConversion -tuv $sel[0]`;
float $uval[] = `polyEditUV -q -uValue $UV[0]`;
}}}
~PyMel has this shorthand:
{{{
uv = meshShape.vtx[52].getUV()
}}}
However, I've had it return {{{[0,0]}}} values more than once: Seems broken.  Best use the above method:
{{{
import pymel.cpre as pm
def getUv(vertex):
    uv = pm.polyListComponentConversion(vertex, toUV=True)
    # the uValue or vValue argument both return U & V values, weird:
    return pm.polyEditUV(uv[0], query=True, uValue=True)
}}}
Where {{{vertext}}} is in the form of a ~PyNode poly mesh, like: {{{myMesh.vtx[52]}}}
{{{system}}} & {{{set}}} (windows command) (and others):
{{{
string $envVar = `eval "system \"set MAYA\""`;
string $buffer[];
tokenize $envVar "=" $buffer;
print $buffer;
}}}
*The system call returns all the variables as a single string, so to make sense of them, tokenize the output by the equals "=" sign.
*To get a list from Windows Command Line, you'd simply type "set MAYA" at the prompt. What's interesting is that Windows doesn't see any environment variables defined by the Maya.env file, but if you make the call like above IN Maya, the variables are visible.
!!!Python:
{{{
import os
msp = os.getenv("MAYA_SCRIPT_PATH")
}}}
!!!Mel:
{{{getenv}}}
*Example:
{{{
string $msp = `getenv MAYA_SCRIPT_PATH`;
}}}
The documentation:
Example:
*Essentials -> Basic Features -> Setting Environment Variables -> Standard Maya environment variables
{{{currentCtx}}}
{{{translator}}}
*Example:
{{{
translator -q -do "animImport";
translator -q -do "animExport";
}}}
{{{
string $user = `getenv USER`;   
string $user = `getenv USERNAME`;   
}}}
i.e., what "kind" of node is' lambert'? This appears to function on non-DAG objects.
{{{getClassification}}}
{{{
getClassification "lambert";
// Result: shader/surface //
}}}
For example, given a poly sphere, named "bob", what type of node is "bob"? (transform).  What kind of node is "bobShape"? (mesh\nurbsCurve\etc.).
{{{nodeType}}}

This is very similar to {{{objectType}}}
For example, given a sphere named "bob", what type of object is "bob"? (transform) What type of object is "bobShape"? (mesh)
{{{objectType}}}

This is very similar to {{{nodeType}}}
Given a list of any number of nodes, how can I find their average position in worldspace?

First, we need to write a function that can calculate the average value of any number of passed in nodes:
{{{
# Python code
def average(*args):
    # find the average of any number of passed in values
    return sum(args) / len(args)
}}}
This function is so simple however, it could be written as a lambda:
{{{
lambda *args:sum(args)/len(args)
}}}
Then we can use Python's {{{map}}} built-in function to apply our average function to our positional values.  Here it is all on one line using a 'list comprehension', based on selected objects:
{{{
import maya.cmds as mc
avg = map(lambda *args:sum(args)/len(args), *[mc.pointPosition(p+".rotatePivot", world=True) for p in mc.ls(selection=True)])
}}}
----
But that one line can be hard to read.  Below illustrates how to 'simplify' it into multiple lines:

Simplification step #1:  Move out list of selected objects to its own line and variable.
{{{
import maya.cmds as mc
transforms = mc.ls(selection=True)
avg = map(lambda *args:sum(args)/len(args), *[mc.pointPosition(p+".rotatePivot", world=True) for p in transforms])
}}}
Simplification step #2:  Move our list of positions to it's own line and variable.
{{{
import maya.cmds as mc
transforms = mc.ls(selection=True)
positions = [mc.pointPosition(p+".rotatePivot", world=True) for p in transforms]
avg = map(lambda *args:sum(args)/len(args), *positions)
}}}
Simplification step #3: Get rid of the lambda, go back to the average() function:
{{{
import maya.cmds as mc
def average(*args):
    return sum(args) / len(args)
transforms = mc.ls(selection=True)
positions = [mc.pointPosition(p+".rotatePivot", world=True) for p in transforms]
avg = map(average, *positions)
}}}
And the end result of all three would look something like this, based on what you had initially picked:
{{{
[110.11913165450096, 163.66115890443325, 40.902932167053223]
}}}
There are two keys to this:
#Our {{{average}}} function \ {{{lambda}}} statement can accept any number of passed in arguments via it's {{{*arg}}} argument.
#The {{{map}}} function also accepts any number of arguments, via it's {{{*positions}}} (simplification #2) or {{{*[list comprehension]}}} (simplification #1 or first example).
I have quite a few notes on list comprehensions and the map function on my [[Python Wiki|http://pythonwiki.tiddlyspot.com/]]
~PyMel makes this pretty easy.  You can mix and match whatever you want (transforms, joints, verts, etc), and this will consume it all, returning a ~PyMel {{{dt.Point}}}:
{{{
import pymel.core as pm

def getCenterPoint(elements):
    pos = []
    for element in elements:
        if pm.attributeQuery('rotatePivot', node=element, exists=True):
            # joint / transform
            pp = pm.pointPosition("%s.rotatePivot"%element, world=True)
        else:
            # component
            pp = pm.pointPosition(element, world=True)
        pos.append(pp)
    return (sum(pos)) / len(pos)
}}}
This is in relation to DAG nodes.
{{{listRelatives}}}
----
Also see:
*[[Query mesh hierarchy without intermeidateObjects]]
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|http://area.autodesk.com/index.php/downloads_plugins/plugins_list/]]'

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.

A solution using [[MMeshIntersector|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_mesh_intersector.html]] works well.    There is an example using it online [[here|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/closest_point_cmd_8cpp-example.html,topicNumber=cpp_ref_closest_point_cmd_8cpp_example_html,hash=_a33]]
Here's a modified snipped from [[Chad Vernon|https://groups.google.com/forum/#!topic/python_inside_maya/EznnAtM0Hgw]]:
{{{
import maya.OpenMaya as om

targetPoint = om.MPoint(1,2,3)

selList = om.MSelectionList()
mesh_mdp = om.MDagPath()
selList.add(myMeshShapeStringName)
selList.getDagPath(0, mesh_mdp)

matrix = mesh_mdp .inclusiveMatrix()
mesh_mo = mesh_mdp.node()

resultPoint = om.MPoint()
polyIntersect = om.MMeshIntersector()
polyIntersect.create(mesh_mo , matrix)
pointOnMesh = om.MPointOnMesh()
polyIntersect.getClosestPoint(targetPoint, pointOnMesh);
hitPoint = pointOnMesh.getPoint()
}}}
Note, as of Maya 2018 I've been unable to get the {{{polyIntersect.create}}} method to work in {{{maya.api.OpenMaya}}}, throws an error.
Expanding the notes on [[How can I query the color at a certain UV point on a render node?]], here's how you can find a color based on all the verts on a mesh.

This works on subclasses of {{{texture2d}}}, like the '{{{file}}}' node.  
If you want to sample a 3d texture, see: [[How to sample a 3D texture]]
{{{
import maya.cmds as mc
def sample2dColorsAtPoints(shadingNode, mesh):
    """
    Works for sampling points on render nodes that are a sublcass of texture2d,
    like the 'file' node.
    """
    colors = []
    for vert in mc.ls(mc.polyListComponentConversion(mesh, toVertex=True), flatten=True):
        uvs = mc.ls(mc.polyListComponentConversion(vert, toUV=True), flatten=True)
        uvVal = mc.polyEditUV(uvs[0], query=True, uValue=True)
        color = mc.colorAtPoint(shadingNode, output="RGB", u=uvVal[0], v=uvVal[1])
        colors.append(color)
    return colors
}}}
Say you have multiple joints selected in a given hierarchy, and you want to know what are the top most roots of your selection.  For example, given this joint hierarchy
*''root''  (this is the shared root)
**''A''     (this is a common root)
***''A1''
***''A2''
**''B''    (this is a common root)
***''B1''
***''B2''

Say you select A, A1, A2 B, B1, B2 :  Based on that selection, you want to know what the top common roots are (which would be A & B), //based on the current selection//.  The below code will find that:
{{{
import pymel.core as pm

def getCommonRoots(joints):
    """
    Based on the passed in list of joints, return back a list of their common
    roots, that live within the list.
    """
    roots = []
    for j in joints:
        allParents = [node for node in j.getAllParents() if pm.nodeType(node)=='joint']
        allParents.reverse()
        # Did we find the parent in our current list of joints?
        listParent = None
        for p in allParents:
            if p in joints:
                listParent = p
                break
        if listParent:
            if not listParent in roots:
                roots.append(listParent)
        elif not j in roots:
            roots.append(j)
    return roots
}}}
{{{
joints = pm.ls(selection=True)
print getCommonRoots(joints)
# [nt.Joint(u'A'), nt.Joint(u'B')]
}}}
----
Now how about based on the same selection of joints, you want to know the root joint they all share (which would be 'root'):
{{{
def getSharedRoot(joints):
    """
    Based on a passed in list of joints, return back the *one parent* joint they
    all share.  Presuming there is a shared root.  If two different joint hierarchies
    are passed in, will return None.
    """
    comRoots = getCommonRoots(joints)
    if len(comRoots) == 1:
        return comRoots[0]

    parentList = []
    for joint in comRoots:
        parents = [node for node in joint.getAllParents() if pm.nodeType(node)=='joint']
        parentList.append(parents)
    # First, do a set intersction that returns only what all lists share.  Then
    # sort them by their full paths, grabbing the last item for return.
    shared = sorted(list(set.intersection(*[set(item) for item in parentList])), key=lambda x:x.fullPath())
    if not shared:
        return None
    else:
        return shared[-1]
}}}
{{{
joints = pm.ls(selection=True, long=True)
print getSharedRoot(joints)
# root
}}}
{{{getFileList}}}
*Example: Get a list all the ".mov" files in a specific directory:
{{{
string $fileList[] = `getFileList -fld "c:/mayaStuff/myFiles/"  -fs "*.mov"`;
}}}
Some math! 
Below example queries the position of two nodes in worldspace, and finds the distance between:

!!!~PyMel:
This works on joints, transforms, or components, or combinations thereof:
{{{
import pymel.core as pm
def distBetween(elementA, elementB):
    posA = None
    posB = None
    if pm.attributeQuery('rotatePivot', node=elementA, exists=True):
        posA = pm.pointPosition("%s.rotatePivot"%elementA, world=True)
    else:
        posA = pm.pointPosition(elementA, world=True)
    if pm.attributeQuery('rotatePivot', node=elementB, exists=True):
        posB = pm.pointPosition("%s.rotatePivot"%elementB, world=True)
    else:
        posB = pm.pointPosition(elementB, world=True)
    return posA.distanceTo(posB)
}}}

!!!Python:
{{{
import maya.cmds as mc
from math import sqrt

def distBetween(nodeA, nodeB):
    posA = mc.xform(nodeA, query=True, worldSpace=True, rotatePivot=True)
    posB = mc.xform(nodeB, query=True, worldSpace=True, rotatePivot=True)
    dist = sqrt( sum( map( lambda x:pow(x[0]-x[1], 2), zip(posA, posB) ) ) )
    return dist
}}}
I think this is a fairly Pythonic way of doing it:  Grab the worldspace rotate pivot positions of the two nodes.  Wrapper a  {{{lambda}}} statement and {{{zip}}} inside a {{{map}}} function. The {{{lambda}}} statement creates an 'anonymous function' that will receive each index in our zipped list as passed to it from the {{{map}}}.   The {{{zip}}} groups their xyz position values into a list, giving you: {{{[ [posA[0],posB[0]], [posA[1],posB[1]], [posA[2],posB[2]] ]}}}.   The {{{lambda}}} then splits out the two values from each list index, subtracts B from A, and raises it to the power of two.  Finally, the three 'powered' values are summed together, and the square root is generated giving us the distance!

This is exactly what the mel example is doing below, it's just doing it on a single line using using Python's built-ins.
!!!Maya Python API:
Via the Python API, you can use the {{{MPoint}}} class, which does all this for you.
http://download.autodesk.com/us/maya/2010help/API/class_m_point.html
{{{
from maya.OpenMaya import MPoint

p1 = MPoint(0,0,0)
p2 = MPoint(3,0,0)
distance = p1.distanceTo(p2)
}}}
{{{
print distance
3.0
}}}
!!!Mel
{{{
float $pointA[] = {0,0,0};
float $pointB[] = {3,0,0};
float $distance = sqrt( pow(($pointA[0] - $pointB[0]), 2) +
        		pow(($pointA[1] - $pointB[1]), 2) +
        		pow(($pointA[2] - $pointB[2]), 2) );
}}}
{{{getAttr}}}  - but it's the .cp attr that holds the local position, while the .pnts attr holds the tweak value
*Example:
{{{
polyCube;
select -r pCube1.vtx[0];
getAttr pCube1.cp[0];
move -r .1 .1 .1;
//Result: 0.1 0.1 0.1 //

// need to source the .xv val separately rather than the whole compound value.
getAttr pCube1.cp[0].xv;  
//Result: -0.4                 // Real x position (-.5 + .1)
}}}
The {{{maxValue}}} attribute on the shape node of your NURBS curve will tell you this.
{{{
import maya.cmds as mc
uVal = mc.getAttr('myCurveShape.maxValue')
}}}
You could then (for the sake of example) select that point on the curve via:
{{{
mc.select('myCurve.u[%s]'%uVal)
}}}
It should be noted that this length is in 'internal units' (cm), not necessarily what your 'ui units' are.
I seem to do this just not quite often enough to remember how to do it, so I'll make a note here.

Func returns a tuple containing the min and max frame range based on the list of nodes passed in.
{{{
# Python code
import maya.cmds as mc

def getMinMaxAnimRange(nodes):
    first = 0
    last = 0
    curves = mc.listConnections(nodes, source=True, destination=False, type='animCurve')
    if curves is not None:
        first = mc.findKeyframe(curves, which='first')
        last = mc.findKeyframe(curves, which='last')
    return (first, last)
}}}
@@Note A@@ : I've seen bugs where the {{{findKeyframe}}} command fails on the passed in {{{animCurves}}}, and instead needs a passed in list of nodes.  Car-azy.
@@Note B@@ : I've seen other bugs where //referenced// curves can cause it to always return 0,0 : You'll need to filter out any referenced curves first.
----
Also see:
*[[How can I find the min and max keyframe values for all objects in the scene?]]
Will return back a float array with two items, the [start, end] frames:
!!!Mel:
Will select the curves in the process:
{{{
global proc float[] returnFrameRange(){
	select -r `ls -type "animCurve"`;
	return {float(`findKeyframe -which first`), 
                float(`findKeyframe -which last`)};
}
}}}
!!!Python
Not based on selection.
{{{
import maya.cmds as mc
def sceneFrameRange():
    animCurves = [curve for curve in mc.ls(type='animCurve', recursive=True) if not mc.referenceQuery(curve, isNodeReferenced=True)]
    minF = mc.findKeyframe(animCurves,  which="first")
    maxF = mc.findKeyframe(animCurves,  which="last")
    return minF, maxF
}}}
@@Note@@ : I've seen  bugs where //referenced// curves can cause it to always return 0,0 : You'll need to filter out any referenced curves first, per the above code.
----
Also see:
*[[How can I set the range slider to match the keyframe range on the selected object(s)?]]
*[[How can I find the min and max keyframe range for the provided objects?]]
!!!Method A, via Python:
{{{
import maya.cmds as mc
menus = mc.window('MayaWindow', query=True, menuArray=True)
# [u'mainFileMenu', u'mainEditMenu', u'mainModifyMenu', u'mainCreateMenu', ...
}}}
!!!Method B, via mel:
All of Maya's main menu's are prefixed with {{{$gMain}}}... Here's how to get a list of them all:
{{{
string $all[] = `env`;
for($i=0;$i<size($all);$i++)
    {
    if(`match "^\\$gMain" $all[$i]` == "$gMain")
        {
        global string $temp;
        eval("$temp = " + $all[$i]);
        print ($all[$i] + " = " + $temp + "\n");
        }
    }
}}}
Also see:
*[[How can I update Maya's menus with my own entry?]]
When referencing multiple files into the same scene while passing each the same namespace, Maya will automatically increment the namespace name.  But via code, this can be tricky to find out.  ~PyMel makes this pretty easy via its {{{createReference}}} command:  It returns back a {{{FileReference}}} object on which you can query a {{{.namespace}}} attr, that holds the namespace name that was used (and possibly incremented):
{{{
import pymel.core as pm

assetPath = "c:/temp/myAwesomeAsset.ma"
ns = "myAwesomeNamespace"
myRef = pm.createReference(assetPath,namespace=ns)
newNS = myRef.namespace
}}}
Or via regular Python:  You can use this to query after the reference has been made:
{{{
import maya.cmds as mc
assetPath = "c:/temp/myAwesomeAsset.ma"
namespace = mc.file(assetPath , query=True, namespace=True)
}}}
~PyMel Docs :
*[[createReference|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.createReference.html#pymel.core.system.createReference]]
*[[FileReference|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.system/pymel.core.system.FileReference.html#pymel.core.system.FileReference]]
----
Thanks to Mason Sheffield for the tip.
!!! Easiest
lol, I can't believe it took me this long to learn this.  Of course, only works if the node in question is referenced.
{{{
print mc.referenceQuery(nodeName, namespace=True)
# ":namespace2:namespace1"
}}}
!!!~PyMel:
{{{
import pymel.core as pm
myNode = pm.PyNode("namespace2:namespace1:objectName")
print myNode.namespace()
print myNode.parentNamespace()
# namespace2:namespace1:
# namespace2:namespace1
}}}
!!!Python:
Single-line solution //not// using regular expressions:
{{{
name = "namespace2:namespace1:objectName"
ns  = name[:-len(name.split(':')[-1])]
# namespace2:namespace1:
}}}
----
Single-line regular-expression solution:
{{{
import re
name = "namespace2:namespace1:objectName"
ns = re.findall('.*:', name)[0] if len(re.findall('.*:', name)) else ""
# namespace2:namespace1:
}}}
----
Older multi-liner regular expression solution:
{{{
import re
name = "namespace2:namespace1:objectName"
ns = ""
try:
    ns = re.match('.*:', name).group(0)
except AttributeError:
    pass
# namespace2:namespace1:
}}}
!!!Via the API
{{{
# Python code:
import maya.OpenMaya as om

node = 'foo:objectName'
MSelectionList = om.MSelectionList()
MSelectionList.add(node)
MDagPath = om.MDagPath()
MSelectionList.getDagPath(0, MDagPath)

MFnDependencyNode = om.MFnDependencyNode(MDagPath.node())
namespace = MFnDependencyNode.parentNamespace()
# foo
}}}
!!!Mel:
Single line using regular expressions:
{{{
string $name = "assG:bzspsG:asdf:bob";        
string $namespace = `match ".*:"  $name`;
// Result: assG:bzsps:asdf: //
}}}
What the match is saying is:  'as long as I can find some sequence of characters followed by a colon, return that".  Once it hits the last colon, it stops matching.
{{{pointOnSurface}}}
*Example, presuming your shape node is called "nurbsSphereShape1?":
{{{
float $normal[] = `pointOnSurface -u .5 -v .5 -normal nurbsSphereShape1`;
}}}
{{{pointOnCurve}}}
{{{
string $sel[] = `ls -sl`;
referenceQuery -f $sel[0];
}}}
Via Maya's Python API 2.0, it's pretty easy via [[MImange|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-SDK/py_ref/class_open_maya_1_1_m_image.html]]:
{{{
import maya.api.OpenMaya as om2

def getResolution(imagePath):
    image = om2.MImage()
    image.readFromFile(imagePath)
    x,y = image.getSize()
    return [int(x), int(y)]
}}}
----
It should be noted that if you add an image to an {{{imagePlane}}}, you can query the resolution via:
{{{
imagePlane -query -imageSize "myAwesomeImagePlane";
}}}
----
If you're using older API 1.0, it's a bit clunkier:
Using [[OpenMaya.MImage|http://download.autodesk.com/us/maya/2010help/api/class_m_image.html#6c551f85e73c1e71b0490dc004fbba4c]] via Python:
{{{
# Python code:
import maya.OpenMaya as om

def getResolution(imagePath):

    image = om.MImage()
    image.readFromFile(imagePath)

    # MScriptUtil magic found here:
    # http://download.autodesk.com/us/maya/2010help/api/custom_image_plane_8py-example.html
    # But there was a bug in their code:  You need to make a unique MScriptUtil object
    # for each pointer:  They were sharing one, which caused the result to be the same
    # x,y resolutions, even on rectangular images
 
    # To get the width and height of the image an MScriptUtil is needed
    # to pass in a pointer to the MImage::getSize() method
    scriptUtilW = om.MScriptUtil()
    scriptUtilH = om.MScriptUtil()
    widthPtr = scriptUtilW.asUintPtr()
    heightPtr = scriptUtilH.asUintPtr()

    image.getSize( widthPtr, heightPtr )

    width = scriptUtilW.getUint(widthPtr)
    height = scriptUtilH.getUint(heightPtr)

    return (width, height)
}}}
{{{
imagePath = r'C:\temp\myImage.BMP'
print getResolution(imagePath)
# (64x64)
}}}

----
{{{getAttr}}} & the {{{file}}} node:

Presuming a texture has been assigned to a {{{file}}} texture node, you can use the {{{.outSizeX}}} and {{{.outSizeY}}} attrs on the {{{file}}} node to get the pixel dimensions.
{{{
string $xSize = `getAttr "file1.outSizeX"`;
}}}
----
Maya also ships with the {{{fileStats}}} app in its bin folder, that will return this.
Given some arbitrary leaf joint in a hierarchy, how can you find the root joint?  This code limits the selection to the given namespace, which in general is what you want.
{{{
import pymel.core as pm

leafJoint = pm.PyNode("someNamespace:someLeafJoint")
rootJoint = [node for node in joint.getAllParents() if pm.nodeType(node)=='joint' and node.namespace() == leafJoint .namespace()][-1]
print rootJoint
# someNamespace:someRootJoint
}}}
The below examples expects {{{$object}}} to be some transform-level name:

Method A:
{{{
// Query the object directly (transform level, presuming it HAS a bound shape)
string $cluster = `findRelatedSkinCluster $object`;
}}}
Method B:
{{{
// query the shape of $object (useful if the object has multiple shape nodes):
string $shape[] = `listRelatives -s -ni $object`;
string $cluster = `findRelatedSkinCluster $shape[0]`;
}}}
Method C:
{{{
// different approach:  Use node connections to find it:
string $shape[] = `listRelatives -s -ni $object`;
string $cluster[] = `listConnections -s 1 -d 0 -type "skinCluster" -scn 1 $shape[0]`;
}}}
----
{{{findRelatedSkinCluster}}} is a mel script located here: 
{{{C:\Program Files\Autodesk\Maya<VERSION>\scripts\others\findRelatedSkinCluster.mel}}}


Given a polygonal object that has different UV shells (it's been mapped different ways), how can you get what uv's are in those contiguous shells?

Maybe there's a fancier way to do this, via some built-in command, but I couldn't find it.  This tool will walk though each UV, expand the selection to the shell it lives in, and then compare the names of what is selected to a list of current shells (and a 'shell' is a sublist of uv's).  If the current list finds no matching sublist, it adds itself in.
{{{
import maya.cmds as mc
import maya.mel as mm

def uvShellsFromSelection():
    sel = mc.ls(selection=True)
    shells = []
    uvs =  mc.ls(mc.polyListComponentConversion(sel, toUV=True), flatten=True)
    for uv in uvs:
        mc.select(uv)
        mm.eval('polySelectBorderShell 0')
        shell = mc.ls(selection=True)
        if shell not in shells:
            shells.append(shell)
    return shells
}}}
Presuming the surface is enclosed, you can use this script that comes with Maya:
It acts on the current selection, returns a float, and, the mesh must be frozen if it has any transformations.
{{{
C:/Program Files/Autodesk/Maya<version>/scripts/others/computePolysetVolume.mel
}}}
Note:  sometimes this likes to return negative values, so use the {{{abs}}} command to correct for it.
This could be a vertex, particle, CV, lattice point, etc. If querying a face (or other poly element), it will return the positions of each vertex associated with the face.
{{{pointPosition}}}

Here's some //Python// code that will return a list with the worldspace position for every component selected, based on using a //list comprehension//:
{{{
import maya.cmds as mc
pts = [mc.pointPosition(point, world=True) for point in mc.ls(selection=True, flatten=True)]
}}}
Given a default polygonal cube, if you selected its verts and executed the above you'd get:
{{{
print pts
[[-0.5, -0.5, 0.5], 
 [0.5, -0.5, 0.5], 
 [-0.5, 0.5, 0.5], 
 [0.5, 0.5, 0.5], 
 [-0.5, 0.5, -0.5], 
 [0.5, 0.5, -0.5], 
 [-0.5, -0.5, -0.5], 
 [0.5, -0.5, -0.5]]
}}}
I ran into an instance where I had an asset with //many// shaders assigned, and I had to import in a new one and assign it.  But when I imported in the new one (not knowing the name), I couldn't figure out which one it was.  I wrote this code to find all //shadingEngines// (shaders) in the scene that weren't assigned to anything.  //Actually// its the other way around:  shadingEngines are sets, so you assign things //to them//.  As well, //materials// (phong, lambert, etc) plug into shadingEngines, nothing is actually 'assigned to a material', or vice-versa:
{{{
# Python code
import maya.cmds as mc

def findUnusedShaders():
    shadingEngines = mc.ls(type='shadingEngine')
    unused = []
    for se in shadingEngines:
        items = mc.sets(se, query=True)
        if items is None:
            unused.append(se)
    return unused
}}}
{{{
unused = findUnusedShaders()
}}}
The mel {{{linstep}}} command.  This command isn't available in Python.  Make your own:
{{{
def percentBetween(val, origMin, origMax):
    return float(val - origMin) / float(origMax - origMin)
}}}
{{{
print percentBetween(5, 0, 10)
0.5
}}}
----
For the opposite, see:
*[[How can I find a value some percentage between two other values?]]
*[[How can I find a 3d point some percentage between two other 3d points?]]
The [[unknownNode|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/unknownNode.html]] command:  For example
{{{
plugin = mc.unknownNode("someUnknownNodeName", query=True, plugin=True)
print plugin
# SomePluginName
}}}
Also lets you query the {{{realClassName}}} and {{{realClassTag}}}.
Had an issue where a user would try and make a {{{floatConstant}}} node, but it would show up as an {{{unknownNode}}} : This usually means that plugin isn't loaded.  How to find which one?  On a machine that //does// have the correct plugin loaded, run this:
{{{
import maya.cmds as mc

nodeType = "floatConstant"

allPlugs = mc.pluginInfo(query=True, listPlugins=True)
for plugin in allPlugs:
    dependNodes = mc.pluginInfo(plugin, query=True, dependNode=True)
    if dependNodes:
        if nodeType in dependNodes:
            print(plugin)
}}}
{{{
lookdevKit     
}}}
In Maya 2016 they (in my opinion) broke the undo:  They decoupled it from time.  I'm told this was an intentional change based on user feedback:  Some people don't like the fact when they hit undo they time changes (if a time change was performed).  I'm guessing those people aren't animators.

Here's an example of why it's bad:
*Maya 2015 (good behavior) :
**Go to frame 10, move node, set key manually.  Make sure autokey is on.
**Go to frame 100, move node, key is set.
**Go to frame 1000, move node, key is set.
** Hit undo two times:  The scene state reverts back to frame 100, time goes to frame 100.  If you then move the node, it keyframes it at frame 100.
* Maya 2016 (bad behavior) :
**Go to frame 10, move node, set key manually.  Make sure autokey is on.
**Go to frame 100, move node, key is set.
**Go to frame 1000, move node, key is set.
** Hit undo two times:  The scene state reverts back to frame 100, but @@time stays at frame 1000@@.  If you then move the node, it auto-keyframes it at frame 1000, even though the scene state is at frame 100.  
**This means every time an animator hits undo, they have to remember which frame they were at last, and scrub maya to that frame (or use ctrl+mmb+drag to set it on the timeline) before they set any keys.  But who can keep track of every previous frame they were on?  Otherwise, the end effect is that it looks like keys are set at random times all over their scenes.
I've talked to quite a few people\lists about this:  No one can convince me otherwise that this isn't a bug.  Having Maya's scene state be on one frame, but the timeline be at another is //broken// (unless manually set that way by the user, via a ctrl+mm+drag on the timeline, for some special purpose they're //consciously aware of//).

In Maya 2016 ~SP3 they fixed this, but it's a secret option.  To put undo back the way it used to be in 2015:
{{{
optionVar -iv timeChangeUndoable 1;
}}}
I understand this could be helpful in some circumstances.  The issue is the developers just 'flipped this switch' for the worldwide 2016 user base, rather than adding it as an opt-in option in the prefs :(
!Quick notes:
* Get some somewhat decent UV's put on it via whatever method you want.
* UV Toolkit -> Unfold [options]
!Docs:
* [[Unfold UVs options|http://help.autodesk.com/view/MAYAUL/2019/ENU/index.html?guid=GUID-039FFDA5-3AC5-4CCF-A53E-546CCA6439FC]]
* [[Unfolding a UV mesh|http://help.autodesk.com/view/MAYAUL/2019/ENU/index.html?guid=GUID-874CBD91-317F-4C7D-B293-EBBC93C2311A]]
!Commands
* [[unfold|http://help.autodesk.com/view/MAYAUL/2019/ENU/index.html?guid=__Commands_unfold_html]]
No online docs?
{{{
Synopsis: u3dOptimize [flags] [String...]
Flags:
  -bi -borderintersection  on|off
 -ite -iterations          Int
  -ms -mapsize             Int
 -pow -power               Int
  -rs -roomspace           Int
  -sa -surfangle           Float
  -tf -triangleflip        on|off

Command Type: Command
}}}
Normal maps are finicky things:  Often it seems that the green channel needs to be flipped to be viewed properly in Maya relative to how it should be in your game engine.  I've written tools to do this, but a modeler just clued me in:  Maya has this built in, it's just a bit buried:

Select the shape node of your mesh, and in the Attribute Editor under the 'Tangent Space' frame, you can swap the 'Coordinate System' from 'Right Handed' to 'Left Handed', which effectively reverses the green channel of the normal map.

Or, here is some ~PyMel to set the {{{tangentSpace}}} enum attr:
{{{
# Make all "Tangent Space" Coordinate Systems "Right Handed"
import pymel.core as pm
allMesh = pm.ls(type='mesh')
for mesh in allMesh:
    mesh.tangentSpace.set(2)
}}}
{{{
# Make all "Tangent Space" Coordinate Systems "Left Handed"
import pymel.core as pm
allMesh = pm.ls(type='mesh')
for mesh in allMesh:
    mesh.tangentSpace.set(0)
}}}

Windows System Call:
{{{
// The number specified is in seconds. However, I have seen this vary on different systems. Weird.
system "sleep 3";
}}}

Maya command:
{{{
pause -sec 5;
}}}
Sometimes when writing code that calls to many other procedures, you can run into issues where code will start executing out of order.  
*In one instance, I'd import a file, and constrain something to it.  But the end result had all sorts of problems:  the item constrained was in the wrong location.  I tried using commands like {{{dgdirty -a}}} and runTimeCommands like {{{EvaluateAll}}} in my code to fix the issues, but neither of them worked.  
*I figured out though, that by advancing the frame by 1 (and then setting it back), it absolutely makes the scene refresh.  This solved the issue.  
*In the case of my example, come to find out when the rig was imported into the scene, some of its plugs were dirty, thus it was in the wrong location.  {{{dgdirty}}} simply made //everything dirty//, and it wouldn't "fix" itself until the next free cycle, which happened //after// I'd constrained my other object and all my code stopped running.  {{{EnableAll}}} seemed to have the same effect:  Nothing was evaluated until the next free cycle, which was after the code finished.  Since I put the frame changing code in the //middle// of my scripts, it forced all the plugs to evaluate themselves //during// the code evaluation.
''Update #3'': I have yet to test it, but there is also the mel command {{{refresh}}} that should be investigated.
''Update #2'':  I got the bottom most code working, but I found the secret was this:  I needed to run it just before my scene import:  Again, I'd have issues where I'd import a scene, run the below refresh code, and it wouldn't work.  But if I found that I ran it just before I imported the scene, success!
''Update #1'':  The bottom code block was what I //had// been doing.  Then I found instances where even it wasn't working:  I had proven examples of frames advancing, but the mel after it executing and finishing //before// the frame advanced had finished.  Argh!
Doing more research, I change approaches:  Using the {{{play}}} command, it has a flag called {{{-wait}}} that //forces// Maya to take a break while things are playing.  THIS, fixed it:  You set the framerange to be one more than the start frame, play that range, then set all your defaults back.
''One other thing'':  I found another issue of when neither of these code bits worked:  I was setting a dynamic initial state on a softbody mesh, and all parts of that dynamic system were hidden.  I found that making that system //visible//, then changing the frame, then setting it back invisible again refreshed it.  I thought that calling {{{dgdirty -a}}} before the frame change would cause the plugs to dirty, and force a refresh, but even that didn't do it.  In this case, Maya was simply too optimized for its own good.
{{{
// define our init values:
float $startF = `playbackOptions -q -minTime`;
float $endF = `playbackOptions -q -maxTime`;
string $loopMode = `playbackOptions -q -loop`;

// start the playback
currentTime $startF;
playbackOptions -loop "once";
playbackOptions -maxTime ($startF+1);
play -wait;

// reset the default values
currentTime $startF;
playbackOptions -maxTime $endF;
playbackOptions -loop $loopMode;
}}}
Another method, still trying to figure out which method is best.
{{{
// force the scene to refresh by changing the frame:
float $time = `currentTime -q`;
currentTime ($time + 1);
currentTime $time;
}}}
Also see:
*[[How can I Enable \ Disable node evaluation?]]
{{{
import maya.cmds as mc
mc.file(renameToSave=True)
}}}
From [[this tip|https://groups.google.com/d/msg/maya_he3d/kiOOkpjbftk/u6oAVxWuAAAJ]] by Morgan Loomis.
Note, I have been unable to find something comparable in ~PyMel, so I end up doing this:
{{{
pm.mel.eval("file -renameToSave 1")
}}}
The animators wanted a tool that would let them grab the faces on the bottom of a characters foot, and generate a representative curve from the border of the selection, which they could use for foot placement reference in their animation.  The below code does that:  Based on a selection of faces, create a nurbs curve that surrounds their border.  However, for speedy coding purposes, it generates a nurbs curve //per edge segment//, and parents them all to a single transform, rather than making a //single curve// for all the points.  That would take more work, since you have to deal with point ordering issues within your selection ;)
{{{
# Python code
# Select polygonal faces, and execute

import maya.cmds as mc

border = mc.polyListComponentConversion(toEdge=True, border=True)
border = mc.ls(border, flatten=True)
if len(border) == 0:
    raise TypeError("Please select polygonal faces before executing")
else:
    curveList = []
    for edge in border:
        edgePts = mc.polyListComponentConversion(edge,  toVertex=True)
        edgePts = mc.ls(edgePts, flatten=True)
        pts = [mc.pointPosition(point, world=True) for point in edgePts]
        curveList.append(mc.curve(name="faceToCurve#", degree=1, point=pts))
        
    parent = mc.group(empty=True, name="faceToCurveNode#")
    for c in curveList:
        shapes = mc.listRelatives(c, shapes=True)
        mc.parent(shapes, parent,  shape=True, relative=True)
        mc.delete(c) 
    mc.xform(parent, centerPivots=True)
    mc.select(parent)
    print "Created '" + parent + "'",
}}}
Name clashing in Maya is a real pain.  Some tools will return back full paths for a node, while others only return back the leaf name.  So a safe way to deal with this is to always generate nodes with unique names, based on what's currently in the scene.
''Update #2:''
'Update #1' works if you can define the names at the time of creation.  But what if your code gives you a name with a number postfix on the end that could be clashing with something else in the scene?  This example shows how to capture the end postfix numbers, remove them, and then add the magical 'pound\hash' symbol that does the name updating that we love:
{{{
#Python code:
import re
import maya.cmds as mc

# create an empty group
mc.group(empty=True, name="group1")
# create another empty group as its child
mc.group(empty=True, name="group2", parent="group1")
# create a third empty group, that clashes names with the second group.
# but since the second group is in a group, Maya allows this
group = mc.group(empty=True, name="group2")

# find the end number on our name:
endNum = re.findall('[0-9]+$', group)
try:
    # try to rename, presuming we found a number
    newName = group[:-len(endNum[0])]+"#"
    mc.rename("|"+group, newName) 
except IndexError:
    # do nothing if no end number is found.
    pass
}}}
''Update #1:''
Originally I had the below (old) code to check to see if a name already exists, and return back the unique name.  But then I got tipped that if you simply use the 'pound' symbol ({{{#}}}) at the end of any object name (when created), it'll always return back a unique name:
{{{
#Python code
import maya.cmds as mc

# make first group.  Is called "group1"
g1 = mc.group(empty=True, name="group#")
# make second group called "group2", which "group1" is now a child of
g2 = mc.group(name="group#")
# make third group as a new hierarchy.  It is automatically named "group3"
g3 = mc.group(empty=True, name="group#")
}}}
The resulting heirarchy looks like this: (no name clashing!)
*{{{group2}}}
**{{{group1}}}
*{{{group3}}}
If you //didn't// use the '{{{#}}}' at the end of the names, the resulting hierarchy would look like this:  (name clashing)
*{{{group1}}}
**{{{group}}}
*{{{group}}}
----
''Old code:''
The below goes into a loop checking for the current '{{{checkName}}}'.  If it finds a node by that name, it simply prepends an increasingly higher number to the end of the name, and does the check again.  When it finds a version of the name with no match, it returns the new name.
{{{
# Python code
import maya.cmds as mc
def uniqueName(name):
    """return back a unique name for a node"""
    checkName = name
    j=0
    searching = True
    while searching:
        if mc.objExists(checkName):
            j = j+1
            checkName = name + str(j)
        else:
            searching = False
    return checkName

# presuming you already have two nodes by the name "checkName" in the
# scene, parented to different hierarchies:
print uniqueName("checkName")
# checkName1
}}}
Maya's File -> Open dialog never seems to open where I want it to.  Ever.  And normally I avoid hacking Maya's internal scripts, since you have to do it every new version that comes out.  But currently this is worth it.  It will change the behavior to open Maya's file dialog to the current directory of the current scene you have open.
You need to edit this file:
{{{
C:\Program Files\Autodesk\Maya<20XX>\scripts\others\projectViewer.mel
}}}
! Maya 2018
In the global proc {{{pv_ActionSetup}}}, starting around line 153, you need to add this chunk of code:
{{{
	string $activeDir = "";
	string $scene = `file -q -sn`;
	if(size($scene) > 0)
		$activeDir = dirname($scene);
	else
		$activeDir = $workspace;
}}}
Then, below in that procedure in all the {{{switch/case}}} statements, for any line that has {{{$actionOptions[9]}}} in it, if you want this behavior, you need to comment out that line, and replace it with below.

For example, the first is {{{case "Open":}}} : You'd make this edit:
{{{
		//$actionOptions[9] = `setWorkingDirectory $workspace $readType "scene"`;
		$actionOptions[9] = $activeDir;
}}}
And you'd continue to do this fore each other case you want the above behavior in.
! Up to Maya 2016
Around line 482 in:  (I was modifying Maya 2014-2016)
Comment out this line like so:
{{{
        //$cmd += (" -startingDirectory \"" + $actionOptions[9] + "\"");
}}}
And replace the block around it with this mel:
{{{
    if ("" != $actionOptions[9]){
        //$cmd += (" -startingDirectory \"" + $actionOptions[9] + "\"");
        string $scene = `file -q -sn`;
        if(size($scene) > 0){
            string $dir = dirname($scene);
            $cmd += (" -startingDirectory \"" + $dir + "\"");
            }
        else
            $cmd += (" -startingDirectory \"" + $actionOptions[9] + "\"");
        }
}}}
Starting in Windows 10, whenever I'd try to access Maya's help, it would open it in Edge/Internet Explorer instead of Chrome, even though I'd set Chrome as my default browser.
My buddy Conant Fong figure out you can edit the Window's registry to make this happen:
{{{
# Change to use default browser to Chrome
import _winreg
_winreg.SetValue(_winreg.HKEY_CURRENT_USER, "Software\\Classes\\.htm", _winreg.REG_SZ, 'ChromeHTML')
}}}
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.
''Notes:''
If you're using Maya's module system ([[Understanding Maya Modules and Pacakges]]), each module can have it's own userSetup.py in it.
For these to execute properly on Maya startup, a common method inside userSetup.py is this:
{{{
import maya.cmds as mc
def main():
    # put all your code in here

mc.scriptJob(event=['DagObjectCreated', main], runOnce=True)
}}}
Maya's module system will auto-execute every userSetup.py it can find in every module it finds, and the scriptJob will wait for a dag object to be made before it runs it.  This helps to get Maya fully initialized.

It should also be noted that userSetup.py files added via modules may not be accessible on the path once Maya starts:  Since Python can't have conflicting module names, my //guess// is the last module loaded is the one Maya sees.  Even though I have my own unique userSetup.py, this is what I get when I print it:
{{{
import userSetup
print userSetup.__file__
C:\Program Files\Autodesk\Maya2018\plug-ins\MASH\scripts\userSetup.pyc
}}}
It prints the MASH userSetup.py, and my guess is it was simply the last one to load.
!!!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|http://pythonwiki.tiddlyspot.com/#%5B%5BUsing%20the%20sitecustomize%20%26%20usercustomize%20modules%5D%5D]].
*These are Python's equivalent to Maya's {{{userSetup.py}}} module.
----
Also see:
*[[How can I update my Python Path?]]
I like to have code report info to the user via the command line.  But Pythons printing syntax is a bit different from mel:

For example in //mel//, if you print this:
{{{
print "foo\n";
}}}
it happily prints in the command line.

But if you do this in //Python//:
{{{
print "foo"
}}}
Nothing shows up in the command line.  

To get something to show up, you need to put a comma after the string, like so:
{{{
print "foo",
}}}
//That// will now show up in the command line.  The comma means "don't add a return character" (and it also adds a an extra space after the string, fyi).  

Another thing to note:  In either mel //or// python, if you put a leading return character in the string like: {{{"\nfoo..."}}}, //that// string won't print to the command line.  So finicky.
With the invention of ~PyMel, this is now built in:  The {{{pymel.core.language.melGlobals}}} is a dict of exactly that data.
{{{
import pymel.core as pm
for key in pm.language.melGlobals.keys():
    val = pm.language.melGlobals[key]
    print key, val
}}}
----
Old docs, for doing it in Python:

mel has the command {{{env()}}} that returns back the name of every global variable in memory at the time.  Via Python, this can be nicely wrappered in to a dictionary containing both the global var name, and its values.  This really only falls down in regards to matrix (which are converted into lists of lists of floats) and vector data types (which are converted into 3-float tuples), since Python doesn't have those types (by default).  But it still handles the conversion ok :)
{{{
# Python code
import maya.mel as mm

env = {}
allMelVars = mm.eval('env()')
for v in allMelVars:
    # Convert mel into Python by assigning it to a dummy var the same
    # name as the original var, but with an underscore added to the name.
    # This lets us get around mel global scope naming conflicts if the tool
    # is ran twice:
    pv = mm.eval('%s_ = %s'%(v,v))
    # strip off mel $ prefix:
    env[str(v[1:])] = pv
}}}
Now you can use all of Pythons dictionary tools on the {{{env}}} dict.  
If you want to print them all nicely:
{{{
# Cool Python printing module:
from pprint import pprint
pprint(env)
}}}
I like to make a lot of subsets while working, to help organize things.  While sets are {{{DG}}} nodes, and have no real hierarchy, by adding sets to one another, it sort of mirrors the child\parent {{{DAG}}} transform hierarchy, and the Outliner displays them this way.  However, since they aren't in a hierarchy, you can't use commands like {{{listRelatives}}} to get a list of all of their 'children'.  They have no 'children', since they aren't in a hierarchy.  But you still may want to get a list of their 'pseudo-chilld sets' anyway.

This code will recursively search a parental sets for all {{{objectSet}}}s connected too it, and all sets connected to them, etc.
{{{
# Python code
import maya.cmds as mc

sets = ["DZS_TORSO"]   
for s in sets:
    subSets = mc.listConnections(s, source=True, destination=False, type='objectSet')
    if subSets is not None:    
        for ss in subSets:
            sets.append(ss)
print sets
}}}
''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']
}}}
{{{
ls -as;
}}}
The {{{-as}}} stands for 'assemblies'.
I always forget how to do this for some reason... so here it is.  In STONE.
{{{
nurbsCurveShape = mc.listRelatives(curveTransform, shapes=True, type='nurbsCurve')
cvs =  mc.ls("%s.cv[*]"%nurbsCurveShape[0] , flatten=True)
# ["curveShapeName.cv[0]", curveShapeName.cv[2]", etc]
}}}
Or, some old mel, slightly different way:
{{{
string $curve = "myCurve";
string $shape[] = `listRelatives -shapes $curve`;
int $numCVs = `getAttr -size ($shape[0] + ".controlPoints")`;
string $cvs[] = `ls -fl ($shape[0] + ".cv[0:" + $numCVs + "]")`;
// myCurveShape.cv[0] myCurveShape.cv[1] myCurveShape.cv[2] etc...
}}}
If you take out the {{{-fl}}} flag from {{{ls}}}, it will collapse them into a single element that still works:
{{{
myCurveShape.cv[0:16] // presuming it had 17 cv's
}}}
I pulled this by digging into the script:
{{{
C:/Program Files/Autodesk/Maya<VER>/scripts/others/selectCurveCV.mel
}}}
Many attributes in in Maya are considered "multi":  They are considered "array" attributes in the API.  They can have many children attributes, and are often dynamically generated.  How to get a list of them?
{{{
import maya.cmds as mc
node = "pPlaneShape1"
attr = "uvSet"

multi = mc.listAttr('%s.%s'%(node,attr), multi=True)
for m in multi:
    val = '<compound>'
    try:
        val = mc.getAttr('%s.%s'%(node,m))
    except RuntimeError:
        pass
    print '%s%s'%(str(m).ljust(40,' '), str(val).rjust(20, ' '))
}}}
Will print something like this (presuming the plane only has four verts):
{{{
uvSet[0]                                          <compound>
uvSet[0].uvSetName                                      map1
uvSet[0].uvSetPoints[0]                         [(0.0, 0.0)]
uvSet[0].uvSetPoints[0].uvSetPointsU                     0.0
uvSet[0].uvSetPoints[0].uvSetPointsV                     0.0
uvSet[0].uvSetPoints[1]                         [(1.0, 0.0)]
uvSet[0].uvSetPoints[1].uvSetPointsU                     1.0
uvSet[0].uvSetPoints[1].uvSetPointsV                     0.0
uvSet[0].uvSetPoints[2]                         [(0.0, 1.0)]
uvSet[0].uvSetPoints[2].uvSetPointsU                     0.0
uvSet[0].uvSetPoints[2].uvSetPointsV                     1.0
uvSet[0].uvSetPoints[3]                         [(1.0, 1.0)]
uvSet[0].uvSetPoints[3].uvSetPointsU                     1.0
uvSet[0].uvSetPoints[3].uvSetPointsV                     1.0
uvSet[0].uvSetTweakLocation             [(0.086363710463047028, 0.047107480466365814)]
}}}
----
Also see:
*[[How can I add a multi attr to my node, query its connections?]]
*[[API: Find all child attributes under a compound attribute]]

{{{env}}}
This will return back textures, references, and presumably audio files:
{{{
> mayaBatch -archive c:\\directory\\with\\file.mb > logFile.txt
}}}
Note:  You ''must use double-backslash'' {{{\\}}} (on Windows at least) when providing the path:  Single backslash {{{\}}} or forwardslash {{{/}}} in the path will cause it to fail.

The resultant txt file collects //all// the startup text that Maya prints.  Finally, towards the bottom it has a line with {{{Result:}}} in it:  That's the line Maya prints when the file finally opens.  After that, there will be lines with paths to things (referenced files, textures) some ending with an asterisk {{{*}}} : These are the recorded dependencies.  If parsing that file, that's where you want to start looking.

Example snippet:
{{{
...
File read in  0.61 seconds.
Result: c:/directory/with/file.mb
c:/directory/with/file.mb
c:/directory/with/otherReferenceFile.mb
c:/directory/with/someTexture.tga*
...
}}}
Not sure why it lists it's own filename twice (and without an c:/directory/with/file.mb), nor why some paths have an asterisk after them while others don't,  but such are things.
----
Here is a Python script that will do the same thing.  //Doesn't// need to be executed from within Maya, since it calls to it in batch mode.  Also, it seems far more flexible when accepting the path names, slash-wise, than the above code.
{{{
import subprocess
def getFileDependencies(filePath):
    """
    Parameters:
    filePath : string : Full path to .ma or .mb file to query.
    Return : list : Full path to all dependencies in file.
    """
    result = subprocess.Popen(["mayaBatch", "-archive", thefile], stdout=subprocess.PIPE).communicate()
    foo = [f.strip() for f in result[0].split("\r\n") if f != ""]

    dependencies = []
    startSeach = False
    for f in foo:
        if f.startswith("Result: "):
            startSeach = True
        if not startSeach:
            continue
        if f.startswith('plugin: '):
            continue
        if startSeach and f.startswith("----"):
            startSeach = False

        if f.endswith("*"):
            dependencies.append(f[:-1])
        elif f.endswith(".ma") or f.endswith(".mb"):
            if f != filePath and f not in dependencies:
                dependencies.append(f)

    return dependencies
}}}
{{{
runTimeCommand -q -ca;
}}}
When I originally created this post in Jan of 09', Maya had no built-in way for tracking component level selection order:  You'd pick a bunch of verts (for example), and the {{{ls}}} command would return them back in... probably some other order.

In later versions of Maya, {{{-ls}}} added a {{{-orderedSelection}}} flag that you could use //instead// of the {{{-selection}}} flag.  But this only worked if you first executed this command:
{{{
selectPref  -trackSelectionOrder 1;
}}}
Doing that, {{{ls}}} would now return component-level selections in the correct order.

Sometime in Maya 2015, maybe during a service-pack update, this functionality seemed to break though.  Long story short:  If you execute the above {{{slectPref}}} command twice in a row, {{{ls -orderedSelection}}} would return an empty list.  Turning it off, then back on would get {{{ls}}} to return items again, but now in the //wrong// order.
SO, if you have code that depends on this, you now need to add in something like this:
{{{
if (`selectPref -q -trackSelectionOrder` == 0){
    selectPref  -trackSelectionOrder 1;
}
}}}
Basically, if it's off, turn it on, but if it's on, just leave it alone.  As of this posting (Maya 2016) I've bugged this with Autodesk.

If you want to prove this yourself, here's an example:
{{{
// make a cube, select some verts in a specific order:
polyCube -w 1 -h 1 -d 1 -sx 3 -sy 3 -sz 3 -ax 0 1 0 -cuv 4 -ch 1;
select -r pCube1.vtx[49] ;
select -tgl pCube1.vtx[1] ;
select -tgl pCube1.vtx[10] ;

// What state is our selection order tracking in?  Mine is always on by default:
int $trackState = `selectPref -q -trackSelectionOrder`;
// 1

// Print the selection:
ls -orderedSelection;
// Result: pCube1.vtx[49] pCube1.vtx[1] pCube1.vtx[10] //  THE CORRECT ORDER!

// Turn selection tracking on.  I'd exepct this to not change anything but...
selectPref  -trackSelectionOrder 1;
ls -orderedSelection;
// PRINTS NOTHING.  What?!?!

// Turn selection tracking OFF
selectPref -trackSelectionOrder 0;
ls -orderedSelection;
// Result: pCube1.vtx[1] pCube1.vtx[10] pCube1.vtx[49] //  THE WRONG ORDER (as expected...)

// Turn selection tracking back ON
selectPref -trackSelectionOrder 1;
ls -orderedSelection;
// Result: pCube1.vtx[1] pCube1.vtx[10] pCube1.vtx[49] //  THE WRONG ORDER (not expected...)
}}}
https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/deformableShape.html
{{{
import maya.cmds as mc
print mc.deformableShape("myMeshShape", chain=True)
[u'tweak2', u'proximityWrap1']
}}}
Starting in Maya 2012, you can use [[melInfo|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/melInfo.html]]
{{{
# Python code
import maya.cmds as mc

procs = mc.melInfo()
for p in sorted(procs):
    print p
}}}
This will return back commands, runTimeCommands, and global procedures.
Hello {{{allNodeTypes}}}

https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/CommandsPython/allNodeTypes.html
As a test, I compared Solution A & B over a 1000 loops.  Here's the results:
*Solution A: .897 seconds.
*Solution B: 40.704 seconds.
API & algorithm FTW, 45.4x faster & ~PyMel & selection.
!Solution A: Maya API & Python Algorithm:
I came up with 'Solution B' first, but it bugged me I had to do it via selection.  'Solution A' is much faster, being based on the API and no selection is needed.  I created the {{{combineSimilarElements}}} algorithm to merge the groups of faces together into their shells.  Note that {{{getPolyShells}}} returns lists of ints:  It's up to you to turn those back into valid faces, based on whatever method suits your needs (Mel\Python\~PyMel).
{{{
import maya.OpenMaya as om

def combineSimilarElements(data):
    """
    Based on the passed in data (list of lists), combine them into the fewest
    possible number of items that have the same shared elements.  For example, if
    these two lists were passed in: [[1,2,3], [3,4,5]] the tool would return the
    combined [1,2,3,4,5] : Since both lists share the int 3, they're combined.

    Parameters:
    data : list : List of sublists.  The sublists will be compared to one another.

    Return : list : The combination of data.
    """
    # Need to copy, otherwise we'll modify in-place:
    retData = data[:]
    intermediateData = []

    curSize = len(retData)
    prevSize = -1

    while curSize != prevSize:
        while len(retData):
            currentCheck = retData.pop(0)
            for data in retData:
                # Check for intersection/shared items:
                setData = set(data)
                setCurrentCheck = set(currentCheck)
                if setCurrentCheck & setData:
                    currentCheck = list(setCurrentCheck.union(setData))
                    retData.remove(data)
            intermediateData.append(currentCheck[:])
        prevSize = curSize
        retData = intermediateData
        intermediateData = []
        curSize = len(retData)

    return retData

def getPolyShells(mesh):
    """
    For the provided mesh string name, return a list of sublists:  Each sublist
    contains the int indicies contained in that shell.
    """
    selList = om.MSelectionList()
    selList.add(mesh)
    mObject = om.MObject()
    selList.getDependNode(0, mObject)
    connected = []
    iterMesh = om.MItMeshPolygon(mObject)
    while not iterMesh.isDone():
        thisIndex = iterMesh.index()
        connectedIndices = om.MIntArray()
        iterMesh.getConnectedFaces(connectedIndices)
        connected.append([thisIndex]+connectedIndices)
        iterMesh.next()
    return combineSimilarElements(connected)
}}}
!Solution B: ~PyMel & Selection
Normally I loath picking things in code, but the [[polySelect|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/polySelect.html]] command was too good to be {{{True}}}, and I couldn't pass up using it.

Based on the passed in mesh, return a list of sublists.  Each sublist is a collection of ~PyMel [[MeshFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshFace.html]] indices that live in the given shell.
{{{
import pymel.core as pm

def getMeshShells(mesh):
    """
    mesh : PyMel Mesh, or string name.
    return : list of lists of MeshFaces
    """
    mesh = pm.PyNode(mesh)
    sel = pm.ls(selection=True)
    facePool = set([f for f in mesh.f])
    shells = []
    for i,f in enumerate(mesh.f):
        if f not in facePool:
            continue
        shell = pm.polySelect(mesh, extendToShell=i, replace=True)
        shellFaces = pm.ls(selection=True, flatten=True)
        facePool = facePool.difference(set(shellFaces))
        shells.append(shellFaces)

    if len(sel):
        pm.select(sel)
    else:
        pm.select(clear=True)

    return shells
}}}
These could be nodes other than DAG nodes.
{{{listHistory}}}
{{{
listConnections -s 1 -d 0 -p 0 -c 0 "setName";
}}}
*Note: Often times, one would think they could simply select the set, and use the {{{ls}}} command to get an array of membership. The problem is, the {{{ls}}} command will return them in a different order than the Outliner may show. Furthermore, the order seems to change from time to time (hard to confirm this). When an item is "added" to a set, what's actually happening is the set has a new attribute generated, and that attr is connecting to the object. By using the "listConnections" command, we can get a list, in order, of all the attribute connections. The order still may be different than what's shown in the Outliner, but at least it should be consistant.
*There is only one caveat: The listConnections command will return DG objects in a set before it lists DAG objects. If you don't have mixed data types in your sets, then there is no issue. However, if you have both DG and DAG objects in the set, then you still could run into future problems of shifting order in the return value.
I recently needed to find the sets shown in the Outliner.  Problem is, commands like this:
{{{
ls -type "objectSet";
ls -sets;
listSets -type 2;
}}}
Return ALL {{{objectSet}}}s in the scene.  

By querying the contents of the {{{itemFilter}}} passed to the {{{setFilter}}} arg on the default {{{outlinerEditor}}} control, we're able to query its contents, which is //just the sets showing up in the Outliner//:
{{{
# Python code
import maya.cmds as mc

# New empty scene, make some sets:
mc.file(new=True, force=True)
mc.sets(name='spam')
mc.sets(name='eggs')
}}}
Now query the sets in the Outliner:
{{{
myOutliner = "outlinerPanel1" # Name of the default Outliner
setFilter = mc.outlinerEditor(myOutliner, query=True, setFilter=True)
outlinerSets = mc.lsThroughFilter(setFilter, nodeArray=True)
print outlinerSets
}}}
{{{
[u'spam', u'eggs']
}}}
----
This will list ALL the sets in the outliner, including any subsets.  What if you want to get only the root most sets (since you can have subsets?).  You can pass the above results into this function to get only sets that aren't members of any others:
{{{
def getRootSets(setData):
    ret = []
    for s in setData:
        outgoing = mc.listConnections('%s.message'%s, source=False, destination=True, type='objectSet')
        if not outgoing:
            ret.append(s)
    return ret
}}}
{{{
renderer -query -namesOfAvailableRenderers;
}}}
...or for multiple objects:
{{{
# Python code
import maya.cmds as mc

meshes = ['myMesh']
}}}
This method requires the selection of objects, which isn't always desirable:
{{{
mc.select(mc.polyListComponentConversion(meshes, toVertex=True))
verts = mc.ls(selection=True, flatten=True)
}}}
Non-selection method, but a bit more code:
{{{
verts = []
for mesh in meshes:
    numVerts = mc.polyEvaluate(mesh, vertex=True)
    vtxList = ['%s.vtx[%s]'%(mesh, val) for val in range(numVerts)]
    verts += vtxList
}}}
{{{
print verts
# [u'myMesh.vtx[0]', u'myMesh.vtx[1]', u'myMesh.vtx[2]', ... ]
}}}
{{{
lsUI -windows;
}}}
I had an issue where a tool would return a list of joints, but I wanted to list them in their hierarchical order.  Below is the solution I came up with:  By querying the long name of each joint, and then simply sorting that list, it will return them in a nice hierarchical order.  I should note that this may not be the //exact// sibling order that Maya has them in since they've been alphabetized at the sibling level:  For example, based on the below print, it's very possible that spineA is the first child of pelvis, rather than hip_L.  But for my usage, it worked fine.
{{{
# Python code
import maya.cmds as mc

# Define a list of joints in random order based on their actual hierarchy:
joints = ['hip_L', 'pelvis', 'hipTwist_L', 'ball_L', 'spineA', 'spineC', 'spineB']
# Sort the joints by their full path.  Generate the list of full paths via a
# Python list comprehension:
sorter = sorted([mc.ls(jntA, long=True)[0] for jntA in joints])
for s in sorter:
    print s
}}}
prints:
{{{
|root|pelvis
|root|pelvis|hip_L
|root|pelvis|hip_L|hipTwist_L
|root|pelvis|hip_L|knee_L|ankle_L|ball_L
|root|pelvis|spineA
|root|pelvis|spineA|spineB
|root|pelvis|spineA|spineB|spineC
}}}
Should node that bad things will happen if there are duplicate named things in the scene.

To do this with ~PyMel is a bit different:  Since each node in the list is actually a ~PyNode, not a string with a long name, how to sort?  Like so:
{{{
import pymel.core as pm
joints = [pm.PyNode(j) for j in ['hip_L', 'pelvis', 'hipTwist_L', 'ball_L', 'spineA', 'spineC', 'spineB']]
sorter = sorted(joints , key=lambda x:x.longName())
}}}
We can sort the ~PyNodes by their 'long name', (full path to node) via the {{{sorted}}} functions {{{key}}} arg.
Credit Steve Kestell.

This will return a list of all the items picked in the main {{{Outliner}}}:
{{{
string $myOutliner = "outlinerPanel1";
string $selectionConnection = `outlinerEditor -q -selectionConnection $myOutliner`;   
string $selectList[] = `selectionConnection -q -obj $selectionConnection`;
}}}
The {{{window}}} command has the flag {{{-rtf}}} (resize to fit), which will make it resize itself (bigger) to fit the child controls.  However, if the UI is dynamic, or its child controls change to be smaller, when the UI is regenerated, it'll keep its larger size.  How can you make it change size every time, to fit the child controls, even if the child controls shrink the overall UI size?

You need to add a call to 'remove the saved window size' at the top of the UI code:
{{{
if(`windowPref -exists "myUI_name"`)
    windowPref -remove "myUI_name";
}}}
In this example, you can change the button size however you’d like, and the UI will always resize correctly when reopened:
{{{
global proc tempWin_UI()
    {
    if(`window -exists tempWin_UI`)
        deleteUI tempWin_UI;
    if(`windowPref -exists "tempWin_UI"`)
        windowPref -remove "tempWin_UI";

    window -t "Temp Window" -rtf 1 tempWin_UI;
        columnLayout -adj 1 -co both 5;
            button -w 200 -h 200;
    showWindow;
    }
tempWin_UI;
}}}
As of v8.0, Maya has no good debugger for scripting :(

The {{{trace}}} command (mel only, not in Python) allows you to call to it inside a procedure.  It will print back values to the Output Window (Maya's standard output, {{{stdout}}}) for debugging purposes.

If you use the {{{-where}}} argument, it will //also// add the prefixed file and line number indicating where the trace call originates.
For example, save this file on disk as {{{foo.mel}}} in your Script Path, and then run {{{rehash}}} so Maya sees it as a script.  Then execute {{{foo}}}.

{{{
global proc foo()
    {
    global int $intvar;
    $intvar = rand(1,100);
    trace -where ("int $intvar = " + $intvar);
    }
}}}
Result in the Output Window when ran would be (executing the procedure multiple times):
{{{
file: C:/scripts/foo.mel line 5: int $intvar = 18
line 5: int $intvar = 71
line 5: int $intvar = 61
line 5: int $intvar = 11
line 5: int $intvar = 30
}}}

{{{
>> maya -h
}}}
*Notes: From initial tests, it looks like an empty scene uses about half the ram (35 megs) as the "UI" version (80 megs).
Presuming you have polygonal components picked, like faces:  How can you get just the edges that live on the border of the mesh.  For example, if you had a poly cube missing the top face, and you made a selection of all the faces in the cube, how could you convert that to just the edges around the top open edge?
{{{
import maya.cmds as mc
import maya.mel as mm

def getBorderEdgesFromSelection():
    mm.eval('ConvertSelectionToEdges;')
    mc.polySelectConstraint(mode=2,type=0x8000,where=1)
    borderEdges = mc.ls(selection=True, flatten=True)
    mc.polySelectConstraint(mode=0, disable=True)
    return borderEdges
}}}
This function both returns a list of the border edges, and selects them in the process.

{{{ConvertSelectionToEdges}}} to a ~RunTimeCommand for '{{{PolySelectConvert 2}}}'.
{{{PolySelectConvert}}} is a global mel proc living in this script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\PolySelectConvert.mel
}}}
Wrote this really quickly in ~PyMel, so there may be a speed hit on large polysets:  It will find any non uv-mapped faces, and return that list.
{{{
import pymel.core as pm

def getUnmappedFaces(mesh):
    """
    For the passed in mesh, return a list of any unmapped faces.

    Parameters:
    mesh : string\PyNode\list : Either the transform or shape level, string or
        PyNode instance of mesh to query.

    Return : list : PyNode MeshFace instances that have no UV mapping.
    """
    if not isinstance(mesh, (list,tuple)):
        mesh = [mesh]

    # Convert to PyNode Mesh instances:
    _mesh = []
    for m in mesh:
        mNode = pm.PyNode(m)
        if not isinstance(mNode, pm.nt.Mesh):
            meshShapes = mNode.getChildren(type='mesh')
            if meshShapes:
                _mesh.append(meshShapes[0])
        else:
            _mesh.append(mNode)
    mesh = _mesh

    badFaces = []
    pm.waitCursor(state=True)
    for m in mesh:
        for face in m.f:
            if not face.hasUVs():
                badFaces.append(face)
    pm.waitCursor(state=False)
    return badFaces
}}}
[[MeshFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshFace.html]] docs
When writing strings to text files, you have to embed a 'newline' character or everything will be on the same line.  For some reason, Notepad doesn't respect the standard {{{\n}}} 'newline' character:  It will print evertying on the same line, with a "square" [] character replacing where the 'newlines' should be.  The secret apparently, is that you need to \\also\\ embed a 'return character', and do it before the 'newline character':
{{{
// snip //
int $fid = `fopen $filepath "w"`;
for($i=0;$i<size($data);$i++)
	fprint $fid ($data[$i] + "\r\n");
// snip //
}}}
You //have// to do it {{{\r\n}}}:  Storing {{{\n\r}}} will provide unsatisfactory results :(

Also see:
*[[How can I write text to my own file?]]
Starting late in Maya 2015, and documented in Maya 2016, there is a [[unknownPlugin|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/unknownPlugin.html]] command that can be used to strip these out.

Here's an example of an error you may see on file open:
{{{
requires "depthOfFieldView" "1.0";
# Error: line 1: Plug-in, "depthOfFieldView", was not found on MAYA_PLUG_IN_PATH.
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# RuntimeError: Plug-in, "depthOfFieldView", was not found on MAYA_PLUG_IN_PATH. # 
}}}
Removal code:
{{{
import pymel.core as pm    
oldplugs= pm.unknownPlugin(query=True, list=True)
for op in oldplugs:
    pm.unknownPlugin(op, remove=True)    
    print "Removed old, unknown plugin: %s"%op 
}}}
Run the above code after opening a scene:  Re-save it, and the errors will go away on next open.
Had an issue where I was trying to use the 'set driven key' window to drive attributes on a {{{layeredTexture}}}:  Each 'layer' is an array compound {{{inputs}}} attr ({{{inputs[0]}}}, {{{inputs[1]}}}, etc), that has sub-attrs called things like {{{color}}} (which is itself a multi (float) ), {{{alpha}}} (float), {{{isVisible}}} (bool), etc.  When you select the {{{layeredTexture}}} node and add it as the 'driven' item in the SDK window, none of these array input attrs show up.

Looking into it, it ended up being a single line fix:
In this script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\setDrivenKeyWindow.mel
}}}
In the local proc {{{genListAttrString}}}, change line 247 from this:
{{{
$cmd = "listAttr -scalarAndArray -visible -connectable -unlocked -multi -leaf ";
}}}
To this, by removing the {{{-leaf}}} at the end:
{{{
$cmd = "listAttr -scalarAndArray -visible -connectable -unlocked -multi ";
}}}
To refresh the SDK window in mel:
{{{
source setDrivenKeyWindow;
SetDrivenKeyOptions;
}}}
When reloading the driven nodes, all the compound {{{inputs}}} and their (float) children now show up, and can be keyed.
@@Be sure to set the SDK window to display the ''long'' names, or the data can be very confusing.@@
~PyMel makes acessing the bounding box data pretty easy actually.  Otherwise you'd have to call to the API.  From there, some simple Python {{{min}}}/{{{max}}} functions do the rest.
{{{
import pymel.core as pm

def intersectBBox(bboxA, bboxB):
    """
    Return the bounding box intersection of one bounding box with another.

    Parameters :
    bboxA, bboxB : PyMel BoundingBox.

    Return : None / PyMel BoundingBox : If the bounding boxes don't intersect,
        None is returned.  Otherwise a new BoundingBox instance that is the
        intersection of the two is returned.
    """
    if not bboxA.intersects(bboxB):
        return None

    thisMin = bboxA.min().cartesian()
    thisMax = bboxA.max().cartesian()
    otherMin = bboxB.min().cartesian()
    otherMax = bboxB.max().cartesian()
    bboxMinPt = pm.dt.Point(max([thisMin[0], otherMin[0]]),
                            max([thisMin[1], otherMin[1]]),
                            max([thisMin[2], otherMin[2]]))
    bboxMaxPt = pm.dt.Point(min([thisMax[0], otherMax[0]]),
                            min([thisMax[1], otherMax[1]]),
                            min([thisMax[2], otherMax[2]]))
    return pm.dt.BoundingBox(bboxMinPt, bboxMaxPt)
}}}
Example using it with poly cubes, to prove out the values:
{{{
import pymel.core as pm

c1 = pm.PyNode("pCube1")
c2 = pm.PyNode("pCube2")
bb1 = c1.getBoundingBox(space='world')
bb2 = c2.getBoundingBox(space='world')

intersect = intersectBBox(bb1, bb2)
print intersect.min().cartesian()
print intersect.max().cartesian()
print intersect.width(), intersect.height(), intersect.depth()
}}}
~PyMel 2015 Docs for [[BoundingBox|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.BoundingBox.html]]
New to Maya 2010, and for use in //mel only//, and only on //procedures// (not built-in commands), is:
{{{getProcArguments}}}
Of course, you //can// wrapper this with Python:
{{{
import maya.mel as mm
args = mm.eval('getProcArguments stringArrayToString')
[u'string[]', u'string']
}}}
They use the term //argument//, which I feel is incorrect:  Arguments are what you pass into the parameters, and this tool queries the parameter //types// ({{{int}}}, {{{string[]}}}, etc), not the values passed in. 
I wanted a way to get a list of all of the parameters that make up the signature of a mel command.  Mel doesn't provide for a lot of introspection like Python does.  However, the mel {{{help}}} command will return back a bunch of info on a command, including the parameter list (when the command is authored properly via the API).

With a bit of string fiddling via Python, we can extract out just the parameter list.  For example, running the mel {{{help}}} on the {{{menu}}} command, it prints this:
{{{
// mel code

help menu;
// Result: 

Synopsis: menu [flags] [String]
Flags:
   -e -edit
   -q -query
 -aob -allowOptionBoxes     on|off
 -dai -deleteAllItems      
  -dt -defineTemplate       String
 -dtg -docTag               String
  -en -enable               on|off
  -ex -exists              
  -fi -familyImage          String
  -hm -helpMenu             on|off
  -ia -itemArray           
   -l -label                String
  -mn -mnemonic             String
  -ni -numberOfItems       
   -p -parent               String
 -pmc -postMenuCommand      Script
 -pmo -postMenuCommandOnce  on|off
  -to -tearOff              on|off
  -ut -useTemplate          String
 -vis -visible              on|off


Command Type: Command
 // 
}}}
We can intercept that with Python, and pull out just what we want:
{{{
# Python code
import maya.cmds as mc

def getParameterList(melCmd):
    helper = [line.strip() for line in mc.help(melCmd ,syntaxOnly=True).splitlines()]
    parameters = []
    for h in helper:
        if h.startswith('-'):
            splitter = h.split(' ')
            parameters.append(splitter[1][1:])
    return parameters
}}}
{{{
print getParameterList('menu')
[u'edit', u'query', u'allowOptionBoxes', u'deleteAllItems', u'defineTemplate', ...
}}}
{{{
import maya.cmds as mc
color = mc.grabColor(hsv=True)
print color
}}}
{{{polyUVSet}}} command.
Has a variety of options including the ability to (from the docs):
*Delete an existing uv set
*Rename an existing uv set.
*Create a new empty uv set.
*Copy the values from one uv set to a another pre-existing uv set.
*Set the current uv set to a pre-existing uv set.
*Modify sharing between instances of per-instance uv sets
*Query the current uv set.
*Set the current uv set to the last uv set added to an object.
*Query the names of all uv sets
Some examples:
{{{
//Get all UV sets on a mesh:
string $allUV[] = `polyUVSet -query -allUVSets "myMesh"`;
}}}
{{{
// Query the current UV via shading engine:
string $currentUV[] = `polyUVSet -query -currentUVSet "someSG"`;
}}}
----
Also see:
*[[Understanding how UV attrs work on polygonal meshes.]]
You can set a global int that will tell Maya to //not// access a saved UI configuration when a scene is opened.  You also need to update an optionVar:
{{{
$gUseScenePanelConfig = 0;
optionVar -iv useScenePanelConfig $gUseScenePanelConfig;
// run file open code
}}}
These layouts are saved (unless you set your options to NOT save these files...) in each scene as a {{{scriptNode}}} called {{{uiConfigurationScriptNode}}} (easily accessed via the Expression Editor).

OR, you could just modify the Maya Preferences through the UI, which basically does what was done above:
*Window -> Settings/Preferences -> Preferences... -> UI Elements -> “When Opening [''uncheck''] Restore Saved Layouts from File”
You can turn on "Auto Frame" in the Graph Editor, but when you restart Maya, it's off again.  It doesn't seem to ever remember, which is quite bothersome.  It even seems to store an optionVar, but upon restart, it seems reset.

Add this to your {{{userSetup.mel}}} file to always force it to be on:
{{{
global proc setAutoFrame()
	{
	animCurveEditor -edit -autoFit 1 graphEditor1GraphEd;
	optionVar -intValue graphEditorAutoFit 1;
	print ("userSetup.mel:		** Turned on Graph Editor 'auto fit'\n");
	}
scriptJob -event SceneOpened setAutoFrame;
}}}
Whenever a scene is opened, the scriptJob will run the {{{setAutoFrame}}} proc, resetting these values.
Also see:
*[[How can I edit \ query the contents of the Graph Editor]]
{{{loadPrefObjects}}}
*Example: By simply saving a scene called loadPrefObjects.ma in your prefs directory, they will be imported every time a scene is opened.
{{{
cmdFileOutput
}}}
It should be noted that this only works with calls via the {{{print}}} statement:  If a command prints stuff to the script editor, it won't be captured :(
[[This blog post|http://danostrov.com/2013/02/06/rerouting-the-maya-script-editor-to-a-terminal-and-other-places/]] presents a robust solution that should allow for it though.
----
Also see:
*[[How can I query the contents of the undo queue?]]
*Maya has the default hotkeys {{{ctrl+c}}} and {{{ctrl+v}}} like most software to copy and paste data.  
*//But//, the hotkeys are //context sensitive//:  They do different things if the Channel Box, Graph Editor, or modeling panel (like, the perspective view) is active.  
*In the Channel Box or Graph Editor, they copy and paste //keys//.  In a modeling panel, they //physically copy and past scene data//, based on what's currently selected.  It does this by exporting and importing data to\from a temp file on disk.
*This can wreck some major havoc when it comes to animators (most commonly) copying\pasting keys, and not realizing they //don't// have the Graph Editor\Channel Box actually active.  They'll get some temp file dumped into their scene, making the file dirty.  Then they yell.  It gets ugly.
*IMO, what Maya should do by default is //prompt the user// with a confirmDialog //before// the paste (scene import) happens.  But //not// prompt for the pasting of keyframes.  The below code does that.  I made a //new// 'user hotkey', and remapped it to {{{ctrl+v}}}
{{{
int $bad = 1;
// get the current panel.  If it's the Graph Editor, we're good.
string $panel = `getPanel -withFocus`;
if(`match "^graphEditor" $panel` == "graphEditor")
    $bad = 0;

// is anything picked in the Channel Box?  If so, we're good.
string $selAttr[] = `channelBox -q -sma "mainChannelBox"`;
if(size($selAttr))
    $bad = 0;

// if things are still 'bad', then fire up our confirmDialog, since we must be trying
// to paste (import) our temp scene:
if($bad)
    {
    string $yesNo = `confirmDialog -title "Confirm File Paste" 
        -message "Are you sure you want to paste (import)\nthe previously 'copied' (exported) Maya file?"
        -button "Yes" -button "No" -defaultButton "Yes"
        -cancelButton "No" -dismissString "No"`;
    if($yesNo == "Yes")
        cutCopyPaste "paste";
    }
else
    cutCopyPaste "paste";
}}}
There have been rare instances where I've needed to create mel procedures inside of Python modules.  For example, the {{{outlinerEditor}}} command has a {{{selectCommand}}} argument that expects a //mel procedure// passed in as a string.  If you're creating the {{{outlinerEditor}}} via Python (via {{{maya.cmds.outlinerEditor()}}}), and you want to access that argument, you can't call to another Python function... you //have to// call to a mel proc. :-(

It's pretty easy to author and execute mel from Python by simply making a multi-line string with the contents of the proc, and having the Python {{{maya.mel.eval()}}} function evaluate it:
{{{
# Python code:

import maya.mel as mm
mel ='''
global proc foo(){
    print "foo is a go!\\n";
}
'''
mm.eval(mel)
}}}
Then to execute the newly created mel through Python:
{{{
# Python code:

mm.eval('foo()');
# foo is a go!
}}}
You can also confirm this in mel of course:
{{{
// mel code:

foo();
// foo is a go!
}}}
Continuing the example from above, your Python {{{outlinerEditor}}} code could look like this:
{{{
# Python code:

import maya.cmds as mc
mc.outlinerEditor(myOutliner, selectCommand='foo();')
}}}
@@Note:@@  Wing can interact with Maya in three different ways.  This is one of the three.  See an overview here on the whole system:  [[Interaction between Wing and Maya]]
The below examples I've gotten to work repeatedly on Windows and Mac.
----

I use [[Wing IDE|For Python: Maya 'Script Editor' style IDE]] when authoring Python and mel code for Maya, and have come up with a solution (presuming you have the 'Professional' version of Wing) to have the code (Python //or// mel) you @@highlight in Wing@@ //execute in Maya// (aka 'evaluate selection').  Also known as [[REPL|http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop]].  This lets me completely replace the Script Editor when authoring Python //or// mel.
>Note that you can have Wing recognize {{{.mel}}} files, with //some// (not full) context sensitive highlighting, correct indentation, scope hilighting, etc:  Under Wing 'Prefs -> Files -> File Types', 'insert' a new file type as {{{mel}}} and set its 'Mime Type' to be {{{C++ Source}}}.  

There are a number of steps involved to get this working.  I should point out again this only works with the 'Professional' version of Wing, and you'll need Maya v8.5 or //newer//: when Maya started supporting Python (below this section I list some old code, for version of Maya8.5 and earlier.)
!!!Important Starting Notes
When it comes to opening {{{commandPorts}}}, the port number is fairly arbitrary.  In the below examples I use {{{6000}}}, but you can use another number if you wish.  Just make sure to update it in both locations:  {{{mayaWingServer.py}}} //and// the function {{{send_to_maya()}}} in {{{wingHotkeys.py}}}.
!!!Overview of the below code:
*Maya launches, creates a listener-server waiting for input from Wing.
*In Wing, the user executes a hotkey that sends the highlighted text from Wing to a temp text file.  At the same time, over a {{{commandPort}}} it pings Maya and tells it to evaluate the text file.
*Maya's server receives the ping, processes the text file, and evaluates it in Maya, as if it was executed from the Script Editor
!!!#1 - userSetup.py & mayaWingServer.py
Starting with Maya 2013, the only success I've had getting Wing to talk to Maya is over a 'server' connection, rather than a more simplistic {{{commandPort}}} connection.  

The module {{{mayaWingServer.py}}} should be authored and placed in the same directory as the user {{{userSetup.py}}} file.  When Maya starts up, it should be called to (via {{{userSetup.py}}}) thus launching a listener-server running in a separate thread that will wait for data incoming from Wing, and when it receives it, process the data.
{{{
"""
mayaWingServer.py
Author : Eric Pavey - 2012-10-23
"""
import sys
import socket
import threading

import maya.utils as mu
import maya.OpenMaya as om

import executeWingCode

#-----------------
PORT = 6000  # Needs to be the same value as authored in wingHotkeys.py below.
SIZE = 1024
BACKLOG = 5

def processDataInMaya(data):
    """
    This function is designed to be passed as the 'processFunc' arg of
    the mayaServer function.  It is mainly a try\except wrapper around the
    executeWingCode.main() function.

    data : string : The data passed from wing.  Currently this is 'python' or 'mel'.
    """
    try:
        # If we don't evaluate in maya.util.executeInMainThreadWithResult(),
        # Maya can crash, and that's no good.
        mu.executeInMainThreadWithResult(executeWingCode.main, data)
    except Exception, e:
        om.MGlobal.displayError("Encountered exception: %s"%e)

def server(processFunc=processDataInMaya, port=PORT, backlog=BACKLOG, size=SIZE):
    """
    Create a server that will listen for incoming data from Wing, and process it.

    Modified example taken from:
    http://ilab.cs.byu.edu/python/socket/echoserver.html

    The server will wait to recieve data from a single client.  When it receives
    data, it will 'process' it via the processFunc function.

    Parameters :
    processFunc : function : A function object that will
        process the data recieved by the client.  It should accept a single
        string argument.
    port : int : Default to global PORT.  The port to connect to.
    backlog : int : Default to global BACKLOG.  The number of connections the
        server can have waiting.
    size : int : Default to global SIZE.  The size in bytes to recieve back from
        the client.
    """
    host = ''
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.bind((host,port))
    except:
        print "Tried to open port %s, but failed:  It's probably already open\n"%port
        return

    s.listen(backlog)
    print "Starting Python server, listening on port %s...\n"%port
    while True:
        client, address = s.accept() # client is a socket object
        data = client.recv(size)
        if data:
            processFunc(data)
        client.close()

def startServer():
    """
    When Maya starts up, execute this to start the Wing listener server:
    """
    threading.Thread(target=server).start()
}}}
The {{{userSetup.py}}} file should import the above module, and call to {{{startSever()}}}:
{{{
# userSetup.py

import maya.cmds as mc
# Need to defer the execution, or on Mac it can fail...
mc.evalDeferred("import mayaWingServer; mayaWingServer.startServer()", lowestPriority=True)
}}}
If you don't have a {{{userSetup.py}}} file, you can make a new one and stick it here (PC):
{{{
C:\Users\<USERNAME>\Documents\maya\python\userSetup.py
}}}
Or here (Mac):
{{{
/Users/<USERNAME>/Library/Preferences/Autodesk/maya/scripts/userSetup.py
}}}
----
Older notes:

Up until Maya 2013, the below code was all that was needed to get a {{{commandPort}}} opened and let Wing talk to Maya.  But something happened in 2013 that broke all this, thus the above code was generated.
<<<
In Maya's {{{userSetup.mel}}} file, add this line to open a network socket.  This will later let Wing talk with Maya.
{{{
// userSetup.mel
commandPort -name ":6000" -echoOutput;
}}}
It appears, if you're running Maya on Vista \ 64bit, or maybe it's a Maya2010 thing, you need to call to {{{commandPort}}} twice to get this working, otherwise your socket will later be refused:
{{{
// userSetup.mel
commandPort -name "127.0.0.1:6000" -echoOutput;
commandPort -name ":6000" -echoOutput;
}}}
For whatever reason, having your {{{userSetup.py}}} execute these commands instead won't let the below process work.  This really confuses me, since Maya claims the port is open.  But Wing says the port is refused.... 
<<<
!!!#2 - wingHotkeys.py
The Wing Python module ({{{wingHotkeys.py}}}) and functions inside are authored to do a few things:  
#Save the text selected in Wing as a temp file on disk.  
#Ping Maya through the opened command port, and tell Maya to execute the contents of that file.  Depending on the type of data sent, either Python or mel, the tool will tell Maya how to intercept it, and execute it.
Again, this code is tailored to //Wing IDE's API//, and saved as part of that program's own "scripts" directory.  
Default location (on winXP) of that dir is here:
{{{
C:\Documents and Settings\<userName>\Application Data\Wing IDE 3\scripts\wingHotkeys.py
}}}
On Windows 7:
{{{
C:\Users\<userName>\AppData\Roaming\Wing IDE 4\scripts\wingHotkeys.py
}}}
On Mac:
{{{
/Users/<userName>/.wingide4/scripts/wingHotkeys.py
}}}
<<<
Note for Mac:  The {{{/.wingide4}}} directory seems to be hidden in Finder.  To access it, open a Terminal, cd to that folder, and execute:
{{{
$ open .
}}}
To launch a Finder to that folder.
<<<
Functions:
*{{{send_to_maya()}}} :  Function that does the heavy lifting, and calls to {{{executeWingCode.main()}}} (discussed below).
*{{{python_to_maya()}}} :  Wrapper to send the code as Python to Maya.  I have this mapped to {{{ctrl+p}}}.  (Wing hotkeys don't allow args, so you need to author wrapper functions)
*{{{mel_to_maya()}}} :  Wrapper to send the code as mel to Maya.  I have this mapped to {{{ctrl+m}}}.
The biggest hangup with this system is getting Maya to properly open a {{{commandPort}}}, getting a proper {{{socket.socket()}}} connection, and getting Maya to properly connect via {{{maya.connect()}}}.  Based on the network settings of your machine, the below code may not work for you as-provided in all instances.  Whenever I change machines I seem to have to modify one or more of these areas. 
I've left code-examples (commented out) for other alternatives that I've used on various machines to get these working, so if something fails, you can try using those examples.  Otherwise you'll need to strap on your networking programmer hat and dig into the docs a bit.

If you already have a {{{wingHotkeys.py}}} module for Wing, you can just add the below code to it:
{{{
# wingHotkeys.py
# Author:  Eric Pavey - warpcat@sbcglobal.net

import wingapi
import socket
import os
import tempfile

def getWingText():
   """
   Based on the Wing API, get the selected text, and return it
   """
   editor = wingapi.gApplication.GetActiveEditor()
   if editor is None:
      return
   doc = editor.GetDocument()
   start, end = editor.GetSelection()
   txt = doc.GetCharRange(start, end)
   return txt

def send_to_maya(language):
   """
   Send the selected code to be executed in Maya

   language : string : either 'mel' or 'python'
   """
   # The port the sever is listening on in mayaWingServer.py :
   commandPort = 6000

   if language != "mel" and language != "python":
      raise ValueError("Expecting either 'mel' or 'python'")

   # Save the text to a temp file.  If we're dealing with mel, make sure it
   # ends with a semicolon, or Maya could become angered!
   txt = getWingText()
   if language == 'mel':
      if not txt.endswith(';'):
         txt += ';'
   # Cross-platform way to get a temp dir:
   tempDir = tempfile.gettempdir()
   tempFile = os.path.join(tempDir, 'wingData.txt')
   f = open(tempFile, "w")
   f.write(txt)
   f.close()

   # Create the socket that will connect to Maya,  Opening a socket can vary from
   # machine to machine, so if one way doesn't work, try another... :-S
   mSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # works in 2013...
   #mSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
   # More generic code for socket creation thanks to Derek Crosby:
   #res = socket.getaddrinfo("localhost", commandPort, socket.AF_UNSPEC, socket.SOCK_STREAM)
   #af, socktype, proto, canonname, sa = res[0]
   #mSocket = socket.socket(af, socktype, proto)

   # Now ping Maya over the command-port
   try:
      # Make our socket-> Maya connection:   There are different connection ways
      # which vary between machines, so sometimes you need to try different
      # solutions to get it to work... :-S
      #mSocket.connect(("127.0.0.1", commandPort)) # works in 2013...
      #mSocket.connect(("::1",commandPort)) 
      mSocket.connect(("localhost", commandPort)) # 2016, mac

      # Send our code to Maya:
      # It is intercepted via the function processDataInMaya(), created via mayaWingServer.py
      mSocket.send(language)
   except Exception, e:
      print "Send to Maya fail:", e

   mSocket.close()

def python_to_maya():
   """Send the selected Python code to Maya"""
   send_to_maya('python')

def mel_to_maya():
   """Send the selected code to Maya as mel"""
   send_to_maya('mel')
}}}
!!!#3 - executeWingCode.py
''3.''  The Python module {{{executeWingCode.py}}} is the one Wing's {{{send_to_maya()}}} function (above, step 2) triggers via the Maya listener-sever.  It is what physically evaluates the code executed in Wing, in Maya.
Be sure to save this in a location seen by //Maya's// Python path (probably same dir as {{{userSetup.py}}}).
{{{
"""
executeWingCode.py
Eric Pavey - 2011-03-23
Module that Maya calls to when Wing pings it through a socket, telling Maya
to execute the commands in the temp file as either Python or mel code.
"""
import __main__
import os
import tempfile

import maya.OpenMaya as om

def main(codeType):
    """
    Evaluate the temp file on disk, made by Wing, in Maya.

    codeType : string : Supports either 'python' or 'mel'
    """
    codeType = codeType.decode() # Python 3 need
    # Cross-platform way to get a temp dir:
    tempDir = tempfile.gettempdir()
    tempFile = os.path.join(tempDir, 'wingData.txt').replace("\\", "/")
    if os.access(tempFile , os.F_OK):
        # Print the lines from the file in Maya:
        with open(tempFile, "r") as f:
            for line in f.readlines():
                print line.rstrip()
        print ("\n",)

        if codeType == "python":
            # execute the file contents in Maya:
            #with open(tempFile , "r") as f: # Python 2
                #exec(f, __main__.__dict__, __main__.__dict__)
            exec(open(tempFile).read(), __main__.__dict__, __main__.__dict__) # Python 3

        elif codeType == "mel":
            melCmd = 'source "%s"'%tempFile
            # This causes the "// Result: " line to show up in the Script Editor:
            om.MGlobal.executeCommand(melCmd, True, True)
    else:
        print "No Wing-generated temp file exists: " + tempFile
}}}
The key here for the ''Python'' execution, is the {{{exec}}} command updating the {{{__main__.__dict__}}} dictionary with any defined variable names passed in from Wing.  It is effectively adding them to the 'main', or 'builtin' scope of Python.  If this //doesn't// happen, after the code is executed, you won't be able to access any variables created\changed from within Maya.

For ''mel'', calling to {{{OpenMaya.MGlobal.executeCommand}}} allows for the result of the execution to be printed to the Script Editor, just like you had originally executed it there.
!!!#4 - Finish
That's it:  You can now highlight code in Wing (either Python or mel), and via a wing hotkey, have it execute directly in Maya.  I use it __every day__...
----
!!!Older methods:
*For Maya versions 8 and //earlier// (Maya 8.5 has Python embedded, yah!)
*Thanks to Hamish ~McKenzie "//macaroniKazoo//" on highend3d.com
*First, in Maya, create an INET domain on the local host at the command port: 
**Win2000 syntax is "{{{computerName:portNumber}}}", I've wondered why you don't have to specify the computerName here?
{{{
commandPort -n ":5055";
}}}
*Second, in Python:
{{{
import socket
maya = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
maya.connect(("127.0.0.1", 5055))
}}}
*Then, to actually have Python talk to Maya:
{{{
maya.send('print "I am typing in Python, but I see it in Maya!\\n";')
}}}
*Of course, you have to embed your //mel commands// in the Python syntax. 
*Python library docs on 'socket' [[HERE|http://docs.python.org/lib/module-socket.html]]
*What's so special about '127.0.0.1'? Check Wikipedia [[HERE|http://en.wikipedia.org/wiki/127.0.0.1]]
Say you have transformed a polygonal mesh off in space, so that it's local axis no longer matches up with what you think the overall axes of the mesh are.  How can you make these axes match some other transform you have deemed "correct"?  

The below code accepts a mesh name, and the name of some transform that will be the 'new axis' : By placing and orienting this transform as you desire, the mesh will get re-parented to it, thus adopting it's transformations.  
{{{
import pymel.core as pm

def main(mesh, axis):
    mesh = pm.PyNode(mesh)
    axis = pm.PyNode(axis)
    ret = None
    pm.undoInfo(openChunk=True)
    try:
        meshName = mesh.nodeName()
        kids = mesh.getChildren(noIntermediate=True)
        if kids:
            meshMtx = mesh.getMatrix()
            axisMtx = axis.getMatrix()
            axisMtxInv = axisMtx.inverse()
            # Transform both back to origin, but keep mesh relative to axis:
            pm.xform(mesh, matrix=meshMtx*axisMtxInv)
            pm.xform(axis, matrix=axisMtxInv*axisMtx)
            # Freeze the mesh, and parent shape nodes to axis:
            pm.makeIdentity(mesh, apply=True)
            kids = mesh.getChildren(noIntermediate=True)
            pm.parent(kids, axis, shape=True, relative=True)
            pm.delete(mesh)
            axis.rename(meshName)
            pm.xform(axis, matrix=axisMtx)
            ret = axis
    finally:
        pm.undoInfo(closeChunk=True)
    return ret
}}}
{{{
mesh = pm.PyNode("myMesh")
axis = pm.PyNode("myTransform")
main(mesh, axis)
}}}
!Worldspace Matrix
If you're only interested in translation values, you can query a nodes {{{worldMatrix}}} attr to get this info directly from the node in question:
{{{
string $node = "pSphere1";
float $offset = 1.0;
float $t = `currentTime -q`;
float $worldMatrix[] = `getAttr -t ($t- $offset) ($node+".worldMatrix")`;
vector $offsetTranslate = <<$worldMatrix[12], $worldMatrix[13], $worldMatrix[14] >>;
print $offsetTranslate;
}}}
!decomposeMatrix node
Before I had a better understanding of nodes matrix attrs, I used to use this method:
*The {{{decomposeMatrix}}} plugin wil help do the trick (got the info for this tip in a forum by Lutz Paelike)
*I'm not a big fan of plugins in development, they can really cause a bottleneck, but since this plugin is distributed with Maya, I find it safer than usual.
*What the decomposeMatrix node does, among other things, is let you pass in a source matrix, and it will spit out the worldspace trans, rot, scale, and shear for that matrix.  From there, we can use {{{getAttr -t}}} to query those values in time.
*Given this concept, regardless of your source objects hierarchical transformations, as long as your destination object lives in worldspace, it will follow along happily in time:

First, load the "decomposeMatrix.mll" plugin:
{{{
int $isLoaded = `pluginInfo -q -l "decomposeMatrix.mll"`;
if (!$isLoaded)
    loadPlugin "decomposeMatrix.mll";
}}}
Next create the decomposeMatrix node:
{{{
string $dm = `createNode decomposeMatrix`;
}}}
given you have an object with it's name in ' string $obj = "myObj" ', connect:
{{{
connectAttr -f ($obj + ".worldMatrix[0]") ($dm + ".inputMatrix");
}}}
Now you can query the decomposeMatrix's output attrs to find the worldspace transformations.  (or, you could connect them to another node, for a direct connection situation) And if we make an *expression* in like the link below, you can query them in time, to generate lag:
{{{
float $t = `currentTime -q`;
followObj.translateX = `getAttr -t ($t - 10) decomposeMatrix1.outputTranslateX`;
}}}
Also see:
*[[How can I compute distance traveled in an expression?]]
*[[How can I make one object "follow" another, offset in time, but without using getAttr or setAttr?]]
The {{{arrayMapper}}} node, and quite a bit of other work. The expression work below comes from the brain of Daniel Roizman:  roizman@kolektiv.com
*Steps, based on a nurbs curve softbody:
**On your particle object there is a .goalPP attr. This allows the user to change the goal weights per particle in the component editor. However, it'd be a lot easier if you could use a ramp to do this instead.
**On your particle object, in the Attribute editor, under "Add Dynamic Attributes", add a "General" attr that is a float "Per Particle (array)" attr, and name it uValuePP?.
**Next make a "Runtime Before Dynamics Expression" on the new uValuePP? attr by RMB on it in the "Per Particle (Array) Attributes) section:
{{{
 myparticle.uValuePP = float(myparticle.particleId) / float(myparticle.count);
}}}
**This isn't super accurate, but it gets the job done.
**RMB on the "goalPP" attr field and choose "Create Ramp (options)".
**Set the "Input V" to uValuePP?, hit OK. This creates the arrayMapper, & associated ramp.
**That's it. Animate your source curve and the default ramp will make it stick on one end, and be very floppy on the other.
*You may want to have a script return back where it physically lives on disk.  You can use this for file processing, tracking of assets... whatever.  How to do?
*The {{{whatIs}}} command is very handy at returning this info.
*For example, you make a mel script called whatsit here:  {{{C:/whatsit.mel}}}  You save it, and then highlight-enter it in the script editor to define it:
{{{
global proc string whatsit()
    {
    string $whatIs = `whatIs whatsit`;
    if(`match "^Mel procedure found in: " $whatIs` == "Mel procedure found in: ")
        $whatIs = `substitute "Mel procedure found in: " $whatIs ""`;
    return $whatIs;	
    }
}}}
*If you then quit Maya, restarted it, and entered the command you'd get this:
{{{
whatsit;
// Result: C:/whatsit.mel //
}}}
*One tricky bit where the code can get hung up is if you’re //currently authoring// the code, and trying to get whatIs to work properly.
*Say you then highlighted the procedure, and defined it in the script editor by pressing Enter.  You //then// run whatsit, and you get:
{{{
whatsit;
// Result: Mel procedure entered interactively. //
}}}
*Hmm… not the full path (but true).  However, if you have it {{{rehash}}}, then {{{source}}} itself first, then it’s happy:
{{{
global proc string whatsit()
    {
    rehash;
    source whatsit;
    string $whatIs = `whatIs whatsit`;
    if(`match "^Mel procedure found in: " $whatIs` == "Mel procedure found in: ")
        $whatIs = `substitute "Mel procedure found in: " $whatIs ""`;
    return $whatIs;	
    }
}}}
*Now try executing the code after ''saving'' the script and 'highlighting-Enter' declaring it in the script editor
**FYI saving it is very important:  Since it's being rehashed and sourced, it will then look a the data ON DISK, not what you last interactively entered in the script editor.
{{{
whatsit;
// Result: C:/whatsit.mel //
}}}
*Again, this is only an issue if you’re in the habit of re-highlighting and re-defining the procedure //while// you’re working on it (which I am).  If you restarted Maya, the first example above would work just fine.  
*Be warned:  If you had mel commands outside of the whatsit //proc// but inside the whatsit.mel //script//, the {{{source}}} statement would executethem, so even then it’s not a best case solution.  Python anyone? ;)
----
Also see 
*[[How can I query the location of a script?]]
*[[How can I add images to my UI, without hardcoding their paths?]]
I find two schools of though when it comes to switching constraint target weights:  One is to blend the weights over time, providing (hopefully) smooth transitions between the targets.  In this case, the constraint weights are controlled by some float attr, usually 0->1 or 0->10, with 0 being off, and 1/10 being on.  The second is it do a hard switch, where the weights are either on or off (0 or 1), with no blending.  This second method is usually accompanied with a 'switch tool' that will help preserve the position of the node being switched in world-space, so that no pop occurs, yet it now moves relative to a new parent.  This discussion deals with the second method.

If you had four constraint targets, you could create for boolean attrs for turning them on\off.  But this is a lot of unnecessary keyed data to deal with when instead you can do it with a single enum attr that has four values (~enumNames).  As long as the enum attr names are in the same order as the constraint weight targets, they'll pair up nicely.  However, to actually get the enum attr to controll each of the constraint weights is a bit tricky:  When enum 0 is active, you want the four weights to be set to 1,0,0,0.  When enum 1 is active, you want the weights set 0,1,0,0, etc.  This functionality can be provided via 'choice' nodes.  Each choice node has an array 'input' attr, that can have values set for each index.  Based on a connection to the choice's 'input' attr, it will decide what 'input' attr should be passed to the 'output'.
The below function will create the choice node and setup this mapping:
{{{
import maya.cmds as mc
def switchConstraintConnect(switchNode, switchAttr, constraint):
    """
    This tool is designed to allow a single enum attr to control the weight switching
    on a given parentConstraint.  IMPORTANT:  The passed in enum attr names and the
    constraint's weight attrs must be in the same order!

    Parameters:
    switchNode : string : Name of the node with the switch enum attr.
    switchAttr : string : Name of the enum attr used for the switching.
    constraint : string : Name of the constraint to control.
    """
    enumAttrs = mc.attributeQuery(switchAttr, node=switchNode, listEnum=True)[0].split(':')
    conWeightAttrs = mc.parentConstraint(constraint, query=True, weightAliasList=True)
    num = len(enumAttrs)
    for i in range(num):
        choice = mc.createNode('choice', name="choice_%s_%s"%(switchNode, enumAttrs[i]))
        mc.connectAttr('%s.%s'%(switchNode, switchAttr), '%s.selector'%choice)
        for j in range(num):
            val = 1 if j == i else 0
            mc.setAttr('%s.input[%s]'%(choice,j), val)
        mc.connectAttr('%s.output'%choice, '%s.%s'%(constraint,conWeightAttrs[i]) )
}}}
I had a recent issue where I needed to have a skinned character, made out of many parts, deform a duplicate of itself, but all polycombined together.  I couldn't copy the skin weights since I was going from a bunch of small skinned parts to one big one. 

I figured out you can have multiple wraps effect the same target mesh, so that was the route I went:

The default behavior of the wrap command is to have one target mesh (the last picked) wrap all the mesh picked before it.  Normally you use this to have a larger mesh wrapper up many smaller ones.  But I needed the exact opposite behavior.

Furthermore, I learned that the {{{createDeformer -type wrap}}} //doesn't// do the job:  This just makes a wrap deoformer, doesn't do all the 'hooking up' required to make the system work.  However, mel supplies you with a {{{CreateWrap}}} //~runTimeCommand// that calls to a bunch of other scripts\procs that do the heavy lifting.

In the below example, the last node picked will be wrapped to all the mesh picked before it.  Note, you first need to go into the wrap tool window, setup your options, and save the preferences.  The runTimeCommand will use those preferences during the wrap creation.
{{{
# Python code:
import maya.cmds as mc
import maya.mel as mm

sel = mc.ls(selection=True)

targets = sel[:-1]
node = sel[-1]

for t in targets:
    mc.select(node)
    mc.select(t, add=True)
    mm.eval('CreateWrap')
}}}
When you create a wrap through the Maya ui, this is the code it calls to (all procs, not actual commands):
*{{{CreateWrap}}} runTimeCommand
*{{{performCreateWrap false}}} : the code it executes.
**That proc lives in: {{{C:\Program Files\Autodesk\Maya20XX\scripts\others\performCreateWrap.mel}}}
*It calls to the local proc {{{assembleCmd()}}}
*It calls to the global proc {{{doWrapArgList}}}
**That proc lives in: {{{C:\Program Files\Autodesk\Maya20XX\scripts\others\doWrapArgList.mel}}}
*Which calls to the local proc {{{createWrap}}} in the same script, that does the final, actual wrap work.
{{{
# Python code
import maya.cmds as mc

huds = mc.headsUpDisplay(listHeadsUpDisplays=True)
for hud in huds:
    mc.headsUpDisplay(hud, edit=True, visible=False)
}}}
Often, animators only want to work on a specific animation channels based on multiple selected objects.  Say you have 10 objects picked, and only want to modify their tx and ry channels in the Graph Editor.  You'd have to, by hand, scroll down through the Graph Editor Outliner, picking those channels.
The Channel Box however has this functionality:  With multiple objects picked, whatever channels are selected in the Channel Box will effect each of those objects.
So the question is, how can you take what's selected in the Channel Box, and 'transfer' it to the Graph Editor?
In a nutshell:
*Find the {{{selectionConnection}}} being used by the Graph Editor Outliner.
*Get a list of all the objects picked.
*Query what attrs are picked in the Channel Box.
*For each obj+attr combo, 'update' the {{{selectionConnection}}} in the Graph Editor Outliner to highlight these channels.  Also need to "un-highlight" the picked //objects//.
This will give the animator the visual filter they need to only work on those channels, in the Graph Editor.
{{{
# Python code
import maya.cmds as mc

selectionConnection = mc.outlinerEditor("graphEditor1OutlineEd",
                                        query=True, selectionConnection=True)
selObj = mc.ls(selection=True, long=True)
selAttr = mc.channelBox("mainChannelBox", query=True,
                        selectedMainAttributes=True)

for so in selObj:
    mc.selectionConnection(selectionConnection, edit=True, deselect=so)
    for sa in selAttr:
        mc.selectionConnection(selectionConnection, edit=True, select=so+"."+sa)
}}}
It's often nice when there is a long operation that you wrapper that operation with a {{{waitCursor}}} command for your users, so they get the comfortable feeling that the machine is actually doing something important, rather than just being locked up.  Via Python, there are two different implementations that allow you to 'wrapper' your code with other code:  The 'context manager' and the 'decorator'.  Context managers wrapper a block of code, while decorators wrapper functions.

Context manager notes on my Python Wiki:
*http://pythonwiki.tiddlyspot.com/#%5B%5Bwith%20statement%5D%5D
Here, we build a context manager and a decorator for use:
{{{
import maya.cmds as mc
}}}
{{{
class WaitCursor_cm(object):
    """
    Our waitCursor context manager:
    """
    def __enter__(self):
        mc.waitCursor(state=True)
    def __exit__(self, *args):
        mc.waitCursor(state=False)
}}}
{{{
class WaitCursor_d(object):
    """
    Our waitCursor decorator:
    """
    def __init__(self, f):
        self.f = f
    def __call__(self, *args, **kwargs):
        mc.waitCursor(state=True)
        try:
            self.f(*args, **kwargs)
        finally:
            mc.waitCursor(state=False)
}}}
----
This shows an implementation using the context manager:
{{{
def implementContextManager():
    with WaitCursor_cm():
        mc.pause(sec=5)

implementContextManager()
}}}
And here an implementation using the decorator:
{{{
@WaitCursor_d
def implementDecorator():
    mc.pause(sec=5)

implementDecorator()
}}}
----
In either case when the functions are executed, the pause command is wrappered, properly turning on and off the {{{waitCursor}}}.
{{{
# Python code
import maya.cmds as mc

# Get a list of all the current references.  They in turn
# may reference other things that will be exposed once
# they are imported:
allrefs = mc.file(query=True, reference=True)
for ref in allrefs:
    try:
        mc.file(ref, importReference=True)
    except RuntimeError:
        pass    
    # See if there are any new references to import:
    newrefs = mc.file(query=True, reference=True)
    if len(newrefs):
        allrefs += newrefs
}}}
Or via ~PyMel:
{{{
import pymel.core as pm

allrefs = pm.getReferences(recursive=True)
for ref in allrefs:
    try:
        allrefs[ref].importContents(removeNamespace=False)
    except RuntimeError:
        pass
}}}
Not the most elegant solution ( since this may not work in batch mode since it counts on the UI to run), but it works.
{{{
string $weightName = "my_blendshape_target";

// Needed to access artBlendShapeSelectTarget:
source artAttrBlendShapeCallback;
// First, select the mesh with the blendshape.  Then:
// Open the 'Paint Blend Shape Weights Tool':
ArtPaintBlendShapeWeightsToolOptions; 
// Select the Target weight in the window:
artBlendShapeSelectTarget artAttrCtx $weightName;
// Import the saved weight onto it:
artAttrCtx -e -importfileload "C:/path/to/my/weight.iff" "artAttrBlendShapeContext";
}}}
I've also found this command that will select the given blendshape weight for import, but it doesn't update the UI, and the above command seems to run without it.  But including if needed in the future.
{{{
artAttrCtx -e -paintattrselected "blendShape.blendShapeName.weightName" artAttrBlendShapeContext;
}}}
The docs for the {{{paintattrselected}}} flag are:
* 	An array of selected paintable attributes. Each element of the array is a string with the following information {{{NodeType.NodeName.AttributeName}}}.
This is hackey, but it works
{{{
# Select the mesh with the wrap and then:
artAttrToolScript 4 "shrinkWrap";
artAttrInitPaintableAttr;
artAttrCtx -e -importfileload "C:/path/to/my/mask.iff" "artAttrContext";
}}}
Maya really seems to try to hid this stuff from the user.  {{{artAttrToolScript}}} and {{{artAttrInitPaintableAttr}}} are both mel scripts that wrapper most of the heavy lifting.
Presuming you have a list of nodes, each with a numbered postfix on the end, like so:
*bob_1
*bob_2
*bob_3
*etc...
Or, in a hierarchy:
*bob_1
**bob_2
***bob_3
****etc...
And you'd like to increment or decrement those numbers, how would you author code to do so?
It's actually a pretty good example, because you can run into a lot of issues:
{{{
// get our list of nodes, full paths:
string $sel[] = `ls -l -sl`;

// define the number we will add to \ subtract from the postifx
int $number = -1;

// loop throug hour list:
for($i=0;$i<size($sel);$i++)
	{	
        // match numbers only on the end of our object name:
	string $endString = `match "[0-9]*$" $sel[$i]`;

        // increment, or decrement the number.  Temporarily convert our string-based name to an int: 
	string $newString = int($endString) + $number;

        // Danger:  Can't rename somthing to less than zero, Maya wouldn't accept the name:
	if(int($newString) >= 0)
		{
                // If greater than zero, generate our new name.  Substitute only numbers on the end of the name
		string $newname = `substitute ($endString + "$") $sel[$i] $newString`;

                // Check to see if an object with this full path name already exists.  If so, print warning, skip
		if(`objExists $newname`)
			warning ("Can't rename node '" + $sel[$i] + "' to this name: '" + $newname + "'; that name alraedy exists in scene");

                // If this is a unique name for the full path:
		else
			{
                        // get just the leaf name, if it is a full path name: 
			string $buffer[];
			tokenize $newname "|" $buffer;
			string $endName = $buffer[size($buffer)-1];

                        // FINALLY rename our full-path'd node with the leaf name:
			rename $sel[$i] $endName;  // print $sel[$i]

                        // Important:  Redefine the selection list.  If you're renaming parental nodes, based on the
                        // full path names, the child path names would then change, and they wouldn't be found
                        // any more by the code.  Must update the full paths:
			$sel = `ls -l -sl`;
			}
		}

        // If the name would have been less than zero,  print warning, and skip
	else
		warning ("Can't rename node '" + $sel[$i] + "' to a lower numbered postfix, would be less than zero");
	}
}}}
Our result would be:
*bob_0
*bob_1
*bob_2
*etc...
or
*bob_0
**bob_1
***bob_2
****etc...
[[Maya docs here|http://help.autodesk.com/view/MAYAUL/2019/ENU/index.html?guid=GUID-2380579F-F59C-4E7D-904C-B6B90E949D35]]
* Select the node in question.
* Run 'Rigging -> Control -> Tag As Controller' : This runs the {{{controller("nodeName")}}} command. 
* This will create a new {{{controller}}} node, via the [[controller|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/controller.html]] command.
* The connection made is : {{{controller.message --> dagNode.controllerVisibility}}}.
* On that {{{controller}}} node, set the {{{visibilityMode}}} attribute to {{{2}}} : 'Show on mouse proximity'.
Note, that adding these nodes can have a significant performance impact on your scene during playback, up to %10 less framerate, even if you have that node's {{{hideOnPlayback}}} attr set on.
----
Also see:
* [[Working with 'controller' types in Maya]]
* [[Increasing animation playback performance]]
''Update #3'':
Actually found this in the [[isolateSelect|https://help.autodesk.com/cloudhelp/2022/ENU/Maya-Tech-Docs/CommandsPython/isolateSelect.html]] command docs:
{{{
import maya.cmds as mc
modelPanel  = mc.paneLayout('viewPanes', query=True, pane1=True)
}}}
----
''Update #2'':
You can use the {{{playblast}}} command, in either mel or Python, to query the active {{{modelPanel}}}.  This has an advantage that it will always return back a {{{modelPanel}}}, even if some other panel (like say, the Outliner) was picked most recently:
{{{
import maya.cmds as mc
modelPanel = mc.playblast(activeEditor=True)
}}}
----

''Update #1'':
{{{
import maya.cmds as mc

visiblePanels = mc.getPanel(visiblePanels=True)
modelPanels = mc.getPanel(type='modelPanel')
modelPanel = list(set.intersection(set(visiblePanels), set(modelPanels)))[0]
}}}
----
I've had times when I'd want to frame the camera on a certain object in code.  But I'm not sure which panel is currently available to view through based on the users currently layout.  Maya has four viewable panels that a camera can exist in:  modelPanel1, modelPanel2, modelPanel3, modelPanel4.  And based on the currently layout, any one of those four could be currently maximized.  The below code will find the //first// one available, and print it's name.
{{{
string $allPanels[] = `getPanel -vis`;
string $camPanel = "";
for($i=0;$i<size($allPanels);$i++)
	{
	if(`match "^modelPanel" $allPanels[$i]` == "modelPanel")
		{
		$camPanel = $allPanels[$i];
		break;
		}
	}  //
print ($camPanel + "\n");
}}}
This code could then be used to make the camera in that panel frame some object:
{{{
// we say "if" $camPanel exists, because it's possible that some other 
// panel is maximized, like the Outliner:
if(size($camPanel))
	{
	setFocus $camPanel;
	select -r "myGeometry";
	FrameSelected;
	fitPanel -selected;
	select -cl;
	}
}}}
----
Also see 
*[[How can I use mel to make the current camera look at an object of choice?]]
*[[How do I query the name of the camera in the active panel?]]
Thanks to a tip from Mason Sheffield:
{{{
import maya.app.general.resourceBrowser as resourceBrowser
resBrowser = resourceBrowser.resourceBrowser()
print resBrowser.run()
}}}
Normally this modal dialog is accessed from the 'Maya Icon' in the Shelf Editor window.  But you can skip that and just run the above code.
*In Windows, if you write your own files to disk using Maya using commands like {{{fprint}}}, using Notepad in Windows, which is the default viewer for text documents, doesn't interpret the 'newline\return' characters correctly ({{{\n}}}, {{{\r}}}).  
*Problem is, "Notepad.exe" //is// in the Windows PATH, so it's really easy to call to to view text files.  ~WordPad, //isn't// in the windows path, but it //will// interpret the 'newline\return' character properly.  
*So what is the best way to call to it?  Below is a good example.  You'd want to do error checking to make sure your $file actually exists first:
{{{
string $file = "c:/temp/myFile.txt";
string $programFiles = `getenv "ProgramFiles"`;
string $wordPad = ($programFiles + "/Windows NT/Accessories/WORDPAD.EXE");
if(`filetest -f $wordPad`)
	system("start " + $wordPad + " " + $file);
}}}
Doing the same thing with Notepad is easier (since it's in the Windows PATH), but the formatting usually looks screwed up :-(
{{{
string $file = "c:/temp/myFile.txt";
system("start notepad " + $file);
}}}
This will open a Windows cmd shell from Maya, but not block Maya in the process:
{{{
import subprocess
subprocess.Popen("start cmd", shell=True)
}}}
The {{{launch}}} command.  Check it out.
----
Note, I haven't actually gotten this command to work for videos or web pages (haven't tried pdf's).
See [[How can I play a movie \ video?]]
----
For web pages, see: [[Launching web browsers]]

{{{
# Python code
import maya.cmds as mc

def getAllNamespaces(start=':'):
    namespaces = [start]
    mc.namespace(setNamespace=":")
    for n in namespaces:
        mc.namespace( setNamespace=n)
        subNs = mc.namespaceInfo(listOnlyNamespaces=True)
        if subNs is not None:
            for sn in subNs:
                if n is not ':':
                    namespaces.append(':%s'%(sn))
                else:
                    namespaces.append('%s%s'%(n,sn))
    mc.namespace(setNamespace=":")
    return namespaces
}}}
{{{
namespaces = getAllNamespaces()

for n in namespaces:
    print n
}}}
Prints (for example):
{{{
:
:UI
:myNamespace
:myNamespace:subNamespace
:etc...
}}}
Without the qualifying reference node, it'll list all edits in the scene.  Or you can pass in a reference node name, to limit it to just that reference
{{{
string $edits[] = `referenceQuery -editStrings refNamespaceRN`;
// Result: setAttr refNamespace:myNode.rotate -type "double3" -21.50129 -10.55454 -9.645982 //
}}}
The {{{dgmodified}}} command will.
----
Also see:
*[[How can I query if a scene has unsaved changes?]]
Notes:
* As of Maya 2022, the {{{loadPlugin}}} command only works on {{{.py}}} files:  {{{.pyc}}} files will fail.
* As of Python 3, Maya stores it's auto-generated {{{.pyc}}} files in a {{{/__pycache__}}} dir, which is nice to help keep them separated from the main code.
----
I've found that when trying to use mel to __load__ a Python scripted plugin, you have to provide the //full path// to the scripted plugin, even if it's already in you 'maya plugin path'.  However to __unload__ it, you only need the //name//:
{{{
# Python code
import os
import maya.cmds as mc

# Import our plugin, so we can extract the full path 
# from it.  Let's pretend it lives in a package under \mayaPython: 
# c:\mayaPython\spDev\myScriptedPlugin.py
import spDev.myScriptedPlugin

# Pull the full path from it:
fullPath = spDev.helloWorld2Cmd_sp.__file__
if fullPath.endswith('.pyc'):
    fullPath = fullPath [:-1]
dirPath, plugin = os.path.split(fullPath)
}}}
{{{
# Load:
mc.loadPlugin(fullPath, quiet=True)
mc.pluginInfo(plugin, edit=True, autoload=True)
}}}
{{{
# Unload:
mc.unloadPlugin(plugin, force=True)
mc.pluginInfo(plugin, edit=True, autoload=False)
}}}
If you have the Plug-in Manager open while doing this, you won't always see the checkboxes check on and off.  But if you close it and reopen it, they'll have updated correctly.
----
Based on the above code, it allows you to do something pretty nice: Author a function inside a scripted plugin module that will auto-load (or unload, to allow for iteration while working on the code) the scripted plugin.  I usually stick this below the {{{initializePlugin()}}} and {{{uninitializePlugin()}}} functions.
{{{
import os
import maya.cmds as mc

def pluginLoader(load):
    """
    Load or Unload the plugin defined in this file.
    
    Parameters:
    load : bool : True to load the plugin, False to unload it.
    """
    fullPath = __file__
    pluginPath = __file__
    # This check shouldn't be needed once using Python3, since it stores its .pyc
    # files in a /__pycache__ subdir.
    if fullPath.endswith('.pyc'):
        fullPath = fullPath [:-1]
    dirPath, plugin = os.path.split(fullPath)

    if load:
        if not mc.pluginInfo(plugin, query=True, loaded=True):
            mc.loadPlugin(fullPath, quiet=True)
            mc.pluginInfo(plugin, edit=True, autoload=True)
    else:
        if mc.pluginInfo(plugin, query=True, loaded=True):
            mc.unloadPlugin(plugin, force=True)
            mc.pluginInfo(plugin, edit=True, autoload=False)
}}}
You no longer have to worry about about managing your scripted plugin load states as long as that function is called to before you ever call to your scripted plugin command\node\etc.
Common operation:
{{{
# Python code
import maya.cmds as mc

plugin = "MyPlugin.mll"
if not mc.pluginInfo(plugin , query=True, loaded=True):
    mc.loadPlugin(plugin, quiet=True)
    mc.pluginInfo(plugin, edit=True, autoload=True)
}}}
If the plugin you're trying to load isn't part of your {{{MAYA_PLUG_IN_PATH}}} Python will raise a {{{RuntimeError}}} [[exception|What kind of exceptions does Python raise when a mel command fails?]]:
{{{
# RuntimeError: Plug-in, "MyPlugin.mll", was not found on MAYA_PLUG_IN_PATH.
}}}
and shoot this error out to Maya:
{{{
# Error: line 1: Plug-in, "MyPlugin.mll", was not found on MAYA_PLUG_IN_PATH.
}}}
----
Also see:
*[[How can I query\update Maya's script path?]] (also applies to plugins)
Presuming your company has this level of support:
* http://www.autodesk.com
* 'log in' via the menubar on the top with an icon of a person
* Via that same menu once logged in -> 'Manage Products & downloads'
* There should be a '?' icon in the top right of that page : 'View My Support Cases'
* Which should link you here:
** https://autodesk-communities.force.com/customer/apex/Case_CustomerPortalLandingPage
** Technically you should be able to go here directly, but if any problems happen, use the above route.
You can write a simple expression to do it.  The below code presumes a few things:
*You've created a NURBS curve, that is closed. (with a min u of 0, and a max of 1)
*You've attached something too it as a motion path.
*On the motionPath itself, you've deleted the default keyframes in the {{{.uValue}}} attr
{{{
// Maya Expression:
int $startFrame = 1;
float $speed = .01;
float $curveLenthU = 1.0;

if(`currentTime -q` <= $startFrame){
	motionPath1.uValue = 0;
}
else{
	motionPath1.uValue = motionPath1.uValue + $speed;
	if(motionPath1.uValue > $curveLenthU ){
		motionPath1.uValue =  motionPath1.uValue -1;
	}
}
}}}
You can change {{{$curveLenthU}}} to be anything you want (if the curve isn't normalize from 0-1), and change the speed to make it faster or slower.
This will generate a custom HUD that will update whenever a new scene is opened \ created.
{{{
# Python code
import os
import maya.cmds as mc

def getSceneName(*args):
    """
    Returns the scene name.  Used by the hud call in sceneNameHud()
    """
    sceneName = 'untitled scene'
    fullSceneName = mc.file(query=True, sceneName=True)
    if fullSceneName:
        sceneName = os.path.split(fullSceneName)[-1]
    return sceneName

def sceneNameHud():
    """
    Create a HUD on the top middle of the screen that displays the scene name.
    """
    if mc.headsUpDisplay('sceneNameHUD', query=True, exists=True):
        mc.headsUpDisplay('sceneNameHUD', remove=True)

    mc.headsUpDisplay('sceneNameHUD', section=2, block=0, event='NewSceneOpened',
                      command=getSceneName)
}}}
{{{rampColorPort}}}
{{{
import maya.cmds as mc

mc.colorEditor()
if mc.colorEditor(query=True, result=True):
    hsv = mc.colorEditor(query=True, hsvValue=True)
    rgb = mc.colorEditor(query=True, rgbValue=True)
    print "HSV", [round(val, 2) for val in hsv]
    print "RGB", [round(val, 2) for val in rgb]
}}}
{{{
HSV [235.95, 0.49, 0.6]
RGB [0.3, 0.32, 0.6]
}}}
An easy way to do it is with the {{{textToShelf}}} script that ships with Maya.  It lives here:
{{{.../MayaX.X/scripts/startup/textToShelf.mel}}}
Don't forget to save your shelves\prefs after this of course (usually don't when Maya exits, but not always).
Here's the guts of its code, for informative purposes:
{{{
global proc textToShelf (string $label, string $script) 
    {
    global string $gShelfTopLevel;
    if (`tabLayout -exists $gShelfTopLevel`) 
        {
        string $currentShelf = `tabLayout -query -selectTab $gShelfTopLevel`;
        setParent $currentShelf;

    //  Create the shelf button on the current shelf.
    //  Give it the default MEL script icon,
    //  and set the short label too.

        shelfButton 
            -command $script
            -label $label
            -annotation $script
            -imageOverlayLabel $label
            -image1 "commandButton.xpm"
            -style `shelfLayout -query -style $currentShelf`
            -width `shelfLayout -query -cellWidth $currentShelf`
            -height `shelfLayout -query -cellHeight $currentShelf`;
        }
    }
}}}
----
Also see:
*[[How can I add Python code as a new shelf button?]]
*The DOS attrib command. If there is a way to do it in mel then I'm missing it. Type 'help attrib' in DOS to get all the arugments
*Example: Make a file writable:
{{{
system ("attrib -r " + $nameOfMyFile);
}}}
! Via {{{playblast}}}
{{{
# Setup required variables:
fileType = 'png'
# This is a modifier for widthHeight:  
percent = 100.0 
widthHeight = [1024,768]
imagePath = "C:/path/to/my/image.%s"%fileType

# Change our image format:
imageFormat = mc.getAttr("defaultRenderGlobals.imageFormat")
mc.setAttr("defaultRenderGlobals.imageFormat", fileType)

# Save the screenshot:
mc.playblast(frame=mc.currentTime(query=True), format="image",
    completeFilename=imagePath, viewer=False, widthHeight=widthHeight, percent=percent)

# Set the image format back to original
mc.setAttr("defaultRenderGlobals.imageFormat", imageFormat)
}}}
Also, if you want to do stuff like hide parts of the UI, you can do something like this first:
{{{
modelEditor = mc.playblast(activeEditor=True)
}}}
Then use the [[modelEditor|https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/modelEditor.html]] docs to find the visibility states of different things, show/hide them, and then re-show them after the screenshot.
{{{
# Start changing settings:
mc.modelEditor(modelEditor, edit=True, displayTextures=True)
mc.modelEditor(modelEditor, edit=True, nurbsCurves=False)
# etc...
}}}
----
! Via [[thumbnailCaptureComponent|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/thumbnailCaptureComponent.html]]
{{{
# open a little ui to make the capture, showing you a preview:
mc.thumbnailCaptureComponent(capture=True, startFrame=1, endFrame=1)

# press the 'capture' button to save it to disk.

Where did it get saved?  Find out here:
print mc.thumbnailCaptureComponent(query=True, previewPath=True)
}}}
----
Also see:
* [[API: How can I get a screen-shot of the active view?]]
I usually like the main menu bar off, but occasionally I like it on, so I can tear off a menu.  Rather than having to go through the steps:
'Marking Menu -> Hotbox Controls -> Window Options -> Show Main Menubar'
I made this mel toggle:
{{{
// Toggle the main menu-bar:
global string $gMainWindow;
int $vis = `window -q -mbv $gMainWindow`;
int $newVis = abs($vis -1);
optionVar -iv mainWindowMenubarVis $newVis;
window -e -mbv $newVis $gMainWindow;
}}}
----
Also see:
*[[How to toggle menubars]]
Starting with the introduction of Qt in Maya 2012, you can make your own custom windows dockable in the Maya UI.
The below examples will take a {{{columnLayout}}} (but any layout type will do) and turn it into a floating dock, that can then be dragged via the mouse and inserted into various parts of the Maya UI.
Docs: [[dockControl|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/dockControl.html]]
{{{
# Python code
import maya.cmds as mc

mc.setParent('MayaWindow')
myLayout = mc.columnLayout()
# Add everything that should go in the dock here...

# Define the name for the docControl:
dcName = 'myDC'

# Just like you need to delete pre-existing windows before you make new ones,
# you need to delete pre-existing dockControls:
if mc.dockControl(dcName, query=True, exists=True):
    mc.deleteUI(dcName)

# Create the dockControl that will house the window:
mc.dockControl(dcName, allowedArea='right', area='right', floating=False, 
               content=myLayout, label='Awesome Dock', visible=True)
}}}
Other stuff I've learned (as of Maya 2015) :
*If the root most layout for a {{{dockControl}}}'s content //isn't// a {{{window}}} (like say, a {{{columnLayout}}}), you won't be able to query the undocked position via the {{{window}}} command.
*If the root most layout isn't a {{{window}}}, you need to set the parent to the {{{MayaWindow}}} before creation (per the above example).
*If the root most layout //is// a {{{window}}}, it mustn't be created with the Python {{{with}}} context manager, or it will complain when trying to be added to the {{{dockControl}}}.
*If a dock's content was created as a {{{window}}}, then docked, the window appears to be destroyed.  Undocking it seems to rebuild the window.
*Even if the {{{dockControl}}}'s content was created as a window, and if it's undocked and floating when Maya is quit, Maya //won't// remember the position/size of it like a normal window when its rebuild :(
----
Also see:
*[[Querying dockControl states]]
*[[Understanding window docking and 'workspaceControls']]
{{{allViewFit}}}
This is nice to use when you want to frame the camera on something, but don't know which panel the user has active, or which camera they're using.

{{{
//Fit the view to all objects:
allViewFit(1);
 
//Fit the view to just the selected objects:
allViewFit(0);
}}}
I've always wanted to add attrs to nodes that act like buttons:  A user can change a value, a script executes, and the value resets.
I've tried doing this via expressions, but have been unsuccessful getting them to execute correctly.  But with scriptJobs, they work just fine.

The idea is, a user changes an attr on a node:  That triggers a 'run once' {{{scriptJob}}}, that in turn executes some code.  When the scriptJob finishes, it kills itself, but before it sets the attr back to its default, and re-creates itself for another run.

But how does that {{{scriptJob}}} get automatically created when the scene is opened?  Via a {{{scriptNode}}}.  The below code shows and example of this.  It expects a few things:  
* There's already a node in the scene called {{{myNode}}} with a bool attr called {{{button}}}.
* The code is also setup to work in namespaces, in case the scene is referenced.  However, it only ever expects to find a single instance of the node in question:  Multiple references of the same file will confuse it, and the code would need to be made more robust.
{{{
import pymel.core as pm

# The code to be added\executed on the scriptNode:
scriptStr='''
import pymel.core as pm

myNodeName = "myNode"
myNodeAttr = "button"

def myNodeAttrChanged():
    """
    The function executed when the attr is changed.
    """
    node = pm.ls(myNodeName, recursive=True)[0]
    ###
    # Insert code that should be executed by the attr change here:
    print "Running Codez!"
    ###
    node.attr(myNodeAttr).set(0)
    makeSj()

def makeNodeSj():
    """
    Creates the scriptJob that triggers on attr change. Only make the scriptJob
    if the node in question could be found.
    """
    node = None
    nodes = pm.ls(myNodeName, recursive=True)
    if nodes:
        node = nodes[0]
    else:
        return
    pm.scriptJob(attributeChange=[node.attr(myNodeAttr), myNodeAttrChanged], runOnce=True, killWithScene=True)

makeNodeSj()
'''

# Create the scriptNode:
scriptNode = pm.createNode('script', name='myNodeAttrChangedScript')
# Set to Python source
scriptNode.sourceType.set(1)
# Set to execute when gui scene opens:
scriptNode.scriptType.set(2)
# Assign the above script to be executed
scriptNode.before.set(scriptStr)

# Connect from node to expression, just for safety, so you can always
# follow the dependency from the Attribute Editor.  Not doing this won't
# cause any problems, I just like having the nodes connected directly for reference.
pm.addAttr(node, longName='attrChangeScript', attributeType='message')
scriptNode.message >> node.attr('attrChangeScript')

# Finally execute the code in the current scene:
pm.scriptNode(scriptNode, executeBefore=True)
}}}
The code should immediately start working.  But to test, save the scene and reopen it:  If you change the {{{button}}} attr on the {{{myNode}}} node to {{{on}}}, you should see the Script Editor print the above text, and the attr get automatically set back to {{{off}}}.  Attr is button!
Around Maya 2012, you can use the [[pointOnPolyConstraint|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/pointOnPolyConstraint.html]] command\node to do this.  Note, it sticks it to a UV of the poly surface, and I've have questionable results using "maintain offset" with it.
----
Older methods:
*This is a general outline, mel to follow
*Particle way (credit Colin Penty):
**Selct the vert you want to constrain to
**"Particles -> Emit from Object [options] -> Emitter Type -> Surface -> Create
**Delete the "particle1" node. You now have "emitter1" you can parent objects to, glued to the object.
*"Hair Follicle" method:
**Found example of this method here: http://www.creativecrash.com/maya/downloads/scripts-plugins/polygon/c/djrivet-mel--3
The [[textureWindow|http://download.autodesk.com/us/maya/2010help/CommandsPython/textureWindow.html]] command.
However, the docs say you can only ever have one of these at a time, and the default one exists on Maya startup.  So given that, it's probably better to work with the default one than make your own.  Based on my tests, I've been unable to get a custom one to work.
The {{{condition}}} command.
----
Also see:
*[[How can I query if certain conditions exist in Maya?]]
Really simple example, running Python:
{{{
import maya.cmds as mc
def myScriptEditor():

    if mc.window('myPyScriptEditor', exists=True):
        mc.deleteUI('myPyScriptEditor')
    mc.window("myPyScriptEditor", title="My Python Script Editor")
    mc.columnLayout(adjustableColumn=True)
    mc.cmdScrollFieldReporter(height=200)
    mc.cmdScrollFieldExecuter(height=200, sourceType='python')
    mc.showWindow()

myScriptEditor()
}}}
*The {{{frameCache}}} node
*This is an alternative to the above "expression based" solution.
*Example:  Make "cube's" .ty lag behind "ball's" .ty by 3 frames:
{{{
// this creates the frameCache node:
createNode frameCache;
// frameCache needs to have time as an input:
connectAttr -f time1.outTime frameCache1.varyTime;
 // frameCache needs to have the animation as an imput:
connectAttr -f sphere.ty frameCache1.stream; 
// To determine the frame offset, you connect the array attr's .past attribute with the
// frame offsetvalue as its element (like .past[3]) into the input of your other node:
connectAttr -f frameCache1.past[3] cube.ty;
}}}
*Notes: It appears that after the nodes are connected, the "frameCache" node caches (duh) the animation data.  This means that changing the original animation has "strange" results on the frameCache node, with the newer animation not being applied properly to the "offset" node.  Don't know the workaround yet.I've spoken with AW about this, and they confirm it's a limitation... A bug report has been submitted.
{{{play -w;}}}
*Example: I have a script that requires the current frame range is played through to define a dynamic simulation. But when I execute my script, the script doesn't wait for the range to finish playing. What to do? Use the -w flag, which is "wait".
{{{
//script;
play -w;
// more script
}}}
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.
Languages like Processing have [[functions|http://www.processing.org/reference/map_.html]] that will take a given value from one range and map it into another.  I can't seem to find this in Python, but here's the code:
{{{
# Python code

def valueRangeMap(val, origMin, origMax, newMin, newMax):
    """
    Will remap val from the original range into the new range.
    
    val : float : The value you are mapping.
    origMin : float :  The original minimum value that val should be greater than.
    origMax : float :  The original maximum value that val should be less than.
    newMin : float :  The new minimum value that val will be mapped into.
    newMax : float :  the new maximum value that val will be mapped into.
    
    return : float : The newly mapped value.
    """
    # Find the percentage val is between origMin and origMax:
    percetVal = float(val - origMin) / float(origMax - origMin)
    # Map into the new range:
    mappedVal = (newMin+newMax)*percetVal
    
    return mappedVal

print valueRangeMap(5, 0, 10, 10, 20)
# 15.0
}}}

Mel examples first, Python (way at the bottom) second.
!Mel
Say you have a line with mixed-case characters in it, and you're trying to match a string in it.  However, the string you're trying to match could in no-way be in the same case as the original string.  In addition, you need to get the result back in the original case.
For example:
{{{
string $src = "My Mixed Case String";
string $matcher = "mixed case";
}}}
What you want to get back is a string like this:
{{{
"Mixed Case"
}}}
There is no "easy" way I've found to do this.  If you don't care about having the return value come back case sensitive, you can use the {{{match}}} command combined with the {{{tolower}}} (or {{{toupper}}}) command to format all the string ahead of time.

But if you want to maintain case sensivity as part of the return, you need to get into some regular expression voodoo.

In a nutshell, the below code will take the {{{$matcher}}} string, and for each alphabetic character, turn it into a matching pair of upper and lower-case characters.  In addition to handling characters that are "special" to {{{match}}}, and need to be escaped when searching.  An example of what this crazy search string looks like is given at the very bottom.
{{{
global proc string matchNonCase(string $matcher, string $line)
	{
// convert our $matcher string into a string array:
	string $array[];
	for($i=0;$i<size($matcher);$i++)
		$array[$i] = `substring $matcher ($i+1) ($i+1)`;

// define characters special to the match command that need to be escaped:
	string $spcl[] = {".", "*", "+", "^", "$", "\\", "[", "]", "(", ")"};
	
// generate our special match pattern string:
	string $matchPattern = "";
	for($i=0;$i<size($array);$i++)
		{
		string $char = `match "[a-z]*[A-Z]*" $array[$i]`;
		if(size($char))
			{
			string $lower = tolower($char);				
			string $upper = toupper($char);
			$matchPattern = ($matchPattern + "[" + $lower + $upper + "]");
			}
		else
			{
			int $special = 0;
			for($j=0;$j<size($spcl);$j++)
				{
				if($spcl[$j]	== $array[$i])
					{
					$matchPattern = ($matchPattern + "\\"+$array[$i]);
					$special = 1;
					break;
					}
				}
			if($special == 0)	
				$matchPattern = ($matchPattern + $array[$i]);	
			}
		
		}		
	// print $matchPattern;
		
// and finally, see if we have a match:
	string $result = `match $matchPattern $line`;
	return $result;	
	}
}}}
{{{
string $line = "lineData -- \"c:/my/\\Dir$/is/MIXED/case\"  -- more Data";
string $matcher = "/my/\\diR$/is/mIxed/";
string $result = matchNonCase($matcher, $line);

// Result: /my/\Dir$/is/MIXED/ // 
}}}
If you're wondering, {{{$matchPattern}}} equals this given the above example:
{{{
/[mM][yY]/\\[dD][iI][rR]\$/[iI][sS]/[mM][iI][xX][eE][dD]/
}}}
!Python
As ever, doing this in Python is way easier:
{{{
import re

search = "/my/dir/is/mixed"
line = "lineData -- C:/my/Dir/is/MIXED/case --moreData"
print re.findall(search, line, re.IGNORECASE)

# ['/my/Dir/is/MIXED']
}}}
However, embedding special chars will confuse it, so the mel is a bit more robust.  I'm currently checking to see if the special chars in Python can b handled as well.  I presume they can, just havn't found it yet...
As of Maya 2016, there is the {{{matchTransform}}} command.  But other solutions below:
----
We query the {{{.worldMatrix}}} of our source node, then via {{{xform}}} set our destination node to that worldspace transformation:
{{{
// mel
string $target= "pCube1";
string $node= "pCube2";

float $m[16] = `getAttr ($target+".worldMatrix")`;
xform -worldSpace -matrix $m[0] $m[1] $m[2] $m[3] 
		$m[4] $m[5] $m[6] $m[7] 
		$m[8] $m[9] $m[10] $m[11] 
		$m[12]$m[13] $m[14] $m[15] $node;
}}}
{{{
# Python
import maya.cmds as mc
target = "pCube1"
node = "pCube2"
mc.xform(node, worldSpace=True, matrix=mc.getAttr("%s.worldMatrix"%target) )
}}}
{{{
#PyMel
import pymel.core as pm
target = pm.PyNode("pCube1")
node = pm.PyNode("pCube2")
pm.xform(node, worldSpace=True, matrix=target.worldMatrix.get() )
}}}
Why is this cool?  Because no matter what you have done to the parental hierarchy of either node, and despite the rotation orders of either node, the destination node will perfectly match the worldspace matrix of the source.  

This also solves problems that other code (like the below example illustrates) will get tripped up on (which include the links at bottom) when dealing with matching orientations:
If the __rotate order__ of the destination //differs// from the source, the below code will __fail__.
{{{
# Python:
import maya.cmds as mc
worldRot = mc.xform(source, rotation=True, query=True,  worldSpace=True)

# two different methods, will fail if differing rotate orders:
mc.xform(destination, rotation=worldRot, worldSpace=True)
mc.rotate(worldRot[0], worldRot[1], worldRot[2], destination, worldSpace=True)
}}}
----
Also see:
*[[How can I rotate an object that has one rotation order to the world space orientation of another object with a different rotation order?]]
*[[How can I move an object to an exact worldspace position? How can I find an exact worldspace position?]]
*[[How can I find the world\local space position of a point?]]
*[[Moving nodes in worldspace]]
This code will detect all the uv sets on a given mesh, and if it has more than one, merge all other uv sets with the base set, and delete the remainder.
{{{
# Python code
import maya.cmds as mc

def combineUvs(mesh):
    uvSets = mc.polyUVSet(mesh, query=True, allUVSets=True)
    if len(uvSets) > 1:
        baseSet =  uvSets.pop(0)
        for uvSet in uvSets:
            mc.polyCopyUV(mesh, uvSetNameInput=uvSet, uvSetName=baseSet)
            mc.polyUVSet(mesh, delete=True, uvSet=uvSet)
}}}
I often find myself needing to mirror nodes from one half of the global YZ plane to the other.  This simple Python script will mirror the positions of nodes on the -x side of the YZ plane based on positions on the +x side.  All the nodes are selected at once:  All the +x nodes first, and the -x nodes second.  And they should be selected parent first, child last.
{{{
import maya.cmds as mc
nodeList = mc.ls(selection=True)
if len(nodeList) % 2 == 1:
    raise(Exception("Please pick an even number of nodes."))

half = len(nodeList)/2
counter=0
while counter < half:
    sourceNode = nodeList[counter]
    destNode = nodeList[half + counter]
    counter = counter+1
    sourcePos = mc.pointPosition((sourceNode+".rotatePivot"), world=True)
    mc.move((sourcePos[0]*-1), sourcePos[1], sourcePos[2], destNode, 
            absolute=True, worldSpace=True, rotatePivotRelative=True)
print "Finished World XY Mirror",
}}}
Some examples:
{{{
// Change the value:
artAttrSkinPaintCtx -edit -value .1 artAttrSkinContext;
// Interactively pick a new value:
artAttrSkinPaintCtx -edit -pickValue artAttrSkinContext;
// Change the opacity:
artAttrSkinPaintCtx -edit -opacity .5 artAttrSkinContext;
// Change the 'Paint operation' to 'Replace':
artAttrSkinPaintCtx -edit -selectedattroper absolute artAttrSkinContext;
// Flood the current value/operation
artAttrSkinPaintCtx -edit -clear artAttrSkinContext;
}}}
The script located here:
{{{../MayaX.X/scripts/startup/generateChannelMenu.mel}}}
This script has all the UI code which builds the marking menu, so edit for your needs.
Of course, what would be safer would be to duplicate this script, rename it to something else (like say, {{{myCustomGenerateChannelMenu.mel}}}), and when Maya starts up, add this line to your {{{userSetup.mel}}} script:
{{{
source myCustomGenerateChannelMenu;
print "Custom generateChannelMenu.mel script sourced -- userSetup.mel\n";
}}}
In the UI, Maya calls the term 'project' (as in File -> Project -> Set...).  But the mel behind the scenes is {{{workspace}}}.
Query the current workspace:
{{{
string $proj = `workspace -query -active`;
}}}
Set the current workspace:
{{{
string $path = "c:/myProject";
if(`filetest -d $path`){
	// This upates the optionVar used by the Preferences UI:
	optionVar -sv ProjectsDir $path;
	// This (procedure, not command) sets the current path as the *default project*.
        // It also makes directories on disk in the process, so it's not always wanted.
	// setProject $path;

	// This physically sets the project directory, which is also used by fileBrowserDialogs:
	workspace -dir $path;
        // this is also needed to cement things for Maya's project settings, but isn't
        // needed if you're just trying to update fileBrowserDialogs:
        workspace -openWorkspace $path;
}
else
	warning("Can't set project:  Directory '" + $path + "' doesn't exist.");
}}}
This sets the '@@current //workspace// directory@@', which also happens to be the path that opens by default when launching a {{{fileBrowser}}} (or a {{{fileBrowserDialog}}} when in //directory// mode), and is also used when searching for files.  This is different from the [[current working directory]] (see those notes), that is used by the {{{pwd}}} and {{{chdir}}} commands.

If you //don't// set the {{{optionVar}}}, things will still work, but if you look at the Maya Preferences UI, you could see a conflicting path.  {{{setProject}}} does the real magic, and makes it persist between Maya sessions.  {{{workspace -dir}}} also works, but doesn't seem to persist between Maya sessions.

It should be noted that using this technique with a {{{fileBrowserDialog}}} doesn't seem to work, unless it is in 'directory mode', which is mode '4'.  For other {{{fileBrowserDialog}}} techniques, see this subject: [[How can I define a start location for my fileBrowserDialog?]]
{{{move}}} \ {{{xform}}}
*Example A: Given a transform:
{{{
float $position[] = `xform -q -ws -rp transformA`;
move -a -ws -rpr $position[0] $position[1] $position[2] transformB;
}}}
*Example B: Given a joint:
{{{
float $position[] = `xform -q -ws -rp transformA`;  // (or jointA, or whatever)
move -a -ws $position[0] $position[1] $position[2] jointB.scalePivot;
move -a -ws $position[0] $position[1] $position[2] jointB.rotatePivot;
}}}
----
Also see:
*[[How can I match the worldspace transformations of two objects?]]
* [[Moving nodes in worldspace]]
{{{autoPlace}}}
*Note: It appears the return values are always in centimeter, so depending on your working units, you may have to use some math to get the correct values.
*Note: It's supposed to work with the mouse position too, but seems very inaccurate
Thanks to 'Campbell' over on highend3d.com for this info
*Do it "C++" style (or so they say...). Here's some examples:
{{{
      string $jnts[] = ls ("-type", "joint", listHistory ("-f", 1, listRelatives ("-p", "-ad", sets("-q", "some_quick_select_set"))));
}}}
{{{
      string $jnts[] = ls ("-type", "joint", listHistory ("-f", 1, 'eval("listRelatives -p -ad 'sets -q \"some_quick_select_set\"'")'));
}}}
This is also called 'function syntax', see notes on [[Commands]].
The function lets you filter out or include referenced animation data as well:
{{{
import pymel.core as pm

def offsetAllKeys(startFrame, offset, includeReferenced=False):
    """
    startFrame : int : Where should the offset begin?
    offset : int : How large should the offset be?
    includeReferenced : bool : Default False : Should referenced animCurves be
        included?
    """
    animCurves = pm.ls(type='animCurve')
    if not includeReferenced:
        animCurves = [ac for ac in animCurves if not ac.isReferenced()]
    pm.keyframe(animCurves, edit=True, includeUpperBound=False, animation="objects",
                time=("%s:"%startFrame,), relative=True, option='over', timeChange=offset)
}}}
{{{
startFrame = 100
offset = 50
offsetAllKeys(startFrame, offset)
}}}
----
Also see:
*[[Offset animCurve data]]
{{{
file -prompt 0;
}}}
Also, at some point Maya started spamming you with a mental-ray nodes dialog.  If you want to turn that off via code:
{{{
optionVar -intValue "showMentalRayNotAvailableDialog" 0 
}}}
BTW, that mental-ray code lives here:
{{{
c:\program files\Autodesk\Maya20XX\Python\Lib\site-packages\maya\app\general\mentalRayNotAvailableDialog.py
}}}
Mel solution, for Windows:
{{{
system("start explorer " + toNativePath(dirname(`file -q -sn`)));
}}}
Python, windows:
{{{
import os
import subprocess
import maya.cmds as mc
fil = mc.file(query=True, sceneName=True)
if fil:
    dir = os.path.dirname(fil).replace('/', '\\')
    subprocess.Popen(['explorer', dir])
    # Or this, which will open to that folder, and select the file:
    os.system('explorer /select, %s'%fil)
}}}
----
Python solution, for Mac:
{{{
import os
import subprocess
import maya.cmds as mc

def main():
    fn = mc.file(query=True, sceneName=True)
    if fn:
        pth, fil = os.path.split(fn)
        # Open a shell:
        #subprocess.Popen(['open', '-a', 'Terminal', pth])
        # Open Finder:
        subprocess.Popen(['open', '-a', 'Finder', pth])
main()
}}}
Note it provides a solution for opening a Terminal shell to the dir as well.
{{{file}}} \ {{{fileDialog}}}
*Example: I want to import a specific kind of file from a specific directory:
{{{
file -i `fileDialog -dm \"Z:/rotk/objects/char/rig/scenes/myFile.*\"`;
}}}
Starting in Maya 2014, they added a {{{-directory}}} flag to the {{{launch}}} command:
{{{
// mel
launch -directory "c:/temp"
}}}
{{{
# Python
import maya.cmds as mc
mc.launch(directory="c:/temp")
}}}
Optionally you can do this in pure Python.  Note on Windows the paths need to be backslashed:
{{{
import os
import subprocess

os.startfile("c:/temp") # Windows only, supports forwardslash.
# or:
os.system('explorer c:\\temp') # must be backslash on windows
# or:
subprocess.Popen(['explorer', 'c:\\temp']) # must be backslash on windows
}}}
On Windows this should open Explorer, on Mac, Finder.
----
Very easily, you can write code that will open a window for the currently (saved) scene:
{{{
import os
import maya.cmds as mc

sceneName = mc.file(query=True, sceneName=True)
sceneDir = os.path.dirname(sceneName)
mc.launch(directory=sceneDir)
}}}
{{{
import maya.cmds as mc

def getSavedPath(ovName):
    """
    Get an optionVar by the given name.  Return an empty string if either no
    optionVar exists, or the path it defines doesn't exist.
    """
    savedPath = mc.optionVar(query=ovName)
    if savedPath == 0:
        savedPath = ""
    elif not os.path.isdir(savedPath):
        savedPath = ""
    return savedPath

def getSavePath(fileType):
    """
    Create a fileDialog the user can use to specify a file to save, either new,
    or pre-existing.  Will store the path to the file for future access.

    returns the name of the file to save.
    """
    try:
        ovName = '%sSaveDir'%fileType
        savedPath = getSavedPath(ovName)
        # Open our file browser:
        f = mc.fileDialog(mode=1, directoryMask="%s*.%s"%(savedPath,fileType),
                          title="Save .%s data"%fileType)
        # Append the extension to the file if the user didn't:
        if f != "":
            ext = os.path.splitext(f)
            f = "%s.%s"%(ext[0], fileType)
        else:
            raise Exception

        # Save our path for later browsing
        browsedPath = os.path.split(f)
        mc.optionVar(stringValue=[ovName, browsedPath[0]+"/"])

        if os.access(f, os.F_OK) and not os.access(f, os.W_OK):
            raise Exception("'%s' is read-only, unable to save."%f)
        return f

    except Exception:
        print "Save canceled by user:",

def getLoadPath(fileType):
    """
    Create a fileDialog the user can use to specify a file to load.  Will store
    the path to the file for future access.

    returns the name of the file to load.
    """
    try:
        # Define the name of our optionVar
        ovName = '%sLoadDir'%fileType
        # Query for saved path data:
        savedPath = getSavedPath(ovName)
        # Open our file browser:
        f = mc.fileDialog(mode=0, directoryMask="%s*.%s"%(savedPath,fileType),
                          title="Load .%s data"%fileType)
        # Save our path for later browsing
        if f != "":
            browsedPath = os.path.split(f)
            mc.optionVar(stringValue=[ovName, browsedPath[0]+"/"])
        else:
            raise Exception
        return f

    except Exception:
        print "Load canceled by user",
}}}
{{{
print getLoadPath("ma")
# C:/temp/file.ma
}}}
{{{
print getSavePath("ma")
# C:/temp/file.ma
}}}
There is a runTimeCommand called:
{{{
HardwareRenderBuffer
}}}
that will launch it, but it's just a wrapper for the procedure:
{{{
glRenderWin()
}}}
which lives in the script:
{{{
C:/Program Files/Autodesk/Maya<VER>/scripts/others/glRenderWin.mel
}}}
----
Also see:
*[[How can I render from the Hardware Render Buffer?]]
In this folder:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others
}}}
Live many "marking menu" scripts, named "{{{context*ToolsMM.mel}}}".  For example:
{{{
contextNurbsCurveToolsMM.mel
contextPolyToolsDefaultMM.mel
contextPolyToolsEdgeMM.mel
contextPolyToolsFaceMM.mel
contextPolyToolsMM.mel
contextPolyToolsObjectMM.mel
contextPolyToolsVertexMM.mel
contextToolsMM.mel
etc...
}}}
The easy way to override them is copy them into a version-specific script dir, like
{{{
C:\Users\<userName>\Documents\maya\2014-x64\scripts
}}}
//Not// the version shared scripts dir here:
{{{
C:\Users\<userName>\Documents\maya\scripts
}}}
And then text-edit them to your hearts content ;)
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
Seems like this should be in a menu somewhere, but I can't find it :S
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";
}}}
New way my buddy Te pointed out to me:
{{{
# Python code:
import maya.cmds as mc
mc.parent('someShapeNode', 'someTransform', shape=True, add=True)
}}}
I thought the code would //re-parent// the shape node.  But it doesn't, it makes it an instance.
----
Older method:
*First, you "instance" your shape node, which will give you a new transform as well.
*Second, you parent your new instanced shape node to it's new parental transform.
*Third, remove the 'no-longer-needed' transform
{{{
string $shapeNode = "myShape";
string $newParent = "someTransform";
string $instancedShape[] = `instance $shapeNode`;
parent -s -r ($instancedShape[0] + "|" + $shapeNode) $newParent;
delete $instancedShape[0];
}}}
Also see:
[[How can I reparent the shape node of one object to another.  OR, how can I have a single transform with multiple shapes?]]
mayaAscii files are easy to parse:  Just open them in a text editor.  But how about a mayaBinary?
----
New version (from Autodesk) that works in 2020.2, and should work going forward:
{{{
import math
import struct

def processFileInfo(file):
    file.seek(8,1)
    data = struct.unpack('>i', file.read(4))
    size = int(math.ceil(data[0] / 8 + 1) * 8)
    fileInfo = ['', '']
    idx = 0
    for i in range(size):
        pc = file.read(1)
        if idx < 2:
            c = struct.unpack('c', pc)
            if c[0] == '\x00':
                idx += 1
            else:
                fileInfo[idx] += c[0]
    return fileInfo

def parseMayaBinary(fileName):
    try:
        nthFINF = 0
        file = open(fileName, mode='rb')
        while True:
            data = file.read(4)
            if data == 'FINF':            
                fileInfo = processFileInfo(file)
                print fileInfo
                nthFINF += 1
            elif nthFINF > 0:
                break
    except:
        print(fileName, 'is not a valid binary file') 
}}}
----
This solution works great up until around Maya 2019.1:
https://github.com/mottosso/maya-scenefile-parser

----
https://sourceforge.net/p/cgkit/code/ci/master/tree/cgkit/mayabinary.py
- I have been unable to get this to work:  I can get it to read Maya iff (image) files, but not mayaBinary files.

----
https://github.com/westernx/mayatools

Example by Michał Frątczak from [[this thread|https://groups.google.com/forum/#!topic/python_inside_maya/kl7t-zdImFE]]
{{{
from mayatools.binary import Parser

def ExtractFileInfo(fPath): 
     RES = {} 
     f = open(fPath) 
     p = Parser(f) 
     Chunk = p.parse_next() 
     max_cnt = 100 
     fileInfoBlockFound = 0 
     while( Chunk and max_cnt ): 
         max_cnt -= 1 
         if(fileInfoBlockFound == 1 and Chunk.tag != "FINF"): 
             break 
         if(Chunk.tag == "FINF"): 
             fileInfoBlockFound = 1 
             bits = string.split( str(Chunk.string), '\x00') 
             RES[bits[0]] = string.join(bits[1:], ' ') 
         Chunk = p.parse_next() 
     return RES 
}}}
----
Also see:
* [[How can I store and read metadata in Maya files, without having to open them?]]
*[[Info on the Maya Binary file format]]
If you have a polygonal asset with multiple materials assigned to multiple faces, and you pick a single face and try to find what material is assigned to it in the Hypershade, the Hypershade will list ALL the materials assigned to that mesh.  

The below code will select the given material assigned to any single face:
{{{
string $sel[] = `ls -fl -sl`;
if(size($sel)!= 1)
	error "Please select a single face";
string $shaders[] = `listSets -type 1 -object $sel[0]`;
string $mat[] = `listConnections -s 1 -d 0 ($shaders[0] + ".surfaceShader")`;
select -r $mat[0];
}}}
Presuming your selection is a //single face//, this code will find the associated {{{shadingEngine}}}, all items in that engine, and select them.
{{{
string $sel[] = `ls -fl -sl`;
if(size($sel)!= 1)
	error "Please select a single face";
string $shaders[] = `listSets -type 1 -object $sel[0]`;
string $items[] = `sets -q $shaders[0]`;
select -r $items;
}}}
The above example will pick all faces assigned to that shader in the scene, spanning all meshes.  The code can be changed slightly:  The //below// example will do the same as the above, but it will only pick faces on the //same mesh// as the original selected face.
{{{
string $sel[] = `ls -fl -sl`;
if(size($sel)!= 1)
	error "Please select a single face";
string $shaders[] = `listSets -type 1 -object $sel[0]`;
string $items[] = `sets -q $shaders[0]`;
string $locMesh[];
tokenize $sel[0] "." $locMesh;
select -cl;
for($i=0;$i<size($items);$i++){
	if(`match $locMesh[0] $items[$i]` == $locMesh[0])
		select -add $items[$i];
}
}}}
for something similar in Python 3, see [[How can I pickle Python data to fileInfo?]]

For Python 2, I blogged about it here:
http://www.akeric.com/blog/?p=1049
----
And notes on the Python functions:
http://docs.python.org/library/pickle.html
http://docs.python.org/library/pickle.html#module-cPickle
http://docs.python.org/library/stringio.html#module-StringIO
Python 3 code:
{{{
import os
import pickle
import datetime

import maya.cmds as mc

#---------------
# Store some complex data as Maya fileInfo:
MY_INFO_KEY = "myInfo"
# What we’ll be pickling to plug into the fileInfo
data = {} 
user = os.getenv("USERNAME", "Unknown.User")
timeNow = datetime.datetime.now()
lastUserSave = (user, timeNow)
data["lastUserSave"] = [lastUserSave]
# bytes! Was Unicode string in Python2
pDataBytes = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
# Need to turn the bytes into string, to store with fileInfo:
# which looks like this: "b'\x80\x04\x95N\x00\x00\x0..."
mc.fileInfo(MY_INFO_KEY, str(pDataBytes))

#----------------------

# Now, let’s try and load the data we just saved, from the fileInfo string:
allInfo = mc.fileInfo(query=True)
info = None
for i in range(0, len(allInfo)-1, 2):
    k = allInfo[i]
    if k == MY_INFO_KEY:
        fileInfoString = allInfo[i+1]
        # Get rid of all the extra backslashes that fileInfo adds:
        # "b'\\x80\\x04\\x95N\\x00\\x00\\x0..."
        # so it's this:
        # "b'\x80\x04\x95N\x00\x00\x0..."
        fileInfoString = fileInfoString.replace("\\\\", "\\")
        # We now have a *literal* string pretending to be bytes. So eval it
        # so it turns back into actual bytes type:
        fileInfoBytes = eval(fileInfoString)
        info = pickle.loads(fileInfoBytes)

print(info)
# {'lastUserSave': [('user.name', datetime.datetime(2021, 10, 13, 17, 29, 55, 197504))]}
}}}
If you're on Windows, the easiest way is to call out to {{{Windows Media Player}}} via the command line:
{{{
string $app = "C:/Program Files/Windows Media Player/wmplayer.exe";
string $vid = "C:/temp/myMovie.avi";
system("start " + $app + " \"" + $vid + "\"");
}}}
This will of course load it in {{{Windows Media Player}}}.  If you wanted to play a movie //in// a Maya UI, you'd need to know HTML programming, and tie that in with the {{{webBrowser}}} mel command (which however, will be removed sometime after Maya 2012).

Note:  You can find a list of the {{{Windows Media Player}}} command line options here:
http://support.microsoft.com/KB/241422
----
Python code:
Here's a Python example using 'Windows Media Player' on my Win7 box:
{{{
# Python code
import os
import subprocess

player = os.path.join(os.getenv("ProgramFiles(x86)"), 
                      "Windows Media Player\wmplayer.exe")
vid = "C:/videos/myVideo.avi"
subprocess.Popen([player, vid])
}}}
Simple overview:
*Make sure your image sequence follows a naming convention listed below.
** I've heard that iff works best, when other image formats 'just won't work'.  I've had success with jpg and png too though.
*Create a poly plane.
*Create a {{{surfaceShader}}}, assign it to the plane (poly, NURBS, your call).  These ignore the scene lighting.
*Create a {{{file}}} (//not// {{{movie}}}) node connected to the {{{.outColor}}} attr of the {{{surfaceShader}}}.
*Point the 'Image Name' to any image in your sequence.
*Check on "Use Image Sequence".  : If you don't see it, make sure you're doing this in  the Attribute Editor, and not the Hypershade:  The Hypershade actually hides these attrs :-O 
*If you want to offset the image sequence, you can enter a value in the 'Frame Offset' field. Note, this is opposite to the expected behavior:  If you want to offset the image sequence by 30 frames into the future (to the right), you would enter -30.  A value of positive 30 would offset it back in time by 30 frames (to the left).
* Make sure the viewport is in texture mode: The image sequence should scrub along with the time slider.
----
Contents of : {{{C:\Program Files\Autodesk\Maya2022\scripts\others\getFileSequenceFormats.mel}}}
{{{
// frame numbers without padding
			"name#", 
			"name.#.ext", 
			"name.ext.#", 
			"name#.ext", 
			"name.#",
			
// frame numbers with padding 
			"name###",
			"name###.ext",
			"name##.ext",
			"name.###.ext",
			"name.ext.####",
			"name.####.ext",
			"name####.ext",
			"name.####"
}}}
Maya has no built-in sound playing to my knowledge.
With Python (on windows), it's pretty easy actually:
http://docs.python.org/library/winsound.html
{{{
# Python code
import winsound
winsound.PlaySound( 'c:/temp/myWav.wav', winsound.SND_ALIAS)
}}}
----
~Pre-Python info:
The Windows {{{sndrec32}}} executable can be ran in command line mode.  In UI mode, it's the little window that ships with Windows and lets you record and playback .wav files.  Here are a list of it's arguments that I've found from online searching: (I haven't verified them all yet)
{{{
sndrec32 arguments:
/embedding - ???
/play - play file
/open - open file but don't play it
/new - open new file ready for recording
/close - close file
}}}
Based on that, and given a {{{.wav}}} file you'd like to play, you could script something like this (info modified from 'Joojaa' on Highend3d.com forums):
{{{
string $wav = "C:/WINDOWS/Media/Notify.wav";
$wav = toNativePath($wav);
system ("shell start /min sndrec32 /play /close " + $wav );
}}}
It //looks// like the {{{system}}} command uses both {{{shell}}} and {{{start}}} (since both shell and start are 'arguments' it can call to when calling to a cmd shell).  In fact, the {{{system}}} command is //only// using '{{{shell}}}'.  '{{{start}}}' is actually being called //by// the command shell once opened, and it has an argument which is {{{/min}}}, which causes the new cmd shell, and thus the sound recorder, to start in minimized mode.  Check out the docs on the {{{system}}} command to learn about what {{{start}}}, {{{shell}}}, and {{{load}}} do in its context.

So here is another way to visualize how the data is being wrappered, with 'arguments' in quotes ('shell' isn't an actual argument of the {{{system}}} command, but can be considered one in this sense):
{{{system "shell" [ start "/min" [ sndrec32 "/play" "/close" [ $wav ] ] ]}}}
https://knowledge.autodesk.com/support/maya/troubleshooting/caas/sfdcarticles/sfdcarticles/Restore-H-264-Codec-in-Maya.html
From that doc:

Download Quicktime Player into your Applications folder.  
https://support.apple.com/downloads/quicktime

Start Maya, then go to: Windows > Setting and Preferences > Preferences  
 
1. At the very bottom of the list on the left side of the Preferences window, click on Applications.  
2. Under the Sequence Viewing Applications, click on the folder in the Image Sequence line and navigate to the quicktime player 
{{{
C:\Program Files (x86)\QuickTime\QuickTimePlayer.exe
}}}
3. Press open. The path should populate in the Image Sequence line. 
4. Press save

{{{H.264}}} should now be an option in your Playblast Options.  But the quality can still be bad.  To fix that:
In your render globals -> Render Settings:
* Set the width/height you want.
* Set the Resolution to 300.
Then, in the playblast options:
* Set 'Format' to 'qt'.
* Set the 'Encoding' to H.264
* Set 'display size' to be 'From Render Settings'
* Set 'Quality' to 100
----
Also see:
* [[How can I Playblast using Quicktime?]]
Check out 'Autodesk FBX Review':
* [[Main Page|https://www.autodesk.com/products/fbx/fbx-review]]
* [[Download|https://apps.autodesk.com/MAYA/en/Detail/Index?id=6979630331069053932&appLang=en&os=Win64]]
It installs here:
{{{
C:\Program Files\Autodesk\FBX Review\fbxreview.exe
}}}
FYI, you can open files directly via the commandprompt:
{{{
> fbxreview.exe "path/to/my/file.fbx"
}}}
----
FYI, I'm told this is end of product now, and no longer maintained.  They have a newer tool you can find here:
[[Online File Viewer : Autodesk Viewer]]
However, it's cloud-based, so there could be security concerns for internal-studio IP.
The Output Window collects {{{stdout}}} and {{{stderr}}} streams from within Maya.  Below are vaious ways to interact with them
!!!Maya Python API:
{{{
import maya.OpenMaya as om
util = om.MStreamUtils()
stdout = util.stdOutStream()
stderr = util.stdErrorStream()
util.writeCharBuffer(stdout, "api stdout to the Output Window\r\n")
util.writeCharBuffer(stderr, "api stderr to the Output Window\r\n")
}}}
!!!Python:
{{{
import sys
sys.__stdout__.write("Python stdout to the Output Window\r\n")
sys.__stderr__.write("Python stderr to the Output Window\r\n")
}}}
!!!Mel:
The {{{trace}}} command (which prints to {{{stdout}}}).  For example:
{{{
trace "Mel stdout to the Output Window";
}}}
----
If you wanted to go the other way, and capture something from {{{stdin}}}, you have two options via Python:
A:  Via Maya's own {{{maya.app}}} package:
{{{
stream =  maya.app.baseUI.StandardInput()
stdin = stream.read()
print stdin
}}}
B:  Via python's own built-in {{{raw_input}}}:
{{{
stdin = raw_input()
}}}
If you were using Python external to Maya you could pass a string to {{{raw_input}}} that it would prompt the user with.  But Maya appears to hijack this functionality.
Found this awesome reference to {{{itertools}}} via the comments in this blog post:
http://tartley.com/?p=1081

http://docs.python.org/library/itertools.html#itertools.product

You need to be running Maya 2010 for {{{itertools.product}}} to work, since {{{product}}} was introduced in Python 2.6
{{{
# Python code
import maya.cmds as mc

import itertools
edge=1
verts=list(itertools.product(*([-edge/2,edge/2],)*3))
print verts
for v in verts:
    mc.spaceLocator(position=v)
}}}
prints:
{{{
[(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (-1, 0, 0), (0, -1, -1), (0, -1, 0), (0, 0, -1), (0, 0, 0)]
}}}
I //always// forget how to do this, and I feel like Maya keeps moving this option around.
In Maya 2016+ at least, in the Hypershade -> Create -> 2D Textures -> Enable '2D Projection'.
It has three options:
# normal 
# projection 
# stencil
You can also change it by modifying the optionVar Maya uses to store it.  Valid string values are:
For example:
{{{
import pymel.core as pm
pm.optionVar['create2dTextureType'] = 'projection'
}}}
----
Also, Maya's node editor gives you the option to make all three, when you press 'tab', and enter in 'file'.


The ~OpenMayaUI API's [[M3dView class|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m3d_view.html]] has several methods for doing this.  An example of projecting a 3d worldspace point to 2d screen-space can be found here:
*[[How can I convert a 3d point to 2d screen space?]]
Here's an overview of the pertinent methods:
*{{{viewToWorld}}} : Takes a point in port coordinates and returns a corresponding ray in world coordinates.  Takes a point in port coordinates and returns a point on the near and far clipping planes.
*{{{viewToObjectSpace}}} : Takes a point in port coordinates and returns a corresponding ray in object coordinates.
*{{{worldToView}}} : Converts a point in world space to port space.
*{{{projectionMatrix}}} : Returns the projection matrix currently being used by ~OpenGL in the current view.
*{{{modelViewMatrix}}} : Returns the modelview matrix currently being used by ~OpenGL in the current view.
It's often handy to have a UI that has a viewport with a custom perspective camera in it.  The below example shows a really easy way of doing it.  By default, it will create a window with the standard {{{persp}}} camera in it.  But it allows the user to pass in their own custom camera name as well.
{{{
# Python code
import maya.cmds as mc

class App(object):
    def __init__(self, camera="persp"):
        self.name = "customModelPanel"
        if mc.window(self.name, exists=True):
            mc.deleteUI(self.name)
        mc.window(self.name, resizeToFitChildren=True)
        mc.paneLayout(configuration='single')
        mc.modelPanel(camera=camera, menuBarVisible=True)
        mc.showWindow()
}}}
To execute:
{{{
App()
}}}
I often have geometry provided to me from the animation staff placed some distance away from the origin.  For rigging purposes, I need them centered about the origin, with their pivots at the origin.  How to get it moved back there quickly?
{{{
string $sel[] = `ls -sl`;
for($i=0;$i<size($sel);$i++)
    {
    xform -cp $sel[$i];
    vector $pos = `xform -q -ws -rp $sel[$i]`;
    move -a -ws (($pos.x)*-1) (($pos.y)*-1) (($pos.z)*-1);
    }

//  After this is done, you probably want to 
//  freeze their transforms too:
makeIdentity -a 1 $sel;
}}}
Sometimes when working with a UI, I want to be able to query the command it executes.  Sometimes you can just turn on 'echo all commands' in the script editor, but not always.  

If the button has a label on it, here is one method to extract the command information:

In this example, I track down the command executed from the "Flood" button in the Pain Smooth Skin Weights Tool.  (irony is you //can// find this out via 'echo all commands')
{{{
# Python code
import maya.cmds as mc

# Get every ui control in the scene:
allCtrls = mc.lsUI(controls=True)
# Filter for just buttons:
allBut = [item for item in allCtrls if mc.button(item, exists=True)]
# Filter buttons by label:
myBut = [b for b in allBut if mc.button(b, query=True, label=True) == 'Flood']
# Extract command:
command = mc.button(myBut[0], query=True, command=True)

print command
# artAttrSkinPaintCtx -e -clear `currentCtx`
}}}
You can use the {{{getAttr -time frameNum}}} to query an attribute (like {{{translateX}}}) at a given frame. And even though you can query matrix attrs (like {{{worldMatrix}}}) on a node via {{{getAttr}}}, you'll run into all sorts of problems if 'freeze transforms' was ever executed on the node.
So this will work, but it may not give you an accurate world matrix:
{{{
mtx = mc.getAttr("myNode.worldMatrix", time=32)
}}}
So... how can you query worldspace matrix data on a given frame, handling freeze transforms?

Code flow below:
* Make a function that:
* Stores the nodes rotate order.
* Get a {{{MVector}}} for the {{{node.translate}}} attr, via {{{getAttr}}} at the query frame.  This is the 'local' translation on that frame.
* Get a {{{MVector}}} for the {{{node.rotatePivot}}} at the current frame: This is to handle if 'freeze transforms' was ever ran on the node.
* Add those two vectors together to get the {{{combinedTrans}}} {{{MVector}}}, at the queried frame.
* Get the value of the {{{node.rotate}}} value, convert from degrees to radians, and pass that into a {{{MEulerRotation}}} with the above rotate order.
* Get a {{{MVector}}} for the {{{node.scale}}} attr.  Or pass in a normalized vector if you want to remove scale from this compute.
* Generate a {{{MTransformationMatrix}}}, passing in the above vectors to its {{{setTranslation}}} (in {{{MSpace.kWorld}}} space), {{{setRotation}}}, and {{{setScale}}} (in {{{MSpace.kObject}}} space) methods.  This is the {{{localMatrix}}} of this node, queried in time.
* Query the full path to the node via {{{ls}}}, and split it by {{{|}}} to get a list of all the parents.  Eject this node name from the end of the list.
* Make a new empty list that will store {{{localMatrices}}}, below.
* Loop over each node in the list of parental nodes:
** Call to this same function on each node, and append that local matrix to our {{{localMatrices}}}.
* Reverse the list of {{{localMatrices}}}.
* Make a new {{{worldMatrix = localMatrix.asMatrix()}}}
* For each {{{locMtx}}} in the list:
** {{{worldMatrix = worldMatrix * locMtx}}}
* You now have the world matrix of that node, but queried at some other frame!
Few other notes:
* You can query the {{{worldInverseMatrix}}} however:  First query the {{{worldMatrix}}} (like above), then after all the above work is done, call to {{{worldMatrix.inverse()}}} for the return.








{{{
string $filters[] = `itemFilter -q -listBuiltInFilters`;
print $filters;
}}}
{{{
DefaultGeometryFilter
DefaultNURBSObjectsFilter
DefaultPolygonObjectsFilter
DefaultSubdivObjectsFilter
DefaultCameraShapesFilter
DefaultJointsFilter
....etc...
}}}
You can also query 'user-defined' filters and 'other filters' as well.
Also see:
*[[Based on an itemFilter, how can I find all the nodes in Maya that match?]]
*[[How can I use itemFilter or itemFilterAttr?]]
{{{
skinCluster -q -inf $object;  
// or leave off $object for the selected object
}}}
The easiest way is to call out to {{{dos}}} using its {{{DIR}}} command.  The default {{{dos}}} syntax to get a list of all the subdirs of a dir is this:
{{{
DIR /ad /s /b
}}}
You could jump through a lot of hoops with the {{{getFileList}}} command, but I've tried, and it's exponentially slower than doing below:
And here is how to wrapper that with mel:
{{{
// good chance the incomming path has forwardslashes
string $path = "c:/temp";

// convert to backslash:
$path = toNativePath(path);

// query the system:
string $dirList = system("DIR "+ $path + " /ad /s /b");

// and convert into an array:
string $dirs[];
tokenize $dirList "\n" $dirs;

print $dirs;
// the last index will be an empty string, FYI
}}}
You'll probably want to run {{{fromNativePath}}} on each array member to put them back in "forwardslash" mode when you itter through the list.

If you want to kick it up a notch, you can also use dos to look for a particular dir IN each of the paths, at the same time the list is being generated.  Say, you want to find all the dirs that END with {{{\foo}}}.  The dos command would be:
{{{
DIR /ad /s /b |FINDSTR "\\foo$"
}}}
As you can see, you pipe the return of the DIR command into the FINDSTR command, which then looks for {{{\foo}}} at the end up each line using the ({{{$}}}) regular expression.  You have to use double-backslashes in front of foo, since the backslash is an escape character in regular expressions.
To wrapper that in mel would look like this:
{{{
string $dirList = system("DIR " + $path + " /ad /s /b |FINDSTR \"\\\\foo$\"");
}}}
And if you wanted to find all the .ma files in that result (notice the {{{/ad}}} was removed):
{{{
string $dirList = system("DIR " + $path + " /s /b |FINDSTR \"\\\\foo$\" |FINDSTR \".ma$\" ");
}}}
And that, is a lot of escape backslashes....

Another dos example querying for a partial name of a file based on a specifically named subdir:
{{{
DIR  /s /b |FINDSTR /R "\\build\\.*_foo\.py$"
}}}
This says:  Look under the current dir for all subdirs named {{{\build}}}, and return a list of all files that end in {{{_foo.py}}}.
The {{{shaderfx}}} command doesn't seem to allow any easy way to make this happen.  And I've been unable to find anything else online as an example, so I reached out to Autodesk for help:  What they gave me worked, but was mel, so here's a pymel rewerite.

One weird thing:  Below, you'll see me do this:
{{{
getPropertyValue=(i+1, "texturepath")
}}}
In the //mel// versions, it looks like this:
{{{
-getPropertyValue $i "texturepath"
}}}
Not sure why in ~PyMel (or Python in general) you have to increase this number, and only in that one spot.  Seems questionable...

This is also a good general example of how one goes about querying internal data in a {{{ShaderfxShader}}} node...
{{{
def getShaderFxTextures():
    sfxTextures = ("Combine Normal Maps", "Derived Normal Z Map", "Flip Book",
                   "LatLong UVs", "MatCapUVs", "Normal Map", "Offset Bump Map",
                   "PNAEN Displacement Map", "Reflection Cube Map", "Refraction Cube Map",
                   "Spherical Reflection UVs", "Texture Map", "UV Panner", "UV Rotator")
    textures = []
    sfxnodes =  pm.ls(type="ShaderfxShader")
    for sfxnode in sfxnodes:
        nodeCount = pm.shaderfx(sfxnode=sfxnode, getNodeCount=True)
        for i in range(nodeCount):
            try:
                propVal = pm.shaderfx(sfxnode=sfxnode, getPropertyValue=(i, "classname"))
                if propVal in sfxTextures:
                    # Why is i+1 needed here?  If I run this as pure mel, it is fine.
                    # Seems sketchy...
                    texPath = pm.shaderfx(sfxnode=sfxnode, getPropertyValue=(i+1, "texturepath"))
                    if texPath not in textures:
                        textures.append(texPath)
            except:
                # Not all ShaderfxShader subnodes can have getPropertyValue ran on them, 
                # and will raise a 'Maya command error' when queried.
                pass
    return textures
}}}
The {{{control}}} command.

While each UI control usually has its own edit and query flags, you can also use the generic {{{control}}} command to do these operations.  Makes it easier, if you know the name of a control, but not its 'type', to edit it.

Good for querying/editing:
control size, visibility, parent, and popup menus among others.
!Windows
You can use {{{dxdiag}}} at the commandline: The 'Display 1' tab will tell you the Graphics card manufacturer and 'main driver' version.
!In Maya
You can call to the {{{ogs}}} Maya command to get info on your graphics card:
{{{
ogs -deviceInformation;
}}}
Prints a long string with all sorts of info for your adapter, driver, api, gpu stuff, etc.
http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/Commands/ogs.html

The {{{listConnections}}} command has a '{{{type}}}' parameter that lets you filter the results by a given type.  Unfortunately, you can only pass in a single string argument.  Below is a little tip that let's you specify the connection types ahead of time.  Via Python, we use a //list comprehension// to generate a list of all matching connection types for each of the ones we query.  Then we use the {{{zip}}} Python command to zip our original connection type list together with our connection results list.  Finally, we wrapper that in Python's {{{dict}}} command, which turns the result into an easily queryable dictionary:
{{{
# Python code
import maya.cmds as mc

myNode = 'myNode'
conType = ['bindPose', 'skinCluster']
con = dict(zip(conType, [mc.listConnections(myNode, type=ct) for ct in conType ]))
print con
# {'skinCluster': [u'skinCluster4', u'skinCluster4'], 'bindPose': None}
}}}
In Windows the {{{tasklist}}} command will return this info.  
Example if you have two Maya's open:
{{{
> tasklist /FI "IMAGENAME eq maya.exe"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
maya.exe                      6064 Console                    1    289,552 K
maya.exe                      1288 Console                    1    289,480 K
}}}
In Maya, you can use Python to query its return:
{{{
import subprocess

def isMayaOpen():
    """
    Will return how many open maya.exe applications are running.
    """
    mayaOpen = 0
    output = subprocess.Popen(['tasklist', '/FI', 'IMAGENAME eq maya.exe'], stdout=subprocess.PIPE).stdout
    for line in output:
        if 'maya.exe' in line:
            mayaOpen = mayaOpen+1
    return mayaOpen
}}}
{{{
print isMayaOpen()
2
}}}
----
On a related note, Maya can query its own //process identifier// number via the //mel// command (not in Python for some reason...), which coresponds to the above PID values:
{{{
import maya.mel as mm
pid = mm.eval('getpid');
print pid
1288 
}}}
(Answer, from Alias:)
{{{
int $sj = `scriptJob -e RecentCommandChanged rcc`; 
global proc rcc() 
    { 
    string $lastCommands[] = `repeatLast -q -cl`; 
    if (`gmatch $lastCommands[0] "Save*"`) 
        print ("scene was saved with " + $lastCommands[0] + "\n");
    }
}}}
{{{filetest}}}
Maybe there's an easier way, but this is one solution :)
The only reason I don't like it is because you have to actually select stuff for the {{{polySelectConstraint}}} to work.
This code simply checks to see if a mesh has border edges.  Border edges only show up if there's a hole, that would create a border.
{{{
import pymel.core as pm

def selectBorderEdges(mesh):
    mesh = pm.PyNode(mesh)
    if len(mesh.e) == 0:
        raise Exception("")
    pm.select(mesh.e)
    pm.polySelectConstraint(mode=2, type=0x8000, where=True)
    pm.polySelectConstraint(mode=0, disable=True)

def getBorderEdges(mesh):
    sel = pm.ls(selection=True)
    selectBorderEdges(mesh)
    borderEdges = pm.ls(selection=True, flatten=True)
    if sel:
        pm.select(sel)
    else:
        pm.select(clear=True)
    return borderEdges

def hasHole(mesh):
    if getBorderEdges(mesh):
        return True
    else:
        return False
}}}
{{{
int $unloaded = `file -query -deferReference $refFilePath`;
}}}
Will return {{{1}}} if unloaded, {{{0}}} if loaded.
{{{
import maya.cmds as mc
needsSaving = mc.file(query=True, modified=True)
}}}
Have yet to find a API call for this: [[MFileIO|http://download.autodesk.com/us/maya/2011help/API/class_m_file_i_o.html]] (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:
*[[How can I list which nodes have been modifed since the last save?]]
You can actually use the {{{objExists}}} command, like:
{{{
int $txAttrExists= `objExists persp.tx`;
// Result: 1 //
}}}
Which is easier than the syntax for {{{attributeQuery}}}, and I'm told that it actually evaluates faster too.

''However #1'', I've learned that if querying for the existence of an attr on a transform, it will also search the shape as well, which often' *isn't* wanted.  Given that, you can use {{{attributeQuery}}}, or the {{{listAttr}}} command to get all the attrs on the node, then test for existence of your attr in that list.
{{{
import maya.cmds
exists = mc.attributeQuery('attrName', node='nodeName', exists=True)
}}}
You can also query a node's //type// for attribute existence:
{{{
exists = mc.attributeQuery('translateX', type='transform', exists=True)
}}}
----
''However #2'' : It's been reported to me (thanks Te) that {{{objExists}}} is up to 45x faster than {{{attributeExists}}}... so if you're doing a lot of looping, that could be a consideration.
----
There is also the [[attributeExists|http://download.autodesk.com/global/docs/maya2012/en_us/Commands/attributeExists.html]] mel //script// (not available in Python unless eval'd)
I've been unable to find anything via mel or the API that returns whether an image has an alpha channel.  The file //node// however, knows this:

Presuming you've mapped some valid texture to a file node:
{{{
int $hasAlpha = `getAttr myFileNode.fileHasAlpha`;
}}}
*Example: Select an object and:
{{{
string $sel[] = `ls -sl`;
referenceQuery -inr $sel[0];
}}}
----
Via the API, you can query this via the {{{MFnDependencyNode}}} class:
http://download.autodesk.com/us/maya/2010help/API/class_m_fn_dependency_node.html
{{{
import maya.OpenMaya as om
dgIterator = om.MItDependencyNodes() 
while not dgIterator.isDone():
	currentItem = dgIterator.thisNode() # MObject
	currentItemFunc = om.MFnDependencyNode(currentItem)
	if currentItemFunc.isFromReferencedFile():
		print "I'm referenced:", currentItemFunc.name()
	dgIterator.next()
}}}
----
Via ~PyMel:
{{{
import pymel.core as pm
node = pm.PyNode("myAwesomeNode")
print node.isReferenced()
}}}
{{{isTrue}}} is a command that you can use to query various condition and events in Maya.  
What are those events and conditions?
The {{{scriptJob}}} command can be used to find them:
{{{
help -doc scriptJob;
}}}
Or you can have the events and conditions listed:
{{{
print `scriptJob -listEvents`;
print `scriptJob -listConditions`;
}}}
So for example, if you wanted to know if Maya was currently playing back animation:
{{{
int $playback = `isTrue playingBack`;
}}}
Or is something picked:
{{{
int $selected = `isTrue SomethingSelected`;
}}}
There are many others.  Handing if while scripting, you need to have your script detect for a certain condition.
{{{
global proc int isKeyed(string $obj)
	{
	string $connections[] = `listConnections -s 1 -d 0 $obj`;
	for($i=0;$i<size($connections);$i++)
		{
		string $type = `objectType($connections[$i])`;
		if(`match "^animCurve" $type` == "animCurve")
			return 1;
		}
	return 0;
	}
	
string $sel[] = `ls –l –sl`;	
int $keyed = isKeyed($sel[0]);
}}}
{{{
float $keys[] = `keyframe -q -sl -vc`;
}}}
changing the "-vc" with other flags will return different values
Constraints (seemingly all of them) have a {{{-tl}}} (target list) flag that can be used to query a list of targets, and a {{{-wal}}} flag that can be used to query the coresponding list of 'weight attributes' on the constraint itself:
Presuming it's a {{{parentConstraint}}}
{{{
// mel
string $targetObjs[] = `parentConstraint -q -tl $constraintName`;
string $weightAttrs[] = `parentConstraint -q -wal $constraintName`;
}}}
Armed with this knowledge, I decided to write a Python function that would capture this data easily for the user.  Since the mel command to deal with the a given constraint conveniently matches the objectType of the given node, it allows me to write one piece of code that can work on any constraint type.
{{{
# Python code:
import maya.cmds as mc

def getConstraintTargetData(constName):
    """
    Based on the given constraint, will return a list of data for each constraint
    weight.  Specifically, each list index is a dict, with the keys "target" 
    (target object) and "attribute" (corresponding attribute weight name on the 
    constraint)
    """
    constType = mc.objectType(constName)
    constraintData = []
    targetList = eval('mc.%s("%s", query=True, targetList=True)'%(constType, constName))
    weightList = eval('mc.%s("%s", query=True, weightAliasList=True)'%(constType, constName))
    for i in range(len(targetList)):
        constraintData.append({"target":targetList[i], "attribute":weightList[i]})
        
    return constraintData 
}}}
To see it in action, parent constrain pSphere1 to pCube1 and pTorus1:
{{{
targetData = getConstraintTargetData("pSphere1_parentConstraint1")
print "Number of targets: %s"%len(targetData)
for i,e in enumerate(targetData):
    print "Index %s: %s"%(i, e)
print "Weight attribute for index 0: %s"%targetData[0]["attribute"]
print "Target object for index 1: %s"%targetData[1]["target"]
}}}
prints:
{{{
Number of targets: 2
Index 0: {'attribute': u'pTorus1W0', 'target': u'pTorus1'}
Index 1: {'attribute': u'pCube1W1', 'target': u'pCube1'}
Weight attribute for index 0: pTorus1W0
Target object for index 1: pCube1
}}}
!!!~PyMel:
In this example, we query values for vert 0 on our mesh:
{{{
import pymel.core as pm

skinCluster = pm.PyNode("someSkinClusterName")
mesh = pm.PyNode("someMeshName")
joint = pm.PyNode("someJointName")

# Get a list of all the weight values on this vert:
values = pm.skinPercent(skinCluster, mesh.vtx[0], query=True, value=True)

# Get a list of all the transforms influencing this vert.  Can't seem to get this command
# to work in pure PyMel or Python, have to eval the mel:
transforms = pm.mel.eval('skinPercent -query -transform "%s" "%s"'%(skinCluster.nodeName(), mesh.vtx[0]))

# Get the float weight value for just this specific joint on this one vert:
jointValue = pm.skinPercent(skinCluster, mesh.vtx[0], query=True, transform=joint)
}}}
!!!Mel
Pick a vertex, and run:
{{{
// get just the object name from selection:
string $selObj[] = `ls -o -sl`;
// get the vert name
string $selPt[] = `ls -sl`;

// find our skindCluster:
string $sc[] = `listConnections -s 1 -d 0 -type "skinCluster" $selObj[0]`;

// get transforms (binding joints and influence object)
string $skinInfluences[]=`skinPercent -q -t $sc[0] $selPt[0]`;
// get skin weight of selected vertex
float $sknVals[]=`skinPercent -q -v $sc[0] $selPt[0]`;

// only print values that are greater than 0.  If you want to know 
// influence names even if they are zero, remove the 'if' line:
for($i=0;$i<size($skinInfluences);$i++)
    {
    if($sknVals[$i] > 0)  // print $sknVals[$i]
        print ($skinTrans[$i] + "\t\t" + $sknVals[$i] + "\n");
    }
}}}
*the index of $skinTrans and $skinVals is 1-1 matched
{{{
import maya.cmds as mc
print mc.window("", query=True, frontWindow=True)
# scriptEditorPanel1Window
}}}
Note you still need to pass an empty string into the {{{window}}} command to access this functionality.
Via the '{{{Deform-> Paint Blend Shape Weights Tool}}}', you can paint the influence that a blend shape has on a mesh.  The tool lets you save the weights out as a map, and read the weights back in.  But where do the weights actually get stored on the object?

On the blendShape node's '{{{.inputTarget.inputTargetGroup.targetWeight}}}' att:
{{{
getAttr blendShape1.it[0].itg[0].tw;
// Result: 1 1 1 1  1 1 1 0.5 0.5 0.50.5 0.5 etc....
}}}
There is a 1:1 relationship beween these values, and the verts on the mesh.

I figured this out by looking in the .ma file itself.  I couldn't find anything //in// Maya that would explain what\where these values were.  You could edit them in the Component Editor, but it wouldn't explain what it was doing.  And when you paint the actual weights, or load a weight file, neither does it tell you what it's doing then either.

I gave this info to someone else, and they came back with this description:
*inputTarget[0]. >> Maybe your source mesh? 
*inputTargetGroup[1]. >> your target by index
*targetWeights[0] >> the actual vert index to modify
*0;  > the value to set
It's easy enough to just query that part of the UI:
{{{
string $char = `textField -q -text characterField`;
}}}
{{{
import pymel.core as pm

cam = None
mp = pm.playblast(activeEditor=True)
if mp:
    # Doesn't like full path to panel, weird.
    cam = pm.modelPanel(mp.split("|")[-1], query=True, camera=True)
print cam
# persp1
}}}
This prints the camera //transform// name, not shape.
With the 'Paint Skin Weights Tool' visible:
{{{
# Python code
import maya.cmds as mc
print mc.artAttrSkinPaintCtx('artAttrSkinContext', query=True, influence=True)
}}}
Update: Hmm... not sure why I thought this was hard in the past:
{{{
import pymel.core as pm
bsNode = pm.PyNode("blendShape1")
targets = pm.blendShape(bsNode, query=True, target=True)

print targets
[u'headShape2', u'headShape1']
}}}
----
Older code:
{{{
string $list[]= `listAttr -m "<name of blendshapes>.w"`;
}}}
*thanks to a post from David Coleman
----
Here's that in some ~PyMel:
{{{
import pymel.core as pm

bsNode = pm.PyNode("myBlendshapeNode")
bsTargetNames = [pm.listAttr(bsNode.weight, multi=True)]
bsTargetAttrs = [bsNode.attr(attr) for attr in pm.listAttr(bsNode.weight, multi=True)]
}}}
prints:
{{{
[[u'bs_flattop', u'bs_ears', u'bs_chin']]
[Attribute(u'myBlendshapeNode.weight[0]'), Attribute(u'myBlendshapeNode.weight[2]'), Attribute(u'myBlendshapeNode.weight[3]')]
}}}
----
Optionally:
{{{
import pymel.core as pm

bsNode = pm.PyNode("myBlendshapeNode")
aliases = pm.aliasAttr(bsNode, query=True)
ret = {}
for i in range(0, len(aliases), 2):
    ret[aliases[i+1]] = aliases[i]

print ret

{u'weight[3]': u'bs_chin', u'weight[2]': u'bs_ears', u'weight[0]': u'bs_flattop'}
}}}
{{{colorAtPoint}}}
*Example given a "ramp" node:
{{{
colorAtPoint -o RGB -u .5 -v .5  ramp1;
}}}
I was told that if your UV's are tiling, UV 0 and 1 can return the same values.  Querying .99999 is a safer bet.

Should also be noted that this only samples the color for the given node, not the final combination of multiple nodes (if they exist).
Haven't yet found an //easy// way to grab the final color of a given uv point based on a complex shading network.
----
Also see:
*[[How can I find the color of a 2D render node at a given vertex?]]
* [[How to sample a 3D texture]]
The {{{undoInfo}}} command will let you print the contents of the undo queue.  But it doesn't let you actually capture that data, which is a bit of a pain.  The below method shows how you //can// capture that data, but turning on script editor history logging:
{{{
# Python code
import os
import maya.cmds as mc

# Define a temp file name, and delete if it exists, since the
# history logging will append to it:
tempfile = os.path.join(os.getenv("TMP"), "undoInfoTemp.txt")
if os.access(tempfile, os.F_OK):
    os.remove(tempfile)

# Set the Script Editor to write out all it's history to this file:
mc.scriptEditorInfo(edit=True, historyFilename=tempfile, writeHistory=True)

# print our undo queue:
mc.undoInfo(query=True, printQueue=True)

# Turn off history capture:
mc.scriptEditorInfo(edit=True, writeHistory=False, historyFilename='')

# Turn the file into a list we can use:
undoQueue = [line.strip() for line in open(tempfile)]

# And print the result:
print "Stuff in queue:"
for line in undoQueue:
    print "\t", line
}}}
{{{
Stuff in file:
	# 0: select -r foo
	# 1: select -cl
	# 2: select -r stuff
}}}
{{{
string $paneConfiguration = `paneLayout -q -configuration $gMainPane`;
string $paneKids[] = `paneLayout -q -ca $gMainPane`;
}}}
{{{about}}}
*Example, query the current date or time:
{{{
string $date = `about -cd`;
string $time = `about -ct`;
}}}
I needed a way to find all the inputs feeding into a node, and all their inputs.  The whole 'input tree'.  This snippet recurisvely searches through all the nodes that input into a given node, and all of their inputs, etc.
{{{
import pymel.core as pm

node = pm.PyNode("myNodeName")
inputs = pm.listConnections(node, source=True, destination=False, skipConversionNodes=True)
for item in inputs:
    newInputs = pm.listConnections(item, source=True, destination=False, skipConversionNodes=True)
    for ni in newInputs:
        if ni not in inputs:
            inputs.append(ni)
}}}
You don't want to query the //current// enumAttr value, you want to query //all// their names:
----
{{{
string $enumAttrs = `addAttr -q -enumName "myNode.myEnumAttr"`;
}}}
This returns a single string of attr names separated by colons:
{{{
"enumA:enumB:enumEct"
}}}
----
Tip from my buddy Te:
{{{
string $enumAttrs[] = `attributeQuery -node "myNode" -listEnum "myEnumAttr"`;
}}}
This returns an array, with a single string value separated by colons:
{{{
{"enumA:enumB:enumEct"}
}}}
----
Or, via ~PyMel's [[Attribute.getEnums()|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html?highlight=attribute#pymel.core.general.Attribute.getEnums]] method:
(Modified example from the ~PyMel docs):
{{{
>>> from pymel.core.general import Attribute
>>> addAttr( "persp", ln='numbers', at='enum', enumName="zero:one:two:thousand=1000:three")
>>> numbers = Attribute('persp.numbers').getEnums()
>>> sorted(numbers.items())
[(u'one', 1), (u'thousand', 1000), (u'three', 1001), (u'two', 2), (u'zero', 0)]
>>> numbers[1]
u'one'
>>> numbers['thousand']
1000
}}}
Note, ~PyMel will raise a {{{TypeError}}} of the enum has no values.
----
Also see:
* [[PyMel : enum attrs]]
This will turn it into a list:
{{{
seData = mc.cmdScrollFieldReporter("cmdScrollFieldReporter1", query=True, text=True)
dataSplit = [line for line in seData.split("\n") if line]
print("#-------------")
for ds in dataSplit:
    print(ds)
print("#-------------")
}}}
The active influence can be found in:
{{{
global string $artSkinLastSelectedInfluence;
}}}
A list of the highlighted influences can be found in:
{{{
global string $gArtSkinOrderedInfluenceSelectionList[];
}}}
These values are queried\modified inside this script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\artAttrSkinJointMenu.mel
}}}
----
The actual control in Maya is a {{{treeView}}} named {{{theSkinClusterInflList}}}.  So you can query the selected influences this way:
{{{
string $infs[] = `treeView -q -si "theSkinClusterInflList"`;
}}}
But this won't tell you the 'lead' influence:  Currently it seems you need the above global var to do it.
The ui is made via the proc {{{skinClusterInflBuildList}}} living in this script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\skinClusterInflMenu.mel
}}}
New in Maya 2010, is the //mel only// command {{{getLastError}}}
First off, you can use the {{{whatIs}}} command, and I have a tiddler specifically authored aroud that here:  [[How can I have a script return where it lives on disk?]]
However, that method can run into problems if you're currently authoring the script, since Maya can think it's an interactively entered procedure possibly, and not give you the path back.

Another solution is to actually use the {{{file}}} command.  Presuming you save a script in your script path, you can query its location this way:
{{{
string $path = `file -query -location "testScript.mel"`;
// Result: C:/temp/./testScript.mel //

$scriptLocation = `substitute "/./" $scriptLocation "/"`;
// Result: C:/temp/testScript.mel //
}}}
With a little formatting via the {{{substitute}}} command, you get the full path back.
Just the image name:
{{{
string $name[] = `renderSettings -firstImageName -leaveUnmatchedTokens`;
}}}
Full path:
{{{
string $name[] = `renderSettings -firstImageName -leaveUnmatchedTokens -fullPath`;
}}}
*http://download.autodesk.com/global/docs/maya2014/en_us/Commands/renderSettings.html
When you reference a scene, Maya creates a node of type {{{reference}}} to help manage the changes.  Most {{{reference}}} nodes are the defined namespace plus "RN".  So if you reference someting in with the "bob" namespace, the {{{reference}}} node created would be called {{{bobRN}}}. 
 
Presuming you know that reference node name (easily gathered by the '{{{ls -type "reference";}}}' command), how can you query the namespace that the referenced file currently resides in?
{{{
string $refNode = "bobRN";
string $file = `getAttr ($refNode +".fileNames[0]") `;
string $ns = `file -q -rpr $file`;
// bob
}}}
After a file has been referenced into a scene, it's possible that it's namespace could be different from what was specified due to namespace clashing.  How to query what the namespace actually is?
{{{
# Python code
import maya.cmds as mc
namespace = mc.file('c:/path/to/my/file.ma', query=True, namespace=True)
}}}
{{{
polyNormalPerVertex -q -xyz  pSphere1.vtx[41];
}}}
Note, this will shoot back multiple sets 3 floats for each "vertexNormal", one for each tri that the vert shares.

Also see:  [[How can I set the normal of a vertex?]]

{{{
string $name = `setParent -q myElementName`;
}}}
Thanks to Eric Vignola ;-)
Say you have this transform hierarchy:
{{{
|breakfast
    |spam
    |eggs
}}}
And you want to know what position eggs is in, relative to it's siblings.  In this case, it would be the 2nd item.
{{{
import pymel.core as pm
node = pm.PyNode("eggs")
parent = node.firstParent()
position = parent.getChildren().index(node)+1
print position
# 2
}}}
Which is the 2nd item in the list.
As you edit a ramp node, you can add and remove "colors" from it.  You may at some point want to be able to query where on the ramp those colors are.  Here is a quick way to do it:
{{{
string $ramp = "ramp1";	
string $mults[] = `listAttr -m ($ramp + ".colorEntryList")`;
for($i=0;$i<size($mults);$i++)
    {
        if(`match "\\.position$" $mults[$i]` == ".position")
        {
        float $pos = `getAttr ($ramp + "." +$mults[$i])`;
        print ($ramp  + "." + $mults[$i] + "  is: " + $pos + "\n");
        }
    }
// result:
ramp1.colorEntryList[0].position  is: 0
ramp1.colorEntryList[1].position  is: 0.5
ramp1.colorEntryList[2].position  is: 1
}}}
{{{
import pymel.core as pm
tc = pm.language.melGlobals["gPlayBackSlider"]
first,last = pm.timeControl(tc, rangeArray=True, query=True)
}}}
Mel:
{{{
string $sceneName = `file -query -sceneName`;
}}}
Python:
{{{
import maya.cmds as mc
sceneName = mc.file(query=True, sceneName=True)
}}}
Via the API:
{{{
import maya.OpenMaya as om
sceneName = om.MFileIO.currentFile()
}}}
----
The main difference between the commands and the API is the return value when the current scene //isn't saved//:  The {{{file}}} command will return back an empty string.  But {{{MFileIO.currentFile()}}} will return the string {{{untitled}}}, with the current project path.  For example:
{{{
c/somePath/untitled
}}}
*Maya 5:
{{{
addAttr -q -en object.attr;
}}}
*Maya 6 and later:
{{{
getAttr -as object.attr\\ (as string)
}}}

This code works in 2013:  I'm not sure when {{{portWidth}}} and {{{portHeight}}} came online, but they're so easy to use I'm guessing they weren't available in earlier versions of Maya that the below code is based on.
{{{
import maya.OpenMayaUI as omui
activeView = omui.M3dView.active3dView()
width = activeView.portWidth()
height = activeView.portHeight()
}}}
----
No API, just mel.  Note, this returns a slightly larger value than the above and below examples (who's values match one another).
{{{
def getCurrentPanelSize():
    # This returns the full name, and is filtered by modelPanel's only:
    #mp = mc.playblast(activeEditor=True)
    # This returns the leaf name, and can be any type of editor:
    mp = mc.getPanel(withFocus=True)

    width = mc.control(mp, query=True, width=True)
    height = mc.control(mp, query=True, height=True)
    return width,height
}}}
What's interesting is the {{{playblast}}} command limits its return to the last active 3D {{{modelPanel}}}, while the {{{getPanel}}} command will return back the last accessed panel or editor of any kind (Graph Editor, Outliner, etc).
----
Based on [[this post|http://www.rtrowbridge.com/blog/2009/05/render-selected-meshes-region/]] by Ryan Trowbridge, I found some code I slightly changed to spit out the width and height in pixels of the active view. The rest of [[the module he wrote|http://www.rtrowbridge.com/rtRenderSelectedMeshesRegion.zip]] to render selected meshes is pretty sweet too.
{{{
import maya.OpenMaya as om
import maya.OpenMayaUI as omui

def getViewportResolution():
	activeView = omui.M3dView.active3dView()
	
	xPtrInit = om.MScriptUtil()
	yPtrInit = om.MScriptUtil()
	widthPtrInit = om.MScriptUtil()
	heightPtrInit = om.MScriptUtil()
	
	xPtr = xPtrInit.asUintPtr()
	yPtr = yPtrInit.asUintPtr()
	widthPtr = widthPtrInit.asUintPtr()
	heightPtr = heightPtrInit.asUintPtr()
	
	activeView.viewport(xPtr, yPtr, widthPtr, heightPtr)
	viewWidth = widthPtrInit.getUint( widthPtr )
	viewHeight = heightPtrInit.getUint( heightPtr )
	
	return (viewWidth, viewHeight)
	
widthHeight = getViewportResolution()
print widthHeight
#(477, 269)
}}}
{{{
# Python code
import maya.cmds as mc

animCurve = 'myNode_translateX' 
time = mc.keyframe(animCurve, query=True, timeChange=True)
vals = mc.keyframe(animCurve, query=True, valueChange=True)
timeValues = zip(time,vals)
print timeValues
# [(0.0, 0.0), (1.0, -0.82123691729992565)]
}}}
----
Also see:
* [[How can I query which frames a node is keyframed on?]]
{{{
getAttr -type "persp.tx";
// doubleLinear // 
getAttr -type "persp.rx";
// doubleAngle // 
getAttr -type "persp.v";
// bool // 
getAttr -type "persp.filmRollOrder";
// enum // 
}}}
The mel to do this is:
{{{
optionVar -q blendShapeEditorTreeViewSelection;
}}}
but it returns a mess of stuff like:
{{{
// Result:     blendA.0/blendA.1/blendA.2/blendB.0/    blendA.0/blendA.1/blendA.2/blendA.0/ blendB.0/ 0 blendA.0/ // 
}}}
It appears the first element always contains what you want though.  So here's some simple Python to get it:
{{{
import pymel.core as pm
selData = [item for item in pm.optionVar["blendShapeEditorTreeViewSelection"] if item][0].split("/")
bsIndices = []
for item in selData:
    if not item:
        continue
    bsNode, index = item.split(".")
    bsIndices.append((bsNode, int(index)))

print bsIndices
[(u'blendA', 0), (u'blendA', 1), (u'blendA', 2), (u'blendB', 0)]
}}}
Of course you now have to convert the indices to legible aliasAttr names, but it's a start!
{{{
string $selAttr[] = `channelBox -q -sma "mainChannelBox"`;
}}}
*Note, you can query the inputs and outputs as well, with different arugments.
{{{file}}}
*Example: Based on some given filename $filename:
{{{
file -q -rpl $filename;
}}}
{{{
# 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)
}}}
Given a selection, it's easy to change from one polygonal component type to another (via commands like {{{polyListComponentConversion}}}).  But what if you just want to query what //type// of polygonal component is picked?  I've not found any built-in commands that do this, so here is one solution:
{{{
# Python code
import maya.cmds as mc

def getSelPolyComponentType():
    """
    Based on the selected polygonal components, return the type selected.  If
    more than one type is selected, raise an exception.  If no poly components
    are picked, return none.
    """
    compTypes = [['.map', 'uv'],
                 ['.vtx', 'vertex'],
                 ['.e', 'edge'],
                 ['.f', 'face'],
                 ['.vtxFace', 'vertexFace']]
    sel = mc.ls(selection=True, flatten=True)
    compType = None
    for s in sel:
        for searchStr, result in compTypes:
            if searchStr in s:
                if not compType:
                    compType = result
                else:
                    if result != compType:
                        raise Exception('More than one type of component selected')
    return compType
}}}
{{{
getPanel -vis;
}}}
Say the user has picked some anim curves in the Graph Editor... what are they?
{{{
string $pickedKeys[] = `keyframe -query -selected -name`;
// Result: pCube2_rotateZ
}}}
{{{
import maya.cmds as mc

keyedFrames = []
node = "myNodeName"
animCurves = mc.listConnections(node, source=True, destination=False, type='animCurve')
for ac in animCurves:
    frames =  mc.keyframe(ac, query=True, timeChange=True)
    keyedFrames.extend(frames)
keyedFrames = list(set(keyedFrames))
print keyedFrames
[1.0, 45.0, 79.0]
}}}
----
Also see:
* [[How can I query time/value keyframe pairs on an animCurve?]]
{{{getModifiers}}}
{{{
int $mods = `getModifiers`;
// note that it returns back "bit" values, so Shift: 1, CapsLock: 2, Ctrl: 4, and Alt: 8.
}}}
----
Here's a modified version of what the docs have, in Python, showing how to grab multiple values at once via [[bitwise operators|http://wiki.python.org/moin/BitwiseOperators]]
{{{
import maya.cmds as mc

def PrintModifiers():
    mods = mc.getModifiers()
    print 'Modifiers are:'
    if (mods & 1) > 0: print ' Shift'
    if (mods & 2) > 0: print ' CapsLock'
    if (mods & 4) > 0: print ' Ctrl'
    if (mods & 8) > 0: print ' Alt'
    print '\n'

mc.window()
mc.columnLayout()
mc.button(label='Press Me', command=lambda *args:PrintModifiers() )
mc.showWindow()
}}}
{{{displayRGBColor}}}
Basically, the stuff found in 'Window -> Settings/Preferences -> Color Settings'

For example, to print a list of every possible UI element and their current values:
{{{
displayRGBColor -list;
}}}
prints:
{{{
template 0.47 0.47 0.47
preSelectHilite 1 0 0
userDefined1 0.63 0.41391 0.189
userDefined2 0.62118 0.63 0.189
userDefined3 0.4095 0.63 0.189
// etc...
}}}
To set the background color to black:
{{{
displayRGBColor background 0.0 0.0 0.0;
}}}
It also lets you create new color presets as well.
The {{{upAxis}}} command.
{{{
# Python code
import maya.cmds as mc
up = mc.upAxis(query=True, axis=True)
# y
}}}
{{{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;
}}}
Couple different ways:
!!!A:
*Pick a bunch of objects, with the target last.
*Highlight a channel value and hit tab:  It will copy that value to all the other selected objects.
*Each time you hit tab, it will copy the next value.
!!!B:
*Pick a bunch of mesh
*Pick the numeric values in the Channel Box
*RMB on the values -> Duplicate Values
There is any annoying feature in Maya that can cause the items highlited in the Graph editor to become de-highlighted... which means you loose all your curves from view.  The below code will re-highlight all the objects in the outliner side of the Graph Editor, which will in-turn re-display all their curves:
{{{
# Python code
import maya.cmds as mc

# Maya's standard graph editor outliner name:
graphEdOutliner = 'graphEditor1OutlineEd'
selectionConnection = mc.outlinerEditor(graphEdOutliner, query=True, selectionConnection=True)
mainListConnection = mc.outlinerEditor(graphEdOutliner, query=True, mainListConnection=True)
selectList = mc.selectionConnection(mainListConnection, query=True, object=True)
if selectList:
    for so in selectList:
        mc.selectionConnection(selectionConnection, edit=True, select=so)
}}}
*Example: Make an evn var with the path to the file, then reference it in:
{{{
// here we build the var in Maya, but it could be declared in Windows first:
putenv "MAYAFILES" "c:/temp";  
file -r -type "mayaAscii" -gl -namespace "TEST" -options "v=0" "$MAYAFILES/myFile.ma";
}}}
*From now on, the scene will look to $MAYAFILES as the directory for the file. If at any point you need to move the file, move it to the new location, and then update the $MAYAFILES enviroment variable. The Maya scene file will automatically look to the new location.
Sometimes my thumbnails go away and I just get a default shader ball.  This will fix it for all the scene.  Otherwise, in the Hypershade, you can RMB on any swatch and 'Refresh swatch'.
{{{
import maya.cmds as mc
import maya.mel as mm
for mat in mc.ls(materials=True):
    mm.eval('updateFileNodeSwatch("%s");'%mat)
}}}
It should note that the {{{updateFileNodeSwatch}}} mel  procedure calls to the {{{renderThumbnailUpdate}}} command.
----
Also see:
* [[How can I refresh all the textures in the scene?]]
This implements what the "Reload" button on a file texture node does, but for all file nodes in the scene.
{{{
import maya.cmds as mc

print "\nRefreshing all textures in scene:"
files = mc.ls(type='file')
for f in files:
    pth = mc.getAttr('%s.fileTextureName'%f)
    mc.setAttr('%s.fileTextureName'%f, pth, type='string')
    print "\tRefreshed:", f, " : ", pth
print "Texture refresh complete"
}}}
----
Also see:
* [[How can I refresh all material swatches in the Hypershade?]]
While this isn't mel code, it can be entirely recreated in mel:

There are some nodes in Maya, notably the lightLinker node, that can't be deleted from a scene file.  It's "special".  This is no real issue until, for whatever reason, they start to breed and fill your scene in the background.  I've seen scenes with hundreds of these nodes taking up unnecessary space.  How to get rid of them?  A fellow by the name of Ted Charlton use the below concepts in a script.  And they should work for any other kind of read-only node:

*Select the bad nodes, and "export them as a reference" from your scene as a new file:
{{{
File->Export Selected->Options->"Keep only a Reference" = yes.
}}}
*Then, remove that reference from your scene:File->Reference Editor...->
{{{
Select Reference File->Reference->Remove Reference
}}}
[[removeMultiInstance|https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/removeMultiInstance.html]]
For example, on a 'layered shader', delete the first input shader connection : This will auto-reorder all the others in the process:
{{{
removeMultiInstance -break true layeredShader1.inputs[0];
}}}
Although it should be noted trying this on {{{.weight[2]}}} attrs on {{{blendShape}}} nodes seems to have no effect :( (but technically it's {{{array}}}, not {{{multi}}}). 
Update:
At a certain point Maya's {{{namespace}}} command made this super easy: (but this will delete all the nodes too...)
{{{
import maya.cmds as mc
mc.namespace(deleteNamespaceContent=True, removeNamespace="myAwesomeNamespace")
}}}
Old stuff:
----
{{{
# Python code
import maya.cmds as mc

nameSpace = "myNamespace:";
mc.namespace(force=True, moveNamespace=[nameSpace, ":"])
mc.namespace(set=":")	  
mc.namespace(removeNamespace=nameSpace)
}}}
This code takes all nodes in the given namespace, 'moves' (renames) them to the root namespace, then deletes the old, empty namespace.
Update:
At a certain point Maya's {{{namespace}}} command made this super easy:
{{{
import maya.cmds as mc
mc.namespace(deleteNamespaceContent=True, removeNamespace="myAwesomeNamespace")
}}}
Old Stuff:
----
Based on unclean scene files, I often need to 'clean out' junk namespaces.  But the junk namespaces often have many, many child-namespaces, which can all have nodes living in them.

The below code first recursively searches through the namspace hierarchy of a given namspace.  Once it has that list, child namespace first, it tries to delete all node in each namespace, then finally delete the namespace itself presuming there were no references or read-only nodes in the namespace.
{{{
# Python code
import maya.cmds as mc

def namespaceClense(namespace):
    """
    For the given namespace, and all child namespaces, delete all nodes and namespaces.

    namespace : string : Starting namespace.

    return : list : Any namespaces that weren't able to be deleted, usually due
       to references, or special Maya undeletable nodes.
    """
    if namespace.endswith(':'):
        namespace = namespace[:len(namespace)-1]

    # Get a list of all namespaces to delete data from.
    namespaces = [namespace]
    for n in namespaces:
        mc.namespace(setNamespace=":")
        mc.namespace(setNamespace=n)
        childNs = mc.namespaceInfo(listOnlyNamespaces=True)
        if childNs:
            namespaces.extend(childNs)
    mc.namespace(setNamespace=":")

    # Delete stuff in our child namespaces first, then delete the child namespace:
    namespaces.sort()
    namespaces.reverse()

    undeletedNamespaces = []
    for n in namespaces:
        # Get a list of everything in the given namespace:
        allInNs = mc.ls('%s:*'%n, recursive=True, long=True)
        # Put in hierarchy order, and reverse.  Needed for happy deleting, don't
        # want to delete parents before kids, or code gets all confused.  And
        # after doing tests, they delete 25% faster than not being reversed.
        allInNs.sort()
        allInNs.reverse()
        for node in allInNs:
            try:
                mc.lockNode(node, lock=False)
                mc.delete(node)
            except:
                pass
        # Remove namespace if empty (it should be...)
        try:
            mc.namespace(removeNamespace=n)
        except:
            undeletedNamespaces.append(n)

    return undeletedNamespaces
}}}
If you reference the same file into your scene multiple times, Maya will put it into a unique namespace each time (incrementing it with a #), plus put a file incrementor # at the end of its filepath in the Reference Editor.  Presuming this is your file:
{{{
c:/temp/foo.ma
}}}
and you referenced it in twice, you'd end up with these namespace \ filepath \ 'reference node name' combos:
{{{
foo | c:/temp/foo.ma | fooRN
foo1 | c:/temp/foo.ma{1} | fooRN1
}}}
(that's 'foo-one', not fool, like a court jester)
To remove a reference, based on those conditions, we jump though a few hoops:  The user supplies a namespace name.  That's turned into a {{{reference}}} node name (which by default is name of namespace + 'RN' + incrementor), from which the file path of that reference can be queried, and then removed.

In this example, we remove the reference in namespace {{{foo1}}}:
{{{
string $namespace = "foo1"; // foo + 'one'
string $increment = `match "[0-9]*$" $namespace`;
string $refNode = substitute(($increment+"$"), $namespace, ("RN"+$increment));
string $refFile = `referenceQuery -f $refNode`;
file -rr $refFile;
}}}
This works by selecting the actual {{{reference}}} type nodes in the Outliner:  Even if the file they're pointing to is missing on disk, this will remove them.  Maya prevents you from removing a missing reference from the Reference Editor for some reason :(
{{{
import maya.cmds as mc

selRef = mc.ls(selection=True, type='reference')
for ref in selRef:
    fName = mc.referenceQuery(ref, filename=True)
    mc.file(fName, removeReference=True, force=True)
    print "For reference node '%s', removed reference to it's file: %s"%(ref, fName)    
}}}
Making custom HUD's (heads up display) can be cool, but Maya doesn't seem to provide an easy way to kill them in the UI.  For example, you get someone else's scene file, full of HUD's you don't want to see.  What to do?
{{{
// get a list of all the hud's in the scene:
string $hud[] = `headsUpDisplay -lh`;

// this is a list of all the default HUD's, retrieved from an empty scene with no custom HUD's (using the above command): 
string $defaultList = "HUDObjDetBackfaces HUDObjDetSmoothness HUDObjDetInstance HUDObjDetDispLayer HUDObjDetDistFromCam HUDObjDetNumSelObjs HUDPolyCountVerts HUDPolyCountEdges HUDPolyCountFaces HUDPolyCountTriangles HUDPolyCountUVs HUDSubdLevel HUDSubdMode HUDCameraNames HUDHQCameraNames HUDFrameRate HUDIKSolverState HUDCurrentCharacter HUDPlaybackSpeed HUDViewAxis HUDFrameCount";

// convert that string to an array:
string $defaultHud[];
tokenize $defaultList " " $defaultHud;

// and for each item in the array of all HUD's in the scene, see if there is a default match.  If not, kill:
for($i=0;$i<size($hud);$i++)  // print $defaultHud[$i]
    {
    if(`stringArrayCount $hud[$i] $defaultHud` == 0)
        {
        headsUpDisplay -rem $hud[$i];
        print ("deleted non-default HUD: " + $hud[$i] + "\n");
        }
    }
}}}
{{{
import pymel.core as pm
refs = pm.getReferences()
for key in refs:
    ref = refs[key]
    if ref.isDeferred():
        ref.load()
    print "Removed reference:", ref
    ref.remove()
}}}
http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.system/pymel.core.system.FileReference.html
When you skin mesh to joints, there is an an option to 'colorize skeleton'.  But what if you want to turn this //off// after the initial bind?  Or if you want to change the individual colors after the fact?

In the UI, the colors are controlled by the 'Display->Wireframe Color...' UI.  In that UI, you can bonk the "Default" button to reset the color.
All this really does is call to the {{{color}}} command:
{{{
import maya.cmds as mc
joints = mc.ls(type='joint')
for j in joints:
    mc.color(j)
}}}
Which with no args sets the default color on the selected objects.
{{{strip}}}
*Example:
{{{
string $word = "asdf\r\n";
int $size = size($word) \\ result: 6
$word = strip($word);
$size = size($word);  \\ result: 4
}}}
Pretty easily:
{{{
import maya.cmds as mc
mc.namespace(rename=["wasCalledThis", "nowCalledThis"], parent=":")
}}}
I'll often have a selection of nodes that I want to have renamed to have concequetive postfix numbers on the end.  Say I have a list of nodes like:
*foo
*foo23
*foo16
*foo1
I want to rename it so that in the same node order their names are:
*foo1
*foo2
*foo3
*foo4
----
A version using ~PyMel, with padding as well:
{{{
import pymel.core as pm

def renamePadded(padding=4, startNum=1):
    """
    Based on selection, rename all nodes to match the first, incrementing numbers
    on the end starting with startNum
    """
    sel = pm.ls(selection=True)
    rootName = sel[0].stripNum().split("|")[-1]
    pm.undoInfo(openChunk=True)
    try:
        for j,item in enumerate(sel):
            i = j+startNum
            padStr = "%0"+str(padding)+"d"
            execStr = 'name = "%s'+padStr+'"%("'+rootName+'",'+str(i)+')'
            exec execStr
            item.rename(name)
    finally:
        pm.undoInfo(closeChunk=True)

renamePadded()
}}}
----
Python
It's pretty easy using Python's {{{enumerate}}} class and {{{re}}} module:
{{{
# Python code

# Rename the selected objects to have incremented postfix values.
# If any numric postfix exists, remove it and add the new one.

import re
import maya.cmds as mc

startNum = 1
# Reverse our lists, so we rename things from child to parent, so that changing
# a parents name won't confuse the childs name-path:
sel = mc.ls(selection=True, long=True)
sel.reverse()
vals = range(startNum, len(sel)+startNum)
vals.reverse()
for i,s in enumerate(sel):
    leaf = s.split('|')[-1]
    try:
        # Find every occurrence of a number 
        # starting at the end of the string:
        num = re.findall('[0-9]+$', leaf)[0]
        crop = leaf[:-len(num)]
        mc.rename(s, '%s%s'%(crop,vals[i]))
    except IndexError:
        mc.rename(s, '%s%s'%(leaf,vals[i]))
}}}
When it comes to rigging and tools, duplicate node names can be a real pain.  How can you easily rename all the node names in the scene that clash?
{{{
# Python code
import re
import maya.cmds as mc

newNames = []
fail = []

# Get two lists of all names in the scene that are duplicates.
# If there are any dupes, they'll have a pipe in their name:
dupes = [name for name in mc.ls() if "|" in name]
# Get another list with just the leaf names:
dupesLeaf = [name.split("|")[-1] for name in mc.ls() if "|" in name]

for i,e in enumerate(dupes):
    # refresh our list of clashing names:
    leafCompare = [name.split("|")[-1] for name in mc.ls() if "|" in name]
    # if our current leaf node is in our clashing list, rename:
    if dupesLeaf[i] in leafCompare:
        # If nodes are referenced (etc) they can't be renamed
        try:
            endNum = re.findall('[0-9]+$', dupes[i])
            try:
                name = dupesLeaf[i][:-len(endNum[0])]+"#"
            except IndexError:
                name = dupesLeaf[i]+"#"
            newName = mc.rename(dupes[i], name)
            newNames.append([dupes[i], newName])
        except Exception, e:
            # Tell user the the node that was tried to be renamed, and why it failed.
            fail.append([dupes[i], str(e).strip()])

if len(fail):
    print "Some nodes couldn't be renamed:"
    for f in fail:
        print "\t"+"  :  ".join(f)
if len(newNames):
    print "Nodes renamed:"
    for n in newNames:
        print "\t"+"  :  ".join(n)
print "Rename complete:  Renamed %s node(s), and had %s failures, see Script Editor for deatails." %(len(newNames), len(fail)),

}}}
To just print a list of all the dupe node names in the scene:
{{{
# Python code
import maya.cmds as mc

dupeNodes = [name for name in mc.ls() if "|" in name]
for d in dupeNodes:
	print d
}}}
See the command:
{{{
glRender
}}}
For example, render the current sequence as defined by the Hardware Render Globals (presuming the Hardware Render Buffer Window is open):
{{{
glRender -renderSequence hardwareRenderView;
}}}
'{{{hardwareRenderView}}}' is the default name of the {{{glRenderEditor}}} used to display the hardware view.  Even though {{{glRenderEditor}}} appears to be a type of UI 'panel\editor' (like say, {{{modelEditor}}}), in the mel docs it's categorized under 'Rendering'.
----
Also see:
*[[How can I open the Hardware Render Buffer Window?]]
*[[How can I change my Render Settings?]]
The....
[[reorder|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/reorder.html]]
command....
Needed a way to sort all the child nodes under a given transform alphabetically.  Below is one way to do it.  I was making a temp group to store the nodes before an in-order reparent, but thanks to a tip from Roy Nieterau I switched to using the {{{reorder}}} command for an in-place sort (thanks Roy!).

I use ~PyMel since it handles duplicate names in the scene, that dealing with the pure string-names would choke on.
Also note that if any of these nodes are locked it will choke: You'll need to unlock them first.
{{{
import pymel.core as pm

def sortKids(grp):
    """
    Reorder all the transforms under the provided group in alphabetical order.
    """
    sortNodes = sorted(pm.PyNode(grp).getChildren(), key=lambda x: x.nodeName())
    map(lambda x:pm.reorder(x, back=True), sortNodes)
}}}
Give this hierarchy:
{{{
|group2
    |foo5
    |bob
    |asdf
    |foo4
    |apple
}}}
Running this command:
{{{
print sortKids("group2")
}}}
Will sort into this hierarchy as a result:
{{{
|group2
    |apple
    |asdf
    |bob
    |foo4
    |foo5
}}}
{{{parent}}}
{{{
// mel
parent -s -r "shapeNode" "transformNode";
}}}
Here's a function in Python that will do it, and clean up the leftover transform nodes:
{{{
# Python code
import maya.cmds as mc

def parentShapes():
    """
    Select nodes in order:  The last node picked will have all other shape nodes
    parented to it.
    Note:  Should freeze transforms on all nodes before execution!
    """
    sel = mc.ls(selection=True)
    if len(sel) < 2:
        raise Exception("Please select at least two objects with shape nodes")

    source = sel[-1]
    targets = sel[0:-1]
    for t in targets:
        shapes = mc.listRelatives(t, shapes=True)
        if shapes is not None:
            for s in shapes:
                mc.parent(s, source, shape=True, relative=True)
            mc.delete(t)
    mc.select(source)
}}}
----
Also see:
[[How can I parent the instance of a shape node from one object to another?]]
For example, you've just created a sphere, and want to redo that action:
{{{
repeatLast;
}}}
Would then execute:
{{{
CreatePolygonSphere;
polySphere -r 1 -sx 20 -sy 20 -ax 0 1 0 -tx 2 -ch 1;
// Result: pSphere1 polySphere1 //
}}}
*Note, this appears to be an undocumented command, but can be accessed via: 
{{{
help repeatLast;
}}}
Also see:
*[[How do I query what the last command entered was?]]
{{{
// this is the name of the referenced scene you're replacing:
string $refScene = "c:/temp/foo.ma";

// find the reference node for this file:
string $refNode = `file -q -rfn $refScene`;

// name of the scene to replace it with
string $newName = "c:/temp/goo.ma";

file -loadReference $refNode $newName;
}}}
If you're wondering, this was deduced by picking apart this script:
{{{
../MayaXXXX/scripts/others/referenceEditorPanel.mel 
}}}
In particular, the procs {{{referenceEdRenameCB}}} and {{{findRefNodeToReplace}}} 
* Maya has the built-in {{{substitute}}} command, but it only substitutes the //first// instance it encounters. Maya also has the {{{substituteAllString}}} //script//, but it never works like I'd expect. So because of that, we loop over the {{{substitute}}} command until our source no longer contains any of our matching elements.
* For example, in our source data have all matching instances of "Happy" replaced with "Sad":
{{{
string $source= "Happy Happy Joy Joy";
string $match = "Happy";
string $replace = "Sad";
while(`match $match $source` == $match)
    $source = `substitute $match $source $replace`;
print ($source + "\n");
Sad Sad Joy Joy
}}}
You can use a combination of this command and mel procedure:
{{{
ogs -reset;
hardwareRenderingGlobalsRevertToDefault;
}}}
Told the [[ogs|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/Commands/ogs.html]] command can fix weird display bugs that show up.
The {{{hardwareRenderingGlobalsRevertToDefault}}} global proc lives in this script:
{{{
C:\Program Files\Autodesk\Maya2019\scripts\others\createViewport20OptionsUI.mel
}}}
And it will reset all the 'preset' data in the 'Hardware Renderer 2.0 Settings' menu (Found in the Panel's menu: 'Renderer -> Viewport 2.0').
Use Maya's {{{imconvert}}} (Image Magick) executable outside of Maya, or from a system call:
*Example: Convert a texture to a new resolution of 800x600 at the commandline
{{{
imconvert -geometry 800x600 -filter Quadratic in.jpg out.jpg
}}}
----
Via the API:
{{{
# Python code
import maya.OpenMaya as om

imageFile = 'c:/temp/myImage.jpg'
reiszedImage = 'c:/temp/myImageResized.jpg'
mimage = om.MImage()
mimage.readFromFile(imageFile)
mimage.resize(360, 240, True)
mimage.writeToFile(reiszedImage, 'jpg')
}}}
[[MImage|http://download.autodesk.com/us/maya/2011help/API/class_m_image.html]] docs
{{{selectedNodes}}}
or
{{{ls -sl}}}

I've never actually seen anyone use {{{selectedNodes}}}
{{{rotate}}} \ {{{xform}}}
*Example: transformA has rotate order xyz, transformB has rotate order zyx:
{{{
float $rotationVals[] = `xform -q -ws -ro transformA`;
xform -p 1 -roo xyz transformB;
rotate -ws $rotationVals[0] $rotationVals[1] $rotationVals[2] transformB;
xform -p 1 -roo zyx transformB;
}}}
----
Also see:
*[[How can I match the worldspace transformations of two objects?]]
{{{rotate}}} \ {{{xform}}}
*Example: Find the worldspace rotation of one object, and assign it to another. Either of these objects can be children of a hierarchy:
{{{
float $rotationVals[] = `xform -q -ws -ro transformA`;
rotate -ws $rotationVals[0] $rotationVals[1] $rotationVals[2] transformB;
}}}
*This will open in a new command prompt:
{{{
>> maya -prompt
}}}
*This will run in the same command prompt as it was executed in:
{{{
>> mayaBatch -prompt
}}}

{{{system}}}
*Example, open notepad:
{{{
system ("start notepad");
}}}
*You don't have to use "start", but if you don't, as long as the system function is running, you'll be locked out of using Maya.
Modified version from [[this post|http://tech-artists.org/forum/showthread.php?4372-Extracting-Maya-Icons]]:
{{{
import pymel.core as pm
for item in pm.resourceManager(nameFilter="*.png"):
    pm.resourceManager(saveAs=(item, "C:/temp/maya/icons/{0}".format(item)))
}}}
Had an animator approach me with a problem:  He wanted a tool that would look at ALL the keyframes in the scene, and based on a user-defined start and end range, scale all the keys in the range, and offset all the keys after the range based on the scale value.

The code example does that.  It will also insert keys (maintaining curve shape) on all objects at the oldRange frames, to do proper scaling
{{{
#Python code
import maya.cmds as mc

# define the old and new range:
oldRange = [5, 10]
newRange = [5, 20]

# figure out how much to offset keys after the oldRange
offset = newRange[1] - oldRange[1]

# stamp down keys based on oldRange
curves = mc.ls(type="animCurve")
mc.select(curves, replace=True)
mc.setKeyframe(insert=True, time=oldRange)

# move all keys after the end of the oldRange
mc.selectKey(time=(str(oldRange[1]+1)+':',)) 
mc.keyframe(edit=True, relative=True, timeChange=offset)

# now scale all keys in the oldRange to match the newRange
mc.select(curves, replace=True)
mc.selectKey(time=(str(oldRange[0])+":"+str(oldRange[1]),))
mc.scaleKey(curves, 
            time=(oldRange[0],oldRange[1]), 
            newStartTime=newRange[0], 
            newEndTime=newRange[1])
}}}
{{{
# Search scriptJobs
import pymel.core as pm
pm.promptDialog(title="Search scriptJobs", message="Search for:", button="Search!")
text = pm.promptDialog(query=True, text=True)
if text:
    sjs = pm.scriptJob(listJobs=True)
    for sj in sjs:
        if text in sj:
            print sj.strip()         
}}}
This is actually all Python code, but since it has to do with Maya, I'll include it here ;)
I've read that using the {{{mmap.mmap}}} object is faster than converting a file to a string for searching:  Based on experimentation, this appears to be true.

Sometimes I'll need to search a whole tree of Maya files (ascii files) for certain data. Sometimes that data is as simple as a short string.   This method is way faster than batching the files in Maya one by one and running commands to find the results.  
For example, I want to find which Maya files have a joint named "FOO" in them, in my tree:
{{{
import os
import mmap

startDir = r"C:\path\to\my\files"
searchString = "FOO"

print "Starting Search:"
# Get a list of all the ma files under the root dir:
files = []
for dirpath, dirnames, filenames in os.walk(startDir):
	for fil in [f for f in filenames if f.endswith(".ma")]:
		fpath = "%s/%s"%(dirpath, fil)
		files.append(fpath)
# I split this into a separate loop, 
# so I can print the above results before the processing starts.

found = []
for fpath in files:
	with open(fpath) as f:
		s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
		if s.find(searchString) != -1:
			found.append(fpath)
for f in found:
	print f
print "Search Done"
}}}

{{{nurbsSelect}}}
Recently had to write a tool that would select all mesh bound to the same hierarchy:  Say you have a human character made out of multiple meshes.  The tool would allow the user to pick a a single mesh (or multiple meshes) and the tool would select all other mesh skinned to the same hierarchy:
{{{
# Python code
import maya.cmds as mc

# Get out selected mesh
sel = mc.ls(selection=True, long=True)

# Get all joint influences on selected:
allInfs = []
for s in sel:
    infs = mc.skinCluster(s, query=True, influence=True)
    for i in infs:
        if i not in allInfs:
            longName = mc.ls(i, long=True)[0]
            allInfs.append(longName)

# Find all the unique root joints:
roots = []
for ai in allInfs:
    foundRoot = False
    split = ai.split('|')
    # Remove empty leading strings:
    split = [s for s in split if s]
    if not foundRoot:
        for i,e in enumerate(split):
            node = '|'.join(split[0:i+1])
            if mc.objectType(node) == 'joint':
                if node not in roots:
                    roots.append(node)
                    foundRoot = True
                    break
                else:
                    break

# Find the complete hierarchy tree under our roots:
allJoints = mc.listRelatives(roots, allDescendents=True, type='joint')

# Find all skinCluster nodes these joints influence:
skinClusters = []
for ai in allJoints:
    sc = mc.listConnections(ai, source=False, destination=True, type='skinCluster')
    if sc:
        sc = list(set(sc))
        for c in sc:
            if c not in skinClusters:
                skinClusters.append(c)

# Find all the mesh shape nodes touching our skinClusters:
mesh = []
for sc in skinClusters:
    geo = mc.skinCluster(sc, query=True, geometry=True)
    for g in geo:
        if g not in mesh:
            mesh.append(g)

# Get all shape transform nodes:
transforms = []
for m in mesh:
    parent = mc.listRelatives(m, parent=True)[0]
    transforms.append(parent)

# Select!
mc.select(transforms)
}}}
{{{
import pymel.core as pm
skinned = [m.firstParent() for m in pm.ls(type='mesh', noIntermediate=True) if m.inputs(type='skinCluster')]
if skinned:
    pm.select(skinned)
    pm.displayInfo("Select %s skinned mesh"%len(skinned))
else:
    pm.displayWarning("No skinned mesh in scene to select")
}}}
This is presuming the {{{skinCluster}}} node is the first input to the mesh in the deformer stack.  
{{{
import pymel.core as pm

meshes = pm.ls(type="mesh", noIntermediate=True)
unskinned = []
for mesh in meshes:
    sc = mesh.inputs(type=pm.nt.SkinCluster)
    if not sc:
        unskinned.append(mesh.firstParent())
if unskinned:
    pm.select(unskinned)
    pm.displayInfo("Selected %s unskinned mesh"%len(unskinned))
else:
    pm.displayInfo("All mesh skinned")
}}}
{{{
import maya.cmds as mc

def getInfluences(sel=None):
    """
    Return influences for the selected mesh.  If mesh names are passed in instead,
    use them instead of selection.
    """
    if not sel:
        sel = mc.ls(selection=True)
    else:
        if type(sel) is not type ([]):
            sel = [sel]        
    if not len(sel):
        raise Exception("Please select a node, or pass in a list of nodes")
    mesh = mc.listRelatives(sel, shapes=True, noIntermediate=True)
    skinClusters = mc.listConnections(mesh, source=True, destination=False, type='skinCluster')
    influences = []
    if skinClusters:
        for sc in skinClusters:
            connections = mc.listConnections('%s.matrix'%sc)
            if connections:
                influences.extend(connections)
                
        influences = sorted(list(set(influences)))
    return influences
}}}
{{{
infs = getInfluences() 
if len(infs):
    for i in infs:
        print i
    mc.select(infs)
}}}
In Maya's prefs, under Settings -> Timeline, there are options to set the 'Playback Speed' and 'Max Playback Speed'.  My maya had a habit of forgetting this stuff.  I wanted it to play every frame of the animation, but lock it to 30fps.

There are two steps to this:  Update {{{optionVars}}} so the settings will persist between Maya sessions, and updating the values for the current Maya session.  I figured out the optionVars by hunting around my {{{userPrefs.mel}}} file.
I added the below code to my {{{userSetup.mel}}} file:
{{{
optionVar -fv "timeSliderPlaySpeed" 0;
optionVar -fv "timeSliderMaxPlaySpeed" 1;
playbackOptions -playbackSpeed 0;
playbackOptions -maxPlaybackSpeed 1;
}}}
As you can see, you're not setting the actual framerate values:  The values are indexed, so you need to choose the correct index for the option that you're after.  The Prefs UI drop-downs show you the options you can choose from.
{{{
windowPref -w # -h # windowName;
}}}
*Note: This should be entered after the "window" command when making the UI.
Presuming that the joints the mesh are bound to have nothing connected to their transform values, this code should work:
{{{
string $dagPose[] = `ls -type "dagPose"`;
for($i=0;$i<size($dagPose);$i++){
	select -r  $dagPose[$i];	
	dagPose -restore;
	}
}}}
----
While I haven't tested it yet, I saw this post to help rest the bind pose after changes have been made to the asset:

1)  Select the root joint of the skel, and save out your "bind pose":
{{{
select -hierarchy;
dagPose -save -selection -name mypose;
}}}
2)  Later, if any changes have happened to the asset, you can go back to that bind pose:
{{{
dagPose -restore mypose;
}}}
----
Also see:
*[[How can I set the selected meshes to their bind pose?]]
Here's the mel directly from the ma file:
{{{
setAttr "myNode.uocol" yes;  // Long: .useOutlinerColor
setAttr "myNode.oclr" -type "float3" 1 0 0 ;  // Long: .outlinerColor
}}}
I can find no docs on either the {{{uocol}}} or {{{oclr}}} attrs on the {{{transform}}} node, or any superclass.  Seems almost hidden?
{{{
import maya.cmds as mc

def setNodeRgbColor():
    """
    Set the RGB wireframe color on the selected nodes.
    Behind the scenes, the color command is setting these attrs in the ma file:
    setAttr ".wireColorRGB" -type "float3" 0 0 1 ;
    setAttr ".useObjectColor" 2; // 1 = by index, 2 = rgb
    """
    sel = mc.ls(selection=True)
    if not sel:
        print "Nothing is selected: Can't set color"
        return
    mc.colorEditor()
    if not mc.colorEditor(query=True, result=True):
        return
        
    hsv = mc.colorEditor(query=True, hsvValue=True)
    rgb = mc.colorEditor(query=True, rgbValue=True)
    print "HSV", [round(val, 2) for val in hsv]
    print "RGB 0-1.0", [round(val, 2) for val in rgb]
    print "RGB 0-255", [int(val*255.0) for val in rgb]
    for node in sel:
        mc.color(node, rgbColor=rgb)
        
setNodeRgbColor() 
}}}
----
Also see:
*[[Understanding Maya's user-defined and override colors]]
*Open the ''UV Texture Editor''
*Image -> Use Image Ratio -> Check ON
By default, the UV 0->1 range is a square.  But what if your texture is 2:1 (landscape), and you want to see the UV's in the same landscape aspect ratio, rather than a square?  Use the above option, and the UV Texture Editor will scale its grid so that the aspect-ratio of the UV's matches that of the texture.
The mel command is:
{{{
polyNormalPerVertex
}}}
However, it only works on one vert at a time.  Furthermore, if you call to it a lot (if working on a thousand normals, you'll call to it a thousand times), it can really slow down Maya, and eat up all your memory (based on some tests, nearly a meg per call, that's crazy!).

Here's a way you can query the normals on the selected items, and then set them again based on the stored values.  It works by directly accessing the normal attributes on the polygonal mesh, rather than using the {{{polyNormalPerVertex}}} command at all.
{{{
// pick a polygonal object, or pick some components.  Then define them:
	string $sel[] = `ls -sl`;

// convert everything to vertex-faces
    select -r `polyListComponentConversion "-tvf" $sel`;
    string $vertFaces[] =  `ls -fl -sl`;

// define our list that we'll later fill with all of our normal info:
    string $cmdList[]; clear $cmdList;
	
// for each vertex face, store a command that will reapply it's normal values later.
    for($i=0;$i<size($vertFaces);$i++)
        {
    // find the shape node of this object:
        string $buffer[];
        tokenize $vertFaces[$i] ".[]" $buffer;
        string $shape = $buffer[0];
    // get the face and vert ID's
        int $face = $buffer[size($buffer)-1];
        int $vert = $buffer[size($buffer)-2];
    // Get the normal value for this particualr vertex-face.  We actually use polyNormalPerVertex here
    // because, if we query the vert normal with getAttr (using a syntax similar to the below $cmdLis)
    // it always returns back these values for all three xyz vert face normals:  100000002004087730000
    // The mesh docs say that these attrs have a default value of 1e20, hmm.....
    // However, once they've been set by the user using the setAttr command, you CAN use getAttr on 
    // them to pull the correct vals.  So weird!
        vector $normalVec = `polyNormalPerVertex -q -xyz $vertFaces[$i]`;
    // and build the command to later re-set these values:
        $cmdList[$i] = ("setAttr " + $shape 
            + ".npvx.vn[" + $vert + "].vfnl[" + $face +"].fnxy " 
            + ($normalVec.x) + " " + ($normalVec.y) + " " + ($normalVec.z) +";");
        }
}}}
Print our findings:
{{{
print $cmdList;
}}}
Now, go to your objects, and screw up all their normals.
Then re-set all the normals to the stored values:
{{{
for($i=0;$i<size($cmdList);$i++)  
    eval($cmdList[$i]);
}}}
After you run this, the normals should be set back again.  Minimal memory usage, and much faster than calling to {{{polyNormalPerVertex}}} mutiple times.
{{{
# Python code
import maya.cmds as mc
animCurves = mc.ls(type='animCurve')
first = mc.findKeyframe(animCurves, which='first')
last = mc.findKeyframe(animCurves, which='last')
mc.playbackOptions(min=first, max=last)
}}}
@@Note@@ : I've seen bugs where //referenced// curves can cause it to always return 0,0 : You'll need to filter out any referenced curves first.
{{{
selectKey -time ":";
float $allKeys[] = sort(`keyframe -q -sl`);
playbackOptions -min $allKeys[0] -max $allKeys[(size($allKeys) -1)];
}}}
----
Also see:
*[[How can I find the min and max keyframe values for all objects in the scene?]]
*[[How can I find the min and max keyframe range for the provided objects?]]
Around Maya 2008 (maybe a version or so before?) when you smooth bound a mesh, there was a new option called "Allow Multiple bind poses".  This is supposed to bring some functionality benefits, but it also breaks some of Maya's own tools:  Notably, if a skeleton is bound to more than one mesh with this option, it will create a bind pose {{{(dagPose}}} node) for each mesh.  But Maya's own UI command {{{Skin -> Go To Bind Pose}}} is expecting only a //single// bind pose... and it will fail.

Furthermore, Maya expects a mesh to be picked when using its tool.  But often I want to pick a joint instead.  The below code addresses both:  It will track down the {{{dagPose}}} node(s) for each selected mesh\joint, and 'restore' them to their bind pose:
{{{
# Python code
import maya.cmds as mc

# Get a list of everything selected
# It is expecting bound mesh\joints or a combination of the two...
sel = mc.ls(selection=True)

dagPose = []
for s in sel:
    if mc.objectType(s) == 'joint':
        # If joints are picked:
        poses = list(set(mc.listConnections(s, source=False, destination=True, type='dagPose')))
        dagPose.extend(poses)
    else:
        # If mesh is picked, find their shape nodes:
        shapes = mc.listRelatives(sel, shapes=True, noIntermediate=True)
        # get all incoming skinClusters connected to the shapes
        skinCluster = mc.listConnections(shapes, source=True, destination=False, type="skinCluster")
        # get all incoming dagPoses connected to the skinClusters.  Remove duplicates via 'set'
        poses = set(mc.listConnections(skinCluster , source=True, destination=False, type="dagPose"))
        dagPose.extend(poses)

# remove any dupes:
dagPose = list(set(dagPose))
# 'restore' each dagPose:
for d in dagPose:
    mc.dagPose(d, restore=True)

print "Bind Pose restored on these nodes: " + ", ".join(sorted(dagPose))
}}}
There's no error detection here, so if you don't have anything picked it will error, or if there are unbound meshes it will error, but it's a start ;)
----
Also see:
*[[How can I set all meshs in the scene to their bind pose?]]
!!!Via the HUD:
This is modified copy of the mel proc {{{setCurrentFrameVisibility()}}} living in {{{initHUDScripts.mel}}}, that simply forces it on:
{{{
# Python code
import maya.cmds as mc

def setFrameCounterOn():
    if mc.headsUpDisplay('HUDCurrentFrame', exists=True):
        mc.headsUpDisplay('HUDCurrentFrame', edit=True, visible=True)
    mc.menuItem('currentFrameItem', edit=True, checkBox=True)
    mc.optionVar(intValue=["currentFrameVisibility", 1])
}}}
I like to map hotkeys to the display of the Channel Box, Attribute Editor, and Tool Settings to force their visibility on, and docked.  This is what I came up with:

Attribute Editor:
{{{
ToggleAttributeEditor;
string $component = getUIComponentDockControl("Attribute Editor", false);
dockControl -edit -visible true $component;
}}}
Channel Box:
{{{
ToggleChannelsLayers;
string $component = getUIComponentDockControl("Channel Box / Layer Editor", false);
dockControl -edit -visible true $component;
}}}
Tool Settings:
{{{
ToggleToolSettings;
string $component = getUIComponentDockControl("Tool Settings", false);
dockControl -edit -visible true $component;
}}}

All the 'Toggle' commands are actually Run Time Commands:
{{{
toggleUIComponentVisibility "Attribute Editor"; updateMainWindowComponentState()
}}}
{{{
if (`isUIComponentVisible("Channel Box / Layer Editor")`) {
	toggleUIComponentVisibility("Channel Box / Layer Editor");
} else {
	setChannelsLayersVisible( true );
}
; updateMainWindowComponentState()
}}}
{{{
if (`isUIComponentVisible("Tool Settings")`) {
	toggleUIComponentVisibility("Tool Settings");
} else {
	toolPropertyWindow -inMainWindow true;
}
; updateMainWindowComponentState()
}}}
I run into instances where for some reason, mesh will stop showing it's vert colors.  There's a {{{displayColors}}} attr on the mesh shape that controls this.
{{{
import maya.cmds as mc
mesh = mc.ls(type='mesh')
for m in mesh:
    mc.setAttr("%s.displayColors"%m, True)
}}}
Sometimes you'll find mesh verts that are stretchy/spikey.  How can you find which ones those are based on their weights, and smooth them.

Maya has some built-in tools for this:
*  'Skin -> Smooth Skin Weights' : This calls to the {{{skinCluster}}} command, and does a great job of smoothing. It both has a tolerance (the UI uses values from 0.01 to 100.0, the {{{skinCluster}}} command uses values 0.0001 -> 1.0), and lets you preview-select the verts it will do the work on
** The LOWER the 'Required Weight Difference', the MORE weights will be smoothed.
** The HIGHER the 'Required Weight Difference', the FEWER weights will be smoothed.
* 'Skin -> Hammer Skin Weights' : This is simply a preset wrapper around 'Smooth Skin Weights', with the 'Required Weight Difference' set to 50%.
** If 'Hammer Skin Weights' isn't giving you the results you want, use 'Smooth Skin Weights', and lower the 'Required Weight Difference'.
This is also the code that the Maya 'Hammer Skin Weights' tool ends up calling to via:
{{{
C:\Program Files\Autodesk\Maya2022\scripts\others\weightHammerVerts.mel
}}}
which really ends up calling to:
{{{
C:\Program Files\Autodesk\Maya2022\scripts\others\doSmoothSkinWeightsArgList.mel
}}}
Which ultimately is just a preset wrapper around the {{{skinCluster}}} command code that 'Smooth Skin Weights' uses, that looks like next.
Note, even though we pass in a specific skinCluster and mesh names below, the Maya {{{skinCluster}}} command will filter by verts if they are selected when the tool is ran.  So if you want to only evaluate a subset of verts:  Select them first.  Which is a little gross, would be nice to pass in a list of verts instead, but such are things.
{{{
# Smooth weights
# Must select them :(
mc.select([list of verts])
mc.skinCluster("mySkinClusterName", edit=True, smoothWeights=.5,
               smoothWeightsMaxIterations=5, obeyMaxInfluences=True)
}}}
If you want to select the verts that should be smoothed, you can do it this way instead:
{{{
# Select verts that should be smoothed:
mesh = "myMeshName"
skinCluster = "mySkinClusterName
# smoothWeights is a secret query arg:  Maya's docs don't list this as queryable, edit only.
smoothWeights = 0.5
vertsToSmooth = mc.skinCluster(skinCluster, query=True, smoothWeights=smoothWeights)
mc.select(clear=True)
if vertsToSmooth:
    for vert in vertsToSmooth:
        mc.select('%s.vtx[%s]'%(mesh, vert), add=True)
}}}
So, armed with the above code, if you set {{{smoothWeights}}} to these values:
* {{{.1}}} : Select verts who's weights are more than 10% >= their neighbors'. : Probably quite a few.
* {{{.5}}} : Select verts who's weights are more than 50%  >= their neighbors'.
* {{{.9}}} : Select verts whos weights are more than 90% >= their neighbors. Probably not very many.
You can test these results easily by:
* Making a plane in Maya, subdivided at least 5 x 5
* Skin it to a root joint
* Add a child joint as an influence.
* Start selecting verts on the plane, each one separated by a vert : Set their weights relative to the second joitn based on intervals of your choosing: 1.0, ,9, .8, etc...
* Translate the child joint up, seeing the spikes emerge.
* Run the above code on it, and see what it picks.
{{{
import maya.cmds as mc

def locatorOnSelected():
    # Create locators positioned at the selected nodes.
    sel = mc.ls(selection=True, long=True)
    locs = []
    for node in sel:
        name = node.split(':')[-1].split('|')[-1]
        loc = mc.spaceLocator(name="loc_%s"%name)[0]
        mList = mc.getAttr("%s.worldMatrix"%node)
        mc.xform(loc, matrix=mList, worldSpace=True)
        mc.setAttr('%s.localScale'%loc, 5, 5, 5)
        mc.setAttr('%s.scale'%loc, 1,1,1)
        locs.append(loc)
    mc.select(locs)
}}}

I started to write some code for this and... there's already a command:  [[snapKey|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/snapKey.html]]
Maya's tool for this is a bunch of mel wrapper code.  If you want to proceduralize it, you can use this hacky way below:
It will snap all mesh to the last picked, based on the same 3 verts that exist on all mesh.  It does this by selecting the 3 verts in order, on pairs of mesh, then running the mel that expects the selection.
{{{
# Snap mesh by 3 verts!

import pymel.core as pm
# All mesh must have the same vert count.
# Define 3 verts here that are the same on all mesh.  This will snap all mesh
# selected to the last mesh picked.
snapVerts = [94,95,114]

sel = pm.ls(selection=True)
mesh = pm.listRelatives(pm.ls(selection=True), shapes=True, type='mesh', noIntermediate=True)
targMesh = mesh.pop(-1)

for m in mesh:
    pm.select([m.vtx[i] for i in snapVerts])
    pm.select([targMesh.vtx[i] for i in snapVerts], add=True)
    pm.mel.eval("snap3PointsTo3Points(0)")
pm.select(sel)
}}}
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=(":",), ...)
}}}
Since... I seem to always forget this.
* Edit Mesh -> Detach
Which executes the {{{DetachComponent}}} {{{runTimeCommand}}}.
Which calls to the mel {{{global proc performDetachComponents()}}}, which lives in:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\performDetachComponents.mel
}}}
* If vert, uses [[polySplitVertex|http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/polySplitVertex.html]]
* If edge or face, uses [[polySplitEdge|http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/polySplitEdge.html]]
Note, if face, it converts to edges via the mel {{{global proc convertToSelectionBorder}}}, which lives here:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\convertToSelectionBorder.mel
}}}
Which leans heavily on [[polyListComponentConversion|http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/polyListComponentConversion.html]] and [[selectType|http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/selectType.html]]
I first noticed this in Maya 2008.  I'm not yet sure if the below solution fixes it for 2008, I'm told it does for 2009.  If you have an Nvidia GeForce card, this may do the trick.
Set this in your {{{Maya.env}}} file
{{{
MAYA_GEFORCE_SKIP_OVERLAY = 1
}}}
Found the info on this page (lots of other maya.env stuff in there):
http://www.toxik.sk/?p=22
{{{optionVar}}}
*Example:
{{{
optionVar -iv "myVariable" 4;
// Result: 4 //
}}}
** After Maya has been restarted, you can query that {{{optionVar}}}.  This is how Maya does it's bulk of preset saving.
I've wanted a way to be able to store metadata to Maya files that could be read without having to actually open those files in Maya. I started a thread here, that got a working answer:
https://groups.google.com/d/msg/python_inside_maya/IzHW1PTEhMk/6rr0NcHeBwAJ

First, you'll need this:
https://github.com/mottosso/maya-scenefile-parser
Unfortunately it appears to have become non-binary compatible around Maya 2019.1 :(

And here's an example use-case by overriding the {{{on_file_info}}} method:
{{{
from maya_scenefile_parser.binary import MayaBinaryParser

fname =  "C:/temp/maya/metaTest.mb"

class Parser(MayaBinaryParser):
    def on_file_info(self, key, value):
        print("%s = %s" % (key, value))

with open(fname, "rb") as f:
    parser = Parser(f)
    parser.parse()
}}}
And when ran, will print something like:
{{{
application = maya
product = Maya 2016
version = 2016
cutIdentifier = 201610141544-1004862
osv = Microsoft Windows 8 Enterprise Edition, 64-bit  (Build 9200)\n
}}}
Which is all the data set by Maya's {{{fileInfo}}} command.
You can easily embed your own {{{fileInfo}}} commands into a file before save by:
{{{
import maya.cmds as mc

mc.fileInfo("myvalue", "mykey")
}}}
----
Also see:
* [[How can I parse a mayaBinary file?]]
* [[Info on the Maya Binary file format]]
There is the {{{stringToStringArray}}} command, but this only works if you have a 'separation character':  Meaning, it works good on string like this:
{{{
string $line = "my words";
string $array[] = `stringToStringArray $line " "`;
print $array;
// my
// words
}}}
However, {{{stringToStringArray}}} //won't// work if there is no separation character.  So how can you convert a string to a string array without using that command?
{{{
string $line = "asdf";
string $array[];
for($i=0;$i<size($matcher);$i++)
    $array[$i] = `substring $matcher ($i+1) ($i+1)`;
print $array;
// a
// s
// d
// f
}}}
You can flip this {{{optionVar}}} switch:
{{{
import maya.cmds as mc
mc.optionVar( intValue=("fileExecuteSN", 0) )
}}}
In Windows, on your Maya "shortcut" icon -> RMB -> Properties.
In the 'Shortcut' tab, in the 'Target' line, that probably looks something like this:
{{{
"C:\Program Files\Autodesk\Maya2018\bin\maya.exe"
}}}
Add {{{-hideConsole}}} to the end:
{{{
"C:\Program Files\Autodesk\Maya2018\bin\maya.exe" -hideConsole
}}}
This little gem found [[here|https://forums.autodesk.com/t5/maya-ideas/output-window-gets-in-the-way/idc-p/8339545]].
If you want to suppress Maya from execuring {{{userSetup.mel}}} & {{{userSetup.py}}} on startup:
And this env var:
{{{
MAYA_SKIP_USER_SETUP = 1
}}}
Optionally, if you launch {{{mayapy.exe}}}, you can run it like this before you bring in Maya
{{{
import os
os.environ['MAYA_SKIP_USER_SETUP'] = "1"

import maya.standalone
maya.standalone.initialize('python')
}}}



Sometimes the modelers get the left\right sides of an asset switched.  This code will swap all _L_ & _R_ characters on all transform node names in the scene.
Code takes advantage of ~PyMel's ~PyNode type:  Even though the string names change, nodes are still easy to access independent from them.
{{{
import pymel.core as pm

allTf = pm.ls(type='transform')
oldL = []
oldR = []

for tf in allTf:
    if '_L_' in tf.nodeName():
        oldL.append(tf)
    elif '_R_' in tf.nodeName():
        oldR.append(tf)

pm.undoInfo(openChunk=True)
try:
    # Rename L to R, but add an underscore to avoid possible name clash
    for l in oldL:
        l.rename('_%s'%l.nodeName().replace("_L_", "_R_"))
    # Rename R to L
    for r in oldR:
        r.rename(r.nodeName().replace("_R_", "_L_"))
    # Remove underscores:
    for ol in oldL:
        ol.rename(ol.nodeName()[1:])
finally:
    pm.undoInfo(closeChunk=True)
}}}
{{{ctxEditMode}}} or {{{EnterEditMode}}}
*Notes: {{{ctxEditMode}}} is the actual command. {{{EnterEditMode}}} is actually a runTimeCommand that simply calls {{{ctxEditMode}}}. They both act as toggles.
{{{
import maya.cmds as mc

def getHasBeenFrozen(node):
    """
    You can detect if a node has been frozen if the rotatePivot attr has any
    non-zero value.

    Parameters:
    node : string : The transform name to query.

    Return : bool : True if it has been frozen or it's rotatePivot has been moved,
        False if not.
    """
    if any(mc.getAttr("%s.rotatePivot"%node)[0]):
        return True
    else:
        return False
}}}
----
Also see:
* [[How can I 'unfreeze' a node?]]
* [[What happens behind the scenes when you 'freeze transforms'?]]
{{{setFocus}}}
*Note: Maya's four main modelPanels are called "modelPanel1" - 4.
{{{timer}}}
{{{timerX}}}
Python:
{{{
import maya.cmds as mc
modelPanel = mc.playblast(activeEditor=True)
value = not mc.modelEditor(modelPanel, query=True, jointXray=True)
mc.modelEditor(modelPanel, edit=True, jointXray=value)
}}}
Mel:
{{{
string $modelPanel = `playblast -activeEditor`;
int $state = `modelEditor -q -jointXray $modelPanel`;
int $value = abs(1-$state);
modelEditor -edit -jointXray $value $modelPanel;
}}}
~PyMel
{{{
import pymel.core as pm
joints = pm.ls(type='joint')
val = not joints[0].displayLocalAxis.get()
for j in joints:
    j.displayLocalAxis.set(val)
}}}
Python
{{{
import maya.cmds as mc
joints = mc.ls(type='joint')
val = not mc.getAttr("%s.displayLocalAxis"%joints[0])
for j in joints:
    mc.setAttr("%s.displayLocalAxis"%j, val)
}}}
Mel:
{{{
string $joints[] = `ls -type joint`;
int $v = `getAttr ($joints[0]+".displayLocalAxis")`;
int $val = abs(1-$v);  // can't do abs on previous line, weird.
for($i=0;$i<size($joints);$i++){
    setAttr ($joints[$i]+".displayLocalAxis") $val;
}
}}}
Animator requested a way that they could have a hotkey that would toggle on and off the {{{TimeDragger}}} context.  Said context allows the user to scrub the timeline by clicking the mouse anywhere on the screen.
{{{
global proc toggleTimeDragger(){
	global string $gPrevCtx;
	string $currentCtx = `currentCtx`;
	if($currentCtx != "TimeDragger"){
		setToolTo TimeDragger;
		$gPrevCtx = $currentCtx;
	}
	else{
		setToolTo $gPrevCtx;
	}
}
toggleTimeDragger();
}}}
Put that whole sucker in a hotkey, and you're good to go.
A couple different solutions I've come up with:
!!!{{{autoPlace}}} running in a Python Thread:
The {{{autoPlace}}} command can return the mouse's current ''3d worldspace position'' (always in cm).  If you execute that in a thread in Maya, it can execute constantly in the scene in the background, reporting the position, and executing any other code you need at the same time.
{{{
# loopMousePos.py

import threading

import maya.cmds as mc
import maya.utils as mu

running = False

def mouseEvent(*args):
    """
    The code to evaluate in the thread.  In this case, it prints the mouse position.
    """
    # This is printing cm values no matter your working unit.  You may need to
    # do some conversion...
    print mc.autoPlace(useMouse=True)

def looper(*args):
    """
    Wrapper for the code that should be evaluated:  This code evaulates in the
    Python thread, but it executes the results in Maya.
    """
    while True:
        # When running in a Python Thread, If you don't evaluate you Maya code 
        # via this function, Maya *will* crash...
        mu.executeInMainThreadWithResult(mouseEvent)

def main():
    """
    Create and start the Python Thread.  Main point of execution.
    """
    global running
    if not running:
        threading.Thread(target=looper).start()
        running = True
}}}
!!!Query a {{{draggerContext}}}'s 'dragPoint':
You can create a custom {{{draggerContext}}} so that when you enter it and ~LMB-drag around the view, it will print the ''2d screen-space'' position of the mouse, and execute any other code you need:
{{{
# mousePosCtx.py

import maya.cmds as mc

CTX = 'mousePosCtx'

def mousePosDragFunc(*args):
    """
    Function called on drag.
    """
    dragPosition = mc.draggerContext(CTX, query=True, dragPoint=True)
    print dragPosition

def main():
    """ 
    Create and enter the conext.
    """
    if mc.contextInfo(CTX, query=True, exists=True):
        mc.deleteUI(CTX, toolContext=True)

    # Make the context:
    mc.draggerContext(CTX, cursor='hand', space='screen', dragCommand=mousePosDragFunc,
                      projection='viewPlane');

    # Set the tool to the context
    # Results can be observed by dragging mouse around main window
    mc.setToolTo(CTX)
}}}
The {{{Lighting/Shading -> Transfer Maps}}} tool.  Noted here since I always forget where this lives.
Starting in Maya 2012, you can use the [[transferShadingSets|http://download.autodesk.com/global/docs/maya2012/en_us/Commands/transferShadingSets.html]] command.  See docs for an example.
The {{{moveVertexAlongDirection}}} command.  
Supports:
* Arbitrary direction (vector)
* Along the normal
* Along U or V.
I needed a way to know whenever a keyframe was set in Maya:  There didn't seem to be a way to do this via a scriptJob, but the API to the rescue.

The below callback will print the names of the animCurve nodes modified whenever a keyframe is set.
However, even though the incoming argument to the {{{editedCurves}}} parameter is an {{{MObjectArray}}}, it only has a single item (based on testing), meaning if you have 10 nodes selected, this code will be executed 10 separate times using {{{addAnimCurveEditedCallback}}}:
{{{
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

def animFunc(editedCurves, *args):
    """
    editedCurves : MObjectArray
    """
    for i in range(editedCurves.length()):
        mFnDependNode = om.MFnDependencyNode(editedCurves[i])
        nodeName = mFnDependNode.name()
        print i,nodeName

cid = oma.MAnimMessage.addAnimCurveEditedCallback(animFunc)
}}}
{{{
# Delete the callback later:
om.MMessage.removeCallback(cid)
}}}
----
If you instead want a callback that iterates over all the channels keyed at once, you can use {{{addAnimKeyframeEditedCallback}}}:
{{{
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma

def animFunc(editedKeys, *args):
    """
    editedKeys : MObjectArray
    """
    for i in range(editedKeys.length()):
        mFnKeyframeDelta  = oma.MFnKeyframeDelta(editedKeys[i])
        paramCurve = mFnKeyframeDelta.paramCurve()
        mFnDependNode = om.MFnDependencyNode(paramCurve)
        nodeName = mFnDependNode.name()
        print i,nodeName

cid = oma.MAnimMessage.addAnimKeyframeEditedCallback(animFunc)
}}}
Delete the callback later
{{{
om.MMessage.removeCallback(cid)
}}}
There is no {{{scriptJob}}} event for 'scene save' detection, so how can you author your own events to trigger when saving the scene?  Two ways I've found, via the API, and via script overriding:
!!!API:
Via the {{{OpenMaya}}} API's {{{MSceneMessage}}} object, you can build many different kinds of callbacks for various events, including file open, close, save, etc.  Find notes on how to set his up here:
[[API: How can I author callbacks for Maya events?]]
It should be noted that in Maya 2017, they split their {{{kBeforeSave}}} into it plus, {{{kBeforeSaveCheck}}}, which is also called to by a new method.
!!!Overriding the menu commands:
You can override the behavior of the //menu// 'File -> Save Scene' pretty easily.  This also works for 'File -> Save Scene [options]'.  However, I highly recomend using the API version above, since its non-destructive.

That menu item executes a {{{runTimeCommand}}} called {{{SaveScene}}} which calls to these procedures:
{{{
// SaveScene runTimeCommand
checkForUnknownNodes(); 
FileMenu_SaveItem;
}}}
You can override that {{{runTimeCommand}}} with your own global proc pretty easily:
{{{
global proc SaveScene()
    {
    print "Put code here to execute before save.\n";
    checkForUnknownNodes();
    FileMenu_SaveItem;
    print "Put code here to execute after save.\n";
    }
}}}
When you then execute File -> Save, you'll see something like this:
{{{
Put code here to execute before save.
file -save;
// Result: C:/Users/epavey/Documents/maya2010goo/badSetTest.ma // 
Put code here to execute after save.
}}}
----
'File -> Save Scene As' (and 'Save Scene As [options]') has a similar {{{runTimeCommand}}} you can override called {{{SaveSceneAs}}}
{{{
checkForUnknownNodes(); 
projectViewer SaveAs;
}}}
Overriding it is just as easy:
{{{
global proc SaveSceneAs()
    {
    print "Put code here to execute before save-as.\n";
    checkForUnknownNodes(); 
    projectViewer SaveAs;
    print "Put code here to execute after save-as.\n";
    }
}}}
Simple example of reading in a text file to Maya, and turning its results into a string array.  There is no error checking here:  You'd want to first confirm that the file actually exists on disk before you try and open it
In the process it will strip off any return chars.
{{{
global proc string[] returnFileAsArray(string $filePath){
    int $fid = `fopen $filePath "r"`;
    string $lines[] = {};
    string $line = `fgetline $fid`;
    $lines[0] = strip($line);
    while ( size($line) > 0 ){
        $line = `fgetline $fid`;
        $lines[size($lines)] = strip($line);
    }
    fclose $fid;
    return $lines;
}
}}}
Also see:
*[[How can I write text to my own file?]]
Often times when engaging in such activities as baking animation curves, you can speed up Maya's performance by replacing the current 3d view ({{{modelPanel}}}) with a non-refreshing item, like the Outliner.  However, there is a way to get even better performance, which is via the {{{refresh}}} command.

Here we'll run a {{{bakeResults}}} over 30 frames, but not update any 3d view in the process:
{{{
import maya.cmds as mc

mc.refresh(suspend=True)
mc.bakeResults(simulation=True, time=(1,30))
mc.refresh(suspend=False)
}}}
Docs [[here|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/refresh.html]]
Some notes from the docs:
<<<
Use this flag ({{{suspend}}}) with caution: although it provides opportunities to enhance performance, much of Maya's dependency graph evaluation in interactive mode is refresh driven, thus use of this flag may lead to slight solve differences when you have a complex dependency graph with interrelations.
<<<
Running a timer on a simple example showed a 3x speedup in evaluation time.
The {{{ikSystem}}} node
*Useful for moving joints around without the IK getting in the way during rig reproportioning.
{{{
setAttr "ikSystem.globalSolve" 0;
}}}
You can turn off the shaded wireframe highlighting via:
{{{
displayPref -wireframeOnShadedActive "off";
}}}
And turn it back on via:
{{{
displayPref -wireframeOnShadedActive "full";
}}}
[[displayPref docs|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/displayPref.html]]
{{{help}}}
I always like having them on, and I find that some code actually turns them off.
{{{
help -popupMode true;
help -popupDisplayTime 10;
}}}

You can ADD tooltips to (nearly) any UI element with their {{{-annotation}}} ({{{-ann}}}) flag.
Here's an example of how you can embed this into a UI for a user, so it's obvious that they can turn them on:
{{{
window -mb 1 -mbv 1;	
    menu -l "Turn on tooltips";
        menuItem -l "Turn On" -c "help -popupMode true; help -popupDisplayTime 10; print \"Tooltips turned on\"";
    columnLayout -adj 1;
        button -l "Mouse over me" -ann "This is a tooltip";
showWindow;
}}}
In more recent versions of Maya, they've given joints 'displayable labels'.  Maya has a bunch of preset labels, but I tend to prefer my own naming conventions, based on the names of the actual joints.  The below code will turn on the joint lables for all joints in the selected hierarchy, and set the label name to the joint name:
{{{
# python code
import maya.cmds as mc

def enableJointNames(onOff=1):
    root = mc.ls(selection=True)[0]
    hier = mc.listRelatives(root, allDescendents=True, type='joint')
    hier.append(root)
    for joint in hier:
        mc.setAttr(joint+".drawLabel", onOff)
        mc.setAttr(joint+".type", 18)
        mc.setAttr(joint+".otherType", joint, type='string')

# to turn on labels, pick the root joint and execute:
enableJointNames()
# to turn off labels, pick the root joint and execute:
enableJointNames(0)
}}}
This command will do it, but as of Maya 2022, it doesn't seem to be undoable.
{{{
mc.polyOptions("meshShapeName", colorShadedDisplay=mode)
}}}
Instead, you can set this attr, that is undoable:
{{{
mc.setAttr("meshShapeName.displayColors", True)
}}}

For all mesh in the scene:
{{{
import maya.cmds as mc

def showVertColors(mode):
    for mesh in [mesh for mesh in mc.ls(type='mesh') if not mc.getAttr('%s.intermediateObject'%mesh)]:
        mc.setAttr(f"{mesh}.displayColors", True)
        
showVertColors(True)
# Or:
showVertColors(False) 
}}}
There is a Maya startup script located here:
{{{
C:/Program Files/Autodesk/Maya<version>/scripts/startup/initHUDScripts.mel
}}}
That is filled with procedures to do this.  Check the script for the particulars, but here are some that can be used:
{{{
setObjectDetailsVisibility(int $visibility)
setPolyCountVisibility(int $visibility)
setSubdDetailsVisibility(int $visibility)
setCameraNamesVisibility(int $visibility)
setFrameRateVisibility(int $visibility)
setCurrentFrameVisibility(int $visibility)
setViewAxisVisibility(int $visibility)
// this list goes on...
}}}
Based on what you have picked, the below code will unload the reference (not remove it, just unload it).  Note that it also attempts to check for the parent reference if one exists:  If you have multiple levels of referencing, it's possible the selection is one of those sub-levels, so you need to search 'up' to find the parental reference to remove.
{{{
string $sel[] = `ls -sl`;
string $fname = `referenceQuery -f $sel[0]`;
string $refNode = `referenceQuery -referenceNode $fname`;
string $pRef = `referenceQuery -referenceNode -parent $refNode`;
file -unloadReference $pRef;
}}}
Example:  Embed a link to this wiki into Maya's Help menu:
{{{
menuItem 
    -p $gMainHelpMenu 
    -l "MEL Wiki" -ann "Your Maya Mel Resource on the Web " 
    -ecr false -c "showHelp -a \"http://mayamel.tiddlyspot.com/\"";
}}}
All of Maya's main menu's have their names defined in global string arrays.
----
Also see:
*[[How can I find the names of Maya's main UI menus?]]
*[[How can I append a custom sub-menu to one of Maya's main menus?]]
A common thing to do is save the state of UI controllers as they're modified.  How can this be easily done?  

The below code snippets show how.  They make use of the behavior of most ui controls:  When a control is changed, it returns the changed values as a tuple.  By capturing those values via a {{{lambda}}}, we can then pass them to some other function to store the {{{optionVar}}}.

I authored this via ~PyMel, since it makes the {{{optionVar}}} access super easy (like a dict):
{{{
import pymel.core as pm

# Method inside our window class that will store an optionVar, based on some
# window controls change command:
def updateOptionVars(self, var, *args):
    pm.optionVar[var] = args[0]

# Inside some window class method, for creating the window contents::
self.myCtrl = pm.intFieldGrp(changeCommand=lambda *args:self.updateOptionVars("myOptionVar", *args))

# Later in the method, to restore a previously saved state:
if pm.optionVar.has_key("myOptionVar"):
    pm.intFieldGrp(self.myCtrl, edit=True, value1=pm.optionVar["myOptionVar"])
}}}
You need to update the {{{XBMLANGPATH}}} environment variable.  This is like {{{MAYA_SCRIPT_PATH}}}, but for icons used for such things as shelf buttons.
{{{
import os
os.environ["XBMLANGPATH"] = "%s;%s"%(os.getenv("XBMLANGPATH"), "c:/some/icon/path/")
}}}
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|{{{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|http://pythonwiki.tiddlyspot.com/#Packages]]
!!! For {{{container}}} node types only:
All DAG objects have a {{{rmbCommand}}} string attribute, but it only works on {{{container}}} nodes.  Reading the docs on it:
<<<
Indicates the name of a mel file that will be called to populate the right-mouse-button (RMB) with entries when a ''container'' member is ~RMBed on in the main scene. The mel file must return an array of strings of the form: 
{"Label for menu entry 1", "{{{CallbackToInvoke1}}}", "Label for menu entry 2", "{{{CallbackToInvoke2}}}", ... }
The callback should be a global mel proc which takes a string argument. The callback will be sent the name of the object which was ~RMBed on. See the mel file {{{containerRmbMenu.mel}}} for an example.
<<<
!!!Old Bad Way:
Edit the Maya script: {{{\Maya20XX\scripts\others\dagMenuProc.mel       }}}Yuck.  
In the procedure {{{dagMenuProc()}}}, '{{{string $shortName}}}' is the name of the object under the curser, which you can then write an "if" statement for allowing you to run other procedures based on that name.
!!!New Good Way:
With some help from the undocumented {{{dagObjectHit}}} command, and [[this great forum post|http://tech-artists.org/forum/showthread.php?t=419]] (authored way back in 2008).  Big kudo's to ''Roger Klado'' for figuring this stuff out and posting about it!
 
I took the guts of what was in that post, modified and re-authored it in Python, with a lot of descriptive text explaining what's going on:  When you run the below code, when you ''Alt''+RMB on a node, a custom {{{popupMenu}}} will be created displaying the name of the node.  Plus, if you have a component highlighted, it will add an item for that component name.  

What's going on?
*{{{makePopup()}}} creates a {{{popupMenu}}} that will execute when something is Alt+~RMB-clicked.
*It in turn has a '{{{postMenuCommand}}}' parameter that executes the {{{postMenuCommand()}}} function each time the {{{popupMenu}}} is created and made visible.
*The {{{postMenuCommand()}}} function is what physically builds the new custom {{{popupMenu}}}.  However, to //start// this process, it actually calls the {{{overrideDagMenuProc()}}} function:
**Via trickery with the (undocumented) {{{dagObjectHit}}} //command//, it (behind the scenes) uses the //mel procedure// {{{dagMenuProc()}}} to re-create that menuing system, //inside our new menu//.  This has the advantage of including the object name as the first item in the menu, which is the data we care about.
**That menu is completely cleared of all items (since we want to build our own menu), and the object name is returned back to {{{postMenuCommand()}}}.
*{{{postMenuCommand()}}} can then start re-building the empty menu with whatever you want, based on the name of the object Alt+RMB'd on.  
**In the example below, it generates a {{{menuItem}}} with the object name Alt+RMB'd on, and if any component level items were highlighted, will make another {{{menuItem}}} for the one under the cursor.
{{{
# Python code
import maya.cmds as mc

# The name to give our custom, context-sensitive, Alt+RMB popupMenu:
POP_NAME = 'myCustomPopupMenu'

def overrideDagMenuProc():
    """
    This function will override the RMB-menu created by dagMenuProc().
    It returns the name of the object Alt+RMB'd on:
    """
    # This creates a new menu, for the object Alt+RMB'd on, populated with the
    # guts of what dagMenuProc() would normally build:
    mc.dagObjectHit(menu=POP_NAME)
    # Get a list of all the children in that menu:
    popChildren = mc.popupMenu(POP_NAME, query=True, itemArray=True)
    # The menu item's name is the leaf name of the node:  If there are duplicate
    # names in the scene, this is always the leaf.  To get the full path, we
    # need to query the select command embedded in the menuItem:
    command = mc.menuItem(popChildren[0], query=True, command=True)
    fullName = command.split(' ')[-1]
    # Now delete the menu items created by dagMenuProc(), giving us an empty menu:
    mc.popupMenu(POP_NAME, edit=True, deleteAllItems=True)
    # Finally return the name of our node, which will be used by postMenuCommand
    # to build the top label in the empty menu:
    return fullName

def postMenuCommand(*args):
    """
    This function is passed to, and executed by the menu created via makePopup()
    whenever the popupMenu is shown.  It's what you'd update to have custom menu
    items appear based on what's selected.
    """
    # Delete all the items from any pre-existing menus:
    if mc.popupMenu(POP_NAME, exists=True):
        mc.popupMenu(POP_NAME, edit=True, deleteAllItems=True)

    # Make our menu the current menu for any new children:
    mc.setParent(POP_NAME, menu=True)
    if mc.dagObjectHit():  # undocumented command!
        # If the user has Alt+RMB'd over a node, capture the return of the
        # function that overrides the dagMenuProc's popupMenu.  The return is
        # the full name of the node that has been Alt+RMB'd over... and use it as
        # the label for the menu.  Extract the leaf name from the full path:
        fullObjectName = eval('overrideDagMenuProc()')
        leafObjectName = fullObjectName.split("|")[-1]
        mc.menuItem(label=leafObjectName)

        # Here, any custom code can be authored that is based on the object name
        # For example, track if there are any highlighted components:
        componentPreSelect = mc.ls(preSelectHilite=True)
        if len(componentPreSelect):
            mc.menuItem(divider=True)
            mc.menuItem(label=componentPreSelect[0])
    else:
        # Otherwise, no thing Alt+RMB'd on:
        mc.menuItem(label="No object under cursor")

def makePopup():
    """
    Will create the custom, context-sensitive, popupMenu displayed by Alt+RMB'ing
    on a node.
    """
    # Delete the popupMenu if it already exists:
    if mc.popupMenu(POP_NAME, exists=True):
        mc.deleteUI(POP_NAME)

    # Build the popupMenu, envoking a postMenuCommand when it is shown:
    mc.popupMenu(POP_NAME,
                 button=3,
                 altModifier=True,
                 markingMenu=True,
                 parent='viewPanes',
                 postMenuCommand=postMenuCommand)
}}}
{{{
# And execute...
makePopup()
}}}
On a side-note:  After several tries, the safest bet is Ctrl+MMB:  Ctrl+RMB is used for zoom.

I pulled these notes from highend3d.com by "picksel"
*Example: Basically, you create an objectFilter node when you execute itemFilter (don't ask why their names are different).
{{{
itemFilter -byName "*lambert*";
}}}
*This creates an {{{objectFilter}}} named {{{objectFilter7}}} or something like that.
*Then you can do:
{{{
lsThroughFilter "objectFilter7";
}}}
*and it should return a long list of all nodes containing 'lambert' anywhere in their names.
*Now, there is another such thing for ATTRIBUTES which is known as an objectAttrFilter? but I still can't get it to work.. it's accessible via the itemFilterAttr? command..
Also see:
*[[Based on an itemFilter, how can I find all the nodes in Maya that match?]]
*[[How can I query all the default itemFilters in Maya?]]
{{{viewPlace}}}
!!!Example
{{{
float $pos[] = `xform -q -ws -t $objName`;
string  $panelName= `playblast -activeEditor`;
// or // string $panelName = `getPanel -wf`;
string $camName = `modelPanel -q -camera $panelName`;
viewPlace -la $pos[0] $pos[1] $pos[2] $camName;
}}}
{{{playblast}}} has the advantage of always returning back a {{{modelPanel}}}: The {{{getPanel}}} command can return back other editor types that may be active.
----
Also look at  the {{{viewLookAt}}} command.  Works pretty darn well.  You can optionally pass in a position in space to look at.  This will fixup the camera so if it has been rotated off-axis that is addressed.
----
Moar code:
{{{
import maya.cmds as mc
def frame(node, aimAt=False):
    """
    Frame the node with the camera.

    Parameters:
    node : string : The name of the node to frame.
    aimAt : bool : Default False : If False, frame the object in the camera.
        If True, aim the camera at it, but don't frame it.
    """
    panel = mc.playblast(activeEditor=True).split("|")[-1]
    camera = mc.modelPanel(panel, query=True, camera=True)
    mc.viewFit(camera, node, center=aimAt)
}}}
----

Also see: [[How can I know which camera panel is currently available based on the current layout?]]
Maya lets you turn any IK chain into a switchable ik\fk system.  Which seems neat at first, until you realize how limiting it is:  You need to parent (for example) nurbs curves as a shape node to the joint to add a selection abstraction layer for the animators (so they're not picking the joints directly.  But they're still keyframing directly on the joints in FK mode.  And this makes you sad.  So you don't use it, and end up rigging a system that has multiple IK\FK chains constrained together to pull this off.

Then, you figure out from the guy sitting next to you, in fact you //can// do this:
*On the ikHandle node turn //off// "IK FK Control".  Turn off "Snap enable".
*Constrain the joints to your fk controllers.
*Now, when switching back and forth between IK and FK, this is what you do:  
**{{{ikBlend}}} is used to switch back and forth between the ik solve, and the fk system.
**Additionally, blend off the constraint weights on the joints.
Boom.
Thanks to for the info from Mason Sheffield.
It's commonly known that you can use operators inside of conditional statements.  Like:
{{{
int $a;
if($b == $c)
    $a = 1;
else
   $a = 0;
//   '==' is the operator
}}}
But you can also use them //outside// of conditional statements as well, sort of a 'short hand' version:
{{{
int $a = $b == $c; \\  If $b and $c are equal, 1, else 0
int $a = $b != $c \\ if $b and $c are NOT equal, 1 else 0
int $a = $b || $c;  \\ If $b or $c is equal, 1 else 0
int $a = $b < $c;  \\ if $b is less than $c, 1 ,else 0
int $a = $b > $c;  \\ if $b is greater than $c 1, else 0 
int $a = $b <= $c;  \\ if $b is less than or equal to $c, 1 ,else 0
int $a = $b >= $c;  \\ if $b is greater than or equal to $c, 1 ,else 0
}}}

----
There is also the '{{{?}}}' operator.  From the Maya docs:

The {{{?}}} operator lets you write a shorthand if-else statement in one statement.
The operator has the form:
{{{condition ? exp1 : exp2;}}}
If condition is true, the operator returns the value of exp1. If the condition is false, the operator returns the value of exp2.
{{{
// If $y > 20, $x is set to 10,
// otherwise $x is set to the value of $y.
$x = ($y > 20) ? 10 : $y;

// If $x > 10, print "Greater than", otherwise
// print "Less than or equal".
print(($x > 10) ? "Greater than" : "Less than or equal");
}}}
I've ran into instances when I'll execute a command from a UI button (for the sake of example), and when I undo the action, I end up undoing a LOT of steps.  I'm not sure yet what causes Maya to decide whether or not it's going to undo the last operation, or all the steps that made up the last operation. But if you run into a similar problem, you can use {{{undoInfo}}} to setup an 'undo chunk' that will store all the operations performed during its creation.  After you close the chunk, if you undo, it will undo everything put in the chunk.

Easy to do, and I recommend doing it with Python, since you can wrapper it in a {{{try}}} \ {{{except}}} clause, so that if any of the code fails in the middle of the chunk, the chunk can still close itself.
{{{
# Python code
import maya.cmds as mc

mc.undoInfo(openChunk=True)
try:
    doBunchOfStuff()
except Exception, e:
    print e
mc.undoInfo(closeChunk=True)
}}}
{{{
objectB.translateX = `getAttr -t (frame-3) objectA.translateY`;
}}}
*This will make objectB match objectA's translateX, but three frame laters.
*DRAWBACK:This is using getAttr & setAttr on your source and destination nodes.  It's not querying their absolute position in worldspace, it's the local transformations applied to the nodes themselves.  If they don't have the same initial transformation values, the destination object could have an offset based on the difference of the trans, rot, an scale between the two nodes.  This obviously could be a major issue.  To work around it, look below to the "decomposeMatrix" section.
*I've read in several places that Maya technical guru's don't recomend this operation since the getAttr somehow breaks the dependency graph evaluation, but I personally have never had anything go haywire with it.  It's basically very similar to expression code I used to write in PowerAnimator with great success.If this isn't a viable solution though, then try the below frameCache proposal.
Simple example of writing text to a file.  You should make sure ahead of time that if the file exists, it's writable.  You also need to check and make sure the directory exists too.

FYI, opening these files in Windows Notepad usually has bad formatting.  But opening them in anything else (Word, ~WordPad, etc) has correct formatting.  I don't think Notepad likes the 'newline character' {{{\n}}}.
{{{
string $filepath = "c:/temp/test.txt";
string $data[] = {"line1", "line2", "line etc..."};

int $fid = `fopen $filepath "w"`;
for($i=0;$i<size($data);$i++)
    fprint $fid ($data[$i] + "\n");
fclose $fid;

filetest -f $filepath; 
// Result: 1 //
}}}
Also see:
*[[How can I turn a text file into a string array?]]
*[[How can I get return characters to work in Notepad properly?]]
By default, when you launch Maya in prompt mode a dos prompt:
{{{
c:\> maya -prompt
}}}
,,,a new session of Maya will launch in a shell, ready for //mel// commands to be entered and executed.  The prompt looks like this:
{{{
mel:
}}}
But what if you want this same functionality, but with //Python// commands?

With Maya 8.5 and later, presuming it's been added to you system path, you can execute (on Windows) {{{C:\Program Files\Autodesk\<version>\bin\mayapy.exe}}} from the command prompt, to launch Maya's version of Python in that shell:
{{{
c:\> mayapy
Python 2.5.1 (r251:54863, Jun  5 2007, 22:56:07) [MSC v.1400 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
}}}
Note, on the mac, you have to pass in the full path to the mayapy app to execute it, or the below initialize call will fail:
{{{
$bash /Applications/Autodesk/maya2017/Maya.app/Contents/bin/mayapy
}}}
Then you can import in maya.standalone (see notes [[here|How can I execute Maya's version of Python outside of Maya?]]), and start running Maya from within Python:
{{{
>>> import maya.standalone
>>> maya.standalone.initialize('python')
}}}
then you can start running Python in maya as usual:
{{{
>>> import maya.cmds as mc
>>> mc.ls()
[u'time1', u'renderPartition', u'renderGlobalsList1', u'defaultLightList1', u'defaultShaderList1'....
}}}
... and yer good to go!
An animator asked me if there was a way to change the color of a given curve in the graph editor.  I realize that animCurves have both a {{{userCurveColor}}} attr (bool), and a {{{curveColor}}} attr (double3).  Armed with this knowledge, I built a function that will query what curves the user has selected in the Graph Editor (both in the Graph Editor Outliner, and in the Graph Editor Panel), launch a color chooser, and color them the defined values:
{{{
def setAnimCurveColor():
    """
    Based on the curves selected in the graph editor, or the obj.attrs selected
    in the Graph Editor channel box, launch a color-chooser allowing the changing
    of the individual curve colors.
    """
    # This finds a list of the "obj.attrs" selected in the graph editor channel box:
    selectionConnection = mc.outlinerEditor("graphEditor1OutlineEd",
                                            query=True, selectionConnection=True)
    outlinerSelection = mc.selectionConnection(selectionConnection, query=True, object=True)
    # convert to incoming connected keyframe nodes:
    outlinerAnimNodes = []
    if outlinerSelection is not None:
        for os in outlinerSelection:
            # make sure we have an attr picked, and not a transform
            if "." in os:
                incoming = mc.listConnections(os, source=True, destination=False, type="animCurve")
                if incoming is not None:
                    outlinerAnimNodes.append(incoming[0])

    # This grabs the acutal keyframe nodes picked in the graph editor panel
    graphEdAnimNodes = mc.keyframe(query=True,  selected=True, name=True)
    if graphEdAnimNodes is None:
        graphEdAnimNodes = []

    # combine lists, and remove dupes
    curves = list(set(outlinerAnimNodes+graphEdAnimNodes))

    # Launch our color chooser:
    if len(curves) > 0:
        mc.colorEditor()
        if mc.colorEditor(query=True, result=True):
            color = mc.colorEditor(query=True, rgb=True)
            for c in curves:
                mc.setAttr("%s.useCurveColor"%c, 1)
                mc.setAttr("%s.curveColor"%c, *color)
        else:
            print "Cancled color chooser",
    else:
        print "No cuves picked in Graph Editor",
}}}
You can overwrite this built in mel script:
{{{
C:\Program Files\Autodesk\Maya2022\scripts\others\performFileDropAction.mel
}}}
With your own logic.
The code is pretty bare bones to start, almost like they're encouraging you to modify it....:
{{{
global proc int performFileDropAction (string $theFile)
{
    return( performFileSilentImportAction( $theFile ));
}
}}}
And it returns 1 for successfully file operation, 0 for errors.

So, you could do something like this, if you have some other Python module to process certain file types, that returns 1 if it processes them, and 0 if it doesn't:
{{{
global proc int performFileDropAction (string $theFile)
{
    // Make sure that importing Python doesn't crash out and block all our drag & drop.
    int $failed = catch(  eval( "python(\"import processFiles\")") );
    if ($failed == 0){
        int $pyResult = python( ("processFiles.main('" + $theFile + "')")  );
        if ($pyResult == 1){
            return 1;
        }
    }
    return( performFileSilentImportAction( $theFile ));
}
}}}
----
Or you can write your own file translator: [[API: Simple scripted plugin file translator]]
Creating history on a node (like a polygonal object) creates what's called an "Intermediate Object", but I prefer to call it the 'pre-deformed shape', since that's what it really is.  For example, applying a deformer to a poly sphere will give this nodal connection:
{{{
itermediateObject mesh shape -> deformer node -> mesh shape 
}}}
With both the {{{intermediateObject mesh shape}}} and the {{{mesh shape}}} both children of the parental transform.  The intermediate object is by default hidden both from camera view, and from the shape-node view in the Outliner.  To make it visible, you need to select the transform and access 'Edit Deformers -> Display Intermediate Objects'.  You may need to collapse/open any Outliner hierarchies at that point to make them show up.
If you 'delete history' on this mesh, it will bake the deformer into the mesh, and remove the intermediate object.

//However//, if you were to duplicate this mesh, you'll now have a duplicate of the {{{intermediateObject mesh shape}}} node as well, but no more connections to maintain the history:  If you delete history on this dupe, the {{{intermediateObject mesh shape}}} will persist due to the lack of connections.  And they start to pile up.

I've logged this as a bug in Maya 2015.  To clean them up in the meantime is to find all the intermediate objects in the scene, and delete those that have no connections.
"ELF controll" {{{-bgc}}}

Most UI (ELF) controlls have options to change their colors.  If they have a {{{-bgc}}} argument, then they can have color added.
*Example: Make a button with a color other than default:
{{{
button -bgc 1.0 0 0;
}}}
{{{polyListComponentConversion}}} & {{{polySelectConstraint}}}

Take the current selection, convert it to the appropriate component type, the expand it to its //shell// (which is slightly different from the subject-line, but I use this more often):
{{{
// Vertex to shell
select `polyListComponentConversion -toVertex`;
polySelectConstraint -type 0x0001 -shell 1 -mode 2;
polySelectConstraint -disable;
}}}
{{{
// Edge to shell
select `polyListComponentConversion -toEdge`;
polySelectConstraint -type 0x8000 -shell 1 -mode 2;
polySelectConstraint -disable;
}}}
{{{
// Face to shell
select `polyListComponentConversion -toFace`;
polySelectConstraint -type 0x0008 -shell 1 -mode 2;
polySelectConstraint -disable;
}}}
{{{
// UV to shell
select `polyListComponentConversion -toUV`;
polySelectConstraint -type 0x0010 -shell 1 -mode 2;
polySelectConstraint -disable;
}}}
Note how the constraint needs to be disabled afterwards.
Requires two object.attrs (source and destination).
{{{isConnected}}}
{{{progressWindow}}}
----
Whenever I set these up I always seem to forget the magic, so below is a solid way of doing it.  In this example, we're looping over some number of frames, just printing the frame number.
{{{
# Python code
import maya.cmds as mc

# Define the range the progressWindow should act in:
minF = 1
maxF = 500
# Setup the progress window:
progressSteps = 100.0 / (maxF-minF)
progress = 0.0
mc.progressWindow(title='Progress Window!',
                  progress=0, minValue=0, maxValue=100,
                  status='Not started yet...',
                  isInterruptable=True )

# Start looping
for i in range(minF,maxF+1):
    # Update our progress window, or quit if the user has hit escape:
    if mc.progressWindow( query=True, isCancelled=True ):
        break
    mc.progressWindow(edit=True, progress=int(progress), status='%.1f%%'%progress)
    progress += progressSteps

    # Do any work that needs to be done this loop\frame (in this case just a print),
    # then advance the frame:
    print i
    mc.currentTime(i, edit=True)
    
# Exit the progress window:
mc.progressWindow(endProgress=1)
}}}
----
Also see:
*[[How can I author a progressWindow context manager?]]
{{{optionMenu}}}
So simple, but I always seem to forget the syntax... :-S
{{{
import maya.cmds as mc
skinCluster = mc.skinCluster('myMesh', edit=True, unbind=True)
}}}
This has the added bonus of unlocking the transform channels for you, compared to just deleting history.
*Export an anim (with one or more objects selected):
{{{
file -es 
    -type "animExport" 
    -op "-cb api -fea 1 -animation objects -option keys -hierarchy none -controlPoints 0 -shape 000" 
    "c:/temp/foo.anim";
}}}
*Import an anim (with one or more objects selected):
{{{
file -i 
    -type "animImport" 
    -op "targetTime=4; copies=1; option=replaceCompletely; connect=0;"
    "C:/temp/test.anim";
}}}
*Notes:
**You must have the "{{{animImportExport.mll}}}" plugin loaded.
**The options listed are based primarily on the copyKey and pasteKey mel commands. In the case of an export, the animation data is copied (via copyKey) into the api clipboard, and then sourced by the plugin for export. In the case of an import, the plugin copys the data from the anim file to the api clipboard, then pastes it (via pasteKey) onto the objects.
{{{whatIs}}}
*Example A:
{{{
whatIs "sphere"
// Result: command
}}}
*Example B:
{{{
int $abc[42];
whatIs "$abc";
// Result: int[] variable //
}}}
Good for figuring out which camera view you're in….
{{{getPanel}}}
*Example: get the current or last active panel:
{{{
string $panel = `getPanel -withFocus`;
}}}
*Example: get the current panel the mouse is over:
{{{
string $panel = `getPanel -underPointer`;
}}}
*You can also use {{{playblast}}}, if you want to limit the values to only be {{{modelPanel}}} items:
{{{
string  $panel = `playblast -activeEditor`;
}}}
----
There is also the //mel// {{{global proc findPanelPopupParent()}}}, which wrappers {{{getPanel -underPointer}}}, and returns the {{{paneLayout}}} under the pointer.  
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\findPanelPopupParent.mel
}}}
From its docs:
<<<
Find the {{{paneLayout}}} under the mouse pointer.  A {{{popupMenu}}} can be attached to this layout for hotkey-enabled marking menus.  If no {{{paneLayout}}} can be found under the pointer, this method returns "{{{viewPanes}}}".  This is so that the user is not annoyed by error messages when they mistakenly use the wrong hotkey on a non-panel window. ("{{{viewPanes}}}" is Maya's main {{{paneLayout}}})
<<<
{{{about}}}
Note it's values are string, not int.
{{{
import maya.cmds as mc

# Maya 2019 and older:
version = mc.about(version=True)
apiVersion = mc.about(apiVersion=True)
installedVersion = mc.about(installedVersion=True)
print "version:", version, type(version)
print "apiVersion:", apiVersion, type(apiVersion)
print "installedVersion", installedVersion, type(installedVersion)

# Maya 2019.1 and newer:
majorVersion = mc.about(majorVersion=True)
minorVersion = mc.about(minorVersion=True)
patchVersion = mc.about(patchVersion=True)
print "majorVersion:",  majorVersion, type(majorVersion)
print "minorVersion:", minorVersion, type(minorVersion)
print "patchVersion:", patchVersion, type(patchVersion)
}}}
{{{
version: 2019 <type 'unicode'>
apiVersion: 20190100 <type 'int'>
installedVersion Autodesk Maya 2019.1 <type 'unicode'>

majorVersion: 2019 <type 'unicode'>
minorVersion: 1 <type 'unicode'>
patchVersion: 0 <type 'unicode'>
}}}
* Note #1 : All the args return strings except {{{apiVersion}}} which returns an int.
* Note #2 : The {{{version}}} arg can return different things based on the version.  For example, {{{version}}} can return just the version ({{{"2016"}}}), or a version with an qualifier after it: ({{{"2016 Extension 2 SP2"}}}).
** Because of that, if you want to always get the 'core' version you can use this method:  Get the {{{-version}}}, and only use the first 4 characters:
{{{
print int(mc.about(version=True)[:4])
2016
}}}
{{{exists}}}
*Example:
{{{
exists ls;
// result: 1
}}}
{{{catch}}}
*Example A: First define some proc "bob". Then:
{{{
catch (`bob`)            // this is the start of a new procedure
}}}
**if "bob" failed execution, without the catch, the rest of the new procedure wouldn't execute, but with the catch, the rest of the proc would execute.
*Example B: define some string that does something, $string. Then:
{{{
catch (`eval($string)`);
}}}
This seems like it should be an easy thing to do, but it seems to be hidden, unless I'm really missing something:
Create a {{{ShaderfxShader}}}, and with it selected, in the Attribute Editor, press the 'Open ~SHaderFX' button.  I can't find a normal menu item to open it anywhere.
{{{
import maya.cmds as mc
print mc.about(batch=True)
# False
}}}
{{{modelPanel}}} & {{{getPanel}}}
*Example: (presuming the active panel IS a camera view…)
{{{
string $currentPanel = `playblast -activeEditor`;
// or // string $currentPanel = `getPanel -withFocus`;
string $cameraName = `modelPanel -q -cam $currentPanel`;
}}}
{{{playblast}}} has the advantage of always returning back a {{{modelPanel}}}: The {{{getPanel}}} command can return back other editor types that may be active.
Also see:
*[[For the current panel, how can I make a named camera be the one viewed through?]]
You can use a sneaky back-door via {{{undoInfo}}}:
{{{
// presuming you say, just moved something:
string $lastCmd = `undoInfo -q -undoName`;
// Result: move -r 0.321949 -0.212393 0  //
}}}
Since Maya's undo-que holds everything just executed (so, it can be undone), you can use this method to query what //would// be undone... which obviously would be the last commands entered.
Also see:
*[[How can I repeat the last action in Maya?]]
!Via Your Current Project
I don't render much these days, but I always found this weird:
It's the {{{\images}}} directory setup in your current //project// that define where the renders go.  If there is no {{{\images}}} dir set, then it uses the root of the  project.  
This path is editable by accessing 'File -> Project Window'..

You can confirm this by open the 'Render Settings' window, and at the top of the 'Common' tab, you'll see the 'Path:' to the save directory.

Why they don't just have a path you can set in the Render Settings.....? :-S
!Via The Render View
Once you render an image in the Render View, you can choose more specifically where to save it via:
* Render -> Render Sequence [Options] -> Alternate Output File Location (browse to it).  then 'Render Sequence and Close'
** If, in the Render Settings, you have your 'Frame/Animation ext' set to 'name.ext (Single Frame)', it will output just one frame there.
* There is also 'File -> Save Image': After you do that, check the script editor, it should print where it goes:  Can be the project path discussed above, or the 'Alternate Output File Location' if you have that set.
{{{objExists}}}
{{{recordAttr}}}
''Note'' : This tool only appears to work if Maya is in ''cm'' UI units :S
*Example:
*Select the object you want to record motion on.
*In the script editor, enter
{{{
recordAttr -at "translateX" -at "translateY"; 
// to control the tx and ty for example
}}}
*Execute that bit of code. You'll see new "record1" outputs on your object
*Next type in the script editor, but don't execute yet:
{{{
play -record;
}}}
*Now:
**select your object in the camera view,
**go back and highlight 'play -record' in the script editor,
**move your mouse back over your object (but do not pick it!),
**and hit the 'Enter' button on the NUMBER pad.
**This will execute the mel code, but leave your mouse right over your object ready to be selected and moved.
*This will start the recorder, capturing your mouse movement.Click on the object, and start moving it.Obviously, you should constrain you translation plane to the two axes you've selected before you start this process.Hit Esc when done.
*This can work for any attr you want actually, I just used trans as an example.
*Also, if you want to delete the motion and try again, you'll need to also delete the "record" nodes that are output connections to your animated node as well. You can use this code:
{{{
delete `ls -type record`;
}}}
If you have a {{{skinCluster}}} node that has `skinning method` set to 'Weight blended' : How do you actually set those values programatically?

Maya's {{{skinPercent}}} command can set the normal weights like so:
{{{
mc.skinPercent(skinClusterName, "myMesh.vtx[3], transformValue=[("jointName", 1.0)])
}}}
But the {{{skinPercent}}} command has nothing for setting the 'blendValue'.

Only solution I've found is setting the attr directly:
{{{
mc.setAttr("skinClusterName.blendWeights[3], 1.0)
}}}
Or, there is an API call, example:
{{{
import maya.api.OpenMaya as om2

# Bunch of code to query the data then:
mFnSkinCluster.setBlendWeights(meshDagPath, importVertexCompObj, arrayBlendWeights)
}}}
Via 
{{{
C:\Users\Eric\Documents\maya\20XX\prefs\pluginprefs.mel
}}}
An example of the code therin:
{{{
evalDeferred("autoLoadPlugin(\"\", \"mayHIK\", \"mayHIK\")");
}}}
When using Python in Maya, to access the bulk of the //mel// commands via Python, you import the {{{mel.cmds}}} package, usually into a namespace, like so:
{{{
import maya.cmds as mc

print mc.ls()
}}}
But if you browse to that package on disk, you'll find it empty, only containing an {{{__init__.py}}} file (required for package creation):
{{{
C:\Program Files\Autodesk\Maya<VERSION>\Python\Lib\site-packages\maya\cmds
}}}
So how are all the 'mel commands' added to that package for execution in Python?

There is a startup Python module living here, that works the magic of populating that package:
{{{
C:\Program Files\Autodesk\Maya<VERSION>\Python\Lib\site-packages\maya\app\commands.py
}}}
It parses a file called {{{commandList}}} (no extension, but it's text) living here:
{{{
C:\Program Files\Autodesk\Maya<VERSION>\bin\commandList
}}}
You can open that file in a text editor, and what you'll find are two columns:  One with a 'mel' command name, and one with a .dll where that command 'lives'.  Those .dll's live in the same dir as the {{{commandList}}} file.  Here's a sample:
{{{
TanimLayer AnimSlice.dll 
about Shared.dll 
addAttr Shared.dll 
addDynamic DynSlice.dll 
addPP DynSlice.dll 
affectedNet Shared.dll 
affects Shared.dll 
agFormatIn Translators.dll 
agFormatOut Translators.dll 
aimConstraint AnimSlice.dll 
air DynSlice.dll 
aliasAttr Shared.dll 
align Shared.dll 
}}}
What {{{commands.py}}} does is parse the {{{commandList}}} file and append each command (via a Python command wrapper function) to {{{maya.cmd}}}'s {{{'__dict__}}}' attribute ({{{maya.cmds.__dict__}}}), which is the lookup table users access when calling their favorite mel command via Python (like {{{maya.cmds.ls(}}})  It also passes the name of the .dll to the command wrapper function, so when the user executes the given command, it first loads the parental .dll.

Crack open commands.py for the nitty-gritty details.
----
If you want to see the end result of all this hard work, you can run this code in Maya.  But it prints a *lot* of stuff:
{{{
import maya.cmds as mc

for k in sorted(mc.__dict__.keys()):
    print k, mc.__dict__[k]
}}}
The 'world rotate pivot' isn't an attr on a node you can query (you can only query the local {{{.rotatePivot}}} attr), so how does Maya populate it in the Attribute Editor?

Inside the mel script:
{{{
C:\Program Files\Autodesk\Maya2019\scripts\AETemplates\AEtransformNoScroll.mel
}}}
{{{scriptJob}}}s are setup so that when the nodes translate attrs change, the '{{{global proc AEupdateWorldPivots}}}' is called to, which updates that UI with the world pivot data.  
Which is queried via the mel:
{{{
float $worldRotatePivot[] = `xform -worldSpace -query -rotatePivot $nodeName`;
float $worldScalePivot[] = `xform -worldSpace  -query -scalePivot $nodeName`;
}}}
Since Python is now available in Maya, it makes sense to use it's power.  One of it's coolest features is being able to use its 'object oriented programming' (oop) features.  This is old hat in the world of programming, but not supported with mel.

Since it's been a relatively new concept for me, I've been having a hard time to find a good description of it in print.  As I learn it, I update another wiki (my Python wiki) with notes specifically on it.  You can find that info here: http://pythonwiki.tiddlyspot.com/#Class

Also, the below link does a pretty good job of it, for the beginner:

http://www.pasteur.fr/formation/infobio/python/ch18.html
This is pulled from the 'Programming Course for Biologists at the Pasteur Institute', so the examples are a bit... genetics related.  But they have nice pictures, and get the point across.  And as of //this// writing, it was updated Feb 2, 2008 (pretty recently).
I have yet to find an answer to this:

When you open a file in Maya, and browse to the path, when you later reopen a file, that path is stored in the top drop-down menu of the {{{fileBrowserDialog}}}.  How that path is stored is currently a mystery to me.  My guess is that it's handled in the guts of the {{{fileBrowserDialog}}} code:  If you do a 'File->Recent Files', then later open a file, the 'Recent Files Opened' path //won't// be in the {{{fileBrowserDialog}}}.  

But if you call to a {{{fileBrowserDialog}}} either through File->Open (which calls to the {{{OpenScene}}} {{{runTimeCommand}}}), or through your own {{{fileBroswerDialog}}} code, the menu will be updated.

You can prove this using this simple example:
{{{
# Python code
import maya.cmds as mc

def importer( fileName, fileType):
   mc.file( fileName, i=True );
   return 1

mc.fileBrowserDialog( m=0, fc=importer, ft='mayaAscii', an='Import', om='Import' )
}}}
After you open a file and then relaunch the example, you'll see the path to the file in the top drop down menu.

I thought that maybe these values were saved as a global var, since they seem to reset when you quit Maya.  However, searching through all of global vars, I could find no path matches.  I even checked {{{userPrefs.mel}}}, since that's where {{{optionVar}}}'s are stored, but there was no comparable data in their either.

So currently... unsolved mystery....
When Maya launches, via GUI or Batch mode, it executes one of these two Python modules:
(From the {{{maya.app.startup}}} Python package)
*{{{\Autodesk\Maya20XX\Python\Lib\site-packages\maya\app\startup\}}}
**{{{gui.py}}}
**{{{batch.py}}}
Both modules import the module {{{maya.app.startup.basic.py}}}.  
In addition, {{{gui.py}}} also does these things:
*Replaces {{{sys.stdin}}} with {{{maya.app.baseUI.StandardInput()}}}
*Replaces {{{sys.stdout}}} with {{{maya.utils.Output()}}}
*Replaces {{{sys.stderr}}} with {{{maya.utils.Output( error=1 )}}}
!!!{{{maya.app.startup.basic}}}:
Things it does (from notes in that module):
*Set up {{{sys.path}}} to include Maya-specific user script directories.
**{{{setupScriptPaths()}}} (local function)
*Set up auto-load stubs for Maya commands implemented in libraries which are not yet loaded.  See "[[How does Maya populate maya.cmds?]]".
**{{{maya.app.commands.processCommandList()}}}
*Run the user's userSetup.py if it exists
**{{{executeUserSetup()}}} (local function)
*Register code to be run on exit:
**{{{atexit.register( maya.app.finalize )}}}
*Set up string table instance for application:
**{{{maya.stringTable = utils.StringTable()}}}
----
Also see:
*[[Understanding Python integration in Maya]]
*".liw" stands for "Lock Influence Weight". By default, a joint doesn't have this attribute, but when it is smooth bound to geometry, each joint receives this new attribute. It is just a custom attribute. Its output connects to the respective skinCluster's ".lockWeights[]" (.lw) imput attribute. ".lockWeights[]" is an array attribute, that can receive inputs from multiple joint.liw attrs.
*There is no reference to this attribute (.liw) in the documentation, since it is a custom attr generated by the smoothBind process. You can find documentation for the ".lw" attr in the "joint" documentation.
Also see:
*[[How can I easily hold or unhold all the influence weights on skinned geo?]]
This is some example mel I took from one of Maya's built-in tools that shows how to do this pretty easily.  A couple things you need to know
* The global {{{$gMainFileMenu}}} holds the name of the main File menu
* To accurately place your menu in that list (instead of the bottom), you need to know the name of the menu item to insert it after.  In this example, that is {{{setProjectFileItem}}} (the 'Project' section) : I'm not exactly sure an easy way to learn //those//? 
{{{
global proc MyCommandCallback()
{   
    // Execute your code
}

global proc string AddMyMenuItems()
{
    // Global variable to hold the test to see if the menu is populated.
    global string $gMainFileMenu;

    // if we haven't created the menu items do it now
    if( ! `menuItem -ex MyMenuDiv` ) 
    {
        menuItem -divider true -insertAfter setProjectFileItem -dividerLabel "MyMenu" -parent $gMainFileMenu MyMenuDiv;
        menuItem -label "My Tool Name!" -insertAfter MyMenuDiv -parent $gMainFileMenu -command "MyCommandCallback" MyMenuName;
    }
    return "RemoveMyMenuItems"; // Returns the callback
}

global proc RemoveMyMenuItems()
{
    if( `menuItem -ex MyMenuDiv` ) 
    {
        if(`menuItem -ex MyMenuDiv`) deleteUI -mi MyMenuDiv;
        if(`menuItem -ex MyMenuName`) deleteUI -mi MyMenuName;
    }
}
}}}
----
Also see:
* [[Why does adding an item to a Maya main menu remove all the items?]]
* [[How can I add a menu to Maya's main menubar?]]
* [[How can I find the names of Maya's main UI menus?]]
* [[How can I update Maya's menus with my own entry?]]
Sometimes, you need to execute code in Maya, that may, based on external dependencies, hang.  Most commonly for me this is Perforce:  I have some piece of code that will manage a file in P4 from Maya.  But P4 goes down, and when my management code runs, it hangs, while it waits for the Python server to come back to life, which locks up Maya seeming like a crash.

The below code is an example using a make-believe myP4Module module that handles this:
{{{
from __future__ import print_function
import time
import thread

import myP4Module

#-------------------------------

WAIT_SEC = 5
gResult = None

#------------------------------

def timer():
    """
    Our timer.  Runs in a non-blocking thread, waiting to P4 to find itself.
    """
    global gResult
    startTime = time.time()
    while not gResult:
        thisTime = time.time()
        elapsedTime = thisTime - startTime
        if elapsedTime > WAIT_SEC:
            raise Exception("Tried to execute 'myP4Module.getP4' for %s seconds but failed, raising Exception here..."%WAIT_SEC)

def getP4():
    """
    Return a P4 instance.  Raise an Exception if it takes to long, thus
    halting calling code rather than locking up Maya.
    """
    global gResult
    # Run the timer function in the thread.  Args are
    #('function', ('tuple of', 'function args') )
    thread.start_new_thread(timer, ())
    gResult = myP4Module.getP4()
    return gResult
}}}
Some good stuff from this post by Peter Shipkov :
http://groups.google.com/group/maya_he3d/browse_thread/thread/5a78a2fce5de57b4#
----
getattr:
{{{
getAttr someNode.boundingBox.boundingBoxMin;
getAttr someNode.boundingBox.boundingBoxMax;
}}}
----
Commands:
{{{
exactWorldBoundingBox("someNode");

xform -query -boundingBox someNode;

polyEvaluate -boundingBox; // whole object
polyEvaluate -boundingBox2d; // object uv's
polyEvaluate -boundingBoxComponent; // component level
polyEvaluate --boundingBoxComponent2d; // component level uv's
}}}
----
API:
{{{
OpenMaya.MFnDagNode.boundingBox() 
}}}
Returns [[MBoundginbBox|http://help.autodesk.com/view/MAYAUL/2015/ENU/?guid=__cpp_ref_class_m_bounding_box_html]]
----
~PyMel:
{{{
import pymel.core as pm
node = pm.PyNode("someNode")
bb = node.getBoundingBox(space='world')
}}}
Returns type [[BoundingBox|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.BoundingBox.html]]
Have had multiple reports of different users getting this error when Maya launches:
{{{
Microsoft Visual C++ Runtime Library

Runtime Error!

Program: C:\Program Files\Autodesk\Maya2018\bin\maya.exe

R6034
An application has made an attempt to lad the C runtime library incorrectly.
Please contact the application's support team for more information.
}}}
Reaching out to Autodesk, this was their reply:
<<<
In regards to the runtime library error.  Every situation is different which makes it difficult to troubleshoot but generally there is something set at the system level which is causing library error.  I would start by removing all the user custom tools and Maya prefs possibly something being loaded from a plugin?  Are you guys using the python variables?  try removing python home if its set.  Also make sure all the Microsoft Redistributables are installed.  One can also try using microsofts process explorer to see what Maya is loading in.  May give you more insight to what Maya is loading in. 
# Download Process Explorer from MSFT site (http://technet.microsoft.com/en-ca/sysinternals/bb896653.aspx)
# Start up Maya and run until you get the R6034 error again
# Launch the process explorer and the lower pane view to DLL's only 
## "View" menu go to "Lower Pane View" and choose "~DLLs"
# Select Maya from the top pane and browse the lower pane to see which DLL's Maya has loaded and possibly locate and rogue ones that are not system or Maya related
Apologies if this is all vague but these errors can be almost anything.  
<<<
It appears that starting in Maya 2022, ~PyMel is now a separate package, depending on your install route, may not install with Maya.  You need to install it manually.  
But, they do make it easy to install it via {{{pip}}} now.
Generic instructions for such things can be found here: [[Loading Python packages in Maya]]
But specifically for ~PyMel:
----
Open a CMD shell as admin, and execute:
{{{
> "C:\Program Files\Autodesk\Maya2022\bin\mayapy.exe" -m pip install pymel
}}}
Which should install it in your local Maya site-packages here:
{{{
C:\Program Files\Autodesk\Maya2022\Python37\Lib\site-packages
}}}
----
If you're using a beta / pre-release version, the above won't work.  You'll have to download a specific cut made for the beta.  For example, this works in ~PR124:
{{{
>  cd C:\Program Files\Autodesk\Maya2023\bin
> mayapy -m pip install pymel==1.3.0a1
}}}
And on ~PR127:
{{{
mayapy -m pip install "pymel>=1.3.*,<1.4.*"
}}}
Search the beta forums for 'pymel' and see what comes up.
Sometimes on 4k monitors, Maya / custom tools can 'look bad'. 
* Open Maya's Preferences window (see how-to above).
* Access: Interface → Interface Scaling
* Turn on 'Use Custom Scaling'.  Then based on your resolution, adjust the 100% drop down.  
* You may need to quit/restart Maya for this to take effect:  Hit 'Save' in the prefs window before closing Maya.
Generally all my tools are up in source control, and my team members sync (via Perforce in our case) to get them.  This isn't bulletproof however, since they'll often forget to.  Deep sigh.
I saw it discussed by Jeremy Ernst (in his [[gdc presentation pdf|http://www.unrealengine.com/files/downloads/Jeremy_Ernst_FastAndEfficietFacialRigging2.pdf]]) that he makes executable installers for his tools via:
*[[Smart Install Maker|http://www.sminstall.com/]]
I've yet to use this system, but I like the sound of it.  Notes will go here if I ever get around to using it (or something similar).
We had a need to render out 16-bit png files for use in our engine.  Maya had been giving us some grief, here's the secret sauce that was needed:
* Render Settings
** Render Using : Arnold Renderer
** Image Format: {{{png}}}
** Format : {{{int16}}}
** Color Space : {{{Raw}}} <-- this was the main secret sauce. If not 'Raw', you still get 8-bit images
It should be noted there is another option instead of setting {{{Raw}}} in the Render Settings:
* Render View -> File -> Save Image -> Options : Choose 'Raw Image', presuming you're saving your images out through the 'Render View'.
Shows how to sample the color of a 3d texture (subclass of {{{texture3d}}}) in worldspace.  
If you want to do this on a 2D texture, see [[How can I find the color of a 2D render node at a given vertex?]]

See the blog post here:
*http://mayastation.typepad.com/maya-station/2011/03/how-to-sample-a-3d-texture.html
Slightly modified version below.

This still requires using {{{maya.OpenMaya}}} as of Maya 2022, since {{{MRenderUtil}}} hasn't yet been ported to {{{maya.api.OpenMayaRender}}} :(  It has a {{{MRenderUtilities}}},  but it's not the same thing.
{{{
import maya.cmds as mc
import maya.OpenMaya as om
import maya.OpenMayaRender as omr

def sample3dColorsAtPoints(shadingNode, points, outAttr="outColor"):
    """
    Parameters
    shadingNode : string : The name of the 3d texture to query.
    points : list : Each item is a 3d point in worldspace: [[-0.5, -0.5, 0.5], ...]
    outAttr : string : Default "outColor" : The shadingNodes output attr to query.
        "outColor" is most common.

    Return : list : Each items is a sublist of the RGB values for each point: [[0.0, 0.0277, 0.973], ...]
    """
    assert mc.attributeQuery(outAttr, node=shadingNode, exists=True), f"There is no '.{outAttr}' output attr on '{shadingNode}'"
    shadingNodeAttr = f"{shadingNode}.{outAttr}"

    numSamples = len(points)
    pointArray = om.MFloatPointArray()
    pointArray.setLength(numSamples)
    refPointArray = om.MFloatPointArray()
    refPointArray.setLength(numSamples)

    for i in range(len(points)):
        point = points[i]
        location = om.MFloatPoint(point[0], point[1], point[2])
        pointArray.set(location, i)
        refPointArray.set(location, i)

    # We don't need to set these arguments
    useShadowMaps = False
    reuseMaps = False
    uCoords = None
    vCoords = None
    normals = None
    tangentUs = None
    tangentVs = None
    filterSizes = None
    cameraMatrix = om.MFloatMatrix()

    # Create the return arguments
    resultColors = om.MFloatVectorArray()
    resultTransparencies = om.MFloatVectorArray()

    # This is the call to sample the points
    omr.MRenderUtil.sampleShadingNetwork(shadingNodeAttr, numSamples, useShadowMaps,
                                         reuseMaps, cameraMatrix, pointArray,
                                         uCoords, vCoords, normals, refPointArray,
                                         tangentUs, tangentVs, filterSizes,
                                         resultColors, resultTransparencies)

    sampledColorsArray = []
    for i in range(resultColors.length()):
        resultVector = om.MFloatVector(resultColors[i])
        rgb = [resultVector.x, resultVector.y, resultVector.z]
        sampledColorsArray.append(rgb)

    return sampledColorsArray
}}}
I just realized there are hotkeys to toggle various menubars:
*ctrl+m : Toggle the main (top of Maya) menubar (status line)
*shift+m : Toggle the individual pane menubars
*ctrl+shift+m : Toggle the pane view\shading\lighting menubars (has all the icons)
----
Also see:
*[[How can I make a toggle for the main menu bar?]]
Human IK allows you to 'characterize' rigs just like Skeletons. Unfortunately, their UI interface for it is sorely lacking (up to Maya 2022).  You can script all this, but need to know the magic numbers for each of the different node types. 

The below code will generate a dict with keys for the 'descriptive names', and values for the corresponding int ~IDs for them.
{{{
import maya.cmds as mc
import maya.mel as mm
from collections import OrderedDict

hikNodeMap = OrderedDict({mel.eval('GetHIKNodeName(%d)'%i): i for i in range(mel.eval('hikGetNodeCount()'))})
}}}
{{{
for key in hikNodeMap:
    print(key, hikNodeMap[key])

Reference 0
Hips 1
LeftUpLeg 2
LeftLeg 3
LeftFoot 4
RightUpLeg 5
RightLeg 6
RightFoot 7
...
}}}

And then depending on the translate/rotate needs of each rig ctrl, you can use a
{{{
regargeter = maya.app.hik.retargeter.HIKRetargeter.createDefaultMapping(source, dest, bodyPart, destRig, type, id, linkNum=0)
}}}
staticmethod, where:
* source : the {{{HIKState2GlobalSK}}} input to the 'hik character node'.
* dest : the 'hik character node'.
* bodyPart : the name of the 'node' per the above dict.
* destRig : the controller name on the rig
* type : {{{R}}} for a 'rotational' mapping, {{{T}}} for translational.
* id :  The id from the above dict.
* linkNum : just {{{0}}} the default.
to create a {{{DefaultRetargeter}}} instance

Then for that list of retargeters:

Get the {{{CustomRigRetargeterNode}}} input to the 'hik character node'.
Setup an index, and set it's value to the number of inputs to the {{{CustomRigRetargeterNode.mappings}}} 

Loop over them, for each loop, capturing the results from
{{{
maya.app.hik.retargeter.DefaultRetargeter.toGraph(retargeter, HIKState2GlobalSK)
}}}
then connecting the {{{.message}}} attr of the result to the {{{.mappings[index]}}} of the {{{CustomRigRetargeterNode}}}, and bump the index for the next loop.


{{{
C:/Program Files/Autodesk/Maya2019/scripts/others/hik*.mel
}}}
! hikGlobalUtils.mel
* Many global strings
* global proc string hikGetCurrentCharacter()
* global proc string[] hikGetSceneCharacters()
* global proc string[] hikGetRawRemoteCharacters( int $onlyLiveClients )
* global proc string[] hikGetRemoteCharacters()
* global proc string hikGetCurrentSource()
* global proc hikSetCurrentSource( string $source )
* global proc hikSetCurrentSourceFromCharacter(string $character)
* global proc string hikGetCharacterInputString(string $character)
* global proc int hikIsNoneCharacter( string $lNewCharacter )
* global proc string hikNoneString()
* global proc int hikIsCharacterEnabled(string $pCharacter) 
* global proc int hikHasCustomRig(string $character)
* global proc int hikHasControlRig(string $character)
* global proc int hikHasDefinition(string $character)
* global proc int hikHasSkeletonGenerator(string $character)
* global proc int hikIsDefinitionLocked(string $character)
* global proc int hikIsCharacterLiveActive( string $character )
* global proc hikExecuteDelayedCommand(int $runTime, string $command)
* global proc string hikCreatePropertyState()
* global proc hikConnectPropState(string $pOutput, string $pInput)
* global proc string hikCreateCharacterNoUpdateOfCurrent()
* global proc string hikCreateCharacter( string $nameHint )
* global proc hikConnectAttribute(string $pNodeOut, string $pAttrOut, string $pNodeIn, string $pAttrIn)
* global proc hikConnectAttributeInv(string $pNodeIn, string $pAttrIn, string $pNodeOut, string $pAttrOut)
* global proc hikConnectArrayAttribute(string $pNodeOut, string $pAttrOut, int $pIndexOut, string $pNodeIn, string $pAttrIn, int $pIndexIn)
* global proc string hikGetNodeNameFromNode(string $node)
* global proc string[] hikGetRetargeterFromCharacter(string $pCharacter)
* global proc string[] hikGetSolverFromCharacter(string $pCharacter)
* global proc int hikTurnOffUndo()
* global proc hikRestoreUndo( int $undoOn )
* global proc string hikGetDefaultResourcesDir()
Notes from Using it in Maya.  Many other subjects on the matter, search by the {{{humanik}}} tag.
[[Maya Help: HumanIK Window|https://help.autodesk.com/view/MAYAUL/2020/ENU/index.html?guid=GUID-9921EDE1-05E2-4062-9F0D-4BADECA29226]]

Based on the {{{MayaHIK.mll}}} plugin, and a lot of other custom script.
At a very high level, a HIK 'character definition' actually can have up to three main parts that work together:
* The 'Character Definition' itself ({{{HIKCharacterNode}}}), that acts like the glue for the other two (optional) systems.
** A 'Control Rig' : This is an acutal Maya rig that gets auto-generated based on the rig in the Motion Builder application.  I've never used this myself, but if you have no rig, this could be a fallback.
** A 'Custom Rig Mapping' : This allows you to build a mapping between your own rig, and the 'Character Definition' : This allows other assets that have been 'characterized' to drive your rig directly.
So, you always need at least a 'Character Definition'.  But say, you also create a custom rig mapping.  This lets you drive your custom rig from some other assets Character Definition.
! ~HumanIK Window
The HIK 'Character Controls' panel is the main interface point.
!!Main dropdowns
* Character : Think of this as the 'retarget to' character definition.  Whatever you select here will have all it's info displayed below.
* Source : Think of this as the 'retarget from' character definition.  The 'motion source'. Only needs set when you actually want to retarget motion.
!!Definition Toolbar 
(but not in the order listed)
Creation:
* Create Skeletal Definition : Create a new one.  Not sure why you'd use this button, since you'd only see it if you were already working on a Skeletal Defintion.
* Create Control Rig : Create the 'Motion Builder Rig' based around your current Skeletal Definition.
* Create Custom Rig Mapping : Create the rig mapping, and allow you to update it based on your own rig.
Modification:
* Lock : This is actually incredibly important:  When you're first setting up a character definition, or modifying one, it should be unlocked.  And the skeleton should be in a t-pose when doing so.  When you're done editing it, you then 'lock' it, and then put it back in the bind pose.  By locking it, it preserves all the angles in the definition based on the T-pose.  If you didn't lock it and put it back in the bind pose, you could see the 'bone colors' change from green to yellow or red based on the new angles.  Lock preserves this.
* Mirror matching : Will try to mirror behavior between the sides of the skeleton.  If your skeleton is A-symmetrical, turn this off.
* Open, Save, Delete : Pretty straight forward.  Save exports an xml file that Open can read.  @@Very Important@@ : While you can Save a preset and load it onto another skeleton, I've found that you can only load it onto 'similar skeletons'.  Meaning, skeletons with similar joint orientations.  While the scale & proportion could be different, if on skeletonA you had +X axis aiming to the child, and on skeletonB, you had +Z, using skeletonA's defintion on skeletonB could cause bad results (flipping during retarget) : Best to make a new mapping for that skeleton type.
!!Custom Rig Toolbar
Presuming it exists:
* Open, Save, Delete : Like above, saves out xml files for reload later.
See here for rolling your own : [[Human IK Custom Rig Node Map]]
! Notes on importing preset data
Per above, you can export xml files for both 'Skeletal Definitions' (SD below) and 'Custom Rig Mappings' (CRM below), and re-load them.
I have found a fundamental problem with the load process in several areas:
* When loading SD and CRM xml presets, not only will it re-create the joint:HIK mappings (good), but it also loads any translate and rotate offsets, rather than auto-generating them on the fly.  
** Unless you are reloading it on the //exact skeleton it was authored on//, this is 'very bad', since those offsets are unique to every skeleton / rig type.  
** The side effect is 'very bad flipping happening during hik retargeting.
** Since there is no UI option to 'ignore offsets', the only 'by hand' way I know to fix this is to remove them from the xml data, since if they're not present, they'll be auto-generated.  Tools can be authored to do this, but by default, this is a really bad implementation.
* During SD preset load, it gives you two options: 'Match all bones with prefix', and 'match only selected bones'.
** The 'match all bones with prefix' is //broken// since it does string name matching left to right, not right to left. 
** For example, let's say you have an eye joint called eyeball_L, and a 'foot ball' joint called ball_L.  ball_L is part of your skeletal definition, eyeball_L is not.  
** But when you import by 'match all bones with prefix', if the eyeball_L joint is found first by the tool before ball_L, it will grab the eyeball_L joint instead, since  it matches the text //right to left//:  {{{<-- eyeBALL_L --}}}.
** The only safe way to load in a prefix via the UI is to pre-select all the joints you want to load onto, and choose the 'match only selected bones' option.
! Notes on adding fingers to custom rig mappings
By default, the Maya ~HumanIK UI in the 'Custom Rig' tab doesn't have any elements to define finger controllers, in the same way the 'Definition' tab.  So how do you add them other than writing a lot of custom tools?
As it turns out, you can, via the 'HIK Menu -> Edit Custom Mapping -> Load UI Configuration...', load other mappings from this folder
{{{
C:\Program Files\Autodesk\Maya2020\resources\CharacterControls
}}}
And {{{CharacterControlsConfig.xml}}} has one with fingers.  But, //not all the fingers//, seriously WTF Maya?  It's possible that xml format could be modified and extended though to include the other fingers.
Looks like you can hack the exported xml preset directly, presuming you know what to put in there. Here's an example of one line:
{{{
    <MappedRetargeter body="RightHandPinky1" className="DefaultRetargeter" destRig="myPinkyController_R01" destSkel="someNamespace:myPinkyJoint_R01" id="90" matrixSource="RightHandPinky1GX" name="CustomRigDefaultMappingNode60" type="R"/>
}}}
Most of that can probably be figured out by looking at stuff in the Character Definition xml. but the one thing I'm not sure how to divine is the IDs:  Each controller in HIK has a unique ID that you need to access and plug in there, and that's the missing piece of the puzzle.  I'm told you can find them in the HIK SDK headers, the others from the maya commands.  Also, the xml's in the CharacterControls dirs hold a lot of them too.
! Nodes
Inputs to {{{HIKCharacterNode}}}
* {{{HIKProperty2State}}}
* {{{joint}}} : many
Outputs from {{{HIKCharacterNode}}}
* {{{HIKSolverNode}}}
* {{{HIKState2GlobalSK2}}}
* {{{CustomRigRetargeterNode}}} many
* {{{HIKState2SK}}}
Sometimes when I orient constraint one object to another (often joints) with 'maintain offset' turned ON, the object being constrained has new rotation values (usually something like 180, -180, 180) applied.  So 'maintain offset' isn't doing its job.  Very frustrating, especially since it seem to happen inconsistently.

I have found the usual culprit is that the two nodes have //different// rotate orders.  If I change the rotate order of node B to match node A, THEN do the orientConstraint, THEN set the rotate order back, it makes it happy, and your rotate values don't change.

Talking to the Autodesk devs (for Maya 2016+) they reported if you use "h.264 codec and mpeg4 codecs that were in .mov", it should work.  Unfortunately neither movies I compress this way work for me, nor does a test video that works for them.  
Tracked it down:  You need to have have quicktime drivers installed.  See here:
http://www.pro-tools-pc.com/apple-quicktime-security-risk/
Specifically the section "Installing the bare minimum Quicktime Components Required for Pro Tools"
But in a nutshell:
* Uninstall Quicktime if it's installed
* Get the download link from the top of this page: https://support.apple.com/kb/DL837?locale=en_US 
* When doing the install, choose "custom"
* Only install the "Quicktime Essentials" option:  Disable everything else.
After that's complete, Maya should now play .mov files just fine.
----
I've read, and need to test, that if Maya's evaluation is in 'Parallel' mode, it can also screw up movie playback.  Need to set it back to DG.
----
Older Maya's:
A buddy of mine ran into this.  He had to encode it as an uncompressed AVI with these values:
*640x480
*Millions Colors (not thousands or millions +)
*30 fps

Presume you have a rig, that is controlling a skeleton.  The skeleton is bound to mesh.  But not all of the joints are bound to the mesh:  Some leaf joints aren't bound to anything.  Before you export to the game, you delete the rig for cleanliness purposes.  When you make the joints visible, any joint not bound to a mesh has snapped to identity (directly on top of its parent).  Why?  How to fix?

It appears that Maya is optimizing too much for its own good.  It seems that if a joint has incoming connections to translate, rotate, etc, //and// it's hidden, //and// if it's not bound, if you delete the nodes that the incoming connections are coming from, Maya doesn't know what to set the joint attributes to, so it sets them to zero.  This snapping them on top of their parent.  Being visible, or having mesh bound to it will leave it in the correct place.

To fix in code, you can make them visible first.  But even then (if this "cleaning step" is happening via code) they may not become visible until after the clean code is done.  To force the visibility, you need to change the frame immediately after, to force a scene refresh.
{{{
showHidden -all;
float $time = `currentTime -q`;
currentTime ($time + 1);
currentTime $time;
}}}
Jump through flaming hoop A, and through flaming hoop B.
This is a common problem with animators, and new versions of Maya.  Often times, they'll hand edit Maya's internal {{{graphEditorPanel.mel}}} script, and put the copy in their {{/documents/maya/scripts}} or {{{/documents/maya/20XX/scripts}}} folders.  When a new version of Maya comes out, their Graph Editor errors, since it's new code in that version of Maya paired to a new script, but their old script is overriding that behavior.
Delete the custom {{{graphEditorPanel.mel}}}, and all should be well.
Tested in 7.01:
*I'm guessing this may be a display bug in Maya:  If the collision object has input creation history, it appears to also show 'OUTPUTS' (thus, the geoConnector) in the channel box.  However, if its history has been delted before it was made to be collision, and thus there are no inputs, after collision has been added, the 'OUTPUTS' section of the channel box just dosn't show up.
*The geoConnector node is still there, just not showing up.  You can see it in the Hypergraph, and check for it with listConnections.  Bothersome!
Never figured out the exact reasoning, but I've seen this from time to time:  It's possible that whatever layout was saved with their scene file isn't jiving with your current version of Maya.  Usually, no matter what one does, they can't get a camera to appear to view though.

To fix, disable Maya from loading the saved layouts on file open (and then reopen the file):
*Window->Settings\Preferences->Preferences:
**UI Elements
***//Uncheck//:  
****{{{When Opening [] Restore Saved Layouts from File}}}
What's going on behind the scenes when you uncheck that box and save the prefs?
{{{
$gUseScenePanelConfig = false;
file -uc false;
optionVar -iv useScenePanelConfig 0;
}}}
It's setting the global int $gUseScenePanelConfig to "off", setting the file command's 'uiConfiguration' flag to off, and setting the optionVar 'useScenePanelConfig' to off.
*The actual window is called {{{toolProperties}}}
*It has a master {{{formLayout}}} called {{{toolProperties_C}}}
*There is a {{{tabLayout}}} called {{{context_T}}} which the custom tool settings go.
*Given the Paint Skin Weights tool as an example, its UI information lives in a {{{columnLayout}}} called {{{artAttrSkin}}}, and this is where you can add your custom UI's
----
Also see:
*[[How can I access \ query the Tool Settings UI?]]
{{{C:/Program Files/Alias/MayaX.X/scripts/others/showEditor.mel}}}
And these are some global vars that define some of its elements:
{{{
global string $gAECurrentTab;
global string $gAttributeEditorWindowName;
global string $gAEMenuBarLayoutName;
}}}
{{{
showEditor($myObjName);
}}}
----
Also see:
*[[How can I create a floating Attribute Editor for the selected node?]]
*From Autodesk support when I queried them on this limitation: 
**Apperantly Microsoft command-line interpreter {{{CMD.EXE}}} can only execute strings up to 8191 characters in length. So when you pass a mel system call to it, that is longer than 8191 characters, it craps out. For more info on it, see [[HERE|http://support.microsoft.com/default.aspx?scid=kb;en-us;830473]].
*For example:
{{{
string $cmd = "someApp someArgs some more args, more args to 8192 characters....";
system($cmd);
// but... nothing happens.... //
}}}
*Workaround: Try creating a .bat file from Maya using the fopen and fprint commands, fill it with the system commands you want, and execute it instead: {{{system("c:/mybat.bat");}}}.
Also see:
*[[Using the system command in Maya, how can I call to a .bat file and have the .bat file execute on the contents of the directory it lives in?]]
If you're not, save your file as a .ma (mayaAscii).  Open the .ma in a text editor, and look for your value in there.  Often you'll perform an operation via the UI, and even when "Echo All Commands" is turned on in the Script Editor, you still aren't to sure what attrs\values were affected by the result.  If you know the value you're looking for, checking the saved .ma file can often reveal a lot about what is going on.
Say you have a list of object names that you try to pick stored in an array.  But you get an error saying that an item in the list dosn't exist.  But you //know// the object //does// exist, what's causing this?
Example:
{{{
// create two null objects:
group -em;  // this is called "null1" by default
select -cl  ;
group -em;  // this is called "null2" by default

// make our list with an extra object name in it, "null3":
string $list[] = {"null1", "null2", "null3"};

// try and pick the list:
select -r $list;
// Error: line 1: No object matches name: null1 //
}}}
*Huh?  Object {{{null1}}} most definitly exists, what's going on?
*I'm not sure if it's a bug, or as designed, but when you try to select a list of objects based on an array, and one or more of the names in the array dosn't exist as an object, it errors that the FIRST object in the array is missing.  This can be //very// confusing to the user.
*Instead, add-select the objects if they exist, and print a warning if they don't:
{{{
select -cl;
for($i=0;$i<size($list);$i++)
    {
    if(`objExists($list[$i])`)
        select -add $list[$i];
    else
        warning ("Object in list dosn't exist: " + $list[$i]);
    }
// Warning: Object in list dosn't exist: null3 //
ls -sl;
// Result: null1 null2 //
}}}
! Original Source Data:
https://github.com/creachadair/imath
https://github.com/AcademySoftwareFoundation/Imath
<<<
~IMath is a library written in portable ANSI C that allows you to perform arithmetic on integers and rational numbers of arbitrary precision. While many programming languages, including Java, Perl, and Python provide arbitrary precision numbers as a standard library or language feature, C does not.
~IMath was designed to be small, self-contained, easy to understand and use, and as portable as possible across various platforms. The API is simple, and the code should be comparatively easy to modify or extend. Simplicity and portability are useful goals for some applications—however, ~IMath does not attempt to break performance records. If you need the fastest possible implementation, you might consider some other libraries, such as GNU MP (GMP), MIRACL, or the bignum library from ~OpenSSL.
<<<
Docs:
https://github.com/creachadair/imath/blob/master/doc.md
!Maya Python Implementation:
It appears that ~IMath is based on ~OpenEXR(?), and Maya may use it for it's Alembic implementation : 
I'm not sure where the official docs on this live, but this seems like a pretty good overview:
https://learn.foundry.com/katana/dev-guide/OpsAndOpScript/Imath/Python.html
{{{
import imath
print imath.__file__
# C:\Program Files\Autodesk\Maya2019\Python\lib\site-packages\imath.pyd

for d in sorted(dir(imath)):
    print d
}}}
{{{stringArrayCount}}}
*Example:
{{{
string $string[] = {"happy", "bob", "angry"}; 
string $strung = "happy";
int $count = stringArrayCount($strung, $string);
// Result: 1 //
}}}
{{{
\\ this will return the shape nodes of the objects based on the selected components:
ls -o -sl;
}}}
Amazingly, seemingly yes.  It's called a "home use licence"
http://usa.autodesk.com/adsk/servlet/ps/dl/item?siteID=123112&id=13812813&linkID=9242258
From that page:
<<<
''Home use license''

''Issue''
How can I run Maya from home?

''Solution''
If you have a network license under subscription, you can apply for a "home use license". 

A home use license is a stand-alone license based on a primary license. The primary license can be either a stand-alone or network license. A home use license based on a stand-alone primary license will have the same serial number as the primary license. A home use license based on a network primary license will have a different serial number, which you must obtain from Autodesk. 

Subscription Contract Managers and Software Coordinators can log on to the Subscription Center to find more details on the Home Use benefit, and to apply for a home use license. To get your license:
#Log on to the [[subscription center|http://subscription.autodesk.com/]]
#Contract Administration.
#Click "Request Home" use.
<<<
Sometimes you'll luck out when Maya crashes, and it'll save a backup of the current scene, in the current state.  It usually tells you this in a dialog box just before it crashes.  

If you //are// so lucky, you can find those files here (on Windows XP):
{{{
C:\Documents and Settings\<userName>\Local Settings\Temp
}}}
Windows 7:
{{{
C:\Users\<userName>\AppData\Local\Temp
}}}
And they usually have fun names like:
{{{
<userName>.20081202.1412.ma
}}}
{{{<userName> . <year> <month> <day> . <hour> <minute> .ma}}}
----
Python code to auto-open that dir in Explorer (For Windows7 paths):
{{{
import os
import subprocess

profile = os.getenv('USERPROFILE')
crashPath = os.path.join(profile, r"AppData\Local\Temp")
subprocess.Popen(['explorer', crashPath])
}}}
*Example:
{{{
listAttr -s -cfo objectName;
}}}
*The -cfo is the important flag.
! hideOnPlayback Attribute
All dagNodes have a {{{hideOnPlayback}}} attribute:  If you check that, the node won't display during playback:  I've noticed a 3%+- increase in rigging performance by enabling this.
Note, this is different than 'Show -> NURBS Curves' (presuming your rig controllers are NURBS curves):  Disabling that 'Show' option appears to have almost no impact on performance.
! Adding controllers to a Display Layer
By adding your anim ctrls to a display layer, and hiding it (toggle the 'V' button off, or the {{{visibility}}} attr), or hide it during playback (toggle the 'P' button off, or the {{{hideOnPlayback}}} attr) , you can get 2-3% improvement in playback speed.

----
Also see:
* [[How can I interactively set a controllers visibility based on mouse proximity?]]
Layer Editor UI Structure:
*{{{global string $gCurrentLayerEditor;}}}  --  {{{formLayout}}}.
**{{{LayerEditorTypeRadio}}}  --  {{{radioButtonGrp}}}.
**{{{DisplayLayerUITabLayout}}}   --   {{{tabLayout}}}
***{{{DisplayLayerTab}}}  --  {{{layout}}}
****{{{formLayout38}}}  --  {{{formLayout}}} -- it's name appears to be generated on the fly 
***** ... and they just keep going ...
***{{{RenderLayerTab}}}   --   {{{layout}}}
****{{{RenderLayerFormLayout}}}}  --   {{{formLayout}}}
***** ... and they just keep going ...
Name of last layer editor button selected:
{{{
global int $gLayerEditorLastButtonSelection;
}}}
UI control type for making layer buttons:
{{{
layerButton;
}}}
Script that holds the guts of the code to build the Layer Editor:
{{{
../MayaXX/scripts/startup/layerEditor.mel
}}}
* The Maya binary file is based on IFF, the [[Interchange File Format|https://en.wikipedia.org/wiki/Interchange_File_Format]], 
* You can parse it via c++ [[MIffFile API class|http://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=Maya_SDK_MERGED_cpp_ref_class_m_iff_file_html]] (No Python API?) and build it against Maya library mode to create a standalone executable.
{{{
C:\Program Files\Autodesk\Maya2020\include\maya\MIffFile.h
}}}
* To parse, it may help to use the IFF library directly. You can find more information in {{{flib.h}}} that shipped with Maya:
{{{
C:\Program Files\Autodesk\Maya2020\include\maya\flib.h
}}}
* {{{flib.h}}} contains some information about IFF format, but its usage is beyond Maya and is not really supported. 
Each line of the IFF binary file format is structured as:
* "keyword", size, dataChunk
To see the parsing of this data in action, see an example here: [[How can I parse a mayaBinary file?]]
----
Also see:
* [[How can I store and read metadata in Maya files, without having to open them?]]
By using the Python {{{inspect}}} module, you can query which //line of a module// is currently being executed.  By using a Python module's special {{{__file__}}} attribute, you can query which //module// is currently being executed.  Combine the two with one of Maya's {{{confirmDialog}}}'s, and you have something useful to tell the user when something bad happens:
{{{
# linetest.py
import inspect
import maya.cmds as mc

def getLine():
    return inspect.currentframe().f_back.f_lineno

def errorWin(message, module, lineNumber):
    mc.confirmDialog( message="%s\n\n%s\nLine : %d"%(message, module, lineNumber), title="Error", button="Bummer..." )
    print "%s\n%s\nLine : %d"%(message, module, lineNumber)

def main():
    if True != False:
        errorWin("You have encountered an error :-(", __file__, getLine() )
        return
}}}
Other than displaying a nice {{{confirmDialog}}} for the user to click through, it will also print the coresponding info:
{{{
import linetest
linetest.main()
}}}
{{{
You have encountered an error :-(
C:\stuff\linetest.py
Line : 15
}}}
Below are some older notes.  See here for Maya 2022+ [[Loading Python packages in Maya]]

Maya runs its own cut of Python, often using a different compiler version than standard system-level Python.  Because of that, getting external Python libraries working in Maya can be a pain.  However, using the below steps, you can find success.

Learnings taken from [[here|https://discourse.techart.online/t/numpy-1-13-1-scipy-0-19-1-for-maya-2018/9121/11]]

In the below example, we'll install {{{numpy}}} in a Python 3.7 version of Maya 2021, specifically this: 
{{{
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
}}}
But I've used the same process to successfully install packages like {{{matplotlib}}}, {{{scipy}}}, {{{pandas}}}, and {{{sklearn}}}.

!Notes
In the below steps, Maya installs executables like {{{pip}}} and {{{easy_install}}} here:
{{{
C:\Program Files\Autodesk\Maya2021\Python37\Scripts
}}}
And the '{{{pip}}} module' itself lives here:
{{{
C:\Program Files\Autodesk\Maya2021\Python37\lib\site-packages\pip\__init__.py
}}}
----
The packages we're installing will end up going here:
{{{
C:\Program Files\Autodesk\Maya2021\Python37\Lib\site-packages
}}}

!Process
Windows 10
Open a cmd shell __as admin__, then run each line one at a time:
{{{
> cd C:\Program Files\Autodesk\Maya2021\bin
> mayapy -m ensurepip
> mayapy -m pip install --upgrade pip setuptools
}}}
Now, to install the packages, one of two different ways.
!!!Install Solution #1
This way, uses the Maya version of the pip //executable//:
{{{
> "C:\Program Files\Autodesk\Maya2021\Python37\Scripts\pip.exe" install numpy
}}}
It worked in Maya 2021, but not 2022, got this error:
{{{
Fatal error in launcher: Unable to create process using '"d:\cpython-3.7.7\install\cpython\3.7.7\relwithdebinfo\python.exe"  "C:\Program Files\Autodesk\Maya2022\Python37\Scripts\pip.exe" install numpy': The system cannot find the file specified.
}}}
!!!Install Solution #2
If it fails, you can try this, which uses the Maya {{{mayapy.exe}}}, calling to the pip //module// (found this solution [[here|https://feedback.autodesk.com/project/forum/thread.html?cap=037743fd817049f18d3a9e014e771fd6&forid={F93EE204-9670-4C0E-AE43-C67B5008E28F}&topid={987E3AD0-E8FC-4122-BAE8-05CB9E55BF04}]]): Be sure to put in the correct Maya-centric {{{\site-packages}}} dir:
{{{
> mayapy -m pip install --target "C:\Program Files\Autodesk\Maya2021\Python37\lib\site-packages" numpy
}}}
!!!Install Results
You should see the results from each line print.
Finally (last line), you should then see something like:
{{{
Collecting numpy
  Downloading numpy-1.19.5-cp37-cp37m-win_amd64.whl (13.2 MB)
     |                                                    | 13.2 MB 6.4 MB/s
Installing collected packages: numpy
Successfully installed numpy-1.19.5
}}}
!Test
Then, launch Maya, and in the Script Editor, confirm it's good to go:
{{{
import numpy as np

a1 = np.array([1, 2, 3])
print (a1, type(a1))
# [1 2 3] <class 'numpy.ndarray'>

print(np.__file__)
# C:\Program Files\Autodesk\Maya2021\Python37\lib\site-packages\numpy\__init__.py
}}}
Nice guide by Eric Vignola [[here|https://groups.google.com/forum/#!topic/python_inside_maya/yTOP-en1fDY]]
Can find version that work up through 2019 here:
https://drive.google.com/drive/folders/0BwsYd1k8t0lEMjBCa2N1Z25KZXc
A new blog post came out that covers part of the install process after I got it working.  Here is part1:
http://around-the-corner.typepad.com/adn/2012/10/building-qt-pyqt-pyside-for-maya-2013.html
----
There are a variety of sources with instructions for installing ~PyQt.  These are simply my notes based on those combined sources.  Resources I pulled from include:
*http://images.autodesk.com/adsk/files/pyqtmaya2013.pdf - Maya 2013 specific instructions.
*http://nathanhorne.com/?p=451 - Maya 2011 & 2012 specific instructions.
*http://www.maya-python.com/2011/09/building-and-installing-pyqt/ - Maya 2012 instructions.
This installation is for Maya 2013 on Windows 7, and I mainly followed the above pdf.
----
#Downloaded these zips, and extracted to a temp folder:
##[[PyQt-win-gpl-4.9.4.zip|http://www.riverbankcomputing.com/software/pyqt/download]] -> {{{\PyQt-win-gpl-4.9.4}}}
##[[qt-4.7.1-modified_for_maya_2013.zip|http://usa.autodesk.com/adsk/servlet/item?id=14561324&siteID=123112]] - "Maya's modified version of the Qt source code" (from the above pdf) -> {{{\Qt-4.7.1-Maya}}}
##[[sip-4.13.3.zip|http://www.riverbankcomputing.com/software/sip/download]] -> {{{\sip-4.13.3}}}
#{{{\Qt-4.7.1-Maya}}} itself contains a compressed tar, and install notes ({{{howToBuildQtOnWindows_m2013.txt}}}):  I extracted the tar here: {{{c:\qt-adsk-4.7.1}}} (per pdf install notes) and then removed {{{\Qt-4.7.1-Maya}}}.
# Build Qt.  The above txt file says to download the sdk from here http://get.qt.nokia.com/qtsdk/qt-sdk-win-opensource-2010.05.exe, but that link seems dead.  So I instead downloaded the sdk from here: http://qt.nokia.com/downloads and installed it.  It is big and huge :-S
##To build qt, the instructions say that I need to unzip the provided {{{openssl-1.0.0e.zip}}},... but that file doesn't exist on my HD and wasn't included with any of the previous downloads.  And now I'm stuck :(
----

Giving up on that, I found a link to a 'Maya 2013 - Windows 7 - 64-bit' precompiled version... here: http://codecg.com/2012/04/15/pyqt4-for-maya2013-on-windows-7-x64/
It was a simple matter to get it working:
#Unzip to temp folder
#Under the {{{\site-packages}}} dir, copy the {{{\PyQt}}} folder along with the three other files ({{{sip.pyd}}}, {{{sipconfig.pyd}}}, {{{sipdistutils.py}}}) to ~Maya-Python's {{{\site_packages}}} dir, which for me lives here:
{{{
C:\Program Files\Autodesk\Maya2013\Python\Lib\site-packages
}}}
Restart Maya, try some example code (copied [[from here|http://nathanhorne.com/?p=183]]):
{{{
import maya.OpenMayaUI as mui
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui
import sip

def getMayaWindow():
    #Get the maya main window as a QMainWindow instance
    ptr = mui.MQtUtil.mainWindow()
    return sip.wrapinstance(long(ptr), QtCore.QObject)

class MayaSubWindow(QtGui.QMainWindow):
    'My custom window, which i want to parent to the maya main window'
    def __init__(self, parent=getMayaWindow()):
        #Init my main window, and pass in the maya main window as it's parent
        QtGui.QMainWindow.__init__(self, parent)

#Show my window
myWindow = MayaSubWindow()
myWindow.show()
}}}
@@Success!!!@@
<<gradient horiz #ddddff  #ffffff  >>This is a [[TiddlyWiki|http://tiddlywiki.com/]].  To really get a grasp on how to navigate it, check out their [[homepage|http://tiddlywiki.com/]].  Quick directions below:
----
Conceptually, each 'subject' I post in the wiki is put in a 'tiddler' (based on how "Tiddlywiki's" work).  The [[Instructions For Use]] section you're in now is a 'tiddler'.  [[Welcome]] is a tiddler.  Tiddlers are like individual blog posts.  I tag each tiddler with keywords relevant to the subject of said tiddler, and you can see those tags on a small box on the top right of each tiddler. Clicking on a tag will display a list of every other subject with that tag. Subjects (tiddlers) are also categorized  on the left menu-bar under larger subject-headings.  ALL the tags from //every// tiddler are in the 'Tags tab', which is on the right most column of the page.

Mainly, you come to the page looking for an answer, and to find it, you ''search'' for tags using keywords that may exist in your problem.  When you find a tag, click on it, and see the resultant subjects... see if one matches what you're looking for.
!SEARCHING FOR DATA
#Browse the "Tags" tab in the //right column// for mel-ish key-words.
**Inside the Tags tab, major ''Categories'' are all in caps, like "[[ATTRIBUTES]]". 
**When picking a 'Tag' with more than one link, you can either:
###'{{{Open all}}}' the topics in that Tag (meaning, fully expand all the topics listed in the middle of that pop-up menu).
###Open a single topic by picking its heading.
###Show all the headings in the Tag by picking: {{{Open tag 'tagname}}}'.
#Use your web browsers screen-search ability ('Ctrl+F' in both Firefox & Internet Explorer) to find key-words you're after (good if 'Tags' tab is open).
#Or start browsing from the ''Categories'' section in the //left column//.  This will open each of their major headings in a new tiddler.
If things get too crowded, use the "close all" from the //right column// to clean up the page.  Or, you can use "close others" from an individual tiddler (This block called "[[Instructions For Use]]" is a tiddler, for example).
----
!COPYING DATA FROM WIKI, TO MAYA
*The way the text has been entered into this wiki, copying code from the source-boxes should work:
{{{
string $source = "source-code in a box"; 
}}}
*Other times it's not in a box, but is still safe for copy:
{{{string $source = "source-code not in a box, but still safe to copy";}}}
*If you copy any code outside of a box that's 'not safe', Maya //may// have a hard time with it's formatting and be angered. Weird
!COPYING A LINK FOR A TIDDLER
If you want to copy a link to a specific tiddler (post) on this wiki:
*Open the tiddler in question
*In the top-right corner of the //tiddler//, press the 'close others' button:  It should be the only tiddler visible now.
*In the top-right corner of the //wiki//, press the 'permaview' button.
*The full path to this specific tiddler will now be in the address bar:  You can copy\paste it.
I reviewed three different solutions for using the Kinect as a mocap source.  I list them in order of usefulness below.
----
Some newer posts:
*[[PyKinect (Yep, Python and Kinect, in Visual Studio yet!)|http://channel9.msdn.com/coding4fun/kinect/PyKinect-Yep-Python-and-Kinect-in-Visual-Studio-yet]]
----
To make sense of some of the tech below:
*[[PrimeSense|http://www.primesense.com/]] has middleware called [[NiTE|http://www.primesense.com/Nite/]] that makes use of [[OpenNI|http://75.98.78.94/]].  They have [[Kinect drivers|https://github.com/avin2/SensorKinect/tree/unstable/Bin]] for it up on github.
**What's interesting is the ~OpenNI page actually links to the ~PrimeSense ~NiTE drivers (see links at bottom of this page).
*Brekel uses ~NiTE (and thus ~OpenNI) to work it's magic, iPi Soft uses ~OpenNI directly.
!ipiSoft
*Main site:  http://www.ipisoft.com/index.php
*Downloads can be found here: http://www.ipisoft.com/downloads_index.php
**Main software is iPi Studio, which does the video->skeletal retargeting.
**To capture the video, you need to download iPi Recorder. 
**It's very important that you uninstall any previously installed ~OpenNI\Kinect drivers\software from any of the below examples and restart your computer before installing their stuff.
**However, [[this wiki|http://www.ipisoft.com/en/wiki/index.php?title=FAQ]] says you can use the drivers and software installed by Brekel, if previously installed.
Notes:
*Wasn't able to export from iPi Studio into Maya using Collada.   FBX worked, but the root axes orientations were goofed up since I'm +Z up, and iPi Studio is +Y up.
!!!Verdict:
*Pros:
**While a slower pipeline than Brekel (since this captures video, then processes it) the quality is exponentially better.
**Allows you to bring your skeleton\mesh into their software, and retarget directly to it:  This allows you to export it back to Maya as a FBX.  This saves you from having to convert from the Kinect skel to your skel.
**Beta version allows for two Kinects, which should help with tracking data that would be occluded by a single Kinect.
**Tracks heads and hands.
*Cons:
**The Kinect-only version is $395, below solutions are free.
**Animation export only, not mesh.
**Things similar to Brekel:  Small capture volume, problems with occluded limbs (but this should be helped by the two-Kinect setup).
!Brekel Kinect
*Process overview: http://torbensko.com/kinect/
*~OpenNI \ Kinect Install guide: (used by above)  http://www.brekel.com/?page_id=170
*Those two pages link to this software:
**''All-in-one ~OpenNI Kinect Auto Installer''
***Download from [[this page|http://www.brekel.com/?page_id=160]]
***Direct link to [[Brekel OpenNI Kinect Auto Installer - Developer Editionv1.3.2.3.exe|http://www.brekel.com/release/Brekel%20OpenNI%20Kinect%20Auto%20Installer%20-%20Developer%20Edition%20v1.3.2.3.exe]]
***This auto-installs the 32-bit versions (which you want even if you're on a 64-bit machine) of ~OpenNI, the Kinect drivers, and NITE.
**''Brekel Kinect'' main application
***Download from [[this page|http://www.brekel.com/?page_id=160]]
***Direct link to [[Brekel Kinect Setup v0.49.exe|http://www.brekel.com/release/Brekel%20Kinect%20Setup%20v0.49.exe]]
**''Maya stuff'':
***Torbensko's 'Maya ~Mo-Cap Scripts' [[here|http://torbensko.com/kinect/KinectMocap_v0.3.zip]]
***Mirage's Maya ~OpenNI Server: [[here|http://mirage.orz.ne.jp/files/MayaOpenNIServer_02Alpha.zip]]
****Note:  This comes with it's own version of {{{OpenNI.dll}}}, which conflicts with the newer one installed above.  Find this install dir, and rename\delete that file.
***There is another port of Torbensko's code [[here|http://www.soup-dev.com/download.html]]:  I had problems getting his working (although I haven't tested this yet)
!!!Install problems:
*Since I'm on a 64-bit machine, I thought I needed 64-bit code.  Turns out this is incorrect:  Even though you can download 64-bit versions of ~OpenNI, NITE, and the Kinect dirvers, the Brekel Kinect software is 32-bit only, and expects 32-bit dll's.
*I had originally installed all 64-bit stuff, and while I got the ~OpenNI examples working, Brekel wasn't.  After re-installing all 32-bit stuff (based on the 'Quick and automatic method' of the 'Install Guide' link listed above), and rebooting my machine (reboot important) it all started working.
----
The below data is to install the 64-bit versions of the software.  As stated above, __these shouldn't be used__, but since I went through the trouble of finding all of them, I thought I'd leave the data here in case it was actually needed in the future.  It should be noted that there are "stable" and "unstable" versions of the data, and they should not be mixed.
*''~OpenNI'' : 
**Need to pick it on [[this page|http://www.openni.org/Downloads/OpenNIModules.aspx]] : "~OpenNI Binaries", "Unstable", "~OpenNI Unstable Build for Windows x64...", and press the download button.
**Direct link to [[openni-win64-1.3.2.3-dev.msi|http://www.openni.org/downloads/openni-win64-1.3.2.3-dev.msi]]
*''Kinect Drivers'':
**Download from [[this page|https://github.com/avin2/SensorKinect/tree/unstable/Bin]]
**Direct link to [[SensorKinect-Win-OpenSource64-5.0.3.4.msi|https://github.com/avin2/SensorKinect/raw/unstable/Bin/SensorKinect-Win-OpenSource64-5.0.3.4.msi]]
*''NITE'' : 
**On the bottom of [[this page|http://www.openni.org/downloadfiles/opennimodules/openni-binaries/latest-unstable/]]
**Direct link to [[NITE-Win64-1.4.1.2-Dev.msi|http://www.openni.org/downloads/NITE-Win64-1.4.1.2-Dev.msi]].
!!!Verdict:
*Couldn't ever get 'Mirage's Maya ~OpenNI Server' working:  When I'd try to connect to it from Maya, it would use up all available memory on my machine, and crash Maya.
*I was able to export .bvh files from Brekel Kinect and import them into Maya via a plugin\script combo I found on ~CreativeCrash, but the quality was very jittery and low:
**Need to be facing camera.
**No fast movements.
**Small capture area.
**No head tracking, hand is questionable.
*Mesh\texture export:  Easy, but quality is low.
!Kinect for Windows SDK
*http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express : Need to install this first, if you don't have it.  Otherwise the next install step will FAIL.
*http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/default.aspx
!!!Verdict:
*Just the SDK:  Doesn't really come with any tools to let you export skeletal animation data:  You have to code it all yourself (unless I'm missing something).
*In the skeleton tracking examples they provide, the quality isn't any better than Brekel.

[[Wing|For Python: Maya 'Script Editor' style IDE]] has the ability to interact with Maya in four different ways.  Each has it's own setup, independent from another.  For clarity, here they are listed below:
#''Send Python & mel commands from Wing to Maya, bypassing the Script Editor''.  Meaning, replace the Script Editor with Wing.  See Docs [[here|How can I have Wing send Python or mel code to Maya?]]
##I use this on a daily basis, and the main reason I use Wing.
#''Remote debug Python code executed in Maya with Wing''.  This lets you use Wing's debugger to step through Python code in an active Maya session.  See Docs [[here|Remote Python debugging in Wing]]
##This has been extremely valuable to track down bugs in large modules.
#''Execute a standalone version of Maya inside Wing's Python shell''.  See Docs [[here|How can I execute Maya's version of Python outside of Maya?]]
##Note, I've yet to have a need for this, as neat as it seems.
#''Get source analysis \ auto-completion of the 'maya' Python package working in Wing''.  
##This allows you when authoring code like this in Wing:
<<<
{{{
import maya.cmds as mc
import maya.OpenMaya as om
}}}
<<<
##To have Wing auto-complete the typed in module\class\function names.
##This is incredibly handy.  See docs [[here|Source Analysis in Wing]].
----
When you are setting these up for the first time, do each separately before moving on to the next, so as not to confuse yourself.
There is a good chance you have an Nvidia __~GeForce__ video card card.  These cards aren't supported by Maya, they expect you to have a much more expensive __Quadro__ card.  I've used ~GeForce cards for years though, with no ill effect.  Every so often though, usually when a new version of Maya comes out, there can be dramatic slowdowns interaction.  Issues I've seen:
*If joints are visible, there is a 1sec pause when picking anything.
*Having 'high quality rendering' turned on in the modeling panels makings any kind of interaction super-slow.
This happened when I switched to Maya 7:  If joints were visible in scene, picking anything had a 1sec pause.  I learned (took until I installed Maya 2008), that by simply updating to the latest drivers from Nvidia, the problem was solved.  Sometimes it takes some fiddling with the driver options too.

http://www.nvidia.com/Download/index.aspx
Starting in Maya 2020(?), you can pop open a little help tool on screen via 'Help -> Search'.

For some reason, it stopped poppup up for me.
But you can call to the code directly to make it appaer:
{{{
import maya.app.TTF.window; 
maya.app.TTF.window.showWindow()
}}}
Or you can use this mel runtimeCommand:
{{{
SearchEngine;
}}}
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.
Pretty darn easy:
{{{
# Python code
import maya.cmds as mc

mc.showHelp("http://www.akeric.com", absolute=True)
}}}
Starting in Maya 2013, they have a 'DG Profiler', that was supported up until around Maya 2016, when they introduced the evaluation graph:  When that happened, a whole new profiler was introduced.  This subject is for the 'old' profiler.  The new profiler can be found here: [[Maya's Profiler]]
----
The DG Profiler window allowed you to see how cheap\expensive node evaluation is.  Great resource.  
Official [[Autodesk Youtube vid|http://www.youtube.com/watch?feature=player_embedded&v=4Nly2Q1mRDM]] on its usage.
It is created via the script:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\dgProfiler.mel
}}}
The window is partly created via the {{{dgTimerSpreadsheet}}} command (no online docs), which is part of the {{{dgProfiler.mll}}} plugin that ships with Maya.
----
One of the benefits it has is the ability to export the info as .csv data, for usage in Excel.  Since I don't use excel too much, here's the overall process for inspecting the data:
*Open Excel and import the csv file.
*Select the top lettered columns:  Double-click on any line in-between them to expand them.
*Drag-select the top entries in the "1" row, from "Node Name" to "Dirty Self (ms)".  In the "Editing" menu-bar section, click on the "Sort & Filter" button, and choose "Filter".
*This will provide a drop-down for each column, allowing you to sort it by different categories.  
*Finally, for some visual fluff, you can easily create charts\graphs.  
**For example, drag select the "Node Name" and "Percent of Runtime" columns from top to bottom.
**Choose the "Insert" menu, then a graph (like Column -> Stacked Column).
**Watch the magic appear on-screen.
----
Talking with Autodesk Support, the terminology used by the DG Profiler is pulled directly from the [[dgtimer command|http://download.autodesk.com/global/docs/maya2013/en_us/Commands/dgtimer.html]], so you can reference those docs for more insight into the values.  But here's a brief overview:
*Timer types:
**self : The time specific to the node and //not// its children.
**inclusive : Time //including children// of the node.
**count : Number of operations of the given metric on the node.
*Metrics : A type of thing being timed.  There are more than what are listed below, but this is all the window exposes.
**compute : The time spent in the node's compute method.
**dirty : The time spent propagating dirtiness on behalf of the node.
So given those definitions, this is my take on what the window values mean.  Note that the reported values are based on the framerange being sampled, //and// the number of samples.  So for example, if you sampled 100 frames 10 times, you'd need to divide all value by 1000 to get the per-frame value.
* % of Runtime : Seemingly self-explanatory.  Talking to the Autodesk dev, this is calculated as {{{(a node’s self-compute time/sum of the self-compute times) * 100}}}
* Number of Computes : Number of calls to the node's compute method.
* Compute Self : Time spent in the nodes compute method.  
* Compute Inclusive : Time spend in the compute method of the node and all the node's children.
* Dirty Self : (This isn't shown in the window, but the data is exported) :  Time spent 'propagating dirtiness' on behalf of the node.
* Dirty Inclusive : Time spent 'propagating dirtiness' on behalf of the node and all its children.
Finally, I've confirmed with the Autodesk dev's that there is a bug in the window itself:  When it lists "(ms)" next to the values, it should really be "(sec)", since that's what the {{{dgtimer}}} command returns.
----
The below values are based on test I've done over time on different node types.
*Number of Computes : per frame
*Compute Self & Dirty Self : milliseconds per frame.  For clarity, .001 isn't 10ms, it's a //thousand of a millisecond//.
Ranges are from high to low.
| Node Type | # of Computes | Compute Self | Dirty Self |
| | | | |
| animCurveTL | 1 | .002 - .001 | 0 |
| animCurveTU | 1 | .002 - .001 | .001 |
| decomposeMatrix | 1 | .006 | .006 |
| ikHandle | .29 | .001 | .006 |
| joint | 2 - .58 |  .013 - .002 | .007 - .002 |
| orientConstraint | .30 | .006 | .002 - .001 |
| parentConstraint | 2 - .58 | .019 - .006 | .008 - .002 |
| pointConstraint | 1 - .30 | .008 - .002 | .004 - .001 |
| transform | 2.98 - .87 | .014 - .003 | .004 - .001 |
| unitConversion | 1.0 | .004 - .001 | .002 - .001 |
Now that I've been recording these values.... I'm seeing some very odd results:  'Simple' scenes with only a handful no nodes in them with few connections will have much larger compute \ dirty self values, compared to scenes that are much more complex with many more connections.  I'm not sure I'm wrapping my head around this yet.
----
''Secondary animation % of runtime:''
I compared three simple systems for implementing 'secondary' animation on a rig:  One setup with 'set driven keys', one setup with an expression, and the other setup with math nodes.  Each setup was designed to have a controller node translate & rotate a target node 2x its amount.
*Expression: 45.2%
*Set Driven Key: 35.8%
*Math nodes (two multiplyDivide) : 16.3%
The values are based on all evaluated keyframe data, 'unitConversion' nodes, and anything else related to each system.  It goes without saying that the math nodes are the clear winner here.  But this was based on a simple behavior of a single linear translation:  For more complex states,that would require more math nodes, I wonder how they'd hold up against something more general purpose like the Set Driven Key?

I expanded on the above test, making the behavior more complex:  Now, the target node needs to translate to 2x the controllers height 50% the way through the controllers animation, then on the second half, translate back down to -2x the height.  The Set Driven Key system was easy to setup:  I just added another set of keyframe data.   In the expression, I introduced an if statement, based on the height of the controller.  The node system required the creation and connection of several new notes:  A total of two multiplyDivide, on condition, and one plusMinusAverage.  Surprisingly, the values changed very little:
*Expression: 47.7%
*Set Driven Key: 31.9%
*Math nodes: 20.5%
Math nodes are still the clear winner.
Take my money
https://www.youtube.com/watch?v=-fU88ESOx7Y
https://gumroad.com/l/BrickIt
Growing list as I find them:
*''python_inside_maya'' Google group.  You must be a member to read and post:
**http://groups.google.com/group/python_inside_maya?lnk=li
*''Python Wiki'':  This is one of my other tiddlywiki's, specifically geared to my notes while learning Python (just like this mel wiki).  While it has nothing to do with mel, it's all about the basics of Python, which the mel user trying to learn Python could find valuable.  Plus, I like to plug my site.
**http://pythonwiki.tiddlyspot.com/
*''~Highend3D'''s [[Maya Python Forums|http://www.highend3d.com/boards/index.php?s=282e161ce612a241c6c68355266cbdbf&showforum=723]]
! Maya 2022+
This only applies to Python 3+, no Python 2.7 support.
Maya has a nice page on this here:
https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-72A245EC-CDB4-46AB-BEE0-4BBBF9791627
The high-level takeaway is:  You can use {{{pip}}} to do all this management, which is great.  If you run the command prompt as an admin, a command like this (making sure to call to Maya's specific cut of python::
{{{
> "C:\Program Files\Autodesk\Maya2022\bin\mayapy.exe" -m pip install numpy
}}}
Should install it here:
{{{
C:\Program Files\Autodesk\Maya2022\Python37\Lib\site-packages
}}}
But if you run the same command as non-admin, it'll go here (what they call the 'user space'):
{{{
c:\users\USERNAME\appdata\roaming\python\python37\site-packages
}}}
HOWEVER, for me, it always installs it to the system-level Python37 dir.  Have been talking to support about this...
You can also 'steer' pip to save it somewhere else, like this, using the {{{--target}}} arg:
(open shell as admin)
{{{
> "C:\Program Files\Autodesk\Maya2022\bin\mayapy.exe" -m pip install numpy --target C:\some\custom\site-packages
}}}
So technically you could do that to move it into some dir managed by your version control, like Perforce.
----
! Older Solutions
There are a number of Python packages that one often wants in Maya (for example, numpy, scipy), but actually getting them installed can be a pain.
Found a elegant solution here:
* https://3deeplearner.com/neural-network-inside-maya/#2
That I will re-explain:
* Add your location of {{{mayapy.exe}}} to the Window's {{{PATH}}} env var.
* Download [[get-pip.py|https://bootstrap.pypa.io/get-pip.py]]
* Open a shell (with admin permissions) & run this from the download folder to install the PIP package manager for //Maya's// version of Python:
{{{
> mayapy get-pip.py
}}}
* Now start searching for packages:
{{{
> pip search h5py
}}}
* And installing them
{{{
> mayapy -m pip install h5py
}}}
They appear to install here, based on your Maya version:
{{{
c:\program files\autodesk\maya20XX\python\lib\site-packages
}}}
The [[Qt Designer|Using Qt Designer]] app gives you a wysiwyg window authoring environment.  Maya provides a [[loadUI|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/loadUI.html]] command to ease the process of loading them.  The below example is a big wrapper around that:  It both loads the UI, and then provides the user the option to either have them be their own window, or a dock (which could be floating as well).  Below I use ~PyMel, but you can just as easily use regular Maya Python.
{{{
import pymel.core as pm

def loadQtUi(path, mode="window", dockFloat=False, docArea="right", dockWidth=256):
    """
    Parameters:
    path : string : Full path on disk to .ui file.
    mode : string : Default "window".  Supports "window" and "dock".
    dockFloat : bool : Default False.  If mode == "dock", floating == True will
        create the doc in a floating window.  Otherwise it will be docked.
    docArea : string : Default "right" : If mode == "dock" and floating == False,
        the part of the ui to dock in.  Supports "top", "left", "bottom", & "right".

    return : string : Name of the Qt window created.
    """
    qtWin = pm.loadUI(uiFile=path)
    if mode == "window":
        pm.showWindow(qtWin)
        # Sometimes the tital-bar is off the top of the screen.  If so, move in:
        if pm.window(qtWin, query=True, topEdge=True) < 1:
            pm.window(qtWin, edit=True, topEdge=32)
        if pm.window(qtWin, query=True, leftEdge=True) < 1:
            pm.window(qtWin, edit=True, leftEdge=32)

    elif mode == "dock":
        # Just like windows, docs need to be named \ removed if they already exist:
        docName = "myCustomDoc"
        if pm.dockControl(docName, query=True, exists=True):
            pm.deleteUI(docName)
        pm.dockControl(docName, allowedArea="all", area=docArea, width=dockWidth,
                       floating=dockFloat, content=qtWin, label="My Custom Dock")

    return qtWin
}}}
In this example, we load the ui as a pane docked on the right side of Maya.
{{{
path = "C:/path/to/my/window/test01.ui"
qtWin = loadQtUi(path, mode='dock', dockFloat=False, docArea="right")
}}}
Note that I believe when authroing windows in Qt Designer, you sould use one of the "Dialog" templates, not the "Main Window" template, since Maya is the 'main window'.
Also, in the designer when you select the "Dialog" object, it's "objectName" field translates to the window name in Maya.
----
Also see:
*[[PySide : Access Qt .ui widget data in Maya]]
In Maya 2016, in the 'Bonus Tools', there's a special {{{shelf_EM.mel}}} that you can load.  This will provide a bunch of custom tools to helping to debug the EG.
Few gotchas:
* After you load the shelf, you'll need to quit Maya, and rename the one from the bonus tools dir you loaded it from, so that the shelf in your prefs that the shelf in the bonus tools dir don't conflict.
* To use the tools in the shelf, you first need to (every time you start Maya) execute:
{{{
source em_commands.mel
}}}
In Maya 2017 this is replaced with a new Profiling window, but there appears to be functionality in the shelf that's missing in the window.
Had a need to lock the translate values of verts on a mesh, to make sure an outsourcer wouldn't go moving them around.  Tool works on selection:
{{{
import pymel.core as pm

def lockSelectedVerts(lock=True):
    sel = pm.ls(selection=True, flatten=True)
    verts = [item for item in sel if isinstance(item, pm.MeshVertex)]
    if not verts:
        pm.displayError("No verts selected to lock")
        return
    pm.waitCursor(state=True)
    try:
        for vert in verts:
            mesh = vert.node()
            index = vert.index()
            for channel in ["px", "py", "pz"]:
                pt = mesh.attr('pt[%s].%s'%(index,channel))
                if lock:
                    pt.lock()
                else:
                    pt.unlock()
    finally:
        pm.waitCursor(state=False)
    vlen = len(verts)
    if lock:
        pm.displayInfo("Locked %s verts"%vlen)
    else:
        pm.displayInfo("Unlocked %s verts"%vlen)
}}}
Python comes with a {{{logging}}} module:
http://docs.python.org/library/logging.html#logging.basicConfig
http://docs.python.org/howto/logging.html#logging-basic-tutorial

Example in Maya where two loggers are created, and updated independently
{{{
import logging

#-------------------------------
# Create the loggers:

# These names can be anything really:
loggerNames = ["mainLog", "errorLog"]
# Where to save the logs on disk:
logFiles = ['c:/temp/mainLog.log', 'c:/temp/errorLog.log']
# Collect the loggers we're about to create:
loggers = []

for i,theLogger in enumerate(loggerNames):
    # Need to get a logger to run from Maya:
    logger = logging.getLogger(theLogger)
    loggers.append(logger)
    # Remove any handles that were made during previous executions, since logger
    # is a singleton.  Otherwise we'll get duplicate lines in our log.
    for handler in logger.handlers:
        logger.removeHandler(handler)

    fh = logging.FileHandler(logFiles[i])
    # Set the level of logging that should be reported:
    fh.setLevel(logging.DEBUG)
    # Note the \r\n that needs to be entered to embed the newline:
    formatter = logging.Formatter('%(asctime)s : %(levelname)s : %(message)s\r\n')
    fh.setFormatter(formatter)
    logger.addHandler(fh)

#---------------------------------------
# Now update the loggers independent from one another:

loggers[0].debug('MainLog: Debug logging message from Maya.')
loggers[0].info('MainLog: Info message from Maya.')
loggers[0].warning('MainLog: Warning message from Maya.')
loggers[0].error('MainLog: Error message from Maya.')
loggers[0].critical('MainLog: Critical error message from Maya.')

loggers[1].debug('Errorlog: Debug logging message from Maya.')
loggers[1].info('Errorlog: Info message from Maya.')
loggers[1].warning('Errorlog: Warning message from Maya.')
loggers[1].error('Errorlog: Error message from Maya.')
loggers[1].critical('Errorlog: Critical error message from Maya.')

#-------------------------------
# Finally, shutdown, so you can edit the files if need be:
logging.shutdown()
}}}
After executing, if you open mainLog.log:
{{{
2015-10-12 16:38:47,188 : DEBUG : MainLog: Debug logging message from Maya.
2015-10-12 16:38:47,190 : INFO : MainLog: Info message from Maya.
2015-10-12 16:38:47,190 : WARNING : MainLog: Warning message from Maya.
2015-10-12 16:38:47,190 : ERROR : MainLog: Error message from Maya.
2015-10-12 16:38:47,190 : CRITICAL : MainLog: Critical error message from Maya.
}}}
And errorLog.log:
{{{
2015-10-12 16:38:47,191 : DEBUG : Errorlog: Debug logging message from Maya.
2015-10-12 16:38:47,191 : INFO : Errorlog: Info message from Maya.
2015-10-12 16:38:47,191 : WARNING : Errorlog: Warning message from Maya.
2015-10-12 16:38:47,193 : ERROR : Errorlog: Error message from Maya.
2015-10-12 16:38:47,193 : CRITICAL : Errorlog: Critical error message from Maya.
}}}
* [[MASH_Python node docs|http://help.autodesk.com/view/MAYAUL/2016/ENU//index.html?guid=GUID-DCB144BD-DFE2-418B-9838-E06963F61A20]] (Maya 2016)
* [[MASH Python Youtube Tutorial|https://www.youtube.com/watch?v=ij5ke9ftyH8&index=25&list=PLd9alCBg4-McRCZ1BBhYoLqmpZIq4GPy8]]
This is the default Python that comes with the {{{MASH_Python}}} node:
{{{
import openMASH

#initialise the MASH network data
md = openMASH.MASHData(thisNode)

#this is how to get the frame number
frame = md.getFrame()
#and this gets the number of objects in the network
count = md.count()

#add the index to the Y position
for i in range(count): 
    md.outPosition[i].y=i

#tell MASH to write the network data
md.setData()
}}}
{{{OpenMASH}}} appears to have one main item to instance:  {{{MASHData}}}.  
{{{{OpenMASH}}} lives here (on the Mac):
{{{
/Applications/Autodesk/maya2016.5/plug-ins/MASH/scripts/openMASH.py
}}}
When executed from within the {{{MASH_Python}}} node, you pass {{{MASHData}}}, the {{{thisNode}}} instance to act on, which is the stringname of the {{{MASH_Python}}} node currently being edited.  Or you could do this in the Script Editor, presuming you made a node with this name: {{{MASH1_Python}}}.
{{{
import openMASH
md = openMASH.MASHData("MASH1_Python")
}}}
This also makes it easy to query custom attrs made on this node itself.  For example, if you addded a {{{pyRadius}}} float, you could query it like so inside the Python node editor:
{{{
import maya.cmds as mc
rad = mc.getAttr('%s.pyRadius'%thisNode)
}}}
Note that attrs & code setup this way only refresh on frame-change, or script re-execute.
!~MASHData
The Python code is evaulated & flushed every frame, so you can't store data it needs in the script itself:  If you need to store data, make a custom attr on a node an set\get it as needed, or use Python's json, or pickle.
I'm told that  all position, rotation, and scale attrs are API ~MVector's behind the scenes.
Also, all datatype use {{{Maya.api.OpenMaya}}} (API 2.0) unless they are unsupported, where it defaults to {{{Maya.OpenMaya}}}.
!!Attributes for query:
* id
* position 
* rotation
* scale
* visibility
For example, to query the fifth object's position in your MASH network, you can use the code:]
{{{
md.position[5]
}}}
You can also query a specific axis. For example, to query the x value only:
{{{
md.position[5].x
}}}
!!Attribues for setting:
* outPosition
* outScale
* outRotation
* outId
* outVisibility
They follow a similar syntax as above.
!!Helper functions:
* count() : Returns the number of MASH points.
* getFrame() : Returns the current frame number.
* getFalloffsCount() : Returns the number of falloff objects connected to this Python node.
* getFalloff(int index) : Returns a list of strengths from the specified Falloff object.
* getVectorArray(string channelName) : Returns the values of the channel specified in the argument as an array of the respective type.
* getDoubleArray(string channelName) : See above
* getIntArray(string channelName) : See above
* getMatrix(int id) : Returns a transformation matrix for the point specified by the id.
* setMatrix(matrix, int id) : Sets the specified matrix to the point specified by the id.
!! Advanced Functions
!!!Inputs
* color
* uvTile
* frame
* isAnimated
* velocity
!!!Outputs
* outColor
* outUvTile
* outFrame
* outIsAnimated
* outVelocity
!!Other unlisted items:
* fnNode
* getData
* getFalloff
* initialised
* nodeName
* resizeMArray
* setData
* setDoubleArray
* setIntArray
* setPointCount
* setVectorArray
* thisNode
* usingDynamicArrays
Scratchpad as I learn MASH.
----
MASH Docs (all Maya 2016):
* [[MASH Procedural Effects|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-5F45C398-D87D-424E-9F00-51D9FAB5A40B-htm.html]]
* [[MASH Node Overview|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-D4FECFDC-F91A-4BDC-A1B0-A24EB087B2DD]] 
* [[MASH Utilities|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-4CAEABB4-2D1B-473B-B5BD-F2FB00AE8CB4]] : MASH Utilities are a set of helpful tools unique to MASH networks. You can access them by going to Create > MASH > Utilities.
* [[MASH Workflows|http://help.autodesk.com/cloudhelp/2016/ENU/Maya/files/GUID-3E99C2B3-E129-41FE-A751-E51FCD0011AA.htm]] : Intro tutorials where you make a spinning pixleated globe.
* [[MASH Reference|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-52154221-F099-46A1-8B53-048110471B76]]
Videos:
* Ian Waters [[MASH|https://www.youtube.com/playlist?list=PLd9alCBg4-McRCZ1BBhYoLqmpZIq4GPy8]] Youtube Playlist
* Ian Waters [[Other Cool MASH|https://www.youtube.com/playlist?list=PLd9alCBg4-MfGHSEuGY_LAGIapNniZJBd]] Youtube Playlist
* Ian Waters [[Type|https://www.youtube.com/watch?v=qZ25BiH2Mw0&list=PLd9alCBg4-MepuSfcPbKYprunmx52sCE9]] Youtube Playlist
* Maya Learning Channel [[Motion Graphics|https://www.youtube.com/playlist?list=PL8hZ6hQCGHMUn5NdkAQpWBG7xxGFP480q]] Youtube Playlist
----
Make sure the plugin is installed.  First shipped with 2016 ext2
Create -> MASH
----
* Select then nodes you wan to instance before creating the MASH network.
* The mesh is hidden by default, and two base MASH node are created ~MASH_Distribute -> ~MASH_Waiter.
** Then, based on the options picked, other nodes are created & connected:
** If 'Instancer': 
*** ~MASH_Distribute -> ~MASH_Waiter -> instancer 
*** The mesh to be instanced are also connected to the instance node
** If 'Mesh' (also known as repros) : 
*** ~MASH_Distribute -> ~MASH_waiter, -> ~MASH_color -> ~MASH_time -> ~MASH_repro -> output 'repro' mesh
*** Repro's support Render Layers. while instances don't.
!Node notes:
See [[MASH Node Overview|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-D4FECFDC-F91A-4BDC-A1B0-A24EB087B2DD]].
* [[instancer|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-033261E4-44EB-4721-9A47-6CCD25F334DE]] : This node manages the instances of scene objects created by a MASH network.
** It is a DAG node that supports parenting.
* [[MASH_Audio|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-3DAA8F69-8BDC-4393-9EB8-690583C2D3E5]] : Use an audio input file to drive animation.
* [[MASH_Color|http://help.autodesk.com/view/MAYAUL/2016/ENU//index.html?guid=GUID-6322679A-61CD-4747-8FB0-0EBDE787F1EA]] : Customizes the CPV data of meshes in the MASH network.
* [[MASH_Curve|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-3D6115AD-0A3E-4263-834E-CE326D74EC21]] : Animate objects along a curve.
* [[MASH_Delay|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-6A522D93-C2E8-4DA4-ADAC-732B84067CF5]] : Offsets an object's existing animation in time.
* [[MASH_Distribute|http://help.autodesk.com/cloudhelp/2016/ENU/Maya/files/GUID-B718F1FE-8688-4A57-95DD-5B22C4D40F1A.htm]] : which handles the initial number and arrangement of instances created by the Instancer.
** Mesh : Distribute via a mesh
*** Voxel : Fill the mesh with Voxels
**** Note : If the voxels don't fill the mesh correctly, it could be because of 90 deg angles and hard normals: Select the mesh and go 'Mesh Display -> Soften Normals' : This should make the voxelator much happier.
* [[MASH_Explode|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-3D03288F-C0C7-49C8-AEFC-B3367B7AD60E]] : Extracts mesh faces and transforms them according to MASH points.
* [[MASH_Falloff|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-95AA0392-3C50-403E-848F-F1A12ADABC36]] : Creates and controls a falloff volume using an input object for its shape.
** This is an actual Transform/Shape node you can move around.
* [[MASH_Flight|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-94DB481F-FFD5-4C5D-B2CF-8011DCD8C547]] : Approximates flocking / schooling behavior.
* [[MASH_ID|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-D3B66995-CC50-4D20-B13E-2B7F44556509]] : Customizes the way that instanced objects are assigned to MASH points.
* [[MASH_Influence|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-184B5098-8348-4347-A92C-6F0C7F404DF2]] : Use guide objects to influence the transforms of your MASH network.
* [[MASH_Merge|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-D8F5E3EE-1D73-45D7-85E5-3920A13FF79B]] : Merges two MASH networks together.
* [[MASH_Mute|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-2BD56CAF-8ADC-4BAF-9C0F-370224BED193]] : Controls the strength of connected nodes.
* [[MASH_Noise|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-4629DB22-3785-4461-864B-580449FAD0E5]] : Adds randomized noise to the network.
* [[MASH_Offset|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-F54566EF-496A-4311-B49F-8D8A61295929]] : Offsets object transforms.
* [[MASH_Orient|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-4B7A8FE1-CBC8-4C49-9B69-19EA853B8E0E]] : Point objects in their direction of travel, or toward another object.
* [[MASH_Points|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-3EC822A0-C029-4CAB-8CDA-14D534FFFCEC]] : Displays points and their ~IDs.
* [[MASH_Python|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-DCB144BD-DFE2-418B-9838-E06963F61A20]] : Allows you to write custom python scripts.
** See my subject here: [[MASH : MASH_Python node]]
* [[MASH_Random|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-110C0277-7793-43C6-9B9D-13C2805B8D35]] : A random number generator.
* [[MASH_Replicator|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-34E5E335-898B-4BB4-89E7-49807CB5E9EA]] : Duplicates the MASH network and allows you to perform transformations on the replicants.
* [[MASH_Repro|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-29709E2E-C73D-447F-B055-1FEDA98EEC72]] : Is used to control various aspects of the meshes created when 'Geometry' Type is set to Mesh during a MASH network's creation
** Every mesh selected when a MASH network is created is connected to this node and given an ID.
** To use vert color on repro mesh, you must:
*** Enable 'Color Per Vertex' on this node.
*** Pick your 'repro mesh' and enable 'Mesh Display -> Toggle Display Color Attributes' (it will turn black).
*** Select the Mash_Color node, and 'enable' it (it will turn white by default).
* [[MASH_Spring|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-3AA2961A-A5A4-45C2-AA12-0CACF8806C30]] : Adds spring to objects' animation.
* [[MASH_Symmetry|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-CDEB4D3D-8B7C-408E-A0F0-26491FF5F30B]] : Reflects an entire MASH network across a specified axis.
* [[MASH_Time|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-BB035215-1676-4A8A-96EA-807F689E30D8]] : Offsets component animation.
* [[MASH_Trails|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-6CBD271F-853A-4EBC-8D06-F8B2B2C56235]] : Creates a trail of quads behind or between points.
* [[MASH_Transform|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-5B0F4B4B-6FFD-4D39-900A-A80229C949F5]] : Moves/Rotates/Scales the entire network.
* [[MASH_Trig|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-5D8DBB64-0CC1-4DE3-9F20-C7ABDACCC0AC]] : Animates objects along a Sin/Cos/Tan wave.
* [[MASH_Visibility|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-8531AB0E-65ED-4E87-A2AD-A578B32ABC41]] : 	Controls the visibility of objects.
* [[MASH_Waiter|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=GUID-6B3FAF64-960F-4C23-A883-98446AD88ABD]] :  Acts as the hub for the MASH network that enables you to add other nodes. 
** Although its not a DAG node, you can still pick it in the Outliner (like a set).  Usually called {{{MASH1}}} by default:  You should rename this to match the network you're working on.


2d recipe:
*http://code.activestate.com/recipes/578047-area-of-polygon-using-shoelace-formula/
----
http://code.activestate.com/recipes/578275/
{{{
def poly_area2D(poly):
    total = 0.0
    N = len(poly)
    for i in range(N):
        v1 = poly[i]
        v2 = poly[(i+1) % N]
        total += v1[0]*v2[1] - v1[1]*v2[0]
    return abs(total/2)
}}}
{{{
poly = [[0,0], [0,1], [1,1]]
print poly_area2D(poly)
.5
}}}
You could use this to easily find the area of a tri's mapped uv area:
{{{
import maya.cmds as mc

def printTriUvArea():
    faces = mc.ls(mc.polyListComponentConversion(mc.ls(selection=True, flatten=True), toFace=True), flatten=True)
    for face in faces:
        uvs = mc.ls(mc.polyListComponentConversion(face, toUV=True), flatten=True)
        if len(uvs) != 3:
            print("Face has %s uvs: This tool only works on triangles/faces with 3 verts: %s"%(len(uvs), face))
            continue
        points = []
        for uv in uvs:
            coords = mc.polyEditUV(uv, query=True, uValue=True)
            points.append(coords)
        uvArea =  poly_area2D(points)
        print("%s UV area: %s"%(tri[0],uvArea))
}}}
----
The original code requires a [[numpy|http://numpy.scipy.org/]] install.  I've re-authored it below to use Maya's {{{OpenMaya.MVector}}} class
http://code.activestate.com/recipes/578276/
{{{
import math
from maya.OpenMaya import MVector

def normal(p1, p2, p3):
    va = MVector(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z)
    vb = MVector(p3.x - p2.x, p3.y - p2.y, p3.z - p2.z)
    n = va^vb # cross
    return n

#area of 3d polygon
def poly_area3D(poly):
    if len(poly) < 3: # not a plane - no area
        return 0
    total = [0, 0, 0]
    N = len(poly)
    vecs = [MVector(points[0], points[1], points[2]) for points in poly]
    for i in range(N):
        vi1 = vecs[i]
        vi2 = vecs[(i+1) % N]
        prod = vi1^vi2 # cross
        total[0] += prod.x
        total[1] += prod.y
        total[2] += prod.z
    nVec = normal(vecs[0], vecs[1], vecs[2])
    tVec = MVector(total[0], total[1], total[2])
    dot = abs(tVec*nVec) # dot
    return math.sqrt(dot)/2
}}}
{{{
poly = [[0,0,0], [0,4,0], [4,0,0]]
print poly_area3D(poly)
# 8
}}}
*https://3deeplearner.com/
** [[Is AI hype? What is Machine Learning and what should I expect of it as a Technical Director or Technical Artist?|https://3deeplearner.com/ai-is-hype/]]
** [[What in the world is a Neural Network? And what can it do for me?|https://3deeplearner.com/what-is-nn/]]
** [[How to use a Neural Network inside Maya|https://3deeplearner.com/neural-network-inside-maya/]]
*** https://3deeplearner.com/neural-network-inside-maya/#2 < How to actually get Keras running in Maya
** [[Mapping Rigs-to-Rigs with Neural Nets|https://3deeplearner.com/mapping-rigs-to-rigs-with-neural-nets/]]
*** [[Mapping Rigs-to-Rigs with Neural Nets|https://www.youtube.com/watch?v=15V70JTboxY]] : Youtube
***[[ikrig| https://github.com/gustavoeb/ikrig]] : Github
*** [[|Ubisoft like IK Rig implementation for Machine Learninghttps://www.youtube.com/watch?v=BaOKa0cT1mo]] : Youtube
** [[CMU Motion Capture Dataset in FBX Format|https://3deeplearner.com/cmu-fbx/]]
*** [[CMU MoCap Dataset in FBX Format + fixes|https://www.youtube.com/watch?v=TdyU3qaYNLg]]
*** http://mocap.cs.cmu.edu/
** [[3DeepLearner SIGGRAPH Series - Part 01 - FDDA|https://www.youtube.com/watch?v=NKjfdR2fmVs]] Youtube
* https://course.fast.ai/
** https://github.com/fastai/course-v3
* [[Accurate Markerless Jaw Tracking for Facial Performance Capture|https://www.youtube.com/watch?v=wHZH4WsEcrM&feature=youtu.be]] : Youtube
* [[Subspace Neural Physics: Fast Data-Driven Interactive Simulation|https://www.youtube.com/watch?v=yjEvV86byxg]] : Youtube
* [[Building ANN to solve Inverse Kinematics of a 3 DOF Robot Arm|https://medium.com/@kasunvimukthijayalath/building-ann-to-solve-inverse-kinematics-of-a-3-dof-robot-arm-2b1c3655a303]]
!Notes from 3deeplearner
High Level overview:
*''Artificial Intelligence'' : A field in computer science that studies synthetic intelligent agents that can perceive the environment, learn from it, and act upon it.
**''Machine Learning'' :  A subset of AI that studies techniques that make it possible for computers to learn from data, instead of using explicit programming instructions
***''Neural Networks'' : Learn patterns from data.
****''Deep Learning'' : an approach to creating and training neural networks that rely on more complex network structures and less on things known as 'parameter tuning'/'feature engineering'.
!!Machine Learning
Machine Learning is an AI subset, two main types:
*Statistical
**PCA Network
**HMM Network
**Bayesian Network
**SVM
*Connectionist
**Neural Network < What 3deeplearner focuses on
ML Data :  Can be:
*Labeled
** Can use 'supervised' learning algorithms : Useful in solving problems where one wants to get a specific answer (output) from certain data (input).  See https://ps.is.tuebingen.mpg.de/publications/streuber-siggraph-2016
*Unlabeled
** Can use 'unsupervised' learning algorithms.  Useful in finding existing patterns in the data.  See http://smpl.is.tue.mpg.de/
!!Neural Networks
Smallest unit of a NN is a neuron (aka 'artificial neuron, or perception)
(multiple) INPUTS -> (multiple) WEIGHTS -> ACTIVATION -> OUTPUT
*Inputs : Can have many, scalar values. 
*Weights : Inputs are filtered (multiplied) by weights (other scalar values).
*Activation : The sum of the filtered inputs results in neuron's activation.  This can be remapped by an 'activation function'.
*Output : The resultant activation is the neuron's output.
@@The process of getting the correct weights is called ''learning''.@@
Single neurons cannot deal with [[linearly non-separable|https://en.wikipedia.org/wiki/Linear_separability]] problems. For these kinds of problems, we mesh neurons into a network.
Neurons can be split into layers of activation:
INPUTS -> WEIGHTS -> 'Hidden (activation) layer' -> 'Output (activation) Layer' -> OUTPUT
You can have as many neurons in the 'hidden layer', and you can have more than one 'hidden layer'.
Technically a NN can approximate any function.
!!!NN Learning
Must get the 'correct weights' so the inputs in turn generate the expected outputs.
*Backpropagation: The process of updating the weights based on the observed data.  It presumes you have a database of observations. How it works:
** 1 : The algorithm assigns a random number to each weight.
** 2 : Using the dataset, it checks for every sample, how far and in which direction the networks predicted output differ from the known outputs.
** 3 : Updates the weights just a bit in the direction that would make the networks results closer to the actual outputs.
** 4 : Repeat steps 2 & 3 until the results are good enough.
The distance to the correct result is calculated by the 'loss function'.
Each step of going through all the sampes in the data and updating the weights is called an 'epoch'.  How many are needed can vary a lot.  The size of the steps taken in each update is called the 'learning rate'.  If you don't have enough memory, divide the data into 'mini-batches'.
!!!NN Applications
Good for:
* Generate Numbers: This is called 'regression'.  In CG? :
** Generate color values for RHD probes based on reference images, create height maps from albedos, character poses from high-level parameters like speed and movement type, motion phase, etc.
* Classify Things.  In CG? :
** Motion types, objects in images, erroneous jobs in a render farm, gestures of user input, etc.
The difference between 'regression and classification' networks are based on the 'activation functions' used in the output layers.
*Regression networks use 'linear' outputs'.
** Means the neurons will be remapped linearly and will be able to represent any number from minus to plus infinity.
*Classification networks use 'sigmod' or 'soft-max' activations.
** Sigmod go from zero to one, soft clamping both ends.
** Soft-Max used when you have more than one neuron in the output layer, and one should be classified as a winner. Values are normalized, so the sum of all activations will be one.
Some networks are better at things than others:
* Convolutional NN : Image classification
* Recurrent NN : Sequenced Data
!Installing in Maya
Based on this doc:
https://3deeplearner.com/neural-network-inside-maya/#2
*Add your mayapy.exe to the Window's PATH.
*Install the PIP manager via mayapy.exe
*Install all the dependencies.
Where do they go?  Appears to be here:
* C:\Program Files\Autodesk\Maya2019\Python\Scripts  < exe's for pip, theano
* C:\Program Files\Autodesk\Maya2019\Python\Lib\site-packages  < numpy, scipy, theano, keras
Notes:
* Many of the examples for install have {{{PATH}}} in the name, like:
{{{
mayapy -m pip install PATH/h5py-2.8.0rc1.post0-cp27-cp27m-win_amd64.whl
}}}
I had to remove the {{{PATH/}}} to get it to work.
Was unable to install the above path for {{{H5PY}}}, got errors.


<<gradient horiz  #ddddff  #ffffff  >>
[[Welcome]]:
[[About mel wiki]]:
[[Instructions For Use]]:
[[Latest Updates|History]]:
[[Maya Links]]:
[[About Author|WarpCat]]:
[[Copyright Information]]:
----
''Subscribe:''
''[[RSS|http://mayamel.tiddlyspot.com/index.xml]]'' [img[http://farm1.static.flickr.com/197/492915948_b4a9f3233e.jpg?v=0]]
----
[[All Subjects]]
----
''Categories:''
[[ANIMATION]]
[[API]]
[[ATTRIBUTES]]
[[BATCH]]
[[BIFROST]]
[[CLASSIFICATION]]
[[COMPONENTS]]
[[CONTEXT]]
[[DEFORM]]
[[DYNAMICS]]
[[EXPRESSION]]
[[EVALUATION GRAPH]]
[[FILE OPERATION]]
[[FUNDAMENTALS]]
[[GENERAL]]
[[HARDWARE]]
[[INFO]]
[[MASH]]
[[MATH]]
[[NETWORKING]]
[[NURBS]]
[[POLYGON]]
[[PREFS]]
[[PYTHON]]
[[PYMEL]]
[[QT]]
[[REFERENCING]]
[[RENDERING]]
[[RIGGING]]
[[SCRIPTING]]
[[SELECTION]]
[[SHADING]]
[[SIMULATION]]
[[SOUP]]
[[STRING]]
[[SYSTEM]]
[[TEXTURE]]
[[TIME]]
[[TRANSFORMATION]]
[[TROUBLESHOOTING]]
[[TUTORIAL]]
[[UI]]
[[VARIABLES]]
[[WEB]]>>
I got tired of running into rigs where everything is locked and non-keyable.  Blam, fixed:  For the selected objects, make all the standard trans, rot, scale (& vis) channels unlocked, and keyable:
{{{
# Python code
import maya.cmds as mc

sel = mc.ls(selection=True)
attrs = ['tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz', 'v']
objAttrs = ['%s.%s'%(obj, attr) for obj in sel for attr in attrs]

map(lambda x.setAttr(x, keyable=True, lock=False), objAttrs)
}}}
This is a pretty great bit of source code to get Tetris running in Maya via PySide
https://vimeo.com/659776763
https://github.com/golubevcg/Tetris_in_Maya_with_PythonAPI
http://golubevcg.com/post/tetris_in_maya_with_pyside_python_api_and_cmds
When I need a refresher course on mathematical symbols:
* https://en.wikipedia.org/wiki/List_of_mathematical_symbols
When I first authored this subject I didn't use matrices very often.  But I've found the more I learn about them, the more I end up using them...
Also see my other subjects: 
*[[Working with matrices with Python]]
*[[Different ways to access matrix and apply data]]
----
Launch help for the root {{{dagNode}}} type node, which has the base matrix attrs:
{{{
showHelp -d "Nodes/dagNode.html
}}}
Launch help for the {{{transform}}} node (lots of matrix info):
{{{
showHelp -d "Nodes/transform.html";
}}}
Web:
*I made an image describing how 4x4 matrix multiplication works.  You can find it on my [[blog here|http://www.akeric.com/blog/?p=693]], or on [[flickr here|http://www.flickr.com/photos/warpcat/3956108191/]]
*[[chapter 9|http://books.google.com/books?id=_ztrTBEDK28C&lpg=PA191&ots=r56b7bLPzY&dq=python%20transformation%20matrix&pg=PA181#v=onepage&q=python%20transformation%20matrix&f=false]] from the book [[Beginning Game Development with Python and PyGame: From Novice to Professional|http://www.apress.com/book/view/9781590598726]] has a really well described overview.
*http://eddieoffermann.com/blog/tag/matrix-multiplication/
*http://www.185vfx.com/2003/03/convert-a-3d-point-to-2d-screen-space-in-maya/
*Online video lectures from [[MIT|http://ocw.mit.edu/OcwWeb/Mathematics/18-06Spring-2005/VideoLectures/]]
*http://knol.google.com/k/koen-samyn/matrices-for-3d-applications/2lijysgth48w1/3#
*Two different blog posts by Andy Nicholas: [[How to Read a 3×3 Matrix|http://www.andynicholas.com/?p=861]], [[Rotating Points With a 3×3 Matrix|http://www.andynicholas.com/?p=911]]
----
''Quick 4x4 matrix review of an identity matrix'' (presuming +X is 'left', +Y is 'up', and +Z is 'forward').  To visualize the x, y, and z-axes, use your hand:  Hold your right hand in front of you, pointer straight ahead, thumb up, and middle-finger left:  Middle-finger = +X-axis, thumb = +Y-axis, pointer = +Z-axis.
*The fourth //column// '{{{W}}}' is for the most part unused, it is always {{{(0, 0, 0, 1)}}}:  This is called the 'homogeneous row'.  W =  A scalar (single, compared to array) value (also known as the 'homogeneous coordinate') used for conversion from Euclidean to Cartesian coordinates.  I'm using those terms because I read it somewhere, not because I necessarily understand what they're talking about :-P
*The first three //rows// represent the vectors that point in the x, y, and z directions of the transformation, which represent the //axis// in 3D space.  Effectively the orientation/heading of the 'local axis' of a transform in Maya.  They also calculate //scale//.
*The fourth //row// ({{{w}}}) represents the translation, or point in space when transformed by this matrix.  This is the 'homogeneous row':
| || X | Y | Z | W || notes |
| || | | | || |
|x-axis || 1 | 0 | 0 | 0 || 'left', mag=scaleX |
|y-axis  || 0 | 1 | 0 | 0 || 'up', mag=scaleY |
|z-axis  || 0 | 0 | 1 | 0 || 'forward', mag=scaleZ |
|(w) translate || 0 | 0 | 0 | 1 || |
(remember, the right 'W' column is ignored for what we do in Maya)
*''translation'':  Derived from the bottom row.
**translateX, translateY, translateZ =  {{{(0 ,0 ,0)}}}
*''orientation'':  Vectors that point in the x, y, and z directions of the transformation.
**{{{x-axis}}} : defines the current heading of the  'left' vector = {{{(1, 0, 0)}}}
**{{{y-axis}}} : defines the current heading of the 'up' vector = {{{(0, 1, 0)}}}
**{{{z-axis}}} : defines the current heading of the 'forward' vector = {{{(0, 0, 1)}}}
*''scale'' : The magnitude of each x, y, and z-axis defines the scale = {{{(1, 1, 1)}}}
**scaleX = {{{mag(x-axis)}}} = {{{mag(1, 0, 0)}}} = 1.0
**scaleY = {{{mag(y-axis)}}} = {{{mag(0, 1, 0)}}} = 1.0
**scaleZ = {{{mag(z-axis)}}} = {{{mag(0, 0, 1)}}} = 1.0
*''shear'' : still working on that one ;)
''Some examples, based on that info:''

Where individual rotation axis data is stored in the matrix.  Meaning, when you perform an Euler rotation on a node on an individual axis, what part of the matrix does it modify?  Do you notice that the row and column for the transformed axis (excluding translation operations) aren't modified during the transformation? (since the other axes are transforming //around it//.)
__X-axis__ rotation:
| || X | Y | Z | W |
| || | | | |
|x-axis || 1 | 0 | 0 | 0 |
|y-axis  || 0 | @@1@@ | @@0@@ | 0 |
|z-axis  || 0 | @@0@@ | @@1@@ | 0 |
|(w) translate || 0 | 0 | 0 | 1 |
__Y-axis__ rotation:
| || X | Y | Z | W |
| || | | | |
|x-axis || @@1@@ | 0 | @@0@@ | 0 |
|y-axis  || 0 | 1 |0 | 0 |
|z-axis  || @@0@@ | 0 | @@1@@ | 0 |
|(w) translate || 0 | 0 | 0 | 1 |
__Z-axis__ rotation:
| || X | Y | Z | W |
| || | | | |
|x-axis || @@1@@ | @@0@@ | 0 | 0 |
|y-axis  || @@0@@ | @@1@@ |0 | 0 |
|z-axis  || 0 | 0 | 1 | 0 |
|(w) translate || 0 | 0 | 0 | 1 |
__Translate XYZ__:
| || X | Y | Z | W |
| || | | | |
|x-axis || 1 | 0 | 0 | 0 |
|y-axis  || 0 | 1 |0 | 0 |
|z-axis  || 0 | 0 | 1 | 0 |
|(w) translate || @@0@@ | @@0@@ | @@0@@ | 1 |
*Identity matrix rotated 45 degrees along the Z axis.  Compare this to the above chart that shows where those values will be stored.  Note how the z-axis remains the same {{{(0, 0, 1, 0)}}} since it hasn't changed orientation.  But both the x-axis and y-axis now have a new heading? (since they were transformed by the z-axis rotation)  And notice how all their magnitudes still equals {{{1.0}}}?  Scale remains at {{{(1,1,1}}}).
**Visualize this using the 'left hand rule' described above:  Start with your left hand at identity.  Imagine there is a point on the tip of each finger corresponding to each identity vector.  Now rotate your hand along the z-axis (pointer) //clockwise// 45 degrees.  Your pointer is still pointing the exact same direction {{{(0, 0, 1)}}}, but the points defined by your thumb (+y) and middle finger (+x) have moved in space.  But they're all still the same distance away from the middle of the transform, so their magnitudes still each equal {{{1.0}}}.
| @@.707107@@ | @@-.707107@@ | 0 | 0 |
| @@.707107@@ | @@.707107@@ | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 1 |
*Identity matrix scaled to be half as wide, and twice as high:
scaleX = {{{mag(x-axis)}}} = {{{mag(.5, 0, 0)}}} = .5
scaleY = {{{mag(y-axis)}}} = {{{mag(0, 2, 0)}}} = 2
scaleZ = {{{mag(z-axis)}}} = {{{mag(0, 0, 1)}}} = 1
| .5 | 0 | 0 | 0 |
| 0 | 2 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 1 |
----
''Maya transformation matrix:''
Simple version:
* Scale Matrix * Rotate Matrix * Translate Matrix = final matrix
Long version:
{{{matrix = SP *(-1) S * SH * SP * ST * RP *(-1) RA * R * RP * RT * T}}}
{{{*}}} = matrix multiplication, (-1) = matrix inversion
*SP = scale pivot point : point around which scales are performed
*S = scale : scaling about x, y, z axes
*SH = shear : shearing in xy, xz, yx
*ST = scale pivot translate :  translation introduced to preserve existing scale transformations when moving pivot. This is used to prevent the object from moving when the objects pivot point is not at the origin and a non-unit scale is applied to the object
*RP = rotate pivot point : point about which rotations are performed 
*RA = rotate axis = rotation to orient local rotation space
**AX * AY * AZ 
*R = rotate = rotation on x, y, z axes.
**RX * RY * RZ (multiplication is defined by rotate order)
*RT = rotate (pivot) translate :  translation introduced to preserve exisitng rotate transformations when moving pivot. This is used to prevent the object from moving when the objects pivot point is not at the origin and the pivot is moved.
*T = translate :  translation in x, y, z axes.
Matrix multiplication happens in order, from left to right.  It can be thought of exactly like a transform hierarchy, with child nodes on the left, and parental nodes on the right.  The matrix to the right will effect the matrix to the left.  So in effect, the root of the hierarchy is '{{{T}}}' (translate), while the leaf-most child would be '{{{S}}}' (scale).  This is why the translation of a node effects where it is rotated and scaled, the rotation of a node effects the orientation of scale but has no effect on translation, and the scale of a node has no effect over rotation or translation.
>It's important to note that Maya internally doesn't store its animation data as matrices.  It stores them on nodes, as keyframe data (scalar values), expression data, dynamic sim, etc.  The matrix is simply a state value that can be queried based on the end result of the incoming anim data for a given transform.
----
Matrices are //multiplied// together to get new matrices.  For example, if you wanted to assign the position of objectC as the position of objectA + objectB:
{{{
matrixC = matrixB * matrixA
}}}
So when you think of 'adding' values, convert that to 'multiplying' matrices.
But what if you want to 'subtract' values?  For example, you want the position of objectC to be the difference between objectA and objectB?  In this case you add (multiply) by the //inverseMatrix//.
{{{
matrixC = matrixB * inverseMatrixA
}}}
----
{{{dagNode}}} node's matrix related attrs (all of these are available through any {{{transform}}} based node):
*__Local__ matrix data, relative to the nodes //parent//:
**{{{.matrix}}} : Local transformation matrix for the dagNode. 
**{{{.inverseMatrix}}}  :  Inverse of matrix attribute.  Multiplying by this value will place it at its parent.
*Matrix data for the nodes __//parent//__:
**{{{.parentMatrix}}} : The parentMatrix instanced attribute represents the world-space transformation matrix for the parents of this dagNode. If the dagNode is a transform node and its inheritTransform attribute is false, then the parentMatrix is identity. 
**{{{.parentInverseMatrix}}} : Inverse of parentMatrix instanced attribute.  Multiply the parent by this value to return it to origin.
*Matrix data relative to the __//world//__:
**{{{.worldMatrix}}} :  The worldMatrix instanced attribute is the 4x4 transformation matrix that transforms the object to world-space. There is a world-space matrix for each unique path to the object. Eg. 'ball.worldMatrix[0]' identifies the world-space transformation matrix for the first instance of the object 'ball'. Each world-space transformation matrix is the result of post multiplying the 'matrix' attribute by corresponding 'parentMatrix' instanced attribute (i.e. worldMatrix[i] == matrix x parentMatrix[i]). Thus, the worldMatrix is the concatenation of the 'matrix' attribute of all the dagNodes along the path from the node up to the root of the dag hierarchy.
***Basically:  {{{dagNode.worldMatrix = dagNode.matrix * dagNode.parentMatrix}}}
**{{{.worldInverseMatrix}}} : Inverse of worldMatrix instanced attribute.  Multiplying by this will place it at the world origin.
{{{transform}}} node's matrix related attrs:
*{{{.xformMatrix}}} :  Local transformation matrix. From the docs:  "Contains the same information as the matrix attribute on dagNode but it is stored in a format that can be interpolated easily.".  Personally, I'm not sure what makes it more easy to interpolate.
It should be noted that on all these attrs you //can// connect to or query their //outputs//, but they //don't// allow for input connections or modifications via {{{setAttr}}}.
----
Notes on getting Matrix attr data:
<<<
Some attributes represent transformation matrices. Although these attributes are represented internally as 4x4 matrices, unfortunately, matrix attributes are not returned as MEL 4x4 matrix types. Instead, they are returned as an array of floats where the elements of the matrix are listed in row major order. For instance, "matrix $m[4][4] = `getAttr ball.worldMatrix`;" will not work. To obtain the value of a matrix attribute, you need something like "float $m[16] = `getAttr ball.worldMatrix`;". 
<<<
These float arrays (for a 4x4 matrix) are indexed like this:
| 0 | 1 | 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 |
----
{{{xform}}} command:
*It should be noted that when querying matrix //translation// values (index 12, 13, & 14) Maya always returns them as cm values.  If your working units are something else, you'll need to convert them!
*If the node ever had 'freeze transformations' ({{{makeIdentity}}}) applied to it, it will throw off the worldspace matrix position.
{{{
// Copy the worldspace matrix from pCube1 to pCube2:
// Using 'getAttr' on the .worldspaceMatrix, it calculates all
// scale operations based on all parental nodes as well.
// This can cause the target .shear attrs to be effected.
float $m[16] = `getAttr pCube1.worldMatrix`;
xform  -matrix $m[0] $m[1] $m[2] $m[3] 
		$m[4] $m[5] $m[6] $m[7] 
		$m[8] $m[9] $m[10] $m[11] 
		$m[12]$m[13] $m[14] $m[15] pCube2;

// you could optionally use this to get the matrix, via xform:
float $m[16] = `xform -query -matrix pCube1`;
// Which queries the .xformMatrix attr (see above), but this only 
// tracks the local scale values, not any extra parental scale
// values:  .shear attrs are not effected.
}}}
*Sets/returns the composite transformation matrix. *Note* the matrix is represented by 16 double arguments that are specified in row order. 
----
''Matrix Nodes''
*{{{decomposeMatrix}}} (standard Maya plugin):
**See Mel Wiki notes [[here|How can I have a node return the worldspace position of my object, offset in time?]]
*{{{addMatrix}}}
**Add a list of matrices together.
*{{{fourByFourMatrix}}}
**This node outputs a 4x4 matrix based on 16 input values. This output matrix attribute can be connected to any attribute that is type "matrix".
*{{{holdMatrix}}}
**Cache a matrix.
*{{{multMatrix}}}
**Multiply a list of matrices together.
*{{{passMatrix}}}
**Multiply a matrix by a constant without caching anything
*{{{pointMatrixMult}}}
**The dependency graph node to multiply a point by a matrix.
*{{{wtAddMatrix}}}
**Add a weighted list of matrices together.
*[[dagPose|http://download.autodesk.com/us/maya/2010help/Nodes/dagPose.html]]
**Created via the {{{dagPose}}} command, used to store matrix data as a pose.  Most often used during skinning operations.
''Matrix Commands''
*[[dagPose|http://download.autodesk.com/us/maya/2010help/CommandsPython/dagPose.html]]
**Used to store and query matrix data in a {{{dagPose}}} node.  Most commonly used during skinning operations.
*{{{pointMatrixMult}}}   (mel script)
**This script returns the multiplication of a point and a matrix as an array of 3 doubles.  It is a wrapper around the {{{pointMatrixMult}}} node, which the script actually creates, modifies, gathers the return value, and then deletes.
**Located: {{{C:/Program Files/Autodesk/Maya<version>>/scripts/others/pointMatrixMult.mel}}}
*For //Python// related matrix commands see:  [[Working with matrices with Python]]
----
Syntax for authoring an identity transformation matrix:
{{{
matrix $m[4][4] = <<1, 0, 0, 0;
                    0, 1, 0, 0;
                    0, 0, 1, 0;
                    0, 0, 0, 1>>;
}}}
----
Printing 4x4 matrix data:
{{{
float $goo[] = `getAttr myNode.worldMatrix`;

for($i=1;$i<size($goo)+1;$i++)
	{
	print ($goo[$i-1] + " ");
	if($i%4==0)
		print("\n");
	}
1 0 0 0 
0 1 0 0 
0 0 1 0 
0 0 0 1
}}}
Using {{{getAttr}}} on a matrix attr will return back a float array.  But when you want to do matrix math, you have to use the {{{matrix}}} variable type.  These funtions will convert back and forth between the two:
{{{
global proc matrix floatArrayToMatrix(float $m[]){
	if(size($m) != 16)
		error("Please pass in a float array with exactly 16 elements");
	matrix $mat[4][4] = <<$m[0], $m[1], $m[2], $m[3];
					$m[4], $m[5], $m[6], $m[7];
					$m[8], $m[9], $m[10], $m[11];  
					$m[12], $m[13], $m[14], $m[15]>>; 
	return $mat;
	}
}}}
{{{
global proc float[] matrixToFloatArray(matrix $m){
	float $return[] = {};
	for($row=0; $row<4; $row++){
		for($column=0; $column<4; $column++)
			$return[size($return)] = $m[$row][$column];
		}
	return $return;
	}
}}}
----
Match the worldspaceMatrix of objectB to objectA:
{{{
string $source = "pCube1";
string $destination = "pCube2";

float $m[16] = `getAttr ($source+".worldMatrix")`;
xform -worldSpace  -matrix $m[0] $m[1] $m[2] $m[3] 
		$m[4] $m[5] $m[6] $m[7] 
		$m[8] $m[9] $m[10] $m[11] 
		$m[12]$m[13] $m[14] $m[15] $destination;
}}}
This will work, no matter what any parental groups have done to the nodes.  Sweet.
----
Another snippet:
Have a destination node match the position of a hierarchy of source nodes.  In this scene, "pCube1" is parented to "group1".  The below code will match the worldspace matrix of "pCube2" to the postion of "pCube1" by multiplying the local matrices of "group1" and "pCube1" together.
{{{
string $sourceParent = "group1";
string $sourceChild= "pCube1";
string $destination = "pCube2";

// query the local matrix of the parent and child
matrix $matrixSP[4][4] = floatArrayToMatrix(`getAttr ($sourceParent+".matrix")`);
matrix $matrixSC[4][4] = floatArrayToMatrix(`getAttr ($sourceChild+".matrix")`);
// Generate our new  destination matrix, order is important!
matrix $dPos[4][4] = $matrixSC * $matrixSP;

float $m[16] = matrixToFloatArray($dPos);

xform -worldSpace  -matrix $m[0] $m[1] $m[2] $m[3] 
		$m[4] $m[5] $m[6] $m[7] 
		$m[8] $m[9] $m[10] $m[11] 
		$m[12]$m[13] $m[14] $m[15] $destination;
// it worked.  Trust me.
}}}
Something I can never remember, so writing it down.
Simple example showing how a local transformation matrix is generated:
{{{
L = T * R * S
}}}
Where:
* L = The local matrix
* T = Transformation matrix
* R = Rotation matrix
* S = Scale Matrix
In the above example, the matrices are applied in the order of Scale, Rotation, Translation.  Meaning,:
# First the object is scaled (multiplied by the scale matrix) by the given amount.
# Next, the object is rotated (multiplied by the rotate matrix).
# Finally, the object is translated (multiplied by the translation matrix).
So, it can be concluded that they are applied from  <-- right to left <--

Think about this in Maya:  When you scale something, it doesn't effect where it lives. This is because the scale happens //before// the translation.  However, when you rotate it, you see the scaled version rotate.  This is because the rotation happens after the scale.  But translation moves where the whole object lives, since it happens last.
----
An example using Maya transforms:

Say you have this hierarchy:
* THE WORLD (aka, worldspace)
** Grandparent
*** Parent
**** Child
You query the local matrix (matrix relative to each parent) for each.  How can you find the worldspace matrix for the child?
{{{
matrix_childWs = childMatrix * parentMatrix * grandparentMatrix
}}}
*http://sourceforge.net/projects/colladamaya/
**At the time of this authoring, this only supports up to Maya2009
*http://opencollada.org/
**Seems to have more current stuff
*http://colladamaya.sourceforge.net/
** ?
http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=10775855
Includes:
*FBX plugin - Multiple OS's, Maya versions, 32\64-bit.
*FBX converter (standalone app)
*FBX ~QuickTime Viewer
NOTE:  This isn't maintained very often.

Quality links you should know about, having to do with Maya, and mel:
----
'Official':
*[[Autodesk's|http://usa.autodesk.com/adsk/servlet/home?siteID=123112&id=129446]] site.  (So long Alias...)
*[[Maya Documentation|http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=8782084]]:  Online, for multiple versions.
*[[Current release notes|http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=13710407&linkID=10809894]]
*[[Autodesk Subscription|http://subscription.autodesk.com/sp/servlet/home/index?siteID=11564774&id=11595437]] - Need to log in here first
**[[Autodesk Maya e-Learning|http://subscription.autodesk.com/sp/servlet/elearning/course?catID=11484791&cfID=Autodesk+Maya&siteID=11564774]] - Can find podcasts buried in here.
**[[Ask Autodesk|http://forums.ask.autodesk.com/category.jspa?categoryID=1010]] forums.
<<<
These sites appear to be down, or lost to the Autodesk takeover.
*[[Autodesk Support & Service|http://pointa.autodesk.com/local/enu/portal/signin.jsp?po=enu]] (need to log in here first to access the next two items)
**The [[Maya Annex|http://www.alias.com/glb/eng/support/maya.jsp]]
**[[Maya Podcasts|http://www.alias.com/glb/eng/support/maya/podcasts/index.jsp]] (in the Annex)
<<<
*Maya's '[[Bonus Tools|http://area.autodesk.com/downloads/plugins?word=bonus+tools&where=1&software=8&script=&os=]]'
*[[Maya Station|http://mayastation.typepad.com/]] blog. (I *think* this can be considered official)
*[[Autodesk Area Maya Forum|http://area.autodesk.com/forum/autodesk-maya/]]
*[[Autodesk Area Maya Python Forum|http://area.autodesk.com/forum/autodesk-maya/python/]]
*[[Autodesk Area Maya SDK Forum|http://area.autodesk.com/forum/autodesk-maya/sdk/]]
----
'Unofficial':
*[[Creative Crash|http://www.creativecrash.com/maya/home]]'s Maya forums.
*[[CG Society|http://forums.cgsociety.org/]]'s (~CGTalk) Maya forums.
*[[Maya Wiki|http://www.tokeru.com/t]] over at tokeru.com.  And they have a pile more links.
*[[Brian Ewerts|http://ewertb.soundlinker.com/maya.htm]] mel page (''great'' stuff!).  I leared a lot from this page when I was just getting started with mel.
*[[python_inside_maya|http://groups.google.com/group/python_inside_maya?hl=en]] Google Group.
*List of [[Script Editor Replacements]].
*[[::SOuP|http://www.soup-dev.com/]] : ~SOuP is a collection of nodes for Maya that are based on the principles of building a flexible, non-destructive procedural pipeline.
----
'Books'
*Scripting\Programming:
**[[Maya Python for Games and Film|http://www.amazon.com/Maya-Python-Games-Film-Reference/dp/0123785782]] (Amazon), [[Homepage|http://www.maya-python.com/]] (with code examples)
***As of this authoring, the more current book for authoring Python code in Maya.  Gives a good overview of Python itself, how it integrates in Maya, Object Oriented Programming, touches on ~PyMel, authoring UI's with Qt, how data works in Maya, & designing scripted plugins via the API.  While it doesn't get as in-depth into the API stuff as 'Complete Maya Programming', this is all written in //Python// (compared to the former which is all c++), which makes it far more approachable.  Definitely the book to get.
**[[Complete Maya Programming|http://www.amazon.com/Complete-Maya-Programming-Extensive-Kaufmann/dp/1558608354/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1204144080&sr=1-1]]
***I've read this, recommended.
**[[Complete Maya Programming Volume 2|http://www.amazon.com/Complete-Maya-Programming-Vol-Depth/dp/0120884828/ref=pd_bbs_sr_3?ie=UTF8&s=books&qid=1202404517&sr=8-3]]
**[[MEL Scripting for Maya Animators|http://www.amazon.com/Scripting-Animators-Kaufmann-Computer-Graphics/dp/0120887932/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1202404517&sr=8-1]]
***I've read this, recommended.
**[[Processing: A Programming Handbook for Visual Designers and Artists|http://www.amazon.com/Processing-Programming-Handbook-Designers-Artists/dp/0262182629/]]
***I've read this, recommended.  It focuses on the [[Processing|2008 02 18]] programming language (wrapper around Java), but it has direct application to mel, and is a great overview to the world of programming and scripting.
*Rigging
**[[Inspired 3D Advanced Rigging and Deformations|http://www.amazon.com/Inspired-3D-Advanced-Rigging-Deformations/dp/1592001165/ref=pd_bbs_11?ie=UTF8&s=books&qid=1202404700&sr=8-11]]
**[[Inspired 3D Character Setup|http://www.amazon.com/Inspired-Character-Setup-Michael-Ford/dp/1931841519/ref=pd_bxgy_b_img_b]]
***The 'Inspired' series of books always seem to have some good fundamentals in them.   Good place to start for character setup \ rigging.
----
Got any other suggestions?  Let [[me|WarpCat]] know...
Why?  Let's make a list:
* Artist had Maya open fine on Friday, but on Monday Maya would disappear on open right after the Output Log launched.  He was however to open Maya in command-line mode, with our full tool suite.  Felt driver related.  After he updated his graphics drivers and still had the problem, he started uninstalling applications.  He uninstalled a 'Huion tablet driver' that he personally never installed himself, nor used: Maya suddenly started working.
Just in case you want to jump ship
https://www.riggingdojo.com/2018/12/06/maya-to-blender-2-8-maya-navigation/
There is a weird bug that can happen, that when you're in 'component select' mode (by pressing any of the {{{F8}}} -> {{{F11}}} hotkeys), it will also select mesh. Super annoying.

I've tracked it down when you've set Maya's Preference for 'Selection -> Priority' to 'Custom' : The defaults for 'Custom' have the same 'Priority for things like 'Polymesh' and 'Polymesh Vertex' (And the other component types) : They're all 2.

If you bump the Priority on the component types to something higher (anything greater than 2:  other presets have them at 5), then this bug goes away, since they're now selected instead of other things marked as 'priority 2'.  

I found this by disabling this optinoVar in {{{userSetup.mel}}}:
{{{
optionVar -sv "selectPrefPriorityMode" "custom";
}}}
Opened a scene, and saw this error listed 3x in a row:
{{{
Maya binary file parse error:  : (5) bad block 
}}}
Not sure why.  But a later version of that same file failed on save, breaking all the animCurve connections on re-open.  
Talked with Autodesk, their description is below.  But none of that applies to my use case. Hmm...
<<<
We only have on issue where that error crops up logged against 2014. It has to do with Linux network storage connected to Windows user machines as a samba share. It happened when the file was too large and the network resource wasn't allowing it to write correctly. The error code means that a block in the save data is corrupted and was not saved correctly. The issue was expected to possibly crop up again if studios used different operating systems for users vs storage as size of files increased, but we haven't seen it since the single report.  The fact that this is happening to your artists on their local machines is very strange. No one else has reported this.
<<<
I recently got this error on file open (and the file didn't open):
{{{
Maya binary file parse error: : (12) bad root block
}}}
Come to find out, the file was saved as mayaAscii, but with a .mb file extension.
Rename as .ma, open the file, then resave correctly as .mb
These posts have a pretty extensive overview:
*[[Maya 2008 -> 2013|http://around-the-corner.typepad.com/adn/2012/06/maya-compiler-versions.html]]
*[[Maya 2014|http://around-the-corner.typepad.com/adn/2013/06/maya-compiler-versions-update.html]]
*[[Maya 2015|http://around-the-corner.typepad.com/adn/2014/04/maya-compiler-versions-update.html]]
*[[Maya 2016|http://around-the-corner.typepad.com/adn/2015/04/maya-compiler-versions-update.html]]
*[[Maya 2016.5|http://around-the-corner.typepad.com/adn/2016/06/compiler-versions-for-maya-2016-extension-2.html]], aka "Extension 2".
*[[Maya 2017|http://around-the-corner.typepad.com/adn/2016/08/compiler-versions-for-maya-2017.html]]
*[[Maya 2018|http://around-the-corner.typepad.com/adn/2018/02/compiler-versions-for-maya-2018.html]]
*[[Maya 2019|https://around-the-corner.typepad.com/adn/2019/01/maya-2019-api-updates-guide.html]]
*[[Maya 2020|https://around-the-corner.typepad.com/adn/2019/12/maya-2020-api-update-guide.html]]
*Maya 2022 : Need to find link...
----
Also see:
*[[Python & Qt versions in Maya]]
Ran into a great bug in Maya 2010 on Win7:
*Made a window, with a {{{modelPanel}}} displaying a custom camera.
*In that {{{modelPanel}}}, I 'isolate select' certain objects.
*If I save with the window //open//, it crashes Maya:  It locks Maya up immediately after save.
*If I turn off 'isolate select' on the window and rebuild it, the problem goes away.
How to solve?  This is the method I came up with:
*Override Maya's 'save' and 'save as' menu commands via [[this code|How can I trigger an event when the scene saves?]]
*When I override that code, I detect if my custom window is open, and I delete it.
*When the window deletes, I have a custom {{{scriptJob}}} running that executes on that window deletion, and it in turn physically deletes the custom modelPanel in the window.
*Deleting that model panel in turn stops the 'isolation selection' for it, since its deleted.
*However, I have to wrapper the standard Maya 'save code' in my override code (above) with '{{{evalDeferred}}}' commands, or the scene will save faster than the window is deleted, and it will still hang.
What a pain!
Ever since Maya switched its back-end to QT, I've noticed some instability, that has continued up to Maya 2014.  During certain window commands, usually based around their deletion, but sometimes their creation, calling to these commands can cause Maya to crash.

For example, in a script you call to {{{deleteUI}}} to delete a window.  And when you do this, Maya crashes.  Or at some point in a script you create a window, and it causes Maya to crash.  How can you make this work?

As it turns out, you need to '{{{evalDeferred}}}' the creation\deletion.  Without this step, Maya will continue to crash:
{{{
import maya.cmds as mc

# Create a window safely (by calling to some window creation class):
mc.evalDeferred(lambda *args:MyWindow(), lowestPriority=True)

# Delete a window safely by string name:
if mc.window("myWindow", exists=True):
    mc.evalDeferred(lambda *args:mc.deleteUI("myWindow"), lowestPriority=True)

}}}
I've seen some files crash when Maya is quit.  A side effect is that the prefs\shelves don't always save.

Reasons I've found:
* Maya 2014 : Users had //referenced// fbx files in their scene (exported from Radiant).  Removing / unloading the fbx references stopped the crash.

It appears Maya 2018->2018.4 has a new bug that can show up in these conditions:
*You have a file with unknown nodes, and file references.
*You delete unknown nodes.
*You either import a new file, or export selected.
*Save:  Maya will crash.
It appears it has something to do with the references too:  If there are no refs, there is no crash on save.  
{{{
# Delete unknown nodes:
import maya.cmds as mc
for u in mc.ls(type='unknown'):
    try:
        mc.delete(u)
    except:
        pass
}}}
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?!?!
Confirmed this is a bug with Autodesk in Maya 2018 as well.  Currently the only fix is to unload the plugin, safe the prefs, quit & restart: That will have it safely cleared from Memory and the bug will go away.
In this example, we have an Python object (made via [[OOP|How does Object Oriented Programming work in Python?]]), but we want to access the attributes of that object //in Maya//, via ''mel''.  How to do so?  But also, //why// would you want to?

The why:  For me, I have a lot of older mel scripts doing work.  But now a-days I like to do all my scripting in Python.  So what I end up with are new tools in Python, but being wrappered and executed with older mel procedures that still chug along.  Plus, a Python 'object' is a much more complex data structure than a mel procedure:  A mel procedure can only return back a single type of variable (string, int, float array, etc).  But with a Python object you can query many different types of data, which is extremely handy.  We are also limited again by mel's variable types:  It has nothing of a greater complexity like Python's dictionaries, or 'lists (arrays) within lists within lists...'.  So if you're using mel to query a more complex data structure in Python, you //first// need to prepare that data //in Python// to be received happily by mel.  For example, mel has no idea  what a Python dictionary is, so you'd first, in Python, need to split the dictionary apart into variable containers that mel could understand.

Here is a simple Python object, as defined by a class.  Presume that this file is called {{{MayaTestObj.py}}} and it is saved in Maya's Python path.  It has some float, string, and list variables ready to be queried, all mel consumable.  From the above discussion, these values //could// have been derived from a more complex Python dictionary, and then made ready for mel consumption.
{{{
# Python code

# MayaTestObj.py
class MObject(object):
    def __init__(self):
        self.floatVar = 23.23
        self.stringVar = "a string"
        self.listVar = ["A", "list", "of", "strings"]       
    def __str__(self):
        return "A Python object trying to live in Maya..."    
    def getFloat(self):
        return self.floatVar 
    def getString(self):
        return self.stringVar 
    def getList(self):
        return self.listVar
}}}
Now, for interacting with it in mel.  We have to use the mel {{{python}}} command to pull in the data we're after:
{{{
// mel code

global proc getPyObjData()
	{
	// First import the module:
	python("import MayaTestObj");
	// Create our object:
	python("obj = MayaTestObj.MObject()");
	// See what our object has to say for itself:
	python("print obj");
	
	// Now lets grab our variables:
	float $floatVar = python("obj.getFloat()");
	string $stringVar = python("obj.getString()");
	string $listVar[] = python("obj.getList()");
	
	// and print the results:
	print ($floatVar + "\n");
	print ($stringVar +"\n");
	print $listVar;
	}
	
getPyObjData();
}}}
{{{
A Python object trying to live in Maya...
23.23
a string
A
list
of
strings
}}}
It should be noted we're calling to the object's 'get' methods to access the attribute data.  You //could// call directly to the attributes themselves if no 'get' methods existed, but this is slightly frowned upon, and the get methods should be provided:
{{{
float $floatVar = python("obj.floatVar");
// Result: 23.23 // 
}}}
Been seeing an error pop open a lot on new installs of Maya in Windows 10.  Something like this:
{{{
Microsoft Visual C++ Runtime Library

Runtime Error!

Program: C:\Program Files\Autodesk\Maya2016\maya.exe

R6034
An application has made an attempt to load the C runtime library incorrectly.
Please contact the application's support team for more information.
}}}
It turns out that it appears that there are multiple copies of {{{msvcr90.dll}}} on the machine, and for whatever reason, if you have //system level// Python installed, the copy of {{{msvcr90.dll}}} that ships with it somehow clashes with Maya.
If you rename /remove this file:
{{{
C:\Python27\msvcr90.dll
}}}
Maya will stop throwing the error.  Not sure how this impacts system-level Python though.
So, it looks like you can hack Maya to do bad things to your system:
* [[Script Exploit In Autodesk Maya|https://www.autodesk.com/trust/security-advisories/adsk-sa-2020-0003]]
** The fix: [[Security Tools For Autodesk Maya 2015-2020|https://apps.autodesk.com/MAYA/en/Detail/Index?id=8637238041954239715]]
** How do you get infected?  From Autodesk: "A little while ago a customer contacted us with an “infected” Maya file and we had some discussions on our beta.  Someone has used the open capabilities of Maya to produce a replicating malware attach on Maya and placed that within a scene file.  We tracked some of this back to a very small number of customers and some got this from a vendor of 3d models online.  We tracked down the data and it turns out this has been out there for some time with very few people “infected” so we believe the risk is low.  If you’re not getting files from online stores, your risk is lower once again."
Self promotional plug here:

https://github.com/AKEric/skinner

Free, and does everything you want for your skin weight export/import pipeline.  Full API and UI.
Everything you need to know here about the below vars:
http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=GUID-228CCA33-4AFE-4380-8C3D-18D23F7EAC72
With snippet note for each.
* {{{MAYA_APP_DIR}}}
** Defines your personal Maya application directory
** You can only set {{{MAYA_APP_DIR}}} from the operating system; you cannot use {{{Maya.env}}}. 
* {{{MAYA_CMD_FILE_OUTPUT}}}
**  Enables the {{{cmdFileOutput}}} command during startup
* {{{MAYA_CONTENT_PATH}}}
** Obtains the paths to the content directories with which you can populate the Examples tab in the Content Browser
* {{{MAYA_CUSTOM_TEMPLATE_PATH}}}
** When used with the Attribute Editor: this variable is used to override the directory where custom Attribute Editor templates are found.
** When used with the Node Editor: you can customize the list of attributes displayed for each node by creating a custom attribute list template.
* {{{MAYA_CUSTOM_TEMPLATE_WRITE_PATH}}}
* For use with the Node Editor. You can customize the list of attributes displayed for each node by creating a custom attribute list template.
* {{{MAYA_FILE_ICON_PATH}}}
** Become obsolete since Maya 3.0. See {{{XBMLANGPATH}}}
* {{{MAYA_MODULE_PATH}}}
** Defines the search paths for Maya module files. A module file describes the install location of a plugin which has been distributed as a module.
** Maya will append subdirectories of this install location to the following path variables: {{{MAYA_PLUG_IN_PATH}}}, {{{MAYA_PRESET_PATH}}}, {{{MAYA_SCRIPT_PATH}}}, {{{PYTHONPATH}}} and {{{XBMLANGPATH}}}. 
* {{{MAYA_MOVIE_DIR}}}
** This variable is used to override the directory where movie files are found.
* {{{MAYA_LOCATION}}}
** The path for the Maya installation directory.
* {{{MAYA_PLUG_IN_PATH}}}
** Search path for plug-ins
* {{{MAYA_PRESET_PATH}}}
** Defines the location for Maya presets. Each entry in the path points to the directory above the {{{attrPresets}}} directory.
* {{{MAYA_PROJECT}}}
** Specifies the location of your project folder on startup.
* {{{MAYA_PROJECTS_DIR}}}
** Specifies the location of your project folder.
* {{{MAYA_SHELF_PATH}}}
** Specifies the directories that Maya searches to access the shelves. 
* {{{MAYA_SCRIPT_PATH}}}
** Colon-separated search path for MEL scripts. 
* {{{MAYA_TOOLCLIPS_PATH}}}
** Specifies the directories that Maya searches for ~ToolClip content.
* {{{PYTHONPATH}}}
** Location for your Python module files when you import them in a program.
* {{{TEMP}}} or {{{TMPDIR}}}
** This variable specifies the directory location Maya uses for various temporary files
* {{{XBMLANGPATH}}}
** Specifies the location of icon files
Starting in Maya 2013, they have a 'DG Profiler', that was supported up until around Maya 2016, when they introduced the evaluation graph:  When that happened, a whole new profiler was introduced.  Because of that, I"ve kept my legacy notes in the [[Legacy DG Profiler]] tiddler.  What you find here applies to Maya 2016 and newer.
{{{
Windows -> General Editors -> Profiler
}}}
!The EM Shelf
If you install bonus tools, it comes with an {{{/bonusGame/scripts/shelf_EM.mel}}} you can load that has a bunch of beneficial tools in it.
Note, for any of them to work, you need to add //another// button to that shelf with this mel code:
{{{
source em_commands.mel;
}}}
You need to run that once per Maya session to get the shelf commands to work.
!Finding Cycles in the Profiler
Once you have  the Profiler up and a profiled frame selected, you can search for cycles in two ways:
In Maya 2016, search for the word "opaque" : Whatever it highlights are 'cycle clusters'.
In Maya 2016.5 and newer, search for "evaluateNodesInCluster".
!Graphing Cycles
Cycles in the EG are bad news.  The EM shelf gives you a way to graph them:  If you select the node with the cycle and press the "CLUS" button, it'll create a PDF showing you the node's cycle dependencies.
This presumes you have this installed:
{{{
http://www.graphviz.org/
}}}
And this added to your system level PATH:
{{{
C:\Program Files (x86)\Graphviz2.38\bin
}}}
!Problems getting ~OpenCL to run in Maya 2016.5 :
I had to set this env var to get ~OpenCL to work in Maya 2016.5:
{{{
MAYA_OPENCL_IGNORE_DRIVER_VERSION = 1
}}}
If ~OpenCL doesn't work, then neither with GPU Override.



I installed ~UltraEdit.  I like ~UltraEdit.  In the process it asked me if it should be the 'default application' for HTML files.  Sure, why not?  Later in Maya, I try to access the docs either through the F1 help, or through the mel:
{{{
help -doc ls;
}}}
And I get an error window:
>Windows cannot find '~UltraEdit.htm\shell\open\command'. Make sure you typed the name correctly, and then try again.
This looks like a registry issue, and sure enough it was.  Long story short, I needed to update (in Windows 7, via regedit.exe) this:
*Computer
**~HKEY_CLASSES_ROOT
***.htm
****(Default) :  Data : Change to -> "~FirefoxHTML"
Since Firefox is my default browser.  I've also seen "~ChromeHTML" for Google Chrome, and "htmlfile" for Internet Explorer.
!Built In:
Float/double
* {{{floatMath}}} : add, subtract, multiply, divide, min, max, power
* {{{plusMinusAverage}}} :  sum, subtract, average
* {{{multiplyDivide}}} : multiply, divide, power
* {{{reverse}}} 
* {{{addDoubleLinear}}} :  add
* {{{multDoubleLinear}}} :  multiply
Vector
* {{{vectorProduct}}} : dot product, cross product, vector matrix product, point matrix product
* {{{angleBetween}}} : angle
Matrix
* {{{addMatrix}}} : add
* {{{multMatrix}}} : multiply
* {{{passMatrix}}} : Multiply a matrix by a constant
* {{{fourByFourMatrix}}} : matrix
* {{{pointMatrixMult}}} : Multiply a matrix by a point
* {{{wtAddMatrix}}} : Add matrices based on weights.
Plugins
* See {{{quatNodes}}}
!Arnold:
https://docs.arnoldrenderer.com/display/A5NodeRef/Math+Shaders
* {{{aiAbs}}}
* {{{aiAdd}}}
* {{{aiAtan}}}
* {{{aiCompare}}}
* {{{aiComplement}}}
* {{{aiCross}}}
* {{{aiDivide}}}
* {{{aiDot}}}
* {{{aiExp}}}
* {{{aiFraction}}}
* {{{aiIsFinite}}}
* {{{aiLength}}}
* {{{aiLog}}}
* {{{aiMax}}}
* {{{aiMin}}}
* {{{aiModulo}}}
* {{{aiMultiply}}}
* {{{aiNegate}}}
* {{{aiNormalize}}}
* {{{aiPow}}}
* {{{aiRandom}}}
* {{{aiRange}}}
* {{{aiReciprocal}}}
* {{{aiSign}}}
* {{{aiSqrt}}}
* {{{aiSubtract}}}
* {{{aiTrigo}}}
* {{{aiFloatToInt}}}
Also see:
* https://github.com/serguei-k/maya-math-nodes
* https://www.chadvernon.com/blog/trig-maya/
* https://apps.autodesk.com/MAYA/en/Detail/Index?id=212304226072872821
** Note, this is Python based, so won't be performant.
!Maya 2020
* [[Main online help docs|https://help.autodesk.com/view/MAYAUL/2020/ENU/]]
** [[Mel commands|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__Commands_index_html]]
** [[Python commands|https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/CommandsPython/index.html]]
** [[Nodes|https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/index.html]]
** [[MASH|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__MASH_index_html]] 
** [[PyMel|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=__PyMel_index_html]]
** [[DevKit|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=Maya_SDK_MERGED_Introduction_html]]
*** [[Maya Python API 2.0|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=Maya_SDK_MERGED_py_ref_annotated_html]]
*** [[Maya C++ API|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=Maya_SDK_MERGED_cpp_ref_annotated_html]]
Common Maya plugin-based renderer's, and their {{{requires}}} lines (in the ma header info) when possible.
Making this since many of thsee plugins can cause errors on file open\reference if the users don't have those plugins installed \ loaded.  Or especially if they're old plugins, like Mental Ray, that have been deprecated.
!Arnold
* {{{mtoa.mll}}}
* {{{ai}}}* node //types//, often with 'Arnold' in their name.
* {{{requires "mtoa" "3.2.2";}}}
Now ships with Maya (at least 2020+)
https://docs.arnoldrenderer.com/display/A5AFMUG/Arnold+for+Maya+User+Guide
Older Maya versions required separate download (but you need an acct) : 
https://www.arnoldrenderer.com/arnold/download/ 
!Turtle
* {{{Turtle.mll}}}
* {{{irl}}}* node //types//.
!Mental Ray
* {{{Mayatomr.mll}}}
*{{{requires "Mayatomr" "8.5.1.3m - 3.5.6.7 ";}}}
* {{{mi}}}* & {{{mentalray}}}* node //types//.
As of Maya 2015

Windows:
{{{
C:\Documents and Settings\<username>\My Documents\maya\<version>\Maya.env
C:\Documents and Settings\<username>\My Documents\maya\Maya.env
}}}
Mac:
{{{
/Users/username/Library/Preferences/Autodesk/maya/<version>\Maya.env
/Users/username/Library/Preferences/Autodesk/maya/Maya.env
}}}
Linux:
{{{
~/maya/<version>/Maya.env
~/maya/Maya.env
}}}
Note you could have both, or neither:
The {{{Maya.env}}} //not// in a version directory applies to all versions of Maya installed.  The one in a version directory only applies to that version.
*Circumference
**Pi (3.14) times Diameter : C = pi * d
*Diameter:
**2 times the Radius :  d = 2 * R
**Circumference / pi : C / pi
*Radius:
**Half the Diameter : R = d / 2
*Area
**Pi times the radius squared : A = pi * r^^2
Python has some shortcuts you can implement when passing arguments to mel commands (or other Python functions).
Take for example a sphere (named {{{pSphere1}}}) that you want to scale.  Here is the basic mel to do so:
{{{
float $scaleVals[] = {1.0, 2.0, .5};
// optionA:
setAttr pSphere1.scale -type double3 $scaleVals[0] $scaleVals[1] $scaleVals[2];
// optionB also works:
setAttr pSphere1.scale $scaleVals[0] $scaleVals[1] $scaleVals[2];
}}}
Likewise in Python, you can do something very similar:
{{{
import maya.cmds as mc

scaleVals = [1, 2, .5]
# optionA:
mc.setAttr("pSphere1.scale", scaleVals[0], scaleVals[1], scaleVals[2], type="double3") 
# optionB also works:
mc.setAttr("pSphere1.scale", scaleVals[0], scaleVals[1], scaleVals[2])
}}}
However, in Python you can pass all the args in as a list\tuple variable by prefixing the variable name with an asterisk '*'. The function (mel command) will unpack the variable as individual positional arguments. It's important that the variable have the same number of items as the command expects argument values, or a Python {{{RuntimeError}}} exception will be raised:
{{{
# option C, much cleaner!:
mc.setAttr("pSphere1.scale", *scaleVals) 
}}}
You can see more about how this argument system works over on my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#argument]].
http://www.open3d.org/
! Install for Maya:
Open cmd prompt as admin:
{{{
> cd C:\Program Files\Autodesk\Maya2022\bin\
> mayapy.exe -m pip install open3d
}}}
! Import/Export formats
Per http://www.open3d.org/docs/release/tutorial/geometry/file_io.html
* Import Point Cloud Formats: [[xyz|https://en.wikipedia.org/wiki/XYZ_file_format]], xyzn, xyzrgb, pts, [[ply|http://paulbourke.net/dataformats/ply/]], [[pcd|http://pointclouds.org/documentation/tutorials/pcd_file_format.html]]
* Export Mesh Formats: [[ply|http://paulbourke.net/dataformats/ply/]], [[stl|http://www.fabbers.com/tech/STL_Format]], [[obj|http://paulbourke.net/dataformats/obj/]], [[off|http://www.geomview.org/docs/html/OFF.html]], [[gltf/glb|https://github.com/KhronosGroup/glTF/tree/master/specification/2.0]]
** Maya can import obj and stl.
! Example
Using these docs as examples:
http://www.open3d.org/docs/release/tutorial/geometry/surface_reconstruction.html
https://towardsdatascience.com/5-step-guide-to-generate-3d-meshes-from-point-clouds-with-python-36bad397d8ba
Look here on my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#Open3D]]
----
Also see:
* [[Surface from point cloud]]
In general, Disney has some pretty sweet research shown here:
* https://www.youtube.com/user/DisneyResearchHub/videos
* https://la.disneyresearch.com/publication/
* For capture specifically:
** https://la.disneyresearch.com/research-area/capture/
!Rigid Stabilization of Facial Expressions
2014, Beeler / Bradly
* https://la.disneyresearch.com/publication/rigid-stabilization/
** https://www.youtube.com/watch?v=wEFJaKsCBU8
** https://s3-us-west-1.amazonaws.com/disneyresearch/wp-content/uploads/20141121164559/RigidStabilizationOfFacialExpressions.pdf
!~Anatomically-Constrained Local Deformation Model for Monocular Face Capture
2016, Beeler / Bradley / Gross / Wu
* https://la.disneyresearch.com/publication/local-anatomical-face-model/
** https://www.youtube.com/watch?v=u5k2EjsIY2Q
** https://s3-us-west-1.amazonaws.com/disneyresearch/wp-content/uploads/20160705220038/Anatomically-Constrained-Local-Deformation-Model-for-Monocular-Face-Capture-Paper.pdf
!An Empirical Rig for Jaw Animation
2018, Beeler / Bradley / Berard / Zoss
* https://la.disneyresearch.com/publication/empirical-jaw-rig/
** https://www.youtube.com/watch?v=wQlSpWeCdLE
** https://s3-us-west-1.amazonaws.com/disneyresearch/wp-content/uploads/20180730115224/An-Empirical-Rig-for-Jaw-Animation-Paper.pdf
This is a modified example of how Maya does it, taken from here:
{{{
c:\program files\Autodesk\Maya20XX\Python\Lib\site-packages\maya\app\general\mentalRayNotAvailableDialog.py
}}}
{{{
from PySide2 import QtWidgets, QtCore

msgBox = QtWidgets.QMessageBox()
if msgBox:
    msgBox.setIcon(QtWidgets.QMessageBox.Information)
    msgBox.setTextFormat(QtCore.Qt.RichText)
    msgBox.setWindowTitle("Title Text!")
    msgBox.setText("Some Text!")
    msgBox.setInformativeText("Some Informative Text!")
    msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
    msgBox.setDefaultButton(QtWidgets.QMessageBox.Ok)
    cb = QtWidgets.QCheckBox("Checkbox title!")
    msgBox.setCheckBox(cb)
    cb.stateChanged.connect(someCallbackFunction)
    msgBox.exec_()
}}}

I keep forgetting this, so I'll make a note:

Outliner -> Display -> Container Contents :
*Under Container
*Under Parent
*Both
*None
----
To modify to dispay 'under container':
{{{
outlinerEditor -e -showContainerContents 1 outlinerPanel1; 
outlinerEditor -e -showContainedOnly 1 outlinerPanel1;
}}}
To modify to display 'under parent' (which I prefer...):
{{{
outlinerEditor -e -showContainerContents 0 outlinerPanel1; 
outlinerEditor -e -showContainedOnly 0 outlinerPanel1;
}}}
This could also be titled:
* Why isn't my node moving where it should?
* Why freezing transforms screws things up.
* Understanding rotate pivots.
When moving nodes in worldspace, sometimes they don't move where you'd expect.  The common culprit is if it's ever had it's transforms frozen.  Below we discuss issues when using the {{{move}}} or {{{xform}}} command to move a node in worldspace, and how to resolve them.

''NOTES'': 
* The limitations discussed below __don't apply to //component level transformations//__, only object transformations.  Components seem to move just fine in worldspace using both {{{move}}} and {{{xform}}} even if the parent transform had been frozen.
* Furthermore, __these issues only apply to {{{transform}}} nodes, not {{{joint}}}__, since you can't 'freeze transforms' on a joint, and Maya handles its pivots differently because of it.
Example code:
! Make nodes
{{{
import maya.cmds as mc

# Create two nodes to showcase the issue:
reg = mc.polyCube(name="reg", constructionHistory=False)[0]
froze = mc.polyCone(name="froze", constructionHistory=False)[0]

# Offset the nodes in worldspace:
for node in (reg, froze):
    mc.setAttr('%s.translate'%node, 10,10,10)
    
# Freeze transformations on the 'frozen' node:
mc.makeIdentity(froze, apply=True)
# The 'localRotatePivot' & 'localScalePivot' now get the offset transformations.
# You can see this by comparing the two nodes in the Attribute Editor.
}}}
! Move using the {{{move}}} command:
{{{
# Now, let's move them back to the origin via the *MOVE* command
for node in (reg, froze):
    mc.move(0, 0, 0, node, absolute=True, worldSpace=True, rotatePivotRelative=False)
# reg jumps to the origin of the world, but froze does not.

for node in (reg, froze):
    mc.move(0, 0, 0, node, absolute=True, worldSpace=True, rotatePivotRelative=True)   
# froze now jumps to the origin of the world too.    
}}}
The secret sauce is {{{rotatePivotRelative=True}}} : Since transforms have been frozen, the {{{rotatePivot}}} has offsets, and we need to move the node relative to that offset.  See  [[What happens when you 'freeze transforms' behind the scenes?]] for more detailed info.
! Move using the {{{xform}}} command
Re-make the nodes, and run this:
{{{
# Now, let's move them back to the origin via the *XFORM* command
for node in (reg, froze):
    mc.xform(node, translation=(0,0,0), absolute=True, worldSpace=True)
# reg jumps to the origin of the world, but froze does not.
}}}
I've been unable to get xform to move it back to the origin regardless the args provide in conjunction with {{{translation}}}:  You //can// move it to the right space by providing an accurate {{{matrix}}}, doing this:
{{{
mc.xform(node, worldSpace=True, matrix=myAwesomeMatrix)
}}}
However, the matrix itself needs to be pre-corrected for rotatePivot issues.  See the notes in [[What happens when you 'freeze transforms' behind the scenes?]] as to why this is, and how to fix.
FYI, please see [[Why doesn't the Node Editor respect my display changes?]] to understand how to actually make these changes.
When i change Machines or get a new Maya install, I tend to always set these up wrong.  This is //how I like it// :

''Display''
* Grid : Checked
* Shapes:  'All Shapes Except Shading Group Members'
* Transforms : Checked
* Extend to Shapes : //Unchecked// : This will stop shape nodes from auto-adding when you add a parental transform.
* Show Namespaces : Checked
* Use Assets and Published Attributes : Checked (I don't use this for anything currently).
* Default node view mode : Connected Mode (show me the connected attrs).
''Refresh Graph Details:''
* Sync Node Editor/Viewport Selection : //Unchecked// : Keep the Node Editor from constantly changing selection.
* Add to Graph on Create : //Unchecked// : Only add stuff I tell it to.
From my bud Te:
----
When you get windows 7 and you use your pen (for weighting or whatever) you might enjoy disabling all the crap that 7 puts on the pen:
<<<
For those of you who use a wacom  here is a link to help remove all of the annoyances dealing with windows 7.
 
http://forum.wacom.eu/viewtopic.php?f=3&t=2146
 
first .. start > (then search for) services.msc > scroll down until you find Tablet PC Input Serv...
(right click) : stop > (then right click) properties > startup type : disable.
<<<
{{{aimConstraint}}}s are great, much more stable for handling rotations than {{{orientConstraint}}}s or {{{parentConstraint}}}s (in my experience).  But they too have their bugs.

One of the options you can set during creation is a '{{{worldUpType}}}', which can be set to a variety of options, one of which is '{{{object}}}'.  This allows you to then set the {{{worldUpObject}}} to be the name of a given node.  The result makes the local {{{upVector}}} of your constrained object aim at this '{{{worldUpObject}}}' //position//.

However, sometimes.... it doesn't aim correctly at all.  Or, even worse, the //rotation// values of your '{{{worldUpObject}}}' effect the orientation of your aimed object, (poorly imitating the effect of having the {{{worldUpType}}} set to '{{{objectrotation}}}').  Why?

I've (finally) tracked this down:  If you ''freeze transforms'' ({{{makeIdentity}}} command) on your '{{{worldUpObject}}}', it... screws up... the aim constraint system.  Or, even more nefarious:  If you're authoring 'locators' via code ({{{spaceLocator}}} command) to use as your {{{worldUpObject}}}, you have the option to give them a default '{{{position}}}'.  This has the same effect as creating them at the origin, moving them to that position, and freezing their transforms there.  {{{worldUpObject}}} = fail.  You need to make them at the origin, and then translate them to their required position, and //not// freeze their transforms.

I haven't looked into the technicalities of the reasons behind this.  But I'm happy enough just to know: ''don't freeze transforms on your '{{{worldUpObject}}}''' ;)
When using parentConstraints or orientConstraints, you run into the problem of the constraints 'flipping' during blended rotational transitions.
This usually occurs when the difference between the set of corresponding target rotational axes becomes greater than 180 degrees.
Meaning:
*If you have objectA constrained to targetA, you can rotate targetA all you want, way past +-180 degrees and all will be well.
*However, if you have objectA constrained to targetA and targetB, and a rotation axis on targetB is +-180 different from the corresponding axis on targetA, as you blend your constraint weights, rotational value flipping will ensue will ensue on objectA.
Why is this happening?  My guess is that internally, the constraints are comparing and blending between the [[matrices|Matrix info]] of the two nodes.  Since matrices store orientation data as vectors, those vectors have no concept of a '720 degree' (for example) rotation.  They simply point in that direction, and blend between the shortest distance.
----
Solutions:
WIP :)
*Many constraints have the option of changing their {{{.interpType}}} attr to '{{{cache}}}'.  I have see this fix the problem, but unfortunately animators don't like it, since it tends to screw up the 'scrubbing' of the animation:  It behaves like dynamics, since the cached data is based on the frames before:  if you scrub, the cache becomes confused and introduces other glitches.
I've heard of this both happening in a Maya session, and in API calls.

I've been told that if an expression doesn't 'reference an attribute', then it can cause it to not always execute.  And by 'reference an attribute' I mean:
{{{
foo.tx = 5;
}}}
In that expression, there is a clear reference to an attribute.

However, you could run into trouble here:
{{{
move 0 5 0 foo.tx;
}}}
Sine you're now calling to the move command to do the work, and not referencing an attribute directly.

A hack I'm told that works is to simply have the expression update some dummy attr on a node.  You could create a dummy attr specifically for this, or set the visibility on a node:
{{{
move  0 5 0 foo.tx;
foo.dummy = 1;
}}}
(from Doug Brooks)
On Windows XP, this can happen.  The common fix is this:
*Find your maya.exe file:
{{{
C:\Program Files\...\MayaX.X\bin\maya.exe
}}}
*'Right Mouse Button' on it -> Properties -> Compability
*Check "Run this program in compability mode for:"
*And choose "Windows 2000"
Sometimes when you try to load a plugin, it may toss some esoteric error\warning and fail.  Maya plugins (.mll's) are really just .dll's (Dynamic Link Libraries).  The Maya .mll may be dependent on other dll's on your disk, that you don't have yet installed (the same way that scripts can point to other scripts\procs).  But knowing what your mll is missing can be hard.

''Until now:''
*http://www.dependencywalker.com/
This gives you the (free) "depends.exe" application that will break down what a mll (or dll) is looking for, so you can figure out if you're missing it or not.

Thanks to Doug Brooks for tip.
Your last line may be missing a semicolon:   {{{;}}}
*Example code, that would have a problem:
{{{
// this is a script called foobar.mel
proc foo(){
    print "bar\n";}
foo  //  <--- missing the semicolon
}}}
{{{
source foo;
// Error: foo //
// Error: "C:/scripts/foo.mel" line 4.3: Syntax error //
}}}
*The last line, "foo" has no semicolon after it.  This will cause Maya to error when either the script is sourced, (or, if 'proc foo()' was instead 'global proc foo()', then it would error when executed).  Add the semicolon, it'll be happy:
{{{
// this is a script called foobar.mel
proc foo(){
    print "bar\n";}
foo;  //  <--- has the semicolon
}}}
{{{
source foo;
bar
}}}
Sometimes windows go missing, usually hiding off-screen somewhere.  The brute force approach is to quit Maya, and delete this file:
{{{
windowPrefs.mel
}}}
Which will reset all the window positions to factory defaults.

However, at one point I had a switchbox between two PC's, and every time my machine would sleep, it would move Maya to another monitor, and all its windows would go off screen.  Deleting windowPrefs.mel wasn't practical.
On //Windows// at least, you can use this process to move your Maya sub-windows back on screen:
*(re)Launch the window.  You may not see it since it's off-screen, but it has context.
*Press {{{Alt + Space Bar}}} To open Windows move settings window.  You may see it appear on the side of the screen the window is hiding.
*Without doing anything else, press 'm' (for __M__ove)
*Immediately press one of the directional arrow keys:  The window should now snap to your current mouse position.
*Move the mouse and click to place the window where you'd like.
The default Node Editor panel name appears to be:
{{{
nodeEditorPanel1NodeEditorEd
}}}
Many scripts for it can be found here:
{{{
C:\Program Files\Autodesk\Maya<VERSION>\Python\Lib\site-packages\maya\app\general
}}}
* nodeEditor.py
* nodeEditorMenus.py
* nodeEditorBookmarks.py
* nodeEditorRendererMenus.py
http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html
{{{
# Python code
from maya.OpenMaya import MVector

# In counter-clockwise direction, vertex positions, based on their IDs in
# Maya. If they were clockwise, the normal will face in the other direction.
vA = MVector(0,0,0)
vB = MVector(1,0,0)
vC = MVector(0,.5,.5)
}}}
{{{
# Calculate vectors from A to B and A to C:
vAB = vB - vA
vAC = vC - vA
# Get the cross product, which is the vector perpendicular to the two,
# which is the normal.  MVector ^ operator = cross product.
cross = vAB ^ vAC
# Normalized (length=1) version of the cross product:
normal = cross.normal()
}}}
{{{
# Shorthand of above code:
normal = (vB-vA ^ vC-vA).normal()
}}}
{{{
# Results:
print normal.x, normal.y, normal.z
# 0.0 -0.707106781187 0.707106781187
}}}
Sometime when using the {{{skinCluster}}} command to set weights, you'll get a Warning like this:
{{{
mc.skinPercent(skinCluster, verts, transformValue=influenceWeights, normalize=True)

# Warning: Some weights could not be set to the specified value. The weight total would have exceeded 1.0. # 
}}}

The issue is, the data in the {{{influenceWeights}}} variable : Based on Python's floating point precision, they may not actually add up to 1.0.

This function will 're-normalize' the weights, and //attempt// to solve for floating point rounding errors.  
{{{
def normalizeWeights(weights) :
    if sum(weights) == 1.0:
        return weights

    normed = [weight/sum(weights) for weight in weights]
    normSum = sum(normed)

    # Deal with floating point rounding errors
    if normSum != 1.0:
        minIndex = normed.index(min(normed))
        normBuffer = normed[:]
        normBuffer.pop(minIndex)
        sumAllButMin = sum(normBuffer)
        newMinVal = 1.0 - sumAllButMin

        if newMinVal >= 0.0:
            normed[minIndex] = newMinVal
        else:
            normed[minIndex] = 0.0
            maxIndex = normed.index(max(normed))
            normed[maxIndex] += newMinVal

    return normed
}}}

What's really interesting is:  If you check the sumed values after this tool is ran, somtimes they //still// don't normalize to 1.0 : They're usually a very smaller value,like {{{.9999999999999999}}} : Values less than 1.0 don't trigger Maya's spam message above, but values //over// 1.0 do.

Other notes:
*  In part based on how Python stores [[floating point values|https://docs.python.org/3/tutorial/floatingpoint.html]].
* Python has a [[decimal|https://docs.python.org/3/library/decimal.html]] module that feels like it will help with this.  And if I convert everything to {{{Decimal}}} classes and do the same work, it seems to be correct.  However, when you convert them back to float for weight assignment, the imprecisions come back the exact same :(
* I've tried writing the above code using {{{numpy}}}, get the same results.
* I've tried rounding the numbers too: This makes the results worse across the board.
Below is a bunch of notes I took while listening to a presentation done by ''Andrea Maiolo'' & ''Tim Naylor'' on their //Bi-directional constraint// rigging system.  Stuck them here mainly just to put them //somewhere// where I wouldn't loose them.  Really, these notes have nothing to do with that constraint system, and more of just a big brain-dump of technical Maya know-how.
<<<
*  Two passes in the DG:
**  The  Push Update Request: 'dirty propogation' pass,
**  Pull Latest Computed Data
* Coordinate Spaces:
** Euclidean - Infinite planes.
** Cartesian - 3d scene with origin.  Ordered valued system in the Euclidean system.  Maya viewport is a Euclidean to Cartesian conversion.
**Polar
**Cylindrical
** Homogenous
* Handedness : Cartesian to Euclidean conversion:  Maya is 'right handed':
** Right hand in front of you:  Thumb is up, pointer is forward, middle finger is left:
**  Pointer is +X, thumb is +Y, middle is -Z.  An 'orthagonal' coordinate system.
**  Positive rotation values go counter-clockwise.
* Vectors:  a direction with a magnitude.
**  Dot Product : Compare directions of two vectors.
***     Normalize vecs before comparing.
***     Returns value between -1 and 1:  -1 = parallel but opposite directions.
***     0 = not parallel.  1 = parallel.
**  Cross Product:  Perpendicular vector of two input vecs
***     Normal vecs first
***     Useful for building orthogonal transforms from vectors.
MEL - sees things as vectors.  API distinguishes between vectors & points.
*  Homogenous Coordinates :  Used to descrive a projection onto an infinite plane. Find a point on an infinitly plane, and you need to convert it to cartesian space, you use the homogenouse coordinate to convert it.
**  Allows for euclidean to cartesian conversion.
**  From infinite space (euclidean) to finite space (cartesian) requires 4 dimensions
**  Positions in space represented as X, Y, Z, W
***  W = Scalar for convertsion from euclidean to cartesian.
**  Vector : direction with arbitrary origin, inifitely long
**  Point : position from a given origin
**  Homogenous vector = << X, Y, Z, 0>>
**  Homogenous point = X, Y, Z, 1
*  The base of all rotations in Maya are vectors.
*  Each Unit vector has it's own XYZ components.  Angle of rotations is calculated from these vectors.
**A matrix is a structure for holding multiple elements that can be recorded or extracted easily and represents useful information to the end user.
*  The fourth (right) column in a 4x4 matrix holds the homogenous scalar values.
*  Each matrix has its own identity, transpose (flip diagonally), and inverse matrix (swapping rows and colums).
*  Can be multipled but are not associateve:  B*A != A*B
*  Multiply matrices in Maya from row to column
*  Transformation matrices are post-multiplied in Maya
Build a point constraint between cube and pyramid:
*  Make VectorProduct node
**  has matrix input
*  Take worldmatrix attr from your cube and feed into matrix input of vectorProduct node
*  Change vectorProduct node operation to 'point matrix product':  Will extract the translation of the worldmatrix.
*  The output of the vectorProduct node is the worldspace translation values of the cube.
*  Make another vectorProduct node, and connect pyramid's parent inversive matrix into it:
**This tells me where the parent of the pyramid is in worldspace.  The path the parentmust take to get back to the origin.
*  Connect the first vectorProduct nodes output (cube's worldspace translation) into the second vectorProduct nodes Input1.  Now in the second vectorProduct node you'll see the worldspace translation values in it's input1 section in the attr editor.
*  Set second vector prodcut node 'operation' to 'Point Matrix Product'.  Which will set it's output to basically be the local space of the pyramid.
*  Connect the output from the second vector product and connect it to the translation of the pyramid.
*  Pyramid now follows cube!
When you use getAttr to query a matrix in Maya, it causes the DG to eval to give
you the most current data.  How do you query this without making the DG eval?  the .matrix attr of a node is output only, so it is only updaed (causing the DG to update)
when it is queried.
*  Invert the parent invert matrix gives you the parent matrix.
**The parent matrix * node matrix = where the node is in worldspace.
*  To apply this to a 'driven node', you multiply the driven nodes inverseParentMatrix times the world-matrix previously generated to get its new local position.
<<<
General numric notes for a 32-bit machine based on c\c++.
| ''Type'' | ''data range'' | ''bits'' | ''data type'' | ''Maya attr type'' |
| boolean | 0 or 1, True or False | 1 | binary | bool |
| byte | -128 -> 127 | 8 | integer | byte |
| unsigned byte | 0 -> 255 | 8 | integer | |
| char | -128 -> 127 | 8 | integer, stored as letters with single quotes in c\c++: 'A' is 65 | char |
| unsigned char | 0 -> 255 | 8 | integer, stored as letters with single quotes in c\c++: 'B' is 66 | |
| int \ short | -32,768 -> 32,767 | 16 | integer | short  |
| unsigned int \ short |  0 ->  65,535 | 16 | integer | |
| long |  -2,147,483,648 -> 2,147,483,647 | 32 | integer | long  |
| unsigned long | 0 -> 4,294,967,295 | 32 | integer | |
| float | -3.4028235E^^38^^ -> 3.4028235E^^38^^ | 32 | floating point | float |
| double | -1.7E^^308^^ -> 1.7E^^308^^ | 64 | floating point | double |
http://msdn.microsoft.com/en-us/library/s3f49ktz.aspx
From docs:
<<<
OGS is one of the viewport renderers.
Renders an image or a sequence using the OGS rendering engine.
<<<
The {{{ogsRender}}} command appears to render the current view, with alpha, based on your current render global settings.  There is also the {{{ogs}}} command that seems to extend that functionality.
* http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/ogsRender.html
* http://help.autodesk.com/cloudhelp/2018/ENU/Maya-Tech-Docs/CommandsPython/ogs.html
{{{
import maya.cmds as mc
mc.ogsRender(width=480, height=270)
# Will save in the format and directory defined by render globals.
}}}
New in Maya 2020!  This is a new matrix attribute on {{{transform}}} node types (and it's children), that can have some {{{otherNode.worldMatrix}}} connect to it.
They can be visualized in the Attribute Editor in the 'Transform Offset Parent Matrix' tab.
!Vids
* [[Maya 2020 - Offset Parent Matrix|https://www.youtube.com/watch?v=YmIdC20_EYQ]]
* [[Maya 2020 - Matrix Widget|https://www.youtube.com/watch?v=87h0FlA2_Tw]]
* [[Maya 2020 - Pick Matrix|https://www.youtube.com/watch?v=_CKMYI0zt3w]]
* [[Maya 2020 - Blend Matrix|https://www.youtube.com/watch?v=eQCGd-x_WB4]]
* [[Empowering rigs using Offset Parent Matrix - Maya 2020|https://www.youtube.com/watch?v=JOYMV-bQdlM]]
* [[Vertex Theory video's using it|https://www.youtube.com/channel/UCDiRnfTIkreguxd0S8FlMrQ]]
!New matrix-related nodes in 2020
* [[pickMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/pickMatrix.html]] : lets you pick what mart of a matrix to pass (translate, orientation etc)
* [[blendMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/blendMatrix.html]] : Notes (see above vid for more detail) 
** Connect the primary target's {{{worldMatrix}}} to the {{{inputTarget}}} attr.
** To add more targets, via the Attribute Edtiro on the node, use the 'Target Matrix -> Target -> Add New Item' button to add new targets to connect in to.
** You can then connect your other targets to them in order of priority, since it will solve them in that order, based on the weights for each.
** Each target weight, when 0, will move the matrix to the value of the previous target
*  [[aimMatrix|http://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/aimMatrix.html]]
I needed a way to offset large amounts of keyframe data while working on a mocap tool.  I figured the best approach would be via the API, but unless I'm 'doing it wrong', it ended up being the slowest of the 4 techniques by about 2x:  I'll list them in order of speed, and put the results at the bottom:
!!!Python Commands:
{{{
import maya.cmds as mc
def py_offsetAllKeys(ns, offset):
    animCurves = mc.ls('%s:*'%ns, type='animCurve')
    mc.keyframe(animCurves, edit=True, includeUpperBound=False, animation="objects",
                relative=True, option='over', timeChange=offset)
}}}
!!!Mel Commands:
{{{
global proc mel_offsetAllKeys(string $ns, int $offset){
    string $animCurves[] = `ls  -type "animCurve" ($ns+":*")`;
    keyframe -edit -includeUpperBound 0 -animation "objects" -relative -option "over" -timeChange $offset  $animCurves;
    }
}}}
!!!~PyMel Commands:
{{{
import pymel.core as pm
def pymel_offsetAllKeys(ns, offset):
    animCurves = mc.ls('%s:*'%ns, type='animCurve')
    mc.keyframe(animCurves, edit=True, includeUpperBound=False, animation="objects",
                relative=True, option='over', timeChange=offset)
}}}
!!! Python API:
{{{
import maya.OpenMaya as om
import maya.OpenMayaAnim as oma
def api_offsetAllKeys(ns, offset):
    pm.select(pm.ls('%s:*'%ns, type='animCurve'))
    selList = om.MSelectionList()
    om.MGlobal.getActiveSelectionList(selList)
    mPlugArray = om.MPlugArray()
    mItSelectionList = om.MItSelectionList(selList)
    offsetTime = om.MTime(offset)
    while not mItSelectionList.isDone():
        mObject_animCurve = om.MObject()
        mItSelectionList.getDependNode(mObject_animCurve)
        animFunc = oma.MFnAnimCurve(mObject_animCurve)
        numKeys = animFunc.numKeys()
        if not numKeys:
            continue
        keyRange = range(0, numKeys-1)
        keyRange.reverse()
        for i in keyRange:
            indexTime = animFunc.time(i)
            newTime = indexTime + offsetTime
            animFunc.setTime(i, newTime)
        mItSelectionList.next()
}}}
!!!Results:
Based on 700 frames of mocap on around 45 joints, translate & rotate:
{{{
# -- Py cmds : ran for 0.421 seconds -- #
# -- Mel cmds : ran for: 0.421 seconds -- #
# -- PyMel anim: ran for 0.453 seconds -- #
# -- API : ran for 0.868 seconds -- #
}}}
Ooof, not so good for the API.  Interesting to see Python cmds and mel cmds having the exact same time.
It should be noted that I tried to get {{{MItKeyframe}}} working, but whenever I'd call to it's {{{setTime}}} function, it would fail.
----
Also see:
*[[How can I offset all animation data in the scene by a fixed amount?]]
Autodesk has an 'online file viewer' called 'Autodesk Viewer'.  While it unfortunately doesn't support ma/mb files, you can use it to say, preview FBX data exported from Maya on the web:
* https://viewer.autodesk.com/
* [[Docmentation|https://help.autodesk.com/view/ADSKVIEWER/ENU/]]
* [[Supported file types|https://help.autodesk.com/view/ADSKVIEWER/ENU/?guid=ADSKVIEWER_Help_SupportedFileTypes_html]]
----
Note, there is a standalone app for viewing FBX files specifically:
[[How can I preview FBX files outside of Maya?]]
But I'm told it's no longer supported/maintained.
~PyMel makes it pretty easy:
{{{
import pymel.core as pm
pm.fcheck("path to my image")
}}}
[[2015 Docs|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.core.system/pymel.core.system.fcheck.html]]
[[Online Maya 2011 Docs|http://download.autodesk.com/us/maya/2011help/API/class_m_script_util.html]]
From the docs:
<<<
Utility class for working with pointers and references in Python.

Many of Maya's API methods require that one or more of their parameters be passed as pointers or references. Their return values, too, can be pointers or references.

In Python parameters of class types are passed by reference but parameters of simple types, like integers and floats, are passed by value, making it impossible to call those API methods from Python. @@The ~MScriptUtil class bridges this gap by providing methods which return pointers to values of simple types and which can extract values from such pointers. These pointers can also be used wherever an API method requires a reference to a simple type or an array of a simple type.@@
<<<
So while we have this nice class for dealing with c++ pointers '{{{*}}}' and references '{{{&}}}', it's not the easiest thing to understand.  
This class also exposes some related helper functions discussed below.

Looks like the Maya 2011 docs have finally give some examples, but before that was around this blog post did a great job of covering how to use it:
http://www.chadvernon.com/blog/resources/maya-api-programming/mscriptutil/
The first example is pulled directly from it (slightly modified), for archival purposes:
----
!!!Pointer generation examples:
''int reference'':
This c++ class expects a int reference ({{{int&}}}) to be passed in.  
{{{
MStatus MItMeshPolygon::setIndex( int index, int& prevIndex )
}}}
To make an int pointer you:
#Make a {{{MScriptUtil}}} object
#Update the {{{MScriptUtil}}} with default int data.
#Return a int pointer that can be used.
#Pass that pointer to the requiring method.
#Optional: Query the value of the updated pointer.
{{{
import maya.OpenMaya as om

itPoly = om.MItMeshPolygon(pathShape)
util = om.MScriptUtil()
util.createFromInt(0)
pInt = util.asIntPtr()
itPoly(faceId, pInt)
}}}
----
''array \ list reference'':
There appears to be two different ways to pass lists of Python data to a pointer.  I'm currently not too sure if one is preferable over the either.  But the below example explains the two systems:
{{{
import maya.OpenMaya as om

# Some list of Python ints:
intList = [0,1,2,3]

# Example A:
utilA = om.MScriptUtil()
# Create the pointer from a defined list of four items:
utilA.createFromList(intList, 4)
# Return a pointer object as an 'int 4':
pointerA = utilA.asInt4Ptr()
# You'd pass pointerA to another class for modification...
# Then you'd extract updated values:
resultA = [utilA.getInt4ArrayItem(pointerA, 0, i) for i in range(len(intList))]
print resultA

# Example B:
utilB = om.MScriptUtil()
# Create the pointer by unpacking our int list:
utilB.createFromInt(*intList)
# Return a pointer as an int (and in our case an array of them):
pointerB = utilB.asIntPtr()
# You'd pass pointerB to another class for modification...
# Then you'd extract updated values:
resultB = [utilB.getIntArrayItem(pointerB, i) for i in range(len(intList))]
print resultB
}}}
{{{
[0, 1, 2, 3]
[0, 1, 2, 3]
}}}
*In 'Example A' you can see from the methods being called that every step of the way you need to tell the system how big the passed in list is.  The methods either have the number '4' in their name, or need it passed in as an argument.
*'Example B' is much more flexible as far as both passing data, and extracting it:  You only need to know how big the original list was at the very end, to extract the resultant data.  The {{{getIntArrayItem()}}} method will not raise an exception if you ask for an index outside the original list size range, so you need to only pull the indices based on the original list size.
!!!Helper Functions
These return new {{{OpenMaya}}} objects based on passed in Python lists.  For some reason, __they're not included in the documentation__.
*{{{MScriptUtil.createMatrixFromList()}}} : Return an {{{OpenMaya.MMatrix}}} object.
*{{{MScriptUtil.createFloatMatrixFromList()}}} : Return an {{{OpenMaya.MFloatMatrix}}} object.
*{{{MScriptUtil.createIntArrayFromList()}}} : Return an {{{OpenMaya.MIntArray}}} object.
*{{{MScriptUtil.createFloatArrayFromList()}}} : Return an {{{OpenMaya.MFloatArray}}} object.
*{{{MScriptUtil.createFromList()}}} : Not sure?
If you use the version control software [[Perforce|http://www.perforce.com]] (P4), and you're reading this, you probably want a way to have Maya talk with it.  In the past I've written a large mel script to call out to the system and manage P4.  But since the release of Maya2008 which has Python on-board, we might as well ride on the shoulders of giants.

Perforce offers up tools to let Python talk with it.  I've documented this on my Python Wiki here:
{{{http://pythonwiki.tiddlyspot.com/#[[Perforce%20access%20via%20Python]]}}}
(due to formatting reasons, I couldn't get the link to work.  Please copy-paste)

However, to get this working in Maya takes an extra step.  Since Maya runs it's own custom cut of Python, it has those executables saved in a different location from the normal Python install.  When you run the P4\Python installer executable, it'll put the resources in a location that Maya doesn't see it.  ~Maya-Python looks here for 'site-packages' in Python: 
{{{
C:\Program Files\Autodesk\Maya<version>\Python\lib\site-packages\
}}}
While 'raw' Python looks here:
{{{
C:\Python<version>\Lib\site-packages\
}}}
When you install P4\Python integration, they go into the //Python// site-packages, not the //~Maya-Python// site-packages.  Copying over the files (listed in my Python Wiki link) seems to work a-ok.
//Or//, you could make another script dir in your ~Maya-Python path, and put the modules there of course.
While not specifically Maya related, this post does a great job of covering concepts and pseudo-code (Python-ish) behind it:
http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
Great page covering many physics topics from cloth simulation to inverse kinematics:
http://freespace.virgin.net/hugo.elias/models/m_main.htm
While not directly Maya related, much of the pseudo-code supplied is easily transferrable to Python.
Recently ran into a problem after importing a fbx file, where Maya's Hypershade\Multilister wouldn't properly pick the shaded components for a given material.  So I wrote this code that does the right thing:
{{{
# Python Code
# Pick materials in the Hypershade:  This will select all objects\components
# they affect:
import maya.cmds as mc

material = mc.ls(selection=True, materials=True)
if len(material):
    pick = []
    shaders = mc.listConnections(material, source=False, destination=True, type='shadingEngine')
    if shaders:
        for s in shaders:
            stuff = mc.sets(s, query=True)
            if stuff:
                pick.extend(stuff)
    if len(pick):
        mc.select(pick)
    else:
        print "No shaded items found"
else:
    print "Please pick one or more materials"
}}}
I've heard this used successfully (Maya 2013):
*Format: qt (quicktime)
*Encoding: H.264, or Sorenson Video
*Quality: 50%
*Display size: Custom, 1280 x 820
*Scale: .5
Notes:  15 sec clip is about 5 megs.
Thanks for the tip TJ ;)
Note:  I have an older subject found [[here|Executing external functions via UI's authord in Python]] that also sheds light on this issue.
<<<
''Update #2'' : Just do this: [[PyMel : Callback objects]]
<<<
<<<
''Update #1'', bit of an addendum to the '{{{lambda}}}' code below:  The Maya 2010 docs describe using the Python module [[functools|http://docs.python.org/library/functools.html]] under '[[Custom UI using a Python class|http://download.autodesk.com/us/maya/2010help/index.html?url=WS73099cc142f48755f2fc9df120970276f7-2158.htm,topicNumber=d0e183276]]'.  Specifically using {{{functools.partial}}}
Here's a code snippet:
{{{
import functools
# inside your UI:
mc.button(command=functools.partial(self.myMethod, someVal))
# presuming myMethod() was expecting a single arg
}}}
I've found that this will work in rare situations when {{{lambda}}} won't.
<<<
When creating UI controls, one of the several ways of accessing the data is via "positional arguments".  This is data the control can pass back to callback functions when executed (via say, their "changeCommand", or "command" argument).  For example, if you make a {{{floatFieldGrp}}} with three fields, you can use 'positional arguments' to directly intercept those values when modified. But how can you actually do this?
----
The Maya docs loosely cover this here:
''Maya Help -> Using Maya -> General -> Python -> Python -> Using Python -> Positional arguments'' (at the bottom). 
Here is a link to the [[online docs|http://download.autodesk.com/us/maya/2008help/General/Using_Python.html]] reference above.  Read that section to get an understanding if the two different methods available.
----
In a nutshell:
*Method #1:  Embedding the callback in a string, using Python string formatting to grab the results:
{{{
mc.floatSlider( cc="print '%(1)s'" )
}}}
**The Python '{{{%(1)s}}}' syntax says capture the first {{{(1)}}} positional arg, and convert it to a string {{{%s}}} for printing.  The UI control returns its positional args as a Python {{{dictionary}}}, that are then passed into the string formatting.
**Given the above example, and presuming that the {{{floatSlider}}} was dragged and produced a value of 3.65, this would be a representation of how the command was being formatted behind the scenes, just before execution:
{{{
print '%(1)s' % {'1': 3.65}
# In the dictionary {}, the '1' represents the first positional arg,
# and 3.65 is the value assigned to it.
}}}
**See Python docs [[online|http://docs.python.org/library/stdtypes.html#string-formatting-operations]]
*Method #2: Uses a //compiled function//, where Maya passes the control's positional arguments to the function.  It's important that your callback function can handle any number of args (usually via {{{*args}}}) since depending on the control type, it can return back a different number of args.  
{{{
def genericCallback( *args ): 
	print( "args: " + str ( args ) ) 
mc.button( command=genericCallback )
}}}
It should be noted that using method #2, I've found two different approaches:  using the compiled function technique (above), //or// wrappering the compiled function in the Python {{{lambda}}} expression.  What is {{{lambda}}} you may ask?  See notes on my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#%5B%5BUnderstanding%20lambda%5D%5D]].  Easier than describing it all here :)
----
Here's a practical example in a UI, using all three methods on a random selection of UI controls, with notes afterwords:
{{{
import maya.cmds as mc

class PyTempWindow(object):
    def __init__(self):
        self.showWindow()
                
    def genericCallback(self, *args): 
        print "Callback args: " + str(args)

    def showWindow(self):
        if mc.window("PyTempWindow", exists=True):
            mc.deleteUI("PyTempWindow")
            
        mc.window("PyTempWindow", title="Positional arg callback UI", resizeToFitChildren=True)
        mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))

        mc.text(label="Printed Positional Args:", align="left")
        mc.floatSlider(changeCommand="print 'Positional args: %(1)s'")
        mc.floatFieldGrp(numberOfFields=3, changeCommand="print 'Positional args: %(1)s %(2)s %(3)s'")
        mc.textField(changeCommand="print 'Positional args: %(1)s'")
        mc.button(command="print 'Positional args: %(1)s'", label="Positional")
        mc.textFieldButtonGrp(changeCommand="print 'Positional args: %(1)s'", buttonCommand="print 'Positional args: %(1)s'")
        mc.separator(height=20)

        mc.text(label="Callback Function:", align="left")
        mc.floatSlider(changeCommand=self.genericCallback)
        mc.floatFieldGrp(numberOfFields=3, changeCommand=self.genericCallback)
        mc.textField(changeCommand=self.genericCallback)
        mc.button(command=self.genericCallback, label="Callback")
        mc.textFieldButtonGrp(changeCommand=self.genericCallback, buttonCommand=self.genericCallback)
        mc.separator(height=20)

        mc.text(label="Lambda Callback Function:", align="left")
        mc.floatSlider(changeCommand=lambda *args:self.genericCallback(args, "Custom Lambda Arg"))
        mc.floatFieldGrp(numberOfFields=3, changeCommand=lambda *args:self.genericCallback(args, "Custom Lambda Arg"))
        mc.textField(changeCommand=lambda *args:self.genericCallback(args, "Custom Lambda Arg"))
        mc.button(command=lambda *args:self.genericCallback(args, "Custom Lambda Arg"), label="Callback")
        mc.textFieldButtonGrp(changeCommand=lambda *args:self.genericCallback(args, "Custom Lambda Arg"), buttonCommand=lambda *args:self.genericCallback(args, "Custom Lambda Arg"))

        mc.showWindow()
        
win = PyTempWindow()
}}}
So what's going on the the above UI?
*In the first "positional arg" section, the commands are embedded directly into the UI itself, using positional arguments and Pythons string formatting notation.
*In the middle section, the commands are calling to a callback function.  Note in the syntax that the callback function isn't being executed in the control command (there is no parenthesis at the end of it: '{{{changeCommand=self.genericCallback}}}'), there is just a pointer back to the callback function.  This is because a UI control command can only execute strings (the first example) and functions //without arguments// (this example).
*In the bottom section, commands are calling to the callback function, but that's being wrappered in a {{{lambda}}} expression.  Things of note:
**{{{lambda}}}'s return 'anonymous functions':  A function object that can then be executed.
**'{{{lambda *args}}} : this lets the {{{lambda}}} intercept all incoming arguments, and then pass them on to their returned function.
**Since {{{lambda}}}'s //are// 'anonymous functions', this lets then both accept //and// 'pass on' arguments to their containing functions, circumventing the issue of UI's 'only being able to execute functions without args'.  Nice.  In the above examples '{{{lambda *args:self.genericCallback(args, "Custom Lambda Arg")}}}', you can see that the {{{lambda}}} both passes on any //positional args// provided by the UI control when executed, and its own arg: {{{"Custom Lambda Arg"}}}.
----
''What's the result of all of this?''
----
Print result from ''Printed Positional Args'' section, with random UI fiddling (with UI type listed before each):
{{{
# floatSlider:
Positional args: 5.28
# floatFieldGrp:
Positional args: 0.0 3 0.0
# textField:
Positional args: foo
# button:
Positional args:
# textFieldButtonGrp (textField)
Positional args: asdf:
# textFieldButtonGrp (button)
Positional args: %(1)s
}}}
Notes:
*The {{{button}}} returns an empty positional arg.
*The {{{textFieldButtonGrp}}} button returned back a literal string of the string formatting instead of an actual positional arg value.  Odd.  Maybe this is because it actually returns back no positional arguments at all (illustrated below).
----
Print result from ''Callback Function'' section, with random UI fiddling (with UI type listed before each):
{{{
# floatSlider:
Callback args: (u'6.6',)
# floatSliderGrp:
Callback args: (u'0.0', u'3', u'0.0')
# textField:
Callback args: (u'asdf')
# button:
Callback args: (u'')
# textFieldButtonGrp (textField):
Callback args: (u'asdf')
 # textFieldButtonGrp (button)
Callback args: ()
}}}
Notes:
*The return value is captured as a Python {{{tuple}}}.  A {{{tuple}}} is an immutable (unchangeable) list.  Based on our {{{genericCallback()}}}, each item in the {{{tuple}}} is turned into a string for printing purposes.
*Like above, the {{{button}}} returned an empty positional arg.
*Here, it can be plainly visualized that the {{{textFieldButtonGrp}}} 'button' returned back //no positional args// to be captured.
----
Print result from ''Lambda Callback Function'' section, with random UI fiddling (with UI type listed before each):
{{{
# floatSlider
Callback args: ((u'11.222',), 'Custom Lambda Arg')    
 # floatSliderGrp
Callback args: ((u'0.0', u'3', u'0.0'), 'Custom Lambda Arg')
# textField
Callback args: ((u'asdf',), 'Custom Lambda Arg')
# button
Callback args: ((u'',), 'Custom Lambda Arg')
# textFieldButtonGrp (textField):
Callback args: ((u'asdf',), 'Custom Lambda Arg')
 # textFieldButtonGrp (button)
Callback args: ((), 'Custom Lambda Arg')
}}}
Notes:
*The return is very similar to the 'Callback Function' section.  But note that rather than printing a list filled with multiple items, the 1st position is another {{{tuple}}}, which has specifically captured the positional arguments from the UI command.
*Since {{{lambda}}} lets us pass our own arguments into the callback, in addition to capturing the //positional arguments// of the control itself, our custom {{{"Custom Lambda Arg"}}} string is listed last.
*This gives another clear indication that the {{{button}}} returns back an empty positional arg, while the {{{textFieldButtonGrp}}} button command returns back no positional args at all.
A shortcut for printing lots of info when it comes to the {{{workspace}}} command.
{{{
# Python code
import maya.cmds as mc

def workspaceInfo():
    """
    Print info based on the workspace command.
    """
    print "openWorkspace:", mc.workspace(query=True, openWorkspace=True)
    print "active:", mc.workspace(query=True, active=True)
    print "baseWorkspace:", mc.workspace(query=True, baseWorkspace=True)
    print "listWorkspaces:"
    for item in mc.workspace(query=True, listWorkspaces=True):
        print "\t", item
    print "listFullWorkspaces:"
    for item in mc.workspace(query=True, listFullWorkspaces=True):
        print "\t", item
    print "list:"
    for item in mc.workspace(query=True, list=True):
        print "\t", item
    print "directory:", mc.workspace(query=True, directory=True)
    print "rootDirectory:", mc.workspace(query=True, rootDirectory=True)
    print "fileRule:"
    fileRule = mc.workspace(query=True, fileRule=True)
    for i in range(0, len(fileRule), 2):
        print "\t", fileRule[i], fileRule[i+1]
    print "fileRuleList:"
    for item in mc.workspace(query=True, fileRuleList=True):
        print "\t", item
    print "variableList:"
    for item in mc.workspace(query=True, variableList=True):
        print "\t", item, mc.workspace(query=True, variableEntry=item)
    print "shortName:", mc.workspace(query=True, shortName=True)
    print "fullName:", mc.workspace(query=True, fullName=True)
}}}
{{{
workspaceInfo()
}}}
''Procedures''  (WIP)
*[[procedures|Procedures]]
**__Can contain__ (when defined):
***[[commands|Commands]]
***//executed// [[procedures|Procedures]] (no nested procedure definitions)
**__Can accept__ (when executed, depending on the procedure):
***procedure [[arguments|Arguments]]
**__Can provide__ (when executed, depending on the procedure):
***[[return values|Return Values]]  (as arguments to other procedures)
----
*Procedures are also known as 'functions' in other languages.
*Procedures can be both defined, and executed.  When defined, they can be //local// to the [[script|Scripts]] they were defined in, or //global//, meaning they're able to be seen by the outside world.  A script needs to be first sourced before any of the 'global procs' defined inside of it can be seen by external code.  However, "local" procedures are never seen outisde of the script they're defined in.
** Why would you want to use local procs?  :  To help remove possible name clashes:  Any global proc can be seen by any other proc or command.  But sometimes you may just want a little "helper" bit of code in a script, and it's not important that can't be seen by anything else outside of the scope of the script.
*A procedure definition can be filled with mel commands, or calls to other pre-defined procedures.  Procedures can be called in other procedures, but you can't 'nest' procedure definitions.
*Once a procedure is defined, it behaves just like a command.
*For example, pretend we have a script called foo.mel, and inside of it we'll make a global procedure definition, that contains one command ({{{print}}}):
{{{
// this is the script foo.mel

// this is a local proc, that is only seen inside of this script:
proc goo(){
    print "local goo!\n";}

// this is the global proc, that has the same name as the script, 
// so when the script is called to, this proc is executed:
global proc foo()
    {
// call to our local proc.
    goo;
    print "foo!\n";
    }
}}}
*After this script is saved on disk (and presuming it's been saved in Mayas [[script path|Script Paths]]), you need to make Maya aware of its existance.  You can either use the source command like so:
{{{
source foo.mel;
}}}
*Or highlight the block of code in the Script Editor, and define it using the //number pad// Enter button.
*This won't actually //execute// the procedure, it will simply let Maya know it exists, and later //be// executed.
*Once define, you can execute your {{{foo()}}} procedure (which lives in the {{{foo.mel}}} script) by typing it at the command line:
{{{
> foo;
local goo!    
foo!
}}}
*  If you typed {{{goo}}} at the command line, you'd get an error:
{{{
> goo;
// Error: line 1: Cannot find procedure "goo". //
}}}
*Because {{{goo}}} is a local proc in script {{{foo.mel}}}.  
**This COULD bring up some confusion though:  If you're editing this code in the script editor, highlight it, and execute that block, irregarless of wheter you specify a script to be local or global, it will become global during that Maya session.  However, restarting Maya will make the local scripts be local, and you can see this behavior.
Maya //mel// code to open a commandPort:
{{{
commandPort -name "127.0.0.1:7777";
commandPort -name ":7777";
commandPort -name "7777";
}}}
If I don't run //all three// of those lines, Processing gives me this Exception:
{{{
java.net.ConnectException: Connection refused: connect
}}}
----
Processing code to create a locator in Maya whenever the left mouse button is pressed.  This uses the {{{Client}}} library:
http://processing.org/reference/libraries/net/Client.html
{{{
// Import the Client library:
import processing.net.*; 
Client mayaClient;

void setup(){
  mayaClient = new Client(this, "localhost", 7777); 
}

void draw(){
  // nothing...
}

void mousePressed(){
  if(mouseButton == LEFT){
    float x = mouseX;
    float y = mouseY * -1 + height; // convert to Maya coordinates
    String mel = ("spaceLocator -p "+x+" "+y+" 0;");
    mayaClient.write(mel);
    ellipse(mouseX, mouseY, 4,4);
  }
}
}}}
----
I got tipped off to this code from this blog post:  http://www.jfernsler.com/blog/?p=29
This site has a bunch of language comparisons, usually grouped into three or four per page:
http://hyperpolyglot.org/
For an understanding of what a 'proxy attr' is, please see [[Working with proxy attrs]].
Found a bug in 2019.2 : If you disconnect your proxy attr from its parent, then try to query it's type Maya will error;
Repro Test:
{{{
// Create two nodes to test on:
string $nodeA = `createNode "transform" -name "nodeA"`;
string $nodeB = `createNode "transform" -name "nodeB"`;

// Create the 'parent' proxy attr:
addAttr -longName "myProxy" -keyable true $nodeA;
// Create the 'child' proxy attr:
addAttr -longName "myProxy" -proxy ($nodeA+".myProxy") $nodeB;

// Print the type of the child proxy attr:
print `getAttr -type ($nodeB+".myProxy")`;
// double

// Disconnect the proxy relationship:
disconnectAttr ($nodeA+".myProxy") ($nodeB+".myProxy");

// Now try to print the type of the child proxy attr again:
print `getAttr -type ($nodeB+".myProxy")`;
// Error: line 1: The value for the attribute could not be retrieved. //
}}}
It appears that even though you make proxy attrs with basically the same {{{addAttr}}} call as the parent, if they loose their connection to the parent, they no longer know what 'type' they are :S
~PyMel provides an [[Attribute|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html]] class for interacting with a node's attributes.  Note that {{{Attribute}}}s inherit from [[PyNode|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html]]s, which have additional attribute access.
Quick overview on usage, using a joint node for an example, since it has a {{{.type}}} attribute that doesn't behave as one would expect.  And for clarity, //node// refers to a physical node in Maya, while //object// refers to a Python/~PyMel class instance.

This creates a Maya node called 'joint1', and returns a ~PyNode object wrapper for it:
{{{
import pymel.core as pm
j = pm.createNode('joint1')
}}}
Node's attrs can be accessed by the same exact name as an attribute on the instanced ~PyNode object:  These object.attribute is a ~PyNode Attribute instance:
{{{
print type(j)
<class 'pymel.core.nodetypes.Joint'> # Subclass of PyNode
print type(j.side)
<class 'pymel.core.general.Attribute'>
}}}
You can also get objects for each attribute:
{{{
attr1 = pm.Attribute("joint1.side")
attr2 = j.side
print `attr1`
print `attr2`
# Attribute(u'joint1.side')
# Attribute(u'joint1.side')
}}}
This makes it very easy (with exceptions show below) to get and set values.
!!! Attribute Existence Querying:
I've not found anything 'special' in ~PyMel for this.  This is the normal way I test to see if an attr exists.  You can pass in string names or ~PyNodes for the 'node':
{{{
pm.attributeQuery('attrName', node='nodeName', exists=True)
}}}
Sometimes I can do this via straight Python, but not always.  Must pass in a ~PyNode for the object being queried:
{{{
hasattr(aPyNode, 'attrName')
}}}
!!!Setting Values:
On any 'normal' attribute, you can access it on the ~PyNode directly.  To set the joint node's {{{.side}}} attr, call to the corresponding Attribute object's {{{set}}} method:
{{{
j.side.set(1)
}}}
However, sometimes there are name collisions:  The ~PyMel ~DependNode superclass of the joint has a {{{type}}} //method// that takes over the joint objects {{{type}}} //Attribute// object:
{{{
j.type.set(1)
# Error: 'function' object has no attribute 'set'
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# AttributeError: 'function' object has no attribute 'set' # 
}}}
In these cases, there are two options for accessing the attr:  You can wrap the string name of the attribute in the {{{attr}}} method, which returns a new Attribute object, and call {{{set}}} on it:
{{{
j.attr('type').set(1)
}}}
Or optionally, you can create a whole new {{{Attriibute}}} object around the obj.attr combo, and set it directly:
{{{
jTypeAttr = pm.Attribute('%s.type'%j)
jTypeAttr.set(1)
}}}
!!!Getting Values:
Just like with {{{set}}}, you can use a {{{get}}} method on a given Attribute object:
{{{
sideVal = j.side.get()
}}}
And just like above, trying to do a get on a superclass overridden Attribute object causes problems:
{{{
typeVal = j.type.get()
# Error: 'function' object has no attribute 'get'
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# AttributeError: 'function' object has no attribute 'get' # 
}}}
So we jump through some hoops:
{{{
typeVal = j.attr('type').get()
}}}
or:
{{{
jTypeAttr = pm.Attribute('%s.type'%j)
typeVal = jTypeAttr.get()
}}}
!!! Getting String Names:
{{{
print attr.longName()
print attr.longName.longName(fullPath=True)
# translateX
# translate.translateX
}}}
!!!Aliasing attr names
Give an attr some other alias.  This isn't renaming the attr, but providing an alias that shows up in the Channel Box for the user.  The old name still works too.
{{{
attr.setAlias("someNewAttrName")
}}}
Getting the alias name from an attr, most commonly used for blendshape .weights attrs:
{{{
weightAttr = pm.Attribute("myBlendshape.weight[42]")
print weightAttr.getAlias()
# awesomeShapeTargetName
}}}
!!! Getting the attributes type:
{{{
print attr.type()
enum
}}}
!!! Connecting Attributes:
Also see [[PyMel : Connection Access]] for more detail.
~PyMel has some nice shorthand:
{{{
# Connect two attrs:
myNode.someAttr >> otherNode.otherAttr
# or:
myNode.someAttr.connect(otherNode.otherAttr)
}}}
!!! Disconnecting Attributes:
{{{
someNode.someOutAttr // otherNode.someInputAttr
# or:
myNode.someOutAttr.disconnect(otherNode.someInputAttr)
# or to disconnect from all:
myNode.someAttr.disconnect()
}}}
!!!Locking & Unlocking:
{{{
myNode.someAttr.lock()
myNode.someAttr.unlock()
}}}
!!!Querying locked state:
{{{
myNode.someAttr.get(lock=True)
}}}
!!!Making keyable or unkeyable:
{{{
myNode.someAttr.setKeyable(True)
myNode.someAttr.setKeyable(False)
}}}
!!!Show in the Channel Box:
{{{
myNode.someAttr.showInChannelBox(True)
# Note, if False, must be made non-keyable (above) first, or will have no effect.
myNode.someAttr.showInChannelBox(False)
}}}
!!!Adding Attributes:
{{{PyNodes}}} have an {{{addAttr}}} //method// (via the [[DependNode|PyMel : DependNode methods]] class).  All the regular arguments to the {{{addAttr}}} command can be passed in.  This will create a simple float attr:
{{{
myNode.addAttr("someAttr")
}}}
Or you can use the regular {{{addAttr}}} (~PyMel) command:
{{{
pm.addAttr(myNode, longName="someAttr")
}}}
!!!Adding and getting multi-array attrs:
If you want to create (for example) a 2d-array of numbers, you can create a multi-array (the 'double2' is the array part) attr.  Note how you can set the values by index.  Also note how you have to print twice to see it show the values correctly when you skip an index.  Bug?  If you print index[3] directly after creation it does show up, so maybe this is a bug in the str version of that type?
{{{
node = pm.PyNode("null1")
pm.addAttr(node, longName="myArray", dataType="double2", multi=True)
node.myArray[0].set([0,0])
node.myArray[1].set([1,1])
node.myArray[3].set([3,3])

print node.myArray.get()
print node.myArray.get()
[(0.0, 0.0), (1.0, 1.0), None]
[(0.0, 0.0), (1.0, 1.0), None, (3.0, 3.0)]
}}}
!!!Deleting Attributes:
{{{
myNode.someAttr.delete()
}}}
!!!Finding the parent node:
{{{
someAttr.node()
}}}
!!!Listing Attrs:
You can use {{{listAttr}}} like normal.  However, this returns the string name, not an {{{Attribute}}} instance, so you need to wrapper them before you do stuff:
{{{
for ud in pm.listAttr(myNode, userDefined=True):
    attr = myNode.attr(ud)
    # Do stuff to attr...
}}}
!!! Dealing with enum attrs
See: [[PyMel : enum attr access]]
!!! Renaming Attrs:
It looks like there is no 'renameAttr' method, and you must rely on the command instead:
{{{
pm.renameAttr("node.oldAttrName", "newAttrName")
}}}
If you're renaming an aliased attr (like, a blendshape target weight attr), you simply give it a new alias:
{{{
pm.aliasAttr("newAliasAttrName", "node.oldAliasAttrName")
}}}
----
Also see:
*[[PyMel : Attribute methods]]
Condensation of the ~PyMel [[Attribute|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html]] docs.   Note that {{{Attribute}}}s inherit from [[PyNode|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html]]s, which have additional attribute access (shown below).
----
Also see: [[PyMel : Attribute Access]]
*{{{affected}}} : No docs
*{{{affects}}} : No docs
*{{{array}}} : Returns the array (multi) attribute of the current element
*{{{attr}}} : Returns {{{Attribute}}}
*{{{attrName}}} : String name of the attribute for this plug
*{{{children}}} : Wrapper for {{{attributeQuery -listChildren}}}
*{{{connect}}} : Connect the attributes of two dependency nodes and return the names of the two connected attributes.
*{{{delete}}} : Wrapper for {{{deleteAttr}}}
*{{{disconnect}}} : Disconnects two connected attributes.
*{{{elementByLogicalIndex}}} : find and return a plug with the given logical index.
*{{{elementByPhysicalIndex}}} :  find and return a plug with the given physical index.
*{{{elements}}} : Wrapper for {{{listAttr -multi}}} : Return a list of strings representing all the attributes in the array.
*{{{evaluate}}} : No docs
*{{{evaluateNumElements}}} : Return the total number of elements in the datablock of this array plug
*{{{exists}}} : Whether the attribute actually exists.
*{{{firstParent}}} : deprecated
*{{{get}}} :  returns the value of the named object’s attribute.
*{{{getAlias}}} : Returns the alias for this attribute, or None
*{{{getAllParents}}} : Return a list of all parents above this
*{{{getArrayIndices}}} : Get all set or connected array indices
*{{{getChildren}}} : Wrapper for {{{attributeQuery -listChildren}}}
*{{{getEnums}}} : Returns [[pymel.util.enum.EnumDict|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.util.enum/pymel.util.enum.EnumDict.html]] for the given attr.
*{{{getMax}}} : Wrapper for {{{attributeQuery -max}}}
*{{{getMin}}} : Wrapper for {{{attributeQuery -min}}}
*{{{getNumElements}}} : Return the total number of elements in the datablock of this array plug
*{{{getParent}}} : No doc description
*{{{getRange}}} : Wrapper for {{{attributeQuery -range}}}
*{{{getSiblings}}} : Wrapper for {{{attributeQuery -listSiblings}}}
*{{{getSoftMax}}} : Wrapper for {{{attributeQuery -softMax}}}
*{{{getSoftMin}}} : Wrapper for {{{attributeQuery -softMin}}}
*{{{getSoftRange}}} : Wrapper for {{{attributeQuery -softRange}}}
*{{{index}}} : Returns the logical index of the element this plug refers to
*{{{indexMatters}}} : No docs
*{{{info}}} : returns a string containing the name of the node this plug belongs to and the attributes that the plug refers to
*{{{inputs}}} : Wrapper for {{{listConnections -source 1 -destination 0}}}
*{{{insertInput}}} : connect the passed node.outAttr to this attribute and reconnect any pre-existing connection into node.inAttr
*{{{isArray}}} : determines if the plug is an array plug
*{{{isCaching}}} : Returns true if this plug or its attribute has its caching flag set
*{{{isChild}}} : This method determines if the plug is a child plug
*{{{isCompound}}} : This method determines if the plug is a compound plug
*{{{isConnectable}}} : Wrapper for {{{attributeQuery -connectable}}}
*{{{isConnected}}} : Determines if this plug is connected to one or more plugs
*{{{isConnectedTo}}} : Determine if the attribute is connected to the passed attribute
*{{{isDestination}}} : Determines if this plug is connected as a destination.
*{{{isDirty}}} : No docs
*{{{isDynamic}}} : Determines whether the attribute is of dynamic type or not.
*{{{isElement}}} : determines if the plug is an element plug
*{{{isFreeToChange}}} : Returns true if the plug’s value is allowed to be set directly.
*{{{isFromReferencedFile}}} :  determines whether this plug came from a referenced file
*{{{isHidden}}} : Wrapper for {{{attributeQuery -hidden}}}
*{{{isIgnoredWhenRendering}}} : Determines whether a connection to the attribute should be ignored during rendering.
*{{{isInChannelBox}}} : Returns true if this plug or its attribute has its channel box flag set
*{{{isKeyable}}} : Determines if this plug is keyable
*{{{isLocked}}} : Determines the locked state of this plug’s value
*{{{isMulti}}} : This method determines if the plug is an array plug
*{{{isNetworked}}} : determines if the plug is networked or non-networked.
*{{{isNull}}} : determines whether this plug is valid.
*{{{isProcedural}}} :  determines if the plug is a procedural plug
*{{{isSettable}}} : Wrapper for {{{getAttr -settable}}}
*{{{isSource}}} : Determines if this plug is connected as a source.
*{{{isUsedAsColor}}} : Wrapper for {{{attributeQuery -usedAsColor}}}
*{{{item}}} : Returns the logical index of the element this plug refers to.
*{{{lastPlugAttr}}} : No docs (but example)
*{{{lock}}} : Wrapper for {{{setAttr -locked 1}}}
*{{{logicalIndex}}} : Returns the logical index of the element this plug refers to. 
*{{{longName}}} : No docs (but example)
*{{{name}}} : Returns the name of the attribute (plug)
*{{{node}}} : plugNode
*{{{nodeName}}} : The node part of this plug as a string
*{{{numChildren}}} : Return the total number of children of this compound plug.
*{{{numConnectedChildren}}} : Return the number of children of this plug that are connected in the dependency graph.
*{{{numConnectedElements}}} : Return the total number of connected element plugs belonging to this array plug.
*{{{numElements}}} : The number of elements in an array attribute. 
*{{{outputs}}} : Wrapper for {{{listConnections -source 0 -destination 1}}}
*{{{parent}}} : No docs (but notes)
*{{{plugAttr}}} : No docs (but example)
*{{{plugNode}}} : No docs
*{{{remove}}} : ~removeMultiInstance
*{{{set}}} : Sets the value of a dependency node attribute.
*{{{setAlias}}} : Sets the alias for this attribute (similar to aliasAttr)
*{{{setCaching}}} : Sets whether this plug is cached internally
*{{{setDirty}}} : No docs
*{{{setEnums}}} : No docs
*{{{setKey}}} : creates keyframes for the specified objects, or the active objects if none are specified on the command line
*{{{setKeyable}}} : This overrides the default keyability of a plug set with {{{MFnAttribute::setKeyable}}}
*{{{setLocked}}} : Sets the locked state for this plug’s value
*{{{setMax}}} : No docs
*{{{setMin}}} : No docs
*{{{setNumElements}}} : The method is used to pre-allocate the number of elements that an array plug will contain.
*{{{setRange}}} : provide a min and max value
*{{{setSoftMax}}} : No docs
*{{{setSoftMin}}} : No docs
*{{{setSoftRange}}} : No docs
*{{{shortName}}} : No docs (but example)
*{{{showInChannelBox}}} : Sets whether this plug is displayed in the channel box.
*{{{siblings}}} : Wrapper for {{{attributeQuery -listSiblings}}}
*{{{type}}} : Wrapper for {{{getAttr -type}}}
*{{{unlock}}} : Wrapper for {{{setAttr -locked 0}}}
{{{PyNode}}} Attribute related methods:
*{{{connections}}} : This command returns a list of all attributes/objects of a specified type that are connected to the given object(s). If no objects are specified then the command lists the connections on selected nodes.
*{{{listConnections}}} : This command returns a list of all attributes/objects of a specified type that are connected to the given object(s). If no objects are specified then the command lists the connections on selected nodes.
!!!High level notes as I learn ~PyMel
[[Main Documentation|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/index.html#pymel-for-maya]]
----
Common ~PyMel //''objects''// (Found under the 'Classes' section of the [[pymel.core.general|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.general.html]] documentation):
*[[PyNode|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html#pymel.core.general.PyNode]] - [[Tutorial|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/pynodes.html]] 
**Also see : [[PyMel : PyNode methods]]
*[[Attribute|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html#pymel.core.general.Attribute]] - [[Tutorial|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/attributes.html]]
----
''Data'' Types [[pymel.core.datatypes|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.datatypes.html#module-pymel.core.datatypes]] (under 'Classes')
* Includes Matrix, Vector, etc, plus many math related functions.
* Can also be accessed as {{{pymel.core.dt}}}
''Node'' Types :[[pymel.core.nodetypes|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.nodetypes.html#module-pymel.core.nodetypes]]
*Wrappers around all of Maya's nodes:  Joints, Transforms, Mesh, etc
* Can also be accessed as {{{pymel.core.nt}}}
''Component'' Types :[[pymel.core.general|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.general.html#module-pymel.core.general]] (under 'Classes')
*Verts, edges, cv's etc.
<<<
When you see stuff like:
{{{
# Result: [nt.Transform(u'pPlane1'), nt.PolyPlane(u'polyPlane1')]
}}}
These are the 'node/component types'.
<<<
''UI'' Types : [[pymel.core.uitype|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.uitypes.html#module-pymel.core.uitypes]]
* Can also be accessed as {{{pymel.core.ui}}}
----
Calling to mel:
[[pymel.core.language.Mel|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.language/pymel.core.language.Mel.html#pymel.core.language.Mel]]
----
Building UI's:
[[Building User Interfaces|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/ui.html]]
[[Using 'with'|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/whats_new.html#mel-gui-creation]]
----
''Cheat Sheets''
It looks like ~PyMel has taken certain commands, like {{{file}}}, and completely split them up into separate functions.  This is darn confusing for the Mel vetern \ ~PyMel noob.  As I figure this stuff out, I'll record it here:
| Command | ~PyMel Location |
| [[maya.cmds.file|http://download.autodesk.com/global/docs/maya2013/en_us/CommandsPython/file.html]] | [[pymel.core.system|http://download.autodesk.com/global/docs/maya2012/en_us/PyMel/generated/pymel.core.system.html#module-pymel.core.system]] |
| [[maya.cmds.optionVar|http://download.autodesk.com/global/docs/maya2013/en_us/CommandsPython/optionVar.html]] | [[pymel.core.language.OptionVarDict|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.language/pymel.core.language.OptionVarDict.html#pymel-core-language-optionvardict]] |
| [[maya.mel.eval|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/eval.html]] | [[pymel.core.language.Mel.eval|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.language/pymel.core.language.Mel.html?highlight=eval#pymel.core.language.Mel.eval]] |
From the [[PyMel Docs|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/tutorial.html#importing-pymel]]:
<<<
''Note'': All the functions in {{{maya.cmds}}} are in the {{{pymel}}} namespace, except the conflicting ones ( {{{file}}}, {{{filter}}}, {{{eval}}}, {{{help}}}, and {{{quit}}}). The conflicting commands can be found in the {{{pymel.cmds}}} namespace, along with all of the unaltered maya commands.
<<<
----
''Importing and namespaces''
Pymel has a variety of subpackages under the {{{pymel}}} root package.  For example:
*{{{pymel.core}}}
*{{{pymel.utils}}}
*{{{pymel.tools}}}
*etc...
{{{pymel.core}}} specifically has many sub-packages:
*{{{pymel.core.animation}}}
*{{{pymel.core.effects}}}
*{{{pymel.core.general}}}
* etc...
And each of those sub-packages have a host of commands\classes to use.

What is interesting is that when doing an import like so:
{{{
import pymel.core as pm
}}}
It seems to also be doing this (based on my observations), on each of the sub packages:
{{{
from pymel.core.animation import *
}}}
So that in the {{{pm}}} namespace, you have access to all the commands in the sub-packages, without needing to add in the intermediate sub-package path.
Meaning, for example, you can do this:
{{{
import pymel.core as pm
stuff = mc.ls(selection=True)
}}}
Rather than having to do this:
{{{
import pymel.core as pm
pm.general.ls(selection=True)
}}}
That being said, there are certain sub-packages, like {{{pymel.core.runtime}}}, that //aren't// part of this automatc import: They're left out of it.  You'd still need to import that package directly to access its contents.



When (most commonly) authoring callbacks for buttons in ui's, you generally have a few options:
* Embed the entire command as a string.
* Use the Python {{{lambda}}} statement to wrapper the command.  This can fail however, if you're procedural generating the ui elements in a loop:  All the lambda's in the loop will end up having the last command entered.
* Use the Python [[functools.partial|https://docs.python.org/2/library/functools.html#functools.partial]] function to wrapper your call.  Works great, fixes the {{{lambda}}} issue.  I usually do something like {{{from functools import partial as callback}}}.
//~PyMel// however provides it's own [[Callback|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/ui.html?highlight=callback#callback-objects]] class that works similarly to {{{functools.partial}}}:
{{{
import pymel.core as pm

# Inside some window code:
pm.button(command=pm.Callback(myFunction, arg1, arg2, argEtc)
}}}
Why use it?  You don't //need// to, but it does a few things:
* Saves you an import at the top of your function.
* From the docs: "It also ensures that the entire callback will be be represented by one undo entry."
Three transforms, 'nodeA', 'nodeB', & 'nodeC'.  {{{nodeC.translateY}}} is connected to {{{nodeB.translateY}}}, & {{{nodeB.translateX}}} is connected to {{{nodeA.translateX}}}.
{{{
import pymel.core as pm
nodeA = pm.group(empty=True, name="nodeA")
nodeB = pm.group(empty=True, name="nodeB")
nodeC = pm.group(empty=True, name="nodeC")
nodeC.translateY >> nodeB.translateY
nodeB.translateX >> nodeA.translateX
}}}
~PyMel [[DependNodes|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html]] have a number of methods to query connections.
{{{
for node in (nodeA, nodeB, nodeC):
    print node
    print "\tInputs:", node.inputs()
    print "\tSources:", node.sources()
    print "\tOutputs:", node.outputs()
    print "\tDestinations:", node.destinations()
    print "\tConnections:", node.connections()
}}}
{{{
nodeA
	Inputs: [nt.Transform(u'nodeB')]
	Sources: [nt.Transform(u'nodeB')]
	Outputs: []
	Destinations: []
	Connections: [nt.Transform(u'nodeB')]
nodeB
	Inputs: [nt.Transform(u'nodeC')]
	Sources: [nt.Transform(u'nodeC')]
	Outputs: [nt.Transform(u'nodeA')]
	Destinations: [nt.Transform(u'nodeA')]
	Connections: [nt.Transform(u'nodeC'), nt.Transform(u'nodeA')]
nodeC
	Inputs: []
	Sources: []
	Outputs: [nt.Transform(u'nodeB')]
	Destinations: [nt.Transform(u'nodeB')]
	Connections: [nt.Transform(u'nodeB')]
}}}
*{{{inputs}}} : Wrapper for {{{listConnections -source 1 -destination 0}}}
*{{{outputs}}} : Wrapper around {{{listConnections -source 0 -destination 1}}}
*{{{sources}}} : Wrapper around {{{listConnections -source 1 -destination 0}}}
*{{{destinations}}} : Wrapper for {{{listConnections -source 0 -destination 1}}}
*{{{connections}}} : Wrapper for {{{listConnections -source 1 -destination 1}}}
It should be noted that these methods accept any arg that {{{listConnections}}} does as well.
----
!!! Connecting Attributes:
~PyMel has some nice shorthand:
{{{
# Connect two attrs:
myNode.someAttr >> otherNode.otherAttr
# or:
myNode.someAttr.connect(otherNode.otherAttr)
}}}
If you want to connect to the next index in an array/multi attr:
*[[How can I connect to the next instance of an array or multi attr?]]
!!! Disconnecting Attributes:
{{{
myNode.someAttr // otherNode.otherAttr
# or:
myNode.someAttr.disconnect(otherNode.otherAttr)
# or to disconnect from all:
myNode.someAttr.disconnect()
}}}
----
You can also of course use the {{{listConnections}}} command directly.  This example will return back actual ~PyMel Attribute objects.  Each pairing is (queriedNode.attr, connectedNode.attr), regardless if it's an output or input.
{{{
for node in (nodeA, nodeB, nodeC):
    print "%s's connections:"%node
    print "\t", pm.listConnections(node, connections=True, plugs=True)
}}}
{{{
nodeA's connections:
	[(Attribute(u'nodeA.translateX'), Attribute(u'nodeB.translateX'))]
nodeB's connections:
	[(Attribute(u'nodeB.translateY'), Attribute(u'nodeC.translateY')), 
         (Attribute(u'nodeB.translateX'), Attribute(u'nodeA.translateX'))]
nodeC's connections:
	[(Attribute(u'nodeC.translateY'), Attribute(u'nodeB.translateY'))]
}}}
If you use {{{source=True, destination=False}}} (inputs only), the return will be a list of sublists of:
{{{
["sourceNode.inputAttr", "otherNode.outputAttr"]
}}}
If you use {{{source=False, destinatino=True}}} (outputs only), the return will be a list of sublists of:
{{{
["sourceNode.outputAttr", "otherNode.inputAttr"]
}}}
----
Also see:
*[[PyMel : Attribute Access]]
*[[PyMel : DependNode methods]]
This is sort of a ~PyMel cheat sheet exposing various methods for the various data types, allowing for conversion between them.  Also included is the {{{Transform}}} node type, since it ties closely with these data types.
----
Docs (2015):
Data Types:
*[[Point|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Point.html]]
*[[Vector|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Vector.html]]
*[[Quaternion|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Quaternion.html]]
*[[EulerRotation|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.EulerRotation.html]]
*[[Matrix|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.Matrix.html]]
*[[TransformationMatrix|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.datatypes/pymel.core.datatypes.TransformationMatrix.html]]
Node Types:
*[[Transform|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Transform.html]]
----
Right side are arguments to methods on the left.
| ''Data Type'' | Method/Attribute | Return | | Point | Vector | Quaternion | ~EulerRotation | Matrix | ~TransformationMatrix |
| | | | | | | | | | |
|''Point'' | init() | Point  | | X | X | X | X | | |
| | | | | | | | | | |
|''Vector'' | init() | Vector  | | X | X | | X | | |
| | rotateBy() | | | | X | X | X | X | X |
| | rotateTo() | Quaternion | |  | X | | | | |
| | transformAsNormal() | Vector | | | | | | X | |
| | | | | | | | | | |
|''Quaternion'' | asEulerRotation()  | ~EulerRotation | | | | | | | |
| | | | | | | | | | |
|''~EulerRotation'' | init() | ~EulerRotation  | | X | X | X | X | X | X |
| | asVector() | Vector | | | | | | | |
| | incrementalRotateBy() | ~EulerRotation | |  | X | | | | |
| | setValue() | ~EulerRotation | | | X | | | | |
| | asMatrix() | Matrix | | | | | | | |
| | decompose() | ~EulerRotation | | | | | | X | |
| | | | | | | | | | |
|''Matrix'' | init() | Matrix | | X | X | X | X | X | X |
| | | | | | | | | | |
|''~TransformationMatrix'' | init() | ~TransformationMatrix | | X | X | X | X | X | X |
| | getRotatePivot() | Point | | | | | | | |
| | getScalePivot() | Point | | | | | | | |
| | setRotatePivot() | | | X |  | | | | |
| | setScalePivot() | | | X |  | | | | |
| | addTranslation() | | | | X | | | | |
| | getRotatePivotTranslation() | Vector | | | | | | | |
| | getScalePivotTranslation() | Vector | | | | | | | |
| | getTranslation() | Vector | | | | | | | |
| | setRotatePivotTranslation() | | | | X | | | | |
| | setScalePivotTranslation() | | | | X | | | | |
| | setToRotationAxis() | | | | X | | | | |
| | setTranslation() | | | | X | | | | |
| | addRotationQuaternion() | | | | | X (as list) | | | |
| | getRotationQuaternion() | Quaternion  (as list) | | | | | | | |
| | setRotationQuaternion() | | | | | X (as list) | | | |
| | rotate | Quaternion | | | | | | | |
| | rotateTo() | | | | | X | X | | |
| | eulerRotation() | ~EulerRotation | | | | | | | |
| | asMatrixInverse() | Matrix | | | | | | | |
| | asRotateMatrix() | Matrix | | | | | | | |
| | asScaleMatrix() | Matrix | | | | | | | |
| | | | | | | | | | |
| ''Node Type'' | Method/Attribute | Return | | Point | Vector | Quaternion | ~EulerRotation | Matrix | ~TransformationMatrix |
| | | | | | | | | | |
|''Transform'' | getRotatePivot  | Point | | | | | | | |
| | getScalePivot | Point | | | | | | | |
| | setRotatePivot | | | X | | | | | |
| | setScalePivot | | | X | | | | | |
| | getRotatePivotTranslation | Vector | | | | | | | |
| | getScalePivotTranslation | Vector | | | | | | | |
| | getTranslation | Vector | | | | | | | |
| | setRotatePivotTranslation | | | | X | | | | |
| | setScalePivotTranslation | | | | X | | | | |
| | setTranslation | | | | X | | | | |
| | translateBy | | | | X | | | | |
| | getRotation | Quaternion, ~EulerRotation | | | | | | | |
| | rotateBy | | | | | X | | | |
| | setRotation | | | | | X | X | | |
| | getMatrix | Matrix | | | | | | | |
| | setMatrix | | | | | | | X | |
| | getRestPosition | ~TransformationMatrix | | | | | | | |
| | getTransformation | ~TransformationMatrix | | | | | | | |
| | setRestPosition | | | | | | | | X |
| | setTransformation | | | | | | | | X |
| | | | | | | | | | |
Condensation of the ~PyMel [[DagNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html]] docs:
Methods, from 2014 docs:
*{{{activeColor}}} : Determines the active color used by this node.
*{{{addChild}}} : parent (reversed)
*{{{boundingBox}}} : Returns the bounding box for the dag node in object space.
*{{{childAtIndex}}} : Queries the DAG Node attached to the Function Set for the child Node corresponding to the given index
*{{{comp}}} : Will retrieve a Component object for this node; similar to {{{DependNode.attr()}}}, but for components.
*{{{dormantColor}}} : Determines the dormant color used by this node.
*{{{drawOverrideColor}}} : Determines the draw override color used by this node.
*{{{drawOverrideEnabled}}} : Determines whether or not draw override is turned on for this node.
*{{{drawOverrideIsReference}}} : Determines whether or not Display Type of the draw override is Reference for this node.
*{{{drawOverrideIsTemplate}}} : Determines whether or not Display Type of the draw override is Template for this node.
*{{{firstParent}}} : No docs
*{{{firstParent2}}} : unlike the firstParent command which determines the parent via string formatting, this command uses the {{{listRelatives}}} command
*{{{fullPath}}} : The full dag path to the object, including leading pipe ( {{{|}}} )
*{{{getAllParents}}} : Return a list of all parents above this.
*{{{getChildren}}} : No docs
*{{{getInstances}}} : No docs, but example
*{{{getObjectColor}}} : Determines the index for the current user defined inactive color used by the node. 
*{{{getOtherInstances}}} :  same as {{{DagNode.getInstances}}} with {{{includeSelf=False}}}.
*{{{getParent}}} : No docs, but example
*{{{getSiblings}}} : No docs
*{{{hasChild}}} : Determines whether or not the given Node is a child of the DAG Node attached to the Function Set.
*{{{hasParent}}} : Determines whether or not the given Node is a parent of the DAG Node attached to the Function Set.
*{{{hide}}} : No docs
*{{{hiliteColor}}} : Determines the current hilite color used by the node.
*{{{inModel}}} : Determines whether or not the DAG Node is in the model.
*{{{inUnderWorld}}} : Determines whether or not the DAG Node is an underworld node.
*{{{instanceCount}}} : Determines the number of times the Node attached to the Function Set is instanced.
*{{{instanceNumber}}} : returns the instance number that this path represents in the DAG. 
*{{{isChildOf}}} : Determines whether or not the DAG Node attached to the Function Set is a child of the given node
*{{{isDisplaced}}} : Returns whether any of this object’s shading groups have a displacement shader input
*{{{isInstanceOf}}} : No docs
*{{{isInstanceable}}} : Returns true if the DAG node can be instanced, and false otherwise.
*{{{isInstanced}}} : Determines whether the DAG Node attached to the Function Set is directly or indirectly instanced.
*{{{isIntermediate}}} : Returns true if this object is an intermediate in a geometry calculation.
*{{{isParentOf}}} : Determines whether or not the DAG Node attached to the Function Set is a parent of the given node.
*{{{isUsingObjectColor}}} : Determines whether or not the user defined inactive color will be used for the node, or whether the default inactive color will be used.
*{{{isVisible}}} : No docs
*{{{listComp}}} : Will return a list of all component objects for this object
*{{{listRelatives}}} : No docs
*{{{longName}}} : The full dag path to the object, including leading pipe ( {{{|}}} )
*{{{makeLive}}} : No docs
*{{{name}}} : No docs
*{{{nodeName}}} : Just the name of the node, without any dag path
*{{{numChildren}}} : Determines the number of child Nodes of the Node.
*{{{parentAtIndex}}} : Queries the DAG Node attached to the Function Set for the parent Node corresponding to the given index.
*{{{parentCount}}} : Determines the number of parent Nodes of the Node.
*{{{root}}} : No docs
*{{{setIntermediate}}} : Sets whether this object is an intermediate in a geometry calculation
*{{{setObjectColor}}} : This command sets the dormant wireframe color of the specified objects
*{{{setParent}}} : No docs
*{{{setUseObjectColor}}} : Sets whether or not the user defined inactive object color will be used.
*{{{shortName}}} : The shortest unique name.
*{{{show}}} : No docs
*{{{transformationMatrix}}} : Returns the object space transformation matrix for this DAG node. 
*{{{usingHiliteColor}}} : Determines whether or not the hilite color will be used for the node
Condensation of the ~PyMel [[DependNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html]] docs:
Methods, from 2014 docs:
*{{{addAttr}}} : This command is used to add a dynamic attribute to a node or nodes
*{{{attr}}} : access to attribute plug of a node. returns an instance of the Attribute class for the given attribute name.
*{{{attrInfo}}} : Wrapper of {{{attributeInfo}}}
*{{{attributeCount}}} : Returns the number of attributes that this node has.
*{{{canBeWritten}}} : Returns the do not write state of the node. 
*{{{cast}}} : Wrapper of {{{nodeCast}}}
*{{{classification}}} : Wrapper of {{{getClassification}}}.
*{{{connectAttr}}} : Connect the attributes of two dependency nodes and return the names of the two connected attributes. 
*{{{deleteAttr}}} : delete a dynamic attribute from a node or nodes.
*{{{deletePreset}}} : No docs.
*{{{destinations}}} : Wrapper for {{{listConnections -source 0 -destination 1}}}
*{{{disconnectAttr}}} : Disconnects two connected attributes
*{{{duplicate}}} : This command duplicates the given objects.
*{{{extractNum}}} : Return the trailing numbers of the node name. 
*{{{getAttr}}} : This command returns the value of the named object’s attribute. 
*{{{getIcon}}} : Returns the custom icon filename associated with the node.
*{{{hasAttr}}} : convenience function for determining if an object has an attribute
*{{{inputs}}} : Wrapper for {{{listConnections -source 1 -destination 0}}}
*{{{isDefaultNode}}} : Returns true if the node is a default node.
*{{{isFlagSet}}} : Retrieves the current state of the specified flag for a node.
*{{{isLocked}}} : Indicates whether or not this node is locked.
*{{{isReadOnly}}} : Indicates whether or not this node came from a referenced file.
*{{{isReferenced}}} : Indicates whether or not this node came from a referenced file. 
*{{{isShared}}} : Indicates whether or not this node is shared.
*{{{isUniquelyNamed}}} : Indicates whether or not this node’s name is unique within the scene.
*{{{listAliases}}} : Wrapper around {{{aliasAttr}}}
*{{{listAnimatable}}} : This command list the animatable attributes of a node.
*{{{listAttr}}} : No docs
*{{{listPresets}}} : No docs
*{{{loadPreset}}} : No docs
*{{{lock}}} : Wrapper around {{{lockNode -lock 1}}}
*{{{longName}}} : This produces the same results as {{{DependNode.name}}}
*{{{name}}} : No docs.
*{{{nextName}}} : Increment the trailing number of the object by 1
*{{{nextUniqueName}}} : Increment the trailing number of the object until a unique name is found
*{{{node}}} : for compatibility with Attribute class.
*{{{nodeName}}} : This produces the same results as {{{DependNode.name}}}
*{{{outputs}}} : Wrapper around {{{listConnections -source 0 -destination 1}}}
*{{{parentNamespace}}} : Returns the name of the namespace in which this node resides.
*{{{pluginName}}} : Returns the name of the plug-in this ~MFnDependendencyNode was defined in. 
*{{{prevName}}} : Decrement the trailing number of the object by 1
*{{{referenceFile}}} : {{{referenceQuery -file}}} Return the reference file to which this object belongs. 
*{{{rename}}} : No docs.
*{{{savePreset}}} : No docs.
*{{{setAttr}}} : Sets the value of a dependency node attribute
*{{{setDoNotWrite}}} : Use this method to mark the “do not write” state of this node.
*{{{setDynamicAttr}}} : Sets the value of a dependency node attribute.
*{{{setIcon}}} : Associates a custom icon with the node for display in the Maya UI.
*{{{setLocked}}} : Locks or unlocks this node
*{{{shadingGroups}}} : list any shading groups in the future of this object - works for shading nodes, transforms, and shapes
*{{{shortName}}} : This produces the same results as {{{DependNode.name}}}
*{{{sources}}} : Wrapper around {{{listConnections -source 1 -destination 0}}}
*{{{stripNum}}} : Return the name of the node with trailing numbers stripped off.
*{{{type}}} : This command returns a string which identifies the given node’s type. 
*{{{unlock}}} : Wrapper around {{{lockNode -lock 0}}}
Static Methods:
*{{{attrDefaults}}} : Access to an attribute of a node. 
Class Methods:
*{{{enableDGTiming}}} : Globally enables or disables the DG node timing mechanism.
*{{{registerVirtualSubClass}}} : Deprecated
~PyMel took the [[file|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/file.html]] command and split it apart into its own package, [[pymel.core.system|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/pymel.core.system.html]].  Cheat sheet for where all the functionality has gone:
For the below examples:
{{{
import maya.cmds as mc
import pymel.core as pm
}}}
!Referencing
More specific reference notes here: [[PyMel : Reference Access]]
!!!File Reference Querying:
With Python, you can query top-level file references via:
{{{
topFileRefs = mc.file(query=True, reference=True)
}}}
In ~PyMel:
{{{
topFileRefs = pm.getReferences()
allFileRefs = pm.getReferences(recursive=True)
}}}
Which has the added advantage of recursively searching through all sub-refs as well.  It returns back a dict, which this {{{key:value}}} pairing:  {"namespace":[[FileReference|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.system/pymel.core.system.FileReference.html]], ...}
You can also use the [[iterReferences|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.iterReferences.html]] or [[listReferences|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.listReferences.html]] commands that have even more robust return values.
Note, that if you want to get just the string names of the references, you need to jump through some hoops:
{{{
topFileRefs = [str(pm.getReferences()[key].path) for key in pm.getReferences()]
print topFileRefs 
}}}
Since the {{{FileReference}}} class houses it's path data in a {{{Path}}} class.
!!!Reference Creation:
Python:
{{{
mc.file(fullPathToTheFile, reference=True)
}}}
~PyMel:
{{{
pm.createReference(fullPathToTheFile)
}}}
!!!Reference Removal:
Python:
{{{
mc.file(fullPathToTheReference, removeReference=True)
}}}
~PyMel:
{{{
# Here, we figure out the path based on namespace:
ns = "myReferenceNamespace"
pthToFile = pm.referenceQuery('%sRN'%ns, filename=True)
ref = pm.FileReference(pthToFile)
ref.remove()
}}}
!!!Reference Replacing/Reloading:
{{{
refNode = "<namespace>RN" # this is the 'reference' node name
fileRef = pm.FileReference(refNode)
fileRef.load("path to file here")
}}}
!Dependencies:
!!!All Dependency Querying:
The above command will give all the (Maya) file references in the scene.  This Python command will include them, plus all other dependencies, like textures, sounds, etc:
{{{
allDependencies = mc.file(query=True, list=True)
}}}
I have yet to be able to track down the ~PyMel equivalent.  But you can do it via mel, in ~PyMel of course:
{{{
allDependencies = pm.mel.eval("file -query -list")
}}}
!Scene Access:
!!!Scene Opening:
Python:
{{{
mc.file(filePath, open=True, force=True)
}}}
~PyMel:
{{{
pm.openFile(filePath, force=True)
}}}
!!!New Scene Creation:
Python:
{{{
mc.file(newFile=True, force=True)
}}}
~PyMel:
{{{
pm.newFile(force=True)
}}}
[[pymel.core.system.newFile|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.newFile.html]]
!!!Scene Renaming
Python:
{{{
mc.file(rename="c:/temp/spam.ma")
}}}
~PyMel:
{{{
pm.renameFile("c:/temp/spam.ma")
}}}
[[pymel.core.system.renameFile|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.renameFile.html]]
!!!Force Rename To Save
Python
{{{
mc.file(renameToSave=True)
}}}
~PyMel doesn't seem to have an option I can find, so instead:
{{{
pm.mel.eval("file -renameToSave 1")
}}}
!!!Scene Type Changing
It Python, even if the scene isn't saved, you can set it's 'type', like so:
{{{
mc.file(type="mayaAscii")
}}}
In ~PyMel, the scene first must have a name assigned (see Scene Renaming, above).  Then you can execute:
{{{
pm.saveFile(type="mayaAscii")
}}}
Which will both change its type, //and// save it at the same time.
[[pymel.core.system.saveFile|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.saveFile.html]]
!!!Scene Name querying:
Python:
{{{
sceneName = mc.file(query=True, sceneName=True)
}}}
~PyMel 
{{{
sceneName = pm.sceneName()
}}}
[[pymel.core.system.sceneName|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.sceneName.html]]
!!!Scene Saving:
With both Python and ~PyMel, a scene needs to have a name assigned first, before it is saved.
Python:
{{{
mc.file(save=True)
}}}
~PyMel:
{{{
pm.saveFile()
}}}
[[pymel.core.system.saveFile|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.saveFile.html]]
!!!Save As:
To my knowledge, you can't do a 'save as' via the {{{file}}} command:  You first need to name the scene (via Scene Renaming, above), then do a save (as shown above).  However in ~PyMel, they have this convenience function that does the scene name assignment and save all in one line:
{{{
pm.saveAs("c:/temp/eggs.ma")
}}}
(no docs)
!Import/Export
!!!File Import:
Python:
{{{
mc.file(pathToFileToImport, i=True)
}}}
~PyMel:
{{{
pm.importFile(pathToFileToImport)
}}}
[[pymel.core.system.importFile|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.importFile.html]]
!!!Export Selected:
Python:
{{{
mc.file(pathToExportedFile, exportSelected=True, type='mayaAscii')
}}}
~PyMel:
{{{
pm.exportSelected(pathToExportedFile, type='mayaAscii')
}}}
[[pymel.core.system.exportSelected|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.exportSelected.html]]
!!!Export All:
Python:
{{{
mc.file(pathToExportedFile, exportAll=True, type='mayaAscii')
}}}
~PyMel:
{{{
pm.exportAll(pathToExportedFile, type='mayaAscii')
}}}
[[pymel.core.system.exportAll|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/functions/pymel.core.system/pymel.core.system.exportAll.html]]
!!!.anim Export:
Maya has its own {{{.anim}}} file format for exporting animation curves.  The options arg is generally a very long string of values that is passed to the export plugin (executing an export from mel will give you a string that can be used in your code).
Python:
{{{
mc.file(pathToAnimExportFile, exportSelected=True, type='animExport',
        options=longStringOfExportOptions)
}}}
~PyMel:
{{{
pm.exportSelected(pathToAnimExportFile,, type="animExport", 
                  options=longStringOfExportOptions)
}}}
!!!.anim Import
Python:
{{{
mc.file(pathToAnimFile, i=True, type="animImport", options=longStringOfImportOptions)
}}}
~PyMel:
{{{
pm.importFile(pathToAnimFile, type="animImport", options=longStringOfImportOptions)
}}}
!Unsupported?:
!!!Disabling file prompting
Via Python, you can disable the prompt dialogs that appear when scenes have errors, like so:
{{{
mc.file(prompt=False)
}}}
Via ~PyMel, it isn't a toggle, but you can run it while you open a file.  However, this still acts like a global toggle for future file-opens, so you'd still need to toggle it off later.
{{{
pm.openFile(fPath, force=True, prompt=False)
}}}
If you want to do the toggle in PyMel, you have to call to the actual mel:
{{{
pm.mel.eval('file -prompt 0')
}}}
!!!Querying Unsaved Changes
The Python wrapper for this is:
{{{
needsSaving = mc.file(query=True, modified=True)
}}}
But I can't find the ~PyMel equivalent.  So, wrapper the mel:
{{{
needsSaving  = pm.mel.eval('file -query -modified')
}}}
Often you want to detect what type of thing is selected and filter that into different lists:  transform, vertex, edge, joint, etc.
This is pretty easy to do via ~PyMel's internal types.  For example, filter by vertex and joint selection:
{{{
import pymel.core as pm

verts = []
joints = []
sel = pm.ls(selection=True, flatten=True)
for item in sel:
    if isinstance(item, pm.MeshVertex):
        verts.append(item)
    elif isinstance(item, pm.nt.Joint):
        joints.append(item)

print verts
print joints
}}}
The various node & component types can be found under these two docs:
*[[pymel.core.general|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.general.html#module-pymel.core.general]] (Components:  Verts, edges, etc)
*[[pymel.core.nodetypes|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/pymel.core.nodetypes.html#module-pymel.core.nodetypes]] (Nodes:  Transforms, joints, etc)
While ~PyMel appears to have many convenience functions for working with verts & faces, they appear to be far more difficult than they're worth, //especially// if your 'ui units' //aren't// cm:  In all the below examples, the 'ui units' are in inches.

For example, here you can query all the points for all the verts of a given ~MeshFace, presumably so you could later average them to find the center:
{{{
points = face.getPoints(space='world')
}}}
Which returns back a list of Point instances.  But if I create a locator at their positions they don't line up with the corresponding verts.  And if I convert them to ui units, they //still// don't match:  They're closer, but not right.

By far the easiest way I've found has nothing to do with any special ~PyMel calls:
{{{
import pymel.core as pm

def average(*args):
    # find the average of any number of passed in values
    return sum(args) / len(args)

# face = some MeshFace instance

verts = pm.ls(pm.polyListComponentConversion(face, toVertex=True), flatten=True)
points = [pm.pointPosition(v, world=True) for v in verts]
center = map(average, *points)
}}}
----
See:
*[[PyMel : Mesh access]]
The below methods are part of the ~PyMel [[DagNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html]] object.  Notes here:  [[PyMel : DagNode methods]]
Given a mesh transform named "foo":
{{{
import pymel.core as pm
node = pm.PyNode("foo")
print `node`
# nt.Transform(u'foo')

print node.getChildren(type="mesh", noIntermediate=True)
# [nt.Mesh(u'fooShape')]

print `node.childAtIndex(0)`
# nt.Mesh(u'fooShape')

print node.hasChild("fooShape")
# True

print node.isChildOf("persp")
# False

print node.numChildren()
# 2
}}}
Note how the {{{getChildren}}} method can filter its results:  This seems to be missing from the ~PyMel docs.
I thought it would be useful to track how to find different parental nodes based on different selection types.
{{{
import pymel.core as pm
}}}
!!!Nodes
{{{
joint = pm.PyNode("joint7") # Joint
allParents = joint.getAllParents()  # DagNode method
parent = joint.firstParent()  # DagNode method
}}}
!!!Vertex
{{{
# Select a single vertex
vert = pm.ls(selection=True)[0]  # MeshVertex
meshShape = vert.node()  # Mesh  :  Component method
transform = meshShape.getParent() # Transform  :  DagNode method
}}}
!!!Attribute
{{{
attr = pm.PyNode("persp.translateX") # Attribute
parentNode = attr.node() # Transform (in this case) :  Attribute method
}}}
or:
{{{
node =  pm.PyNode("persp") # PyNode
# later, to re-find the node based on some attr:
itself = node.translateX.node() # Transform (in this case) :  Attribute method
}}}
!!!Docs:
~PyMel node-types:
*[[PyNode|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html]] Also see : [[PyMel : PyNode methods]]
**[[DependNode|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html#pymel.core.nodetypes.DependNode]] Also see : [[PyMel : DependNode methods]]
***[[DagNode|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html#pymel.core.nodetypes.DagNode]] Also see : [[PyMel : DagNode methods]]
****[[Mesh|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html]] Also see : [[PyMel : Mesh methods]]
****[[Transform|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Transform.html]]
*****[[Joint|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Joint.html#pymel.core.nodetypes.Joint]]
**[[Attribute|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html#pymel.core.general.Attribute]]
**[[Component|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Component.html#pymel.core.general.Component]]
***[[MeshVertex|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertex.html]] Also see : [[PyMel : MeshVertex methods]]
----
Also see:
*[[PyMel : Parenting]]
Running certain ~PyMel commands will often spam all sorts of these warnings, super annoying:
{{{
# Warning: pymel.core.general : Could not create desired MFn. Defaulting to MFnDagNode. # 
}}}
They are harmless, other than the fact they spam the Script Editor, and can actually slow down code execution.

An easy way to get around them it to simply set ~PyMel's logging to a higher threat level:
{{{
import logging
import pymel.core as pm

pm.general._logger.setLevel(logging.ERROR)
}}}
That's it:  ~PyMel will quit spamming you on warnings, but still report errors.

Note in Maya 2016, these warnings are being triggered on lines 2241 & 2245 of the {{{pymel.core.general.py}}} module.
https://pypi.org/project/pymel/ 
For how to do this, see: [[Loading Python packages in Maya]]

you can use a syntax like this to install a certain version, for a given beta PR if needed
{{{
mayapy -m pip install pymel==1.3.0a
}}}
Nice overview:
* http://christianakesson.com/2012/218#Logger
Here is an example from that page:
{{{
import logging
from pymel.internal.plogging import pymelLogger

pymelLogger.debug('Debug message'))
pymelLogger.info('User Message')
pymelLogger.warning('Colored Text Output')
pymelLogger.error('Colored Text Output - That red output box we all know and love in Maya')
pymelLogger.critical('Critical Message')

# Manage Levels Through Code:
pymelLogger.setLevel(logging.DEBUG)
pymelLogger.setLevel(logging.INFO)
}}}
~PyMel has it's own logger menu you can enable to manage the above settings::
{{{
from pymel.tools import loggingControl
loggingControl.initMenu() 
}}}
Note in 2014 when you open the window there can be an error:  The above link provides a download to fix this.
~PyMel has a tremendous amount of power for accessing polygonal data via the [[Mesh|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html]] class, and others.
{{{
import pymel.core as pm
}}}
Creating new polygonal mesh returns both the transform, and the input creation node type (no shape listed), so we have to find the mesh shape:
{{{
cubeTrans, cubeNode = pm.polyCube()
cubeMesh = cubeTrans.getChildren(type='mesh')[0]

print `cubeTrans`, `cubeNode`, `cubeMesh`
nt.Transform(u'pCube1') nt.PolyCube(u'polyCube1') nt.Mesh(u'pCubeShape1')
}}}
Or if a mesh already exists:
{{{
cubeTrans = pm.PyNode('pCube1')
cubeMesh = cubeTrans.getChildren(type='mesh')[0]

print `cubeTrans`, `cubeMesh`,
nt.Transform(u'pCube1') nt.Mesh(u'pCubeShape1')
}}}
Polygonal components can be accessed/iterated over the below //attrs//.  Note, these can be accessed on the transform directly, ~PyMel will figure it out.  So while you can find the mesh shape node and work on it directly, you can just as easily access the values via the transform.
*{{{.vtx}}}  : [[MeshVertex|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertex.html]] (note, {{{.vertex}}} doesn't seem to work, raises {{{AttributeError}}})
*{{{.f}}} / {{{.face}}}  :  [[MeshFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshFace.html]]  / {{{Attribute}}}
*{{{.e}}} / {{{.edge}}}  :  [[MeshEdge|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshEdge.html]] / {{{Attribute}}}
*{{{.vtxFace}}} : [[MeshVertexFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertexFace.html]]
*{{{.map}}} : [[MeshUV|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshUV.html]]
*{{{.uv}}} / {{{.uvpt}}} :  [[Attribute|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html]] wrapper around the {{{.uvpt}}} attr.
In addition, there are a number of //methods// that can be called to that return info on the mesh:
*{{{numEdges()}}} : returns the number of edges as an int
*{{{numFaceVertices()}}} : Returns the number of face-vertices for this mesh.
*{{{numFaces()}}} : returns the number of faces as an int
*{{{numNormals()}}} : Returns the number of per-polygon per-vertex normals for this mesh.
*{{{numPolygonVertices()}}} : Returns the number of vertices for the specified polygon.
*{{{numSelectedEdges()}}} : returns the object’s number of selected edges as an int
*{{{numSelectedFaces()}}} : returns the object’s number of selected faces as an int
*{{{numSelectedTriangles()}}} : returns the number of triangles of selected components as an int
*{{{numSelectedVertices()}}} : returns the object’s number of selected vertices as an int
*{{{numTriangles()}}} : In the docs, but isn't supported in code.  Probably need to call to {{{pm.polyEvaluate(node, triangle=True)}}} instead...
*{{{numUVs()}}} : Returns the number of texture (uv) coordinates for this mesh.
*{{{numVertices()}}} : returns the number of vertices as an int
Even though no shape is listed, ~PyMel will try to look for one if it needs to:
{{{
print `cubeTrans.f`
MeshFace(u'pCubeShape1.f[0:5]')
}}}
It's easy to find the parent node, based on some component:
{{{
vert = cubeTrans.vtx[0] # MeshVertex
meshShape = vert.node()  # Mesh  : DependNode method
transform = meshShape.getParent() # Transform  :  DagNode method

print `vert`
print `meshShape`
print `transform`
}}}
{{{
MeshVertex(u'pCubeShape1.vtx[0]')
nt.Mesh(u'pCubeShape1')
nt.Transform(u'pCube1')
}}}
----
''For a given ~MeshFace, find connected [[MeshEdges|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshEdge.html]]''
{{{
edges = [face.node().e[i] for i in face.getEdges()]
# [MeshEdge(u'pCylinderShape1.e[73]'), MeshEdge(u'pCylinderShape1.e[74]'),
}}}
Note that {{{face.connectedEdges()}}} returns back a list of {{{MeshEdges}}}, but ALL {{{MeshEdges}}} that touch every vert, not just the perimeter ones.
----
~PyMel node-types:
*[[PyNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html]] : Also see: [[PyMel : PyNode methods]]
**[[DependNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html]] : Also see : [[PyMel : DependNode methods]]
***[[DagNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html]]  Also see : [[PyMel : DagNode methods]]
****[[Mesh|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html]]  Also see : [[PyMel : Mesh methods]]
****[[Transform|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Transform.html]]
**[[Component|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Component.html#pymel.core.general.Component]]
***[[MeshVertex|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertex.html]]  Also see : [[PyMel : MeshVertex methods]]
***[[MesnFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshFace.html]] Also see: [[PyMel : MeshFace methods]]
***[[MeshEdge|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshEdge.html]] Also see : [[PyMel : MeshEdge methods]]
***[[MeshUv|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshUV.html]]
***[[MeshVertexFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertexFace.html]]
**[[Attribute|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.Attribute.html]] Also see : [[PyMel : Attribute methods]]
----
Also see:
*[[PyMel : UV access]]
Also see:  [[PyMel : Mesh access]]
----
Condensation of the ~PyMel [[Mesh|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html]] docs:
Methods, from 2014 docs:
*{{{addHoles}}} : Adds holes to a mesh polygon.
*{{{area}}} : returns the surface area of the object’s faces in local space as a float
*{{{assignColor}}} : This method maps all colors for the mesh.
*{{{assignUV}}} : Maps a texture coordinate to a specified vertex of a polygon
*{{{assignUVs}}} : This method maps all texture coordinates for the mesh.
*{{{cleanupEdgeSmoothing}}} : updates the mesh after setEdgeSmoothing has been done
*{{{clearColors}}} : clears out all color for the mesh, and leaves behind an empty color set.
*{{{clearUVs}}} : clears out all texture coordinates for the mesh, and leaves behind an empty UVset.
*{{{createColorSet}}} : Create a new empty color set for this mesh.
*{{{createUVSet}}} : Create a new empty uv set for this mesh.
*{{{deleteColorSet}}} : Deletes a named color set from the object.
*{{{deleteUVSet}}} : Deletes a named uv set from the object.
*{{{getAssignedUVs}}} : Get assigned UVs. This method finds all texture coordinates for the mesh that have been mapped, and returns them in the same format as the assignUVs.
*{{{getAssociatedUVSetTextures}}} : Get a list of texture nodes which are using a given uv set.
*{{{getBinormals}}} : Return the binormal vectors for all face vertices.
*{{{getCheckSamePointTwice}}} : Return true if checking for duplicate points is turned on. Return false otherwise.
*{{{getClosestNormal}}} : Returns the closest point on this surface to the given point. This method also returns the surface normal at that point.
*{{{getClosestPoint}}} : Returns the closest point on this surface to the given point.
*{{{getClosestPointAndNormal}}} : Returns the closest point on this surface to the given point. This method also returns the surface normal at that point.
*{{{getColor}}} : Get the value of the specified texture coordinate from this mesh’s color list. 
*{{{getColorRepresentation}}} :  returns the color representation (RGB/RGBA/A) of a color set.
*{{{getColorSetFamilyNames}}} : Get the names of all of the color set families on this object.
*{{{getColorSetNames}}} : Get the names of all of the colors sets on this object.
*{{{getColors}}} : copies the color array for this mesh into the given color array.
*{{{getCurrentColorSetName}}} : Get the name of the “current” or “working” color set.
*{{{getCurrentUVSetName}}} : Get the name of the “current” uv set. 
*{{{getDisplayColors}}} : Determine if the mesh node is set to display vertex colors.
*{{{getEdgeVertices}}} : retrieves the object-relative (mesh-relative/global) vertex indices corresponding to the specified edge.
*{{{getFaceNormalIds}}} : Return normal indices for all vertices for a given face.
*{{{getFaceUVSetNames}}} :  returns the list of UV sets mapped to a face.
*{{{getFaceVertexBinormal}}} : Return the binormal vector at a given face vertex.
*{{{getFaceVertexBinormals}}} : Return all per-vertex-per-face binormals for a given face.
*{{{getFaceVertexColorIndex}}} : Get an index into the array returned by getFaceVertexColors.
*{{{getFaceVertexColors}}} : Get colors for all vertex/faces of the given color set. 
*{{{getFaceVertexNormal}}} : Return a per-vertex-per-face normal for a given face (polygon) and given vertex.
*{{{getFaceVertexTangent}}} : Return the normalized tangent vector at a given face vertex.
*{{{getFaceVertexTangents}}} : Return all per-vertex-per-face tangents for a given face.
*{{{getHoles}}} : Retrieves a list of the holes in the polygon.
*{{{getNormalIds}}} : Return normal indices for all vertices for a all faces.
*{{{getNormals}}} : This method copies the normal list for this mesh into the given array.
*{{{getPoint}}} : Get the position of the specified vertex in this mesh’s vertex list.
*{{{getPointAtUV}}} : Return the position of the point at the given UV value in the current polygon.
*{{{getPoints}}} :  copies the vertex list for this mesh into the given point array.
*{{{getPolygonNormal}}} : Return the normal at the given polygon
*{{{getPolygonTriangleVertices}}} : retrieves the object-relative (mesh-relative/global) vertex indices for the specified triangle in the specified polygon
*{{{getPolygonUV}}} : Get the value of the specified texture coordinate for a vertex in a polygon.
*{{{getPolygonUVid}}} : Get the id of the specified texture coordinate for a vertex in a polygon.
*{{{getPolygonVertices}}} : retrieves the object-relative (mesh-relative/global) vertex indices for the specified polygon. 
*{{{getTangentId}}} : Return the tangent index for a given face vertex.
*{{{getTangents}}} : Return the tangent vectors for all face vertices. 
*{{{getTriangles}}} : Returns the number of triangles for every polygon face and the vertex Ids of each triangle vertex.
*{{{getUV}}} : Get the value of the specified texture coordinate from this mesh’s uv list. 
*{{{getUVAtPoint}}} : Find the point closet to the given point, and return the UV value at that point.
*{{{getUVSetFamilyNames}}} : Get the names of all of the uv set families on this object. 
*{{{getUVSetNames}}} : Get the names of all of the uv sets on this object
*{{{getUVSetsInFamily}}} : Get the names of the uv sets that belong to this set family.
*{{{getUVs}}} : copies the texture coordinate list for this mesh into the given uv arrays.
*{{{getUvShellsIds}}} : Constructs an array of unique integer for each UV shell. 
*{{{getVertexNormal}}} : Return the normal at the given vertex. 
*{{{getVertices}}} : retrieves the object-relative (mesh-relative/global) vertex indices for all polygons. 
*{{{hasAlphaChannels}}} : returns true if the color set has Alpha component.
*{{{hasColorChannels}}} : returns if the color set has RGB components.
*{{{intersect}}} : Determines whether the given ray intersects this polygon and if so, returns the points of intersection
*{{{isColorClamped}}} : returns if the color set has its R,G,B,and A components clamped in the range from 0 to 1.
*{{{isColorSetPerInstance}}} :  Return true if this color set is per-instance, and false if it is shared across all instances. 
*{{{isEdgeSmooth}}} :  determines if the specified edge is smooth (soft).
*{{{isNormalLocked}}} : Test if the normal for a face/vertex pairs is locked (user defined).
*{{{isPolygonConvex}}} : determines if the specified polygon is convex.
*{{{isUVSetPerInstance}}} : Return true if this set is per-instance, and false if it is shared across all instances.
*{{{lockFaceVertexNormals}}} : Lock Normals for these face/vertex pairs
*{{{lockVertexNormals}}} : Lock Shared Normals for these vertices.
*{{{numColorSets}}} : Returns the number of color sets for an object.
*{{{numColors}}} : Returns the number of (vertex) color for this mesh.
*{{{numEdges}}} : returns the number of edges as an int
*{{{numFaceVertices}}} : Returns the number of face-vertices for this mesh.
*{{{numFaces}}} : returns the number of faces as an int
*{{{numNormals}}} : Returns the number of per-polygon per-vertex normals for this mesh.
*{{{numPolygonVertices}}} : Returns the number of vertices for the specified polygon.
*{{{numSelectedEdges}}} : returns the object’s number of selected edges as an int
*{{{numSelectedFaces}}} : returns the object’s number of selected faces as an int
*{{{numSelectedTriangles}}} : returns the number of triangles of selected components as an int
*{{{numSelectedVertices}}} : returns the object’s number of selected vertices as an int
*{{{numTriangles}}} : (no docs)
*{{{numUVSets}}} : Returns the number of uv sets for an object.
*{{{numUVs}}} : Returns the number of texture (uv) coordinates for this mesh.
*{{{numVertices}}} : returns the number of vertices as an int
*{{{onBoundary}}} : determines whether the specified face in the mesh is a boundary face.
*{{{removeFaceColors}}} : Remove previously set color these faces.
*{{{removeFaceVertexColors}}} : Remove colors for these face/vertex pairs
*{{{removeVertexColors}}} : Remove color from these vertices.
*{{{renameUVSet}}} : Renames a uv set from one name to another for this mesh. 
*{{{setCheckSamePointTwice}}} : This method allows the turning on or off of duplicate point checking when polygons are created or added using this class. 
*{{{setColor}}} : Sets the specified color values. 
*{{{setColorClamped}}} : Set the color set to be clamped.
*{{{setColors}}} : Sets all of the colors for this mesh.
*{{{setCurrentColorSetName}}} : Set the “current” or “working” color set for this object. 
*{{{setCurrentUVSetName}}} : Set the “current” uv set for this object.
*{{{setDisplayColors}}} : Set whether the mesh node should display vertex colors.
*{{{setEdgeSmoothing}}} : This method sets the specified edge to be hard or smooth (soft)
*{{{setFaceColor}}} : Set vertex-face Color for all vertices on this face.
*{{{setFaceColors}}} : Set color for these faces. 
*{{{setFaceVertexColor}}} : Set color for this vertex in this face.
*{{{setFaceVertexNormal}}} : Set Normal for this face/vertex pair
*{{{setNormals}}} : Set the normal array (user normals)
*{{{setPoint}}} : Sets the position of specified vertex in the vertex list for this mesh.
*{{{setPoints}}} : copies the points in the given point array to the vertices of this mesh.
*{{{setSomeColors}}} : Sets the specified colors for this mesh.
*{{{setSomeUVs}}} : Sets the specified texture coordinates (UV’s) for this mesh.
*{{{setUV}}} : Sets the specified texture coordinate. 
*{{{setUVs}}} : Sets all of the texture coordinates (uv’s) for this mesh.
*{{{setVertexColor}}} : Set color for this vertex.
*{{{setVertexNormal}}} : Set Shared Normal for this vertex
*{{{syncObject}}} : If a non-api operation happens that many have changed the underlying Maya object wrapped by this api object, make sure that the api object references a valid maya object. 
*{{{unlockFaceVertexNormals}}} : Unlock Normals for these face/vertex pairs
*{{{unlockVertexNormals}}} : Unlock Shared Normals for these vertices
*{{{updateSurface}}} : Signal that this polygonal mesh has changed and needs to redraw itself.
*{{{worldArea}}} : returns the surface area of the object’s faces in world space as a float
Classmethods:
*{{{polyTriangulate}}} : Triangulates a polygon. 
Also see:  [[PyMel : Mesh access]]
----
Condensation of the ~PyMel [[MeshEdge|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshEdge.html]] docs:
Methods, from 2014 docs:
*{{{connectedEdges}}} : Return ~MeshEdge list
*{{{connectedFaces}}} : Return ~MeshFace list
*{{{connectedVertices}}} : Return ~MeshVertex list
*{{{getLength}}} : This method returns the length of the current edge.
*{{{getPoint}}} : Return the position of the specified vertex of the current edge.
*{{{isConnectedTo}}} :  Returns bool
*{{{isConnectedToEdge}}} : This method determines whether the given edge is connected to the current edge
*{{{isConnectedToFace}}} : This method determines whether the given face contains the current edge
*{{{isOnBoundary}}} : This method checks to see if the current edge is a border edge
*{{{isSmooth}}} : This method determines if the current edge in the iteration is smooth (soft).
*{{{numConnectedEdges}}} : This method returns the number of edges connected to the current edge.
*{{{numConnectedFaces}}} : This method returns the number of faces (1 or 2 ) connected to the current edge.
*{{{setPoint}}} : Set the specified vertex of the current edge to the given value.
*{{{setSmoothing}}} : This method sets the current edge to be hard or smooth (soft).
*{{{updateSurface}}} : Signal that this polygonal surface has changed and needs to redraw itself. 
Also see:  [[PyMel : Mesh access]]
----
Condensation of the ~PyMel [[MeshFace|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshFace.html]] docs:
Methods, from 2014 docs:
*{{{connectedEdges}}} : Return ~MeshEdge list.
*{{{connectedFaces}}} : Return ~MeshFace list.
*{{{connectedVertices}}} : Return ~MeshVertex list.
*{{{geomChanged}}} : Reset the geom pointer in the ~MItMeshPolygon 
*{{{getArea}}} : This method gets the area of the face
*{{{getColor}}} : This method gets the average color of the all the vertices in this face
*{{{getColorIndex}}} : This method returns the colorIndex for a vertex of the current face.
*{{{getColorIndices}}} : This method returns the colorIndices for each vertex on the face.
*{{{getColors}}} : This method gets the color of the each vertex in the current face.
*{{{getEdges}}} : This method gets the indices of the edges contained in the current face.
*{{{getNormal}}} : Return the face normal of the current polygon.
*{{{getNormals}}} : Returns the normals for all vertices in the current face
*{{{getPoint}}} : Return the position of the vertex at index in the current polygon.
*{{{getPointAtUV}}} : Return the position of the point at the given UV value in the current polygon.
*{{{getPoints}}} : Retrieves the positions of the vertices on the current face/polygon that the iterator is pointing to.
*{{{getUV}}} : Return the texture coordinate for the given vertex.
*{{{getUVArea}}} : This method gets the UV area of the face
*{{{getUVAtPoint}}} : Find the point closest to the given point in the current polygon, and return the UV value at that point.
*{{{getUVIndex}}} : Returns the index of the texture coordinate for the given vertex.
*{{{getUVSetNames}}} : This method is used to find the UV set names mapped to the current face.
*{{{getUVs}}} : Return the all the texture coordinates for the vertices of this face (in local vertex order).
*{{{getVertices}}} : This method gets the indices of the vertices of the current face
*{{{hasColor}}} : This method determines whether the current face has color-per-vertex set for any vertex.
*{{{hasUVs}}} : Tests whether this face has UV’s mapped or not 
*{{{hasValidTriangulation}}} : This method checks if the face has a valid triangulation.
*{{{isConnectedTo}}} : return bool
*{{{isConnectedToEdge}}} : This method determines whether the given edge is connected to a vertex in the current face
*{{{isConnectedToFace}}} : This method determines whether the given face is adjacent to the current face
*{{{isConnectedToVertex}}} : This method determines whether the given vertex shares an edge with a vertex in the current face.
*{{{isConvex}}} : This method checks if the face is convex.
*{{{isHoled}}} : This method checks if the face has any holes.
*{{{isLamina}}} : This method checks if the face is a lamina (the face is folded over onto itself).
*{{{isOnBoundary}}} : This method determines whether the current face is on a boundary
*{{{isPlanar}}} : This method checks if the face is planar
*{{{isStarlike}}} : This method checks if the face is starlike. 
*{{{isZeroArea}}} : This method checks if its a zero area face
*{{{isZeroUVArea}}} : This method checks if the UV area of the face is zero
*{{{normalIndex}}} : Returns the normal index for the specified vertex. 
*{{{numColors}}} : This method checks for the number of colors on vertices in this face.
*{{{numConnectedEdges}}} : This method checks for the number of connected edges on the vertices of this face
*{{{numConnectedFaces}}} : This method checks for the number of connected faces
*{{{numTriangles}}} : This Method checks for the number of triangles in this face in the current triangulation
*{{{numVertices}}} : Return the number of vertices for the current polygon.
*{{{polygonVertexCount}}}
*{{{setPoint}}} : Set the vertex at the given index in the current polygon.
*{{{setPoints}}} : Sets new locations for vertices of the current polygon that the iterator is pointing to.
*{{{setUV}}} : Modify the UV value for the given vertex in the current face
*{{{setUVs}}} : Modify the UV value for all vertices in the current face.
*{{{updateSurface}}} : Signal that this polygonal surface has changed and needs to redraw itself.
Also see:  [[PyMel : Mesh access]]
----
Condensation of the ~PyMel [[MeshVertex|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshVertex.html]] docs:
Methods, from 2014 docs:
*{{{connectedEdges}}} : Return ~MeshEdge list.
*{{{connectedFaces}}} : Return ~MeshFace list.
*{{{connectedVertices}}} : Return ~MeshVertex list.
*{{{geomChanged}}} : Reset the geom pointer in the ~MItMeshVertex .
*{{{getColor}}} : No docs
*{{{getColorIndices}}} : returns the colorIndices into the color array
*{{{getColors}}} : This method gets the colors of the current vertex for each face it belongs to. 
*{{{getNormal}}} : Return the normal or averaged normal if unshared of the current vertex.
*{{{getNormalIndices}}} : This method returns the normal indices of the face/vertex associated with the current vertex.
*{{{getNormals}}} : Return the normals of the current vertex for all faces
*{{{getPosition}}} : Return the position of the current vertex in the specified space.
*{{{getUV}}} : Get the shared UV value at this vertex
*{{{getUVIndices}}} : returns the uv indices into the normal array 
*{{{getUVs}}} : Get the UV values for all mapped faces at the current vertex.
*{{{hasColor}}} : This method determines whether the current Vertex has a color set for one or more faces.
*{{{isConnectedTo}}} : pass a component of type ~MeshVertex, ~MeshEdge, ~MeshFace, with a single element
*{{{isConnectedToEdge}}} : This method determines whether the given edge contains the current vertex
*{{{isConnectedToFace}}} : This method determines whether the given face contains the current vertex
*{{{isOnBoundary}}} : This method determines whether the current vertex is on a Boundary
*{{{numConnectedEdges}}} : This Method checks for the number of connected Edges on this vertex
*{{{numConnectedFaces}}} : This Method checks for the number of Connected Faces
*{{{numUVs}}} : This method returns the number of unique UVs mapped on this vertex
*{{{setColor}}} : No docs.
*{{{setPosition}}} : Set the position of the current vertex in the given space.
*{{{setUV}}} : Set the shared UV value at this vertex
*{{{setUVs}}} : Set the UV value for the specified faces at the current vertex.
*{{{translateBy}}} : Translate the current vertex by the amount specified by the given vector.
*{{{updateSurface}}} : Signal that this polygonal surface has changed and needs to redraw itself. 
''Inherited Methods'':
~Component1D:
* {{{currentItem}}} :
* {{{currentItemIndex}}} :
* {{{dimensions}}} : 
* {{{index}}} : Current int index of this vert
* {{{indicesIter}}} : An iterator over all the indices contained by this component, as integers.
* {{{name}}} : 
~DiscreteComponent:
*{{{count}}} :
*{{{currentItem}}} :
*{{{currentItemIndex}}} : Returns the component indices for the current item in this component group
*{{{getIndex}}} : Returns the current ‘flat list’ index for this group of components
*{{{indices}}} : 
*{{{indicesIter}}} :
*{{{next}}} :
*{{{reset}}} :
*{{{setIndex}}} : index
*{{{totalSize}}} : The maximum possible number of components.  ie, for a polygon cube, the totalSize for verts would be 8, for edges would be 12, and for faces would be 6
Component
*{{{isComplete}}} : 
*{{{name}}} :
*{{{node}}} :
*{{{numComponentsFromStrings}}} : Does basic string processing to count the number of components given a number of strings, which are assumed to be the valid mel names of components.
*{{{plugAttr}}}
*{{{plugNode}}}
Plus, of course:  [[PyMel : PyNode methods]]
~PyNodes are handy wrappers around Maya nodes.  Sometimes I want to extend them, to be even more helpful, by adding my own methods per instance.  The general term for this is 'monkey-patching'.  Here's how to do it:

Say, you want to add your own method to return the worldspace bounding box for some mesh:
{{{
import types
import pymel.core as pm

node = pm.PyNode("myAwesomeMesh")

def bboxWorld(self):
    """
    Our method to monkeypatch in.
    """
    return self.getBoundingBox(space='world')

node.bboxWorld= types.MethodType( bboxWorld, node )
}}}
Now, we can call to {{{node.bboxWorld()}}} on that specific ~PyNode instance.
{{{
data = node.bboxWorld()
print type(data)
<class 'pymel.core.datatypes.BoundingBox'>
}}}
See: 
*[[PyMel : NurbsCurve methods]]
*[[PyMel : NurbsCurveCV methods]]
{{{
import pymel.core as pm
}}}
{{{
curve = pm.PyNode("myCurve"}}}
# Get the shape specifically.  Note, you can use the Transform for all the below stuff too.
curveShape = curve.getChildren(type='nurbsCurve')[0] # nt.NurbsCurve(u'myCurveShape')
}}}
Curve components can be accessed \ looped over via these attrs:
*{{{.cv}}} : List of [[NurbsCurveCV|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.NurbsCurveCV.html]]
*{{{.ep}}} : List of [[NurbsCurveEP|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.NurbsCurveEP.html]] (no docs really)
* more?
For example:
{{{
for cv in curve.cv:
    print cv
}}}
Handy {{{NurbsCurve}}} methods:
* {{{closestPoint}}}
* {{{cvs(start, end)}}} 
* {{{degree}}}
* {{{getCV}}} : get the position of a cv
* {{{getCVs}}} : get the position of many cvs
* {{{getParamAtPoint}}}
* {{{getPointAtParam}}}
* {{{isPlanar}}}
* {{{isPointOnCurve}}}
* {{{length}}}
* {{{normal}}} : get normal at the given point
* {{{numCVs}}}
* {{{numEPs}}}
* {{{numKnots}}}
* {{{numSpans}}}
* {{{tangent}}} : get tangeent at given point
Handy {{{NurbsCurveCV}}} methods:
* {{{getPosition}}} : Note this is the cm (internal unit) location, not necessarily the ui unit (could be inch, for example)
* {{{setPosition}}}
* {{{translateBy}}}
Condensation of the ~PyMel [[NurbsCurve|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.NurbsCurve.html]] docs.
Also see: [[PyMel : NurbsCurve access]]
*{{{area}}} : returns the area bounded by this curve. The curve must be closed and planar.
*{{{closestPoint}}} :  determines the closest point on the curve to the given point.
*{{{copy}}} : creates a copy of a nurbs curve.
*{{{createWithEditPoints}}} : creates a nurbs curve from the given edit points and sets this function set to operate on the new curve
*{{{cvs}}} : used to directly access a contiguous group of ~CVs
*{{{degree}}} : Return the degree of this curve. If the degree cannot be determined then 0 is returned
*{{{distanceToPoint}}} : determines the distance from the given point to closest point on the curve
*{{{findParamFromLength}}} : Given the length along the curve, find the parameter value that corresponds to it.
*{{{form}}} :  returns the form of the curve. The curve can be open, closed, or periodic.
*{{{getCV}}} : Get the CV at the given index.
*{{{getCVs}}} : Get the positions of the ~CVs of this curve.
*{{{getDerivativesAtParm}}} : Evaluate the surface at the given parameter returning the position, first derivative and optionally the seecond derivative
*{{{getKnot}}} : Get the parameter value of the specified knot for this curve
*{{{getKnotDomain}}} : Return the range corresponding to the maximum and minimum parameter values for this curve
*{{{getKnots}}} :  retrieves a copy of the knot array for this curve
*{{{getParamAtPoint}}} : retrieves the parameter value corresponding to the given point on the curve
*{{{getPointAtParam}}} : Returns the point in space that is at the given parameter value of the curve
*{{{hasHistoryOnCreate}}} :  determines if the shape was created with history
*{{{isParamOnCurve}}} : Determines whether the specified parameter value is within the bounds of the knot vector of this curve
*{{{isPlanar}}} : determines if this curve is a planar curve
*{{{isPointOnCurve}}} : Determines whether the given point is on this curve
*{{{length}}} :  Return the arc length of this curve or 0.0 if it cannot be computed.
*{{{makeMultipleEndKnots}}} :  This method gives end knots full multiplicity. This ensures the end points of the curve interpolate the first and last ~CVs
*{{{normal}}} : returns the normal at the given parameter value on the curve
*{{{numCVs}}} : Returns the number of ~CVs
*{{{numEPs}}} : Returns the number of ~EPs
*{{{numKnots}}} : Return the number of knots for this curve
*{{{numSpans}}} : Return the number of spans for this curve
*{{{removeKnot}}} : Remove knot(s) from this curve.
*{{{reverse}}} : reverse the curve direction
*{{{setCV}}} : Set the CV at the given index to the given point
*{{{setCVs}}} : Set the CVs for this curve to the given points
*{{{setKnot}}} : Set the given knot’s parameter value.
*{{{setKnots}}} : set the values of a contiguous group of knots of the curve
*{{{tangent}}} : returns the tangent at the given parameter value on the curve
*{{{updateCurve}}} : signals that this curve has changed and needs to be recalculated
Condensation of the [[NurbsCurveCV|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.NurbsCurveCV.html]] docs.
Also see:
*[[PyMel : NurbsCurve access]]]
*[[PyMel : NurbsCurve methods]]
----
* {{{getPosition}}} : Return the position of the current CV
* {{{hasHistoryOnCreate}}} : determines if the shape was created with history
* {{{isDone}}} : Returns true if the iteration is finished, i.e. there are no more CVs to iterate on.
* {{{setPosition}}} : Set the position of the current CV to the specified point.
* {{{translateBy}}} : Translates the current CV by the amount specified in vec .
* {{{updateCurve}}} : This method is used to signal the curve that it has been changed and needs to redraw itself.
~PyMel makes using {{{optionVar}}}s very easy:  Docs [[here|http://download.autodesk.com/global/docs/maya2013/en_us/PyMel/generated/classes/pymel.core.language/pymel.core.language.OptionVarDict.html]]

Print every {{{optionVar}}} and its value:
{{{
import pymel.core as pm
for key in pm.optionVar:
    print key, pm.optionVar[key]
}}}
Set and retrieve:
{{{
import pymel.core as pm
pm.optionVar['spam'] = 'eggs'
print pm.optionVar['spam']
# eggs
}}}
However, they have some issues that should be realized:
* If you add an empty list to an optionvar, //no action is taken//.
* If you add a list with one item, //the list is removed, and only the item is added//.
* If you add a list with two or more items, //its added as a tuple// (not list).
~PyNodes have a {{{setParent}}} method (differing from the separate {{{setParent}}} //command// for budiling UI's) that basically wrappers the [[parent|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/parent.html]] command.  Pretty simple to use.  While the ~PyMel docs don't list all the arguments to it, you can use the above link.
{{{
import pymel.core as pm

loc1 = pm.spaceLocator()
loc2 = pm.spaceLocator()

# Parent loc2 to loc1
loc2.setParent(loc1)
# Parent loc2 back to the world
loc2.setParent(world=True)
}}}
----
Also see:
*[[PyMel : Finding parent nodes]]
[[PyNode overview|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/advanced.html]]
Condensation of the ~PyMel [[PyNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.PyNode.html]] API docs:
Methods, from 2014 docs:
*{{{addPrefix}}} : Returns the object’s name with a prefix added to the beginning of the name
*{{{connections}}} :  returns a list of all attributes/objects of a specified type that are connected to the given object(s)
*{{{deselect}}} : No docs.
*{{{exists}}} : {{{objExists}}}
*{{{future}}} : No docs
*{{{history}}} : This command traverses backwards or forwards in the graph from the specified node and returns all of the nodes whose construction history it passes through. 
*{{{listConnections}}} : returns a list of all attributes/objects of a specified type that are connected to the given object(s). 
*{{{listFuture}}} No docs
*{{{listHistory}}} : This command traverses backwards or forwards in the graph from the specified node and returns all of the nodes whose construction history it passes through. 
*{{{listSets}}} : Returns list of sets this object belongs
*{{{namespace}}} : Returns the namespace of the object with trailing colon included.
*{{{namespaceList}}} : Useful for cascading references. Returns all of the namespaces of the calling object as a list
*{{{nodeType}}} : No docs.
*{{{objExists}}} : No docs.
*{{{select}}} : No docs.
*{{{stripNamespace}}} : Returns the object’s name with its namespace removed. The calling instance is unaffected.
*{{{swapNamespace}}} : Returns the object’s name with its current namespace replaced with the provided one. The calling instance is unaffected.
[[This PyMel doc|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/non_existent_objs.html?highlight=objexists]] shows how you can query a nodes existence using a try/except clause, trying to create a ~PyNode.  In my head, this seemed like a really slow option compared to using the {{{objExists}}} command, so I thought I'd do a speed test on them.

As you can see from the below results: calling to Python {{{objExists}}} command was 3x faster than the equivilant ~PyMel ''and'' mel command, and it was 171x faster than the try/except/~PyNode clause.
{{{
import maya.cmds as mc
import pymel.core as pm

import time

class Timer(object):
    def __init__(self, f):
        self.f = f
    def __call__(self):
        start = time.time()
        for i in range(1000):
            self.f()
        end = time.time()
        total = end - start
        print ("Completed 1000 iterations of %s() in %.6f seconds, %.6f per loop"+
               "")%(self.f.__name__, total, total/1000.0)
        return [total, self.f.__name__]
        
@Timer
def pymelTestExistence():
    try:
        pm.PyNode("asdf")
    except:
        pass

@Timer       
def pymelCmdTestExistence():
    pm.objExists("asdf")
 
@Timer   
def cmdTestExistence():
    mc.objExists("asdf")

speedTest = []        
speedTest.append(pymelTestExistence())
speedTest.append(pymelCmdTestExistence())
speedTest.append(cmdTestExistence())

speedTest.sort()
for speed in speedTest:
    print speed
}}}
{{{
// Here's a mel version too:
timer -startTimer;
for($i=0;$i<1000;$i++){
    objExists("asdf");
}
float $elapsedTime = `timer -endTimer`;
float $eachLoop = $elapsedTime / 1000.0;
print ("Completed 1000 iterations of mel 'objExists' in " + $elapsedTime + " seconds, " + $eachLoop + " per loop.\n");
}}}
{{{
Completed 1000 iterations of pymelTestExistence() in 0.856000 seconds, 0.000856 per loop
Completed 1000 iterations of pymelCmdTestExistence() in 0.018000 seconds, 0.000018 per loop
Completed 1000 iterations of cmdTestExistence() in 0.005000 seconds, 0.000005 per loop
[0.004999876022338867, 'cmdTestExistence']
[0.017999887466430664, 'pymelCmdTestExistence']
[0.8559999465942383, 'pymelTestExistence']
}}}
{{{
Completed 1000 iterations of mel 'objExists' in 0.015 seconds, 0.000015 per loop.
}}}
The ~PyMel [[DagNode|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DagNode.html]] and [[DependNode||http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.DependNode.html]] classes provide a number of methods for querying the string name of a ~PyNode.

In this example, there are two {{{pCube1}}} nodes in the scene, to cause a name-clash on purpose.
In addition, the methods can also strip off namespaces if needed:  The methods that support this functionality are shown.
{{{
cube = pm.PyNode("|group1|pCube1")

# Just the leaf name
print cube.nodeName(stripNamespace=False)
# pCube1

# Shortest unique path to node:
print cube.shortName()
# group1|pCube1

# Shortest unique path to node, with no trailing number:
print cube.stripNum()
# group1|pCube

# Just like shortName(), but can also strip namespace:
print cube.name(stripNamespace=False)
# group1|pCube1

# Full path to node:
print cube.longName(stripNamespace=False)
print cube.fullPath(stripNamespace=False)
#|group1|pCube1
#|group1|pCube1

# Get new unique names by incrementing the number on the end.
# If there is no number on the end, nextName will fail and raise a ValueError :
print cube.nextName()
print cube.nextUniqueName()
# pCube2
# pCube2
}}}
It should be noted that if a node is in a namespace, just the namespace can be queried via:
{{{
ns = cube.namespace()
# or
ns = cube.parentNamespace()
# someNamespace:
# someNamespace
}}}
In addition, the name without namespace can be queried via the ~PyNode method:
{{{
node = pm.PyNode("foo:null1")
print node.stripNamespace()
# null1
}}}
There is also a {{{swapNamespace}}} method, but I am unable to get it to function.

Nodes can be renamed as well:
{{{
node.rename("spam")
}}}
Attribute names can also be queried:
{{{
attr = pm.PyNode("pCube1.translateX")
print attr.longName()
}}}
----
Also see:
*[[PyMel : DependNode methods]]
*[[PyMel : DagNode methods]]


Check out [[PyMel : File command access]] for other reference-related notes.
----
The core to dealing with references in ~PyMel is the [[FileReference|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.system/pymel.core.system.FileReference.html]] class.  The first thing you need to do is get one for the reference in question:
{{{
import pymel.core as pm
}}}
{{{
# Reference in a file, create a FileReference wrapper around it:
fRef = pm.FileReference(pm.createReference(fullPathToTheFile))
}}}
You'd think {{{createReference}}} would return a ~FileReference, but it doesn't, it returns a string...

Query existing ~FileReference instances in the scene:
{{{
# Returns a dict of {"namespace":FileReference, }
topRefs = pm.getReferences()  # Just assembly-level refs
allRefs = pm.getReferences(recursive=True) # All refs in scene.
}}}
{{{
# Based on a given *namespace*, get the FileReference.  Will be None if no match.
ns = "vm"
fRef = allRefs.setdefault(ns)
}}}
{{{
# Based on a given path, get the FileReference:
pth = "c:/path/to/my/file.mb"
myRef = None
for ns in allRefs:
    if allRefs[ns].path == pth:
        myRef = allRefs[ns]
        break
}}}
You can easily query child ~FileReference instances:
{{{
childRefs = pm.getReferences(parentReference=fRef, recursive=True)
# or:
childRefs = fRef.subReferences()
}}}
Query or set the namespace:
{{{
ns = fRef.namespace
fRef.namespace = "newNamespace"
}}}
----
Once you have a {{{FileReference}}} instance, methods you can access:
*{{{clean}}} : Remove edits from the passed in reference node. The reference must be in an unloaded state.
*{{{copyNumberList}}} : Returns a string array containing a number that uniquely identifies each instance the file is used.
*{{{exportAnim}}} : Export the main scene animation nodes and animation helper nodes from all referenced objects.
*{{{exportSelectedAnim}}} : Export the main scene animation nodes and animation helper nodes from the selected referenced objects.
*{{{getReferenceEdits}}} : Get a list of {{{ReferenceEdit}}} objects for this node
*{{{importContents}}} : Remove the encapsulation of the reference around the data within the specified file. 
*{{{isDeferred}}} : When used in conjunction with the -reference flag, this flag determines if the reference is loaded, or if loading is deferred.
*{{{isLoaded}}} : When used in conjunction with the -reference flag, this flag determines if the reference is loaded, or if loading is deferred
*{{{isUsingNamespaces}}} : Returns boolean. Queries whether the specified reference file uses namespaces or renaming prefixes.
*{{{load}}} : This flag loads a file and associates it with the passed reference node. 
*{{{lock}}} : Locks attributes and nodes from the referenced file.
*{{{namespaceExists}}} : Returns true if the specified namespace exists, false if not.
*{{{nodes}}} : List of nodes in that reference (as PyNode instances). A main flag used to query the contents of the target reference.
*{{{parent}}} : Returns the parent {{{FileReference}}} object, or None
*{{{remove}}} : Remove the given file reference from its parent. 
*{{{removeReferenceEdits}}} : Remove edits from the reference
*{{{replaceWith}}} : This flag loads a file and associates it with the passed reference node
*{{{selectAll}}} : Select all the components of this file as well as its child files.
*{{{subReferences}}} 
*{{{unload}}} : This flag will unload the reference file associated with the passed reference node.
*{{{unlock}}} : Locks attributes and nodes from the referenced file.
*{{{unresolvedPath}}} : [[Path|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.system/pymel.core.system.Path.html]] instance.  But basically a string.
*{{{withCopyNumber}}} : return the path with the copy number at the end
And attributes:
*{{{fullNamespace}}} : string
*{{{namespace}}} : string
*{{{path}}} : [[Path|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.system/pymel.core.system.Path.html]] instance.  But basically a string.
*{{{refNode}}} : Instance of [[Reference|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Reference.html]] node type.  The ~PyNode instance of the {{{<namespace>RN}}} node.
----
[[Reference|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Reference.html]] are instances around {{{reference}}} nodes.
If you already have a {{{FileReference}}} instance (from the above example):
{{{
refNode = fRef.refNode()
}}}
OR, if you know the namespace already, you can hack it:
{{{
refNode = pm.PyNode('%sRN'%ns)
}}}
Note this can fail though, if the namespace changed after the {{{reference}}} node was created.
Their methods:
*{{{associatedNamespace}}} : Returns the namespace associated with this reference.
*{{{containsNode}}} : Returns true if the specified node is from this reference or one of its child references
*{{{containsNodeExactly}}} : Returns true if the specified node is from this reference
*{{{fileName}}} : Returns the name of file associated with this reference
*{{{isExportEditsFile}}} : Returns true if the reference is an export edits file.
*{{{isLoaded}}} : Returns true if the reference is loaded.
*{{{nodes}}} : Returns an array of the nodes associated with this reference.
*{{{parentAssembly}}} : Returns the parent assembly node that contains this reference
*{{{parentFileName}}} : Returns the name of parent file associated with this reference.
*{{{parentReference}}} : Returns the reference node associated with the parent reference.

By default, ~PyNodes have a {{{rename}}} method that is pretty straight forward:
{{{
import pymel.core as pm

node = pm.PyNode("null1")
node.rename("awesomeNull")
}}}
You can also easily rename them with a namespace, or swap namespaces if needed:  Presuming namespace "foo" alreadyexists:
{{{
# This returns a string, doesn't actually do any renaming yet:
newName = node.swapNamespace("foo")
node.rename(newName)
}}}
http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Transform.html
* {{{attr}}} : when checkShape is enabled, if the attribute does not exist the transform but does on the shape, then the shape’s attribute will be returned.
* {{{centerPivots}}} : Set pivot points to the center of the object’s bounding box
* {{{clearRestPosition}}} : Clear the saved rest position of this transform
* {{{deletePriorHistory}}} : If true then delete the construction history before the operation is performed
* {{{getBoundingBox}}} : xform -boundingBox and xform -boundingBoxInvisible
* {{{getBoundingBoxInvisible}}} : Returns the bounding box of an object. This includes the bounding boxes of all invisible children which are not included using the boundingBox flag
* {{{getBoundingBoxMax}}}
* {{{getBoundingBoxMin}}}
* {{{getLimit}}} : Determine the current value of the specified limit
* {{{getMatrix}}} : Sets/returns the composite transformation matrix
* {{{getPivots}}} : convenience method that changes both the rotate and scale pivots simultaneously
* {{{getRestPosition}}} : Retrieve the saved rest position of this transform
* {{{getRotateAxis}}} : rotation axis orientation 
* {{{getRotatePivot}}} : Returns the pivot about which the rotation is applied.
* {{{getRotatePivotTranslation}}} : Return the rotate pivot translation in linear units.
* {{{getRotation}}} : Retrieve the rotation component of this transformation
* {{{getRotationOrder}}} : Returns the rotation order for the transform matrix
* {{{getScale}}} : Retrieve the scaling component of this transformation.
* {{{getScalePivot}}} : Returns the pivot around which the scale is applied
* {{{getScalePivotTranslation}}} : Returns the scale pivot translation in linear units.
* {{{getShape}}}
* {{{getShapes}}} 
* {{{getShear}}} : Retrieve the shearing component of this transformation
* {{{getTransformation}}} : Retrieve the transformation matrix represented by this transform.
* {{{getTranslation}}} : Retrieve the translation component of this transformation in linear units
* {{{isLimited}}} : Determine if the specified limit attribute is enabled or disabled
* {{{preserveUV}}} : When true, UV values on rotated components are projected across the rotation in 3d space
* {{{reflection}}} : To move the corresponding symmetric components also.
* {{{reflectionAboutBBox}}} : Sets the position of the reflection axis at the geometry bounding box
* {{{reflectionAboutOrigin}}} : Sets the position of the reflection axis at the origin
* {{{reflectionAboutX}}} 
* {{{reflectionAboutY}}}
* {{{reflectionAboutZ}}}
* {{{reflectionTolerance}}} : Specifies the tolerance to findout the corresponding reflected components
* {{{resetFromRestPosition}}} : Reset the transform to its rest position.
* {{{rotateBy}}} : Relatively change the rotation of this transformation using a quaternion.
* {{{scaleBy}}} : Relatively scale this transformation.
* {{{setLimit}}} : Change the limit value for the specified limit type, and automatically enable the limit to be true
* {{{setLimited}}} : Enable or disable the limit value for the specified limit type.
* {{{setMatrix}}} : rotation axis orientation
* {{{setPivots}}} : convenience method that changes both the rotate and scale pivots simultaneously
* {{{setRestPosition}}} : Change the saved rest position of this transform. 
* {{{setRotateAxis}}} : rotation axis orientation
* {{{setRotatePivot}}} : Set the rotate pivot in linear units about which rotation is applied.
* {{{setRotatePivotTranslation}}} : Set the rotate pivot translation in linear units.
* {{{setRotation}}} : Change the rotation component of this transformation using a quaternion
* {{{setRotationOrder}}} : Change the rotation order for the transform
* {{{setScale}}} : Set the scaling component of this transformation.
* {{{setScalePivot}}} : Set the pivot around which the scale is applied in linear units.
* {{{setScalePivotTranslation}}} : Set the scale pivot translation in linear units.
* {{{setShear}}} : Set the shearing component of this transformation.
* {{{setTransformation}}} : Change this transform to equal the given matrix.
* {{{setTranslation}}} : Change the translation component of this transformation.
* {{{shearBy}}} : Relatively shear this transformation.
* {{{translateBy}}} : Relatively change the translation component of this transformation.
* {{{ungroup}}} 
* {{{zeroTransformPivots}}} : reset pivot points and pivot translations without changing the overall matrix by applying these values into the translation channel
The same way you can wrapper the string name of a node with a {{{PyNode}}} instance, you can do the same with a {{{PyUI}}} instance:
{{{
// Mel:
window -title awesome awesomeWin;
columnLayout awesomeCol;
button -label awesomeButton awesomeBut;
showWindow;
}}}
{{{
# PyMel
import pymel.core as pm

win = pm.ui.PyUI("awesomeWin")
col = pm.ui.PyUI("awesomeCol")
but = pm.ui.PyUI("awesomeBut")

print `win`
print `col`
print `but`

ui.Window('awesomeWin')
ui.ColumnLayout('awesomeWin|awesomeCol')
ui.Button('awesomeWin|awesomeCol|awesomeBut')
}}}
All ~PyMel UI types inheirt from one of the below classes.  
{{{pymel.core.uitypes}}}
*[[PyUI|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.uitypes/pymel.core.uitypes.PyUI.html]]
**Methods:
***{{{asQtObject}}}, {{{delete}}}, {{{getParent}}}, {{{name}}}, {{{parent}}}, {{{rename}}}, {{{shortName}}}, {{{type}}}, {{{window}}}
**Class Methods:
***{{{exists}}}
**Inheritance:
***[[Layout|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.uitypes/pymel.core.uitypes.Layout.html]]
****Methods:
*****{{{addChild}}}, {{{asQtObject}}}, {{{children}}}, {{{clear}}}, {{{findChild}}}, {{{getChildren}}}, {{{makeDefault}}}, {{{pop}}}, {{{walkChildren}}}
***[[Panel|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.uitypes/pymel.core.uitypes.Panel.html]]
****Methods
*****{{{copy}}}, {{{getControl}}}, {{{getDockTag}}}, {{{getIsUnique}}}, {{{getLabel}}}, {{{getMenuBarVisible}}}, {{{getNeedsInit}}}, {{{getPopulateMenuProcedure}}}, {{{getTearOff}}}, {{{init}}}, {{{replacePanel}}}, {{{setDocTag}}}, {{{setLabel}}}, {{{setMenuBarVisible}}}, {{{setNeedsInit}}}, {{{setPopupMenuProcedure}}}, {{{setTearOff}}}, {{{unParent}}}.
The ~PyMel [[Mesh|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.nodetypes/pymel.core.nodetypes.Mesh.html]] class has a number of methods for interacting with ~UVs:
*{{{.assignUV}}} : Maps a texture coordinate to a specified vertex of a polygon.
*{{{.assignUVs}}} : This method maps all texture coordinates for the mesh.
*{{{.clearUVs}}} : This method clears out all texture coordinates for the mesh, and leaves behind an empty ~UVset.
*{{{.createUVSet}}} : Create a new empty uv set for this mesh.
*{{{.deleteUVSet}}} : Deletes a named uv set from the object.
*{{{.getAssignedUVs}}} : This method finds all texture coordinates for the mesh that have been mapped, and returns them in the same format as the assignUVs.
*{{{.getAssociatedUVSetTextures}}} : Get a list of texture nodes which are using a given uv set. 
*{{{.getCurrentUVSetName}}} : Get the name of the “current” uv set. The “current” uv set is the uv set which is used for uv operations when no uv set is explicitly specified.
*{{{.getFaceUVSetNames}}} : returns the list of UV sets mapped to a face
*{{{.getPolygonUV}}} : Get the value of the specified texture coordinate for a vertex in a polygon. 
*{{{.getPolygonUVid}}} : Get the id of the specified texture coordinate for a vertex in a polygon.
*{{{.getUV}}} : Get the value of the specified texture coordinate from this mesh’s uv list. 
*{{{.getUVAtPoint}}} : Find the point closet to the given point, and return the UV value at that point.
*{{{.getUVSetFamilyNames}}} : Get the names of all of the uv set families on this object. 
*{{{.getUVSetNames}}} : Get the names of all of the uv sets on this object.
*{{{.getUVSetsInFamily}}} : Get the names of the uv sets that belong to this set family. 
*{{{.getUVs}}} : This method copies the texture coordinate list for this mesh into the given uv arrays.
*{{{.getUvShellsIds}}} : Constructs an array of unique integer for each UV shell. 
*{{{.isUVSetPerInstance}}} : Return true if this set is per-instance, and false if it is shared across all instances. 
*{{{.numUVSets}}} : Returns the number of uv sets for an object.
*{{{.numUVs}}} : Returns the number of texture (uv) coordinates for this mesh. 
*{{{.renameUVSet}}} : Renames a uv set from one name to another for this mesh.
*{{{.setCurrentUVSetName}}} : Set the “current” uv set for this object. 
*{{{.setSomeUVs}}} : Sets the specified texture coordinates (UV’s) for this mesh. 
*{{{.setUV}}} : Sets the specified texture coordinate.
*{{{.setUVs}}} : Sets all of the texture coordinates (uv’s) for this mesh. 
Even though UVs are attributes, you can't use {{{getAttr}}} and {{{setAttr}}} on them.  Instead:
{{{
myUv = "someMeshShape.map[2]"

# Get the UV val:
uvVal = pm.polyEditUV(myUv , query=True, uValue=True) # This returns a list of both U and V vals

# set the UV val.  Note here, you have to call to each value separately:
pm.polyEditUV(uvVal , uValue=.5, relative=False)
pm.polyEditUV(uvVal , vValue=.5, relative=False)
}}}
If you select a bunch of UV's, ~PyMel will tell you have [[MeshUV|http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.general/pymel.core.general.MeshUV.html]]s instances picked, but the docs on that class are empty.

If you want to convert from a {{{MeshUV}}} to a {{{MeshVertex}}}, the only current way I've found is via {{{polyListComponentConversion}}}:  However the ~PyMel wrapper returns strings, rather than ~PyNodes, so you need to wrapper it:
{{{
# select a UV:
myUv = pm.ls(selection=True)  # MeshUV
theVert = pm.PyNode(pm.polyListComponentConversion(myUv[0], toVertex=True)[0]) # MeshVertex
}}}
Modified example code from Mason Sheffield:
{{{
import pymel.core as pm

mesh = pm.ls(selection=True)[0]
print "Mesh:", mesh
print "\tNum uv sets:", mesh.numUVSets()
uvSets = mesh.getUVSetNames()
for uvSet in uvSets:
    print "\t", uvSet,
    mesh.setCurrentUVSetName(uvSet)
    print ": UVs :", mesh.numUVs()
}}}
{{{
Mesh: myAwesomeMesh
	Num uv sets: 2
	map1 : UVs : 204
	uvSet1 : UVs : 204
}}}
----
Also see:
*[[PyMel : Mesh access]]
So, it looks like the {{{scriptTable}}} is broken in Maya 2016 ~PyMel.  Here's a hack around it:  Make a regular maya.cmds wrapper for it, when created.
{{{
def scriptTable(*args, **kwargs):
    """
    PyMel errors when trying to execute the cellChangedCmd callback, but Python
    commands work, so we have to wrap our scriptTable calls in this hack.
    """
    import maya.cmds as mc
    return mc.scriptTable(*args, **kwargs)
}}}
There's enough complexities with {{{enum}}} attrs they deserve their own section.
Code used in the next sessions:
{{{
node = pm.PyNode("null1")
attrName = "myEnum"
enumVals = ["one", "two", "three", "four"]
}}}
!!!Add an enum attr:
Note you ''have to pass in some default value to the {{{enumName}}} arg'', or later calls will fail.
{{{
pm.addAttr(node, longName=attrName, attributeType="enum", keyable=True, enumName="null")
}}}
!!!Query enum attr values:
This query returns an //unordered// ~PyMel [[EnumDict|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.util.enum/pymel.util.enum.EnumDict.html?highlight=enumdict]]
{{{
currentVals = node.myEnum.getEnums() 
}}}
This query returns an //ordered list//:
{{{
# attributeQuery returns ["one:two:three:four"]
strVals = pm.attributeQuery(attrName , node=node, listEnum=True)[0]
listVals = strVals.split(":")
}}}
!!!Replace the values of the enum attr.
To append, you'd first need to query what was there, and add to it.  You must make a string of values separated by colons:
{{{"one:two:three:four"}}} : 
{{{
enumStr = ":".join(enumVals)
node.myEnum.setEnums(enumStr)
}}}
!!!Query the keyframed values of an enum attr:
This creates a list of (frame, enumName) pairs:
{{{
keyTimes = pm.keyframe(node.attr(attrName), query=True, timeChange=True)
keyVals = [int(val) for val in pm.keyframe(node.attr(attrName), query=True, valueChange=True)]

strEnumVals = pm.attributeQuery(attrName, node=node, listEnum=True)[0]
listEnumVals = strEnumVals.split(":")
keyedEnumVals = [listEnumVals[i] for i in keyVals]

timeEnumPairs = zip(keyTimes,keyedEnumVals)
print timeEnumPairs
# [(1.0, u"one"), (15.0, u"four"), (35.0, u"two")]
}}}
This returns the enum's string value at the current frame:
{{{
pm.getAttr(node.attr(attrName), asString=True)
# two
}}}
https://github.com/theodox/nodule
From the githubs:
<<<
Pythonic property access to maya nodes; aka "Pymel without the pymel".
Nodule offers a much more limited feature set, but in a lighter and faster package.
Nodule is a single-file python module. Place it anywhere on your Maya's python path.
<<<
Say you've made a snazzy window in Qt Designer:  After you've created it, you want to access its internals in Maya, and muck with them.  Some Qt widgets, like {{{QPushButton}}} have a direct relation to some Maya control, like {{{button}}}.  But others don't, like say, the {{{QSpinBox}}} (see [[Using Qt Designer]]).

If there is a 1:1 correspondence, you can query the Qt widget directly via the corresponding Maya command.  For example, if you've made a {{{QPushButton}}} named "myPushButton" in Qt Designer (by setting the {{{QObject}}}'s '{{{objectName}}}' property to a valid string), you can then later directly access it in Maya:
{{{
import pymel.core as pm
print pm.button("myPushButton", query=True, command=True)
}}}
But what if there is no correspondence, like in the case of a {{{QSpinBox}}}?  As long as you have given it 's {{{MObject}}} a valid '{{{objectName}}}' in Qt Designer, you can access it directly via ~PySide in Maya: 
{{{
import maya.OpenMayaUI as omui
from shiboken import wrapInstance
from PySide.QtGui import *

def getQtType(strName, qtType):
    """
    strName : The string assigned to the QObject's 'objectName' property.
    qtType : some Q* class : Like QSpinBox, QPushButton, etc.
    
    return : The instance define by qtType.
    """
    ptr = omui.MQtUtil.findControl(strName)
    return wrapInstance(long(ptr), qtType)
}}}
{{{
spinBox =  getQtType("mySpinBox", QSpinBox)
print spinBox
# <PySide.QtGui.QSpinBox object at 0x000000004597F788>
}}}
Once you have that instance, you can start hooking up signals\slots, or do any other work you need to have it interact with Maya.
----
Here is a //very simple// example using the above ideas:  The user has authored some elaborate ui in Qt Designer.  They've added a {{{QSpinBox}}} for which there is no Maya equivalent, yet want access to the widget in Maya.  Note at the bottom of the code example the window instance is captured in a variable.  This is ''very important'', see the notes below.
{{{
import pymel.core as pm
import maya.OpenMayaUI as omui
from shiboken import wrapInstance
from PySide.QtGui import QSpinBox

class MyQtWin(object):

    uiFile = "C:/Users/epavey/Documents/maya/python/test02.ui"
    # This is the 'objectName' of the QDialog:
    dialogName = "MyDialog"

    def __init__(self):
        if pm.window(MyQtWin.dialogName, query=True, exists=True):
            pm.deleteUI(MyQtWin.dialogName)
        pm.loadUI(uiFile=MyQtWin.uiFile)

        # Get access to the Qt widget:
        ptr = omui.MQtUtil.findControl("spinBox")
        self.spinBox = wrapInstance(long(ptr), QSpinBox)
        # Connect signal to slot:
        self.spinBox.valueChanged.connect(self.spinBoxCmd)

        # Show window, and make sure the title-bar isn't off-screen:
        pm.showWindow(MyQtWin.dialogName)
        pm.window(MyQtWin.dialogName, edit=True, topEdge=32)
        pm.window(MyQtWin.dialogName, edit=True, leftEdge=32)

    def spinBoxCmd(self, val):
        print self.spinBox, val

# Very Important!  Must capture this instance, or Python will
# garbage collect, and the signal/slot won't work.
qtWin = MyQtWin()
}}}
----
Also see:
*[[Loading Qt .ui files]]
*[[PySide : Convert a Maya control into a widget]]
If you want to add a Maya ui control to a ~PySide Layout\Widget, you first need to get the 'Maya window hierarchy path' to that widget, so you can use the {{{setParent}}} command.  The important key is that you need to assign a string name to each widget\layout, so Maya can later find it.  Here's how to get that path:
{{{
import pymel.core as pm
from PySide.QtGui import QVboxLayout, QDialog
from shiboken import wrapInstance, getCppPointer

class App(QDialog):

    # Other methods go here to build the window...

    def makeSomeLayout():

        # Create some main layout to attach to our PySide window:
        self.mainLayout = QVBoxLayout(self)
        # Need to give it a string name so it can be referenced by maya ui path.  Important!
        self.mainLayout.setObjectName("myAwesomePySideLayoutThingy")
        # Get the 'Maya string path' of the PySide layout: shiboken's getCppPointer function
        # is used to get a pointer to that object, which MQUtil reverse engineers into a
        # valid Maya ui path:
        mayaMainLayout = omui.MQtUtil.fullName( long(getCppPointer(self.mainLayout)[0]) )
        # Now we can use the regular maya command to set the parent, before our Maya
        # layout is created:
        pm.setParent(mayaMainLayout)

        # Create a Maya paneLayout that will be a child.  It will end up
        # later getting added as a widget to the pyside main layout.
        mayaPaneLayout = pm.paneLayout()
        # Find a pointer to the paneLayout that just created
        pl_ptr = omui.MQtUtil.findControl(mayaPaneLayout)
        # Wrap the pointer into a PySide QWidget:
        self.paneLayout = wrapInstance(long(pl_ptr), QWidget)

        # Here, other child Maya controls could be added to the layout.

        # Finally add our QWidget reference of the paneLayout to our main layout:
        self.mainLayout.addWidget(self.paneLayout)
}}}
I learned this based on this great post:
http://justinfx.com/2011/11/20/mixing-pyqt4-widgets-and-maya-ui-objects/
How can you add a ~QWidget to a Maya control?  See docs on {{{OpenMayaUI.MQtUtil.addWidgetToMayaLayout}}}
http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__cpp_ref_class_m_qt_util_html 
Also see:
*[[PySide : Convert a Maya control into a widget]]
*[[PySide : Add Maya controls to PySide layout\widget]]
https://www.w3schools.com/cssref/
{{{
def changeMayaMenuColors(fontStyle='italic', fontWeight='bold', fontColor='cyan'):
    ....
    widget.setStyleSheet(
        'font-style:%s;'%fontStyle +
        'font-weight:%s;'%fontWeight +
        'color:%s;'%fontColor
        )
}}}
And this for changing widget background and text colors.  A variety of ways to set them:
{{{
widget.setStyleSheet('background-color:rgb(200,200,64); color:black')
}}}
From:
https://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__files_GUID_3F96AF53_A47E_4351_A86A_396E7BFD6665_htm
* https://amorten.com/blog/2017/maya-pyside-cheatsheet/
Really good blog post here, by [[Nathan Horne|http://nathanhorne.com/?p=381]]
I made my own version below
{{{
import maya.OpenMayaUI as omui
from PySide.QtGui import QWidget, QAction
from PySide.QtCore import QObject
from shiboken import wrapInstance

def toPySideObj(mayaName):
    '''
    For the passed in string name of a Maya window, control, or layout, return
    QWidget.  For a menuItem, return QAction.  Otherwise return None.
    http://download.autodesk.com/us/maya/2011help/API/class_m_qt_util.html
    '''
    ptr = omui.MQtUtil.findWindow(mayaName)
    if ptr:
        return wrapInstance(long(ptr), QWidget)

    ptr = omui.MQtUtil.findControl(mayaName)
    if ptr:
        return wrapInstance(long(ptr), QWidget)

    ptr = omui.MQtUtil.findLayout(mayaName)
    if ptr:
        return wrapInstance(long(ptr), QWidget)

    ptr = omui.MQtUtil.findMenuItem(mayaName)
    if ptr:
        return wrapInstance(long(ptr), QAction)

    return None
}}}
Here's another example, showing how you can get a ~QWidget for Maya's 'timeline':
{{{
# Modified snippet pulled from here:
# http://dannywynne.com/scripts/timeline_markers.zip

import maya.OpenMayaUI as omui
from shiboken import wrapInstance

ptr = omui.MQtUtil.mainWindow() # QtGui.QWidget pointer to the QtGui.QMainWindow
maya_window = wrapInstance(long(ptr), QtCore.QObject) # Returns QtCore.QObect
maya_timeline = maya_window.findChild(QtGui.QWidget, "timeControl1")
}}}
Here is another example:
*http://www.justinfx.com/2011/11/20/mixing-pyqt4-widgets-and-maya-ui-objects/
----
Also see:
*[[PySide : Access Qt .ui widget data in Maya]]
From an answer in [[this thread|https://groups.google.com/forum/#!topic/python_inside_maya/q245Vh6W7uI]] by Justin Israel:
{{{
from PySide import QtGui

# Print out the title of all non-hidden top-level windows
tops = QtGui.qApp.topLevelWidgets()
for top in tops:
    if top.isWindow() and not top.isHidden():
        print top.windowTitle()
}}}
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|http://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2015/ENU/Maya-SDK/files/GUID-66ADA1FF-3E0F-469C-84C7-74CEB36D42EC-htm.html]]
Maya has its own set of callbacks (via scriptjobs & the API), but now that modern Maya's backend is written in Qt, you can use it's event handlers as well.

Here's a cool example I found in this [[Google Groups Thread|https://groups.google.com/forum/?fromgroups#!topic/python_inside_maya/NP56SrmivRg]] :  In this example, any time Maya looses focus (you switch to another app), when Maya regains focus, it puts the focus back in the Script Editor
{{{
# Code by Marcus Ottosson

from PySide import QtCore, QtGui

class RestoreScriptEditorFocus(QtCore.QObject):
    def eventFilter(self, obj, event):
        if event.type() != QtCore.QEvent.ApplicationActivate:
            return super(RestoreScriptEditorFocus, self).eventFilter(obj, event)

        script_editor = next(w for w in QtGui.qApp.topLevelWidgets() if w.objectName() == "scriptEditorPanel1Window")
        script_editor.activateWindow()
        return True

f = RestoreScriptEditorFocus()
QtGui.qApp.installEventFilter(f)
}}}
You could embed this in your {{{userSetup.py}}} for example, so it executes every time Maya starts.
Maya has its {{{lsUI}}} command that's great for searching for //Maya authored// (via Python or mel) UI controls, layouts etc.  And even though Maya's whole backend is now in Qt, it still works.
I've found however, that ui's authoed via ~QtDesigner, and loaded with the {{{loadUI}}} command aren't seen by {{{lsUI}}}.  So the question is, how can you find them in a similar way?

The below code example shows how to get a wrapper around the ~QWidget defining Maya's main window, and then using ~PySide to search through its children.

{{{
import maya.OpenMayaUI as omui
from PySide import QtGui, QtCore
from shiboken import wrapInstance

def getMayaMainWin():
    ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(ptr), QtGui.QWidget)

mayaWin = getMayaMainWin()

# Let's look for windows made via .ui files:
childWin = mayaWin.findChildren(QtGui.QMainWindow) + mayaWin.findChildren(QtGui.QDialog)
print len(childWin)
for cw in childWin:
    print cw
    print "\t objectName:", cw.objectName()
}}}
----
Also see:
* [[Find a PySide widget by string name]]
Just a collection of links right now.
* https://fredrikaverpil.github.io/2016/07/25/dealing-with-maya-2017-and-pyside2/
* http://blog.virtualmethodstudio.com/2017/03/embed-maya-native-ui-objects-in-pyside2/
Docs
* https://wiki.qt.io/PySide2
* http://pyqt.sourceforge.net/Docs/PyQt5/
*[[Understanding how PyQt, Python, and C++ play together|http://enki-editor.org/2014/08/23/Pyqt_mem_mgmt.html]] : great info.
----
Class lists per module:
* {{{QtCore}}} : http://doc.qt.io/qt-5/qtcore-module.html
* {{{QtGui}}} : http://doc.qt.io/qt-5/qtgui-module.html
* {{{QtWidgets}}} : http://doc.qt.io/qt-5/qtwidgets-module.html
Classes moved from {{{PySide.QtGui}}} to {{{PySide2.QtWidgets}}} :
* {{{QWidget}}}
* {{{QMainWindow}}}
* {{{QDialog}}}
* {{{QAbstractButton}}}
* many more...
----
{{{shiboken}}} is now {{{shiboken2}}}
{{{pysideuic}}} is now {{{pyside2uic}}}
----
Example of how I'm authoring code to be backwards compatible:
{{{
import maya.cmds as mc
MAYA_VER = int(mc.about(version=True))
if MAYA_VER <= 2016:
    from PySide.QtGui import QWidget, QMainWindow, QDialog, QAbstractButton, QIcon
    from shiboken import wrapInstance
    import pysideuic
else:
    from PySide2.QtGui import QIcon
    from PySide2.QtWidgets import QWidget, QMainWindow, QDialog, QAbstractButton
    from shiboken2 import wrapInstance
    import pyside2uic as pysideuic
}}}
http://doc.qt.io/qt-5/gallery.html
Unconfirmed:  Does Maya follow the 'VFX Reference Platform'?
http://vfxplatform.com/
| Maya | [[Python|http://python.org/]] | [[Qt|http://qt.nokia.com/products/]] | Compatible [[PyQt|http://www.riverbankcomputing.com/software/pyqt/download]] | Compatible [[PySide|http://qt-project.org/wiki/PySide]] | Win Compiler | Linux Compiler | Mac Compiler |
| | | | | | | | |
| 8.5 | 2.4.3 | | | | | | |
| 2008 | 2.5.1 | | | | 32bit & 64bit vc8.0 + ~SP1 + qtHotfix, Intel 9.1.034  |  32bit & 64bit, gcc 4.1.2, Intel 9.1.039 | 32bit ~XCode 2.4.1, gcc 4.0.1, Intel 9.1.037* |
| 2009 | 2.5.1 | | | | 32bit XP ~SP2 & 64bit  XP x64 ~SP2, vc8.0+~SP1+qtHotfix, Intel 10.1.013 | 64bit ~RHEL4.4, gcc 4.1.2 Intel 9.1.039 | 32bit Tiger 10.4.11, ~XCode 2.4.1, gcc 4.0.1, Intel 10.1.007* |
| 2010 | 2.6.1 | | | | 32bit Xp ~SP2 & 64bit Xp x64 ~SP2, Visual Studio 2008 ~SP1, Intel 10.1.022 | 64bit ~RHEL5.1 gcc 4.1.2, Intel 11.0.083 | 32bit & 64bit Leopard 10.5.6, ~XCode 3.0, gcc 4.0.1, Intel 11.0.064  |
| 2011 | 2.6.4 | 4.5.3 (it begins...) | ? | ? | 32bit Xp ~SP2 & 64bit Xpx64 ~SP2, Visual Studio 2008 ~SP1, Intel 11.1.051 | 64bit    ~RHEL5.1, gcc 4.1.2, Intel 11.1.059  | 32bit & 64bit Leopard 10.5.x, ~XCode 3.1.2, gcc 4.0.1, Intel 11.1.076 |
| 2012 | 2.6.4 | 4.7.1 | ? | ? | 32bit XP ~SP2 & 64bit ~XPx64 ~SP2, Visual Studio 2008 ~SP1 + ATL security update, Intel 11.1.067 | 64bit ~RHEL5.1, gcc 4.1.2, Intel 11.1.073 | 64bit Snow Leopard 10.6.4, ~XCode 3.2.1, gcc 4.2.1, Intel 11.1.089 |
| 2013 | 2.6.4 | 4.7.1 | 4.9.1 | ? | 32bit Win7 & 64bit Win7x64, Visual Studio 2010 (MSC v.1600) ~SP1, Intel 12.0.4.196  | 64bit RHEL 6.0, ~FC14              gcc 4.1.2, Intel 11.1.073 | 64bit ~SnowLeopard 10.6.8, Xcode3.2.1 gcc 4.2.1, Intel 11.1.089 |
| 2014 | 2.7.3 | 4.8.2 | 4.10  | 1.1.2 - Ships with Maya! | 64bit Win7x64, Visual Studio 2010 (MSC v.1600) ~SP1, Intel Composer XE 2011  | 64bit RHEL 6.0, ~FC14, gcc 4.1.2, Intel Composer XE 2011 Update 11 | 64bit Lion 10.7.4, Xcode 4.3.3 with SDK 10.6 (~SnowLeopard), gcc 4.2.1, Intel Composer XE 2011 Update 11  |
| 2015 | 2.7.3 | 4.8.5 |  ? | 1.2 | 64bit Win7x64, Visual Studio 2012 Update 4 (MSC v.1700), Intel Composer XE 2013 Update 5 (13.1.3.198) |  64bit RHEL/~CentOS 6.2, ~FC14, gcc 4.1.2, Intel Composer XE 2013 ~SP1 Update 1 (14.0.1.106) | 64bit Mountain Lion 10.8.5, Xcode 5.0.2 with SDK 10.8 (Mountain Lion), clang with libstdc++, Intel Composer XE 2013 ~SP1 Update 1 (14.0.1.103)  |
| 2016 | 2.7.6 | 4.8.6 | ? | 1.2 | Windows 64bit Win7x64, Visual Studio 2012 Update 4 (MSC v.1700) + Win 8 SDK, .Net framework 4.5.1, Intel Composer XE 2015 Initial release (15.0.0.108) | Linux 64bit RHEL/~CentOS 6.5, ~FC20, gcc 4.8.2, Intel Composer XE 2015 Update 1 (15.0.1.133) | Mac 64bit Mavericks 10.9.5, Xcode 6.1 or 6.1.1 with SDK 10.9 (Mavericks), clang with libstdc++, Intel Composer XE 2015 Update 1 (15.0.1.108) |
| 2017 | 2.7.11 | 5.6.1 | "~PyQt5" | ~PySide2 | Windows 64bit Win7x64, Visual Studio 2012 Update 4 (MSC v.1700) + Win 8 SDK, .Net framework 4.5.1, Intel Composer XE 2015 Initial release (15.0.0.108) | Linux 64bit RHEL/~CentOS 6.5, ~FC20, gcc 4.8.2, Intel Composer XE 2015 Update 1 (15.0.1.133) | Mac 64bit Mavericks 10.9.5, Xcode 6.1 or 6.1.1 with SDK 10.9 (Mavericks), clang with libc++, Intel Composer XE 2015 Update 1 (15.0.1.108) |
| 2018 | 2.7.11 | 5.6.1 | "~PyQt5" | ~PySide2  | Visual Studio 2015 Update 3 (MSC v.1900) [[notes|http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=__files_Setting_up_your_build_env_Windows_env_32bit_and_64bit_htm]] | ? | Xcode 7.3.1 SDK 10.11 |
| 2019 | 2.7.11 | 5.6.1 | "~PyQt5" | ~PySide2 | Windows 7 x64 - Visual Studio 2015 Update 3 (MSC v.1900) - Windows SDK Version 10.0.10586.0 | ~CentOS/RHEL 7.x(x>=2) - gcc 6.3.1(DTS 6.1) | El Capitan 10.11.6 - ~XCode 7.3.1 |
| 2020 | 2.7.11 | 5.6.1 (?)  | "~PyQt5" | ~PySide2  v5.12.5 | Windows 10 x64 - Visual Studio 2017 - Windows SDK Version 10.0.10586.0 | ~CentOS/RHEL 7.3(x>=3)	gcc 6.3.1(DTS 6.1) | Mojave 10.14.- ~XCode 10.2.1 |
| 2022 | 3.7.7 | ?  | "~PyQt5" | ~PySide2  v5.15.2 | ? | ? | ? |

Starting in 2011, Maya's entire ui back-end was converted to Qt.  You still use the same Maya commands to author UI's, but behind the scenes Qt is appearing on your screen.  
Starting in 2017, Maya migrated to Qt5 / ~PyQt5 / ~PySide2, which broke a bunch of backwards compatibility.  See notes [[here|https://fredrikaverpil.github.io/2016/07/25/dealing-with-maya-2017-and-pyside2/]] on this.

This is not to be confused with //~PyQt// or ~PySide, which are //Python// (not Maya) implementations of Qt.  You can also get //~PyQt// & //~PySide// working in Maya, if you jump through [[some hoops|Installing PyQt]], if using versions before 2014:  Starting in 2014, ~PySide started shipping with Maya:  No more needing to compile ~PyQt.
----
Also see:
*[[Maya compiler versions]]
You can run Python threads in Maya.  The key is via Maya's {{{maya.utils.executeInMainThreadWithResult}}} function, which is illustrated below.  This allows you to run code that won't 'hang' Maya while its executing.

The below example came from the "Practical Applications of Maya Python in Game Production" GDC 2010 Masterclass by Adam Mechtley & Ryan Trowbridge.
*http://area.autodesk.com/gdc/class2  {{{<--}}} Masterclass video
*http://209.216.55.247/~www/gdc10/python/Python_downloadable_materials_March1.zip  {{{<--}}} Source Files
*http://docs.python.org/library/thread.html  {{{<--}}} Python {{{thread}}} docs.
*[[Maya threading docs|http://download.autodesk.com/us/maya/2010help/index.html?url=Python_Python_and_threading.htm,topicNumber=d0e182779]]
I've actually modified it quite a bit so it makes more sense to my brain:
{{{
import thread
import maya.utils as utils
from functools import partial
import os

def myFunc(path):
    """
    Some function to execute.
    """
    # Get a list of files to pas to myFunc():
    fileList = os.listdir( path )
    for file in fileList:
        print file

def myFuncThreadWrapper(path):
    """
    Define a function that wrappers myFunc() so it can
    be ran in a Python thread.
    """
    # Wrapper the myFunc() function in functools.partial(),
    # which allows it to be passed to Maya's threadding function:
    myfunc = partial(myFunc, path)
    # Execute our wrapped function in Maya's main thread:
    utils.executeInMainThreadWithResult(myfunc)

path = "C:/Program Files/Autodesk/Maya2010"
# Run the function in the thread.  Args are
#('function', ('tuple of', 'function args') )
thread.start_new_thread(myFuncThreadWrapper, (path,))
}}}
----
Notes from their presentation:
*Maya does not allow for multiple Python thread.  So there is no advantage of running multiple simultaneous threads, since Maya will execute them in order.
*Executing any Maya commands \ API call is not allowed in a Python thread.  If you need to, use the {{{maya.utils}}} function {{{executeInMainThreadWithResults}}}.
*{{{executeInMainThreadWithResults}}} calls a function that is executed in the main Maya thread.
*Calling the function {{{executeInMainThreadWithResults}}} more than one at a time will crash Maya.
*Use a Python {{{functools.partial}}} or {{{lambda}}} function to pass the callable function with arguments to {{{executeInMainThreadWithResults}}}.
For Qt itself, look here:
*[[Main Reference Documentation|http://doc.qt.nokia.com/4.8/index.html]]
**[[All Classes|http://doc.qt.nokia.com/4.8/classes.html]]
**[[All Functions|http://doc.qt.nokia.com/4.8/functions.html]]
**[[All Modules|http://doc.qt.nokia.com/4.8/modules.html]]
*[[Qt 4.8 Widget Gallery|http://qt-project.org/doc/qt-4.8/gallery.html]]
*[[Qt 4.8 Widgets and layouts|http://qt-project.org/doc/qt-4.8/widgets-and-layouts.html]]
----
Other tutorials from the web:

~PySide docs for Maya:
* Personal notes over my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#PYSIDE]]
* Good over view by Chris Zurbrigg : http://zurbrigg.com/maya-python/category/pyside-for-maya
*~PySide example code on [[Github|https://qt.gitorious.org/pyside/pyside-examples]]

~PyQt docs for Maya:
*[[Understanding how PyQt, Python, and C++ play together|http://enki-editor.org/2014/08/23/Pyqt_mem_mgmt.html]] : great info.
*[[Nathan Horne : Guide to PyQt/PySide in Maya (Pt. 1)|http://nathanhorne.com/?p=451]]
*[[Vimeo : PyQt GUI for Maya|http://vimeo.com/40380911]]
**Source Code in [[GitHub|https://github.com/drumboy354/MayaNodeInterface]]
*Example of a slick ~PyQt ui system in Maya build by Martin Tomoya for Aardman's Pirates movie :[[Images|http://www.martintomoya.com/tools_info.html]], [[Video|http://www.martintomoya.com/tools_reel.html]]
*[[Chris Zurbrigg|http://zurbrigg.com/]] has a series of video's showing how to make a 'renaming ui' in Maya via ~PyQt:
**[[Video 01a|http://vimeo.com/48091904]], [[Video 01b|http://vimeo.com/48098610]] ,[[Video 02a|http://vimeo.com/48099224]], [[Video 02b|http://vimeo.com/48099581]], [[Source Code|http://zurbrigg.com/component/ars/repository/tutorials/tutorials-release/pyqtrenamingsrc]]
*Jason Parks: [[PyQT in Maya – Custom Class inheritance|http://www.jason-parks.com/artoftech/?p=439]]
~PyQt specific:
*[[Zetcode : PyQt4 tutorial|http://zetcode.com/tutorials/pyqt4/]]
Maya 2014 & newer using ~PySide:
{{{
import maya.OpenMayaUI as omui

from PySide import QtGui
from shiboken import wrapInstance

def getMayaMainWin():
    ptr = omui.MQtUtil.mainWindow()
    return wrapInstance(long(ptr), QtGui.QWidget)
}}}

Older versions of Maya using Qt directly:
{{{
import maya.OpenMayaUI as omui
import sip
import PyQt4.QtCore as QtCore
def getMayaMainWin():
    """
    Return the Maya main window as a QtGui.QMainWindow instance
    """
    ptr = omui.MQtUtil.mainWindow() # QWidget pointer
    return sip.wrapinstance(long(ptr), QtCore.QObject)
}}}
This can be used to then parent your ~PyQt UI's to, so they won't 'pop' behind it when they loose focus.

A [[QMainWindow|http://qt-project.org/doc/qt-4.8/qdialog.html]] is //the main application window//, and Maya's main window is an instance of this class (since Maya 2011?).  When making your own ~PyQt windows, you usually instance [[QDialog|http://qt-project.org/doc/qt-4.8/qdialog.html]]'s, and then parent them to this main window.
----
Docs:
*[[sip|http://www.riverbankcomputing.co.uk/static/Docs/sip4/python_api.html]]
*[[QtGui.QMainWindow|http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qmainwindow.html]]
*[[QtGui.QWidget|http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qwidget.html]]
*[[QtCore.QObject|http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qobject.html]]
*[[OpenMayaUI.MQtUtil.mainWindow|http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/cpp_ref/class_m_qt_util.html#a78ad18c490dfcb3bae34cef238d0d6c6]]
Check out the docs for [[objectTypeUI|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/objectTypeUI.html]] for examples of how to print out the types.  From that example, a modified version:
{{{
import maya.cmds as mc
allTypes = mc.objectTypeUI(listAll=True)
for i in range(0, len(allTypes), 3):
    print allTypes[i], allTypes[i+1], allTypes[i+2]
}}}
Prints: "command name", "ui type", "class name" (Note that the class name is internal and is subject to change).
{{{
commandLine   commandLine   TcommandLine  
cmdShell   cmdShell   TcommandShell  
button   button   QPushButton  
canvas   canvas   QPushButton  
checkBox   checkBox   QmayaCheckBox  
etc...
}}}
! From Windows:
You can use the {{{where}}} command ({{{which}}} on Linux/Mac).
For example, you can give it a starting path to narrow the search, so it's not searching the whole HD:
{{{
> where /R "c:\Program Files\Autodesk" maya.exe
c:\Program Files\Autodesk\Maya2016\bin\maya.exe
c:\Program Files\Autodesk\Maya2016.5\bin\maya.exe
}}}
!From Maya:
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 // 
}}}
I always forget how to do this, as easy as it is:
{{{
> maya - help
}}}
FYI, this opens in an 'Output Window', not the shell you typed in.
[[Here's a link|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2017/ENU/Maya/files/GUID-2E5D1D43-DC3D-4CB2-9A35-757598220F22-htm.html]] to the list.
* -file [file] : Open the given file.
* -archive [file] : Displays a list of files required to archive the specified scene and then exits Maya.
* -command [mel command] : Run the *mel* on startup.
* - log [file] : Copies output and error messages (from the output window) to the file.
* -noAutoloadPlugins
* -optimizeRender [file] [outfile] : Processes the specified scene file to optimize it for rendering, puts the result in outfile and then exits. 
* -proj [dir] : Looks for scene files in the specified project directory.
* -recover : Recovers the last journal file.
* -script [file] : Sources the specified file (which is expected to be a MEL script) on startup.
* -v : Displays the product version and cut number, and then exits.
* -3 : Enable Python 3000 compatibility warnings.
''Update'': Some great blog posts by Chris Evans on this subject back in 2016:
* http://www.chrisevans3d.com/pub_blog/import-skin-weights-without-leaving-coffee/
* http://www.chrisevans3d.com/pub_blog/saveload-skinweights-125x-faster/
----
Maya has the option (since 201X?) to "Export Deformer Weights" (and import).  It saves these out as xml file, looking something like this:
{{{
<?xml version="1.0"?>
<deformerWeight>
  <headerInfo fileName="C:/temp/maya/myAwesomeWeightFile.xml" worldMatrix="1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 1.000000 "/>
  <shape name="awesomeMesh0Shape" group="907" stride="3" size="959" max="959">
    <point index="0" value=" -46.970287 -22.764431 118.459167"/>
    <point index="1" value=" -48.517704 -21.456564 117.263725"/>
    <point index="2" value=" -49.214066 -20.140640 116.087372"/>
    <point index="3" value=" -45.769356 -16.708261 111.223663"/>
    etc....
  </shape>
  <weights deformer="skinCluster3" source="awesomeJoint01" shape="awesomeMesh0ShapeShape" layer="0" defaultValue="0.000" size="185" max="957">
    <point index="0" value="0.943"/>
    <point index="1" value="0.936"/>
    <point index="2" value="0.915"/>
    <point index="3" value="0.825"/>
    etc...
}}}
@@IMPORTANT@@ : By default it only stores out 3 decimal places of precision (see above ^):  You can use the {{{weightPrecision=6}}} during export to save out all the data you need.

What if you want to get a list of all the influences in one of those files?  Python to the rescue:
{{{
import xml.dom.minidom
def getInfFromXml(xmlFile):
    infs = []
    mDom = xml.dom.minidom.parse(xmlFile)
    for i in mDom.getElementsByTagName("weights"):
        infs.append(i.getAttribute("source"))
    return infs
}}}
{{{
xmlFile = r"c:\temp\maya\myAwesomeWeightFile.xml"
print getInfFromXml(xmlFile)
# ["awesomeJoint01", "awesomeJoint02", etc..]
}}}
----
The initial mel code (called to from the UI) that writes out the weight data lives here:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\others\performExportDeformerWeights.mel
}}}
Which is a wrapper for this command:
{{{
deformerWeights -export -deformer "skinCluster3" -path "C:/temp/maya/" "AwesomeWeightFile.xml";
}}}
Docs for [[deformerWeights|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/deformerWeights.html]]
Starting in Maya 2011: [[movieInfo|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/movieInfo.html]]
Can query width, height, and frame-count
'Intermediate objects' are the 'pre-deformed shape nodes' Maya generates when you apply deformers, or other nodes with history to a polygonal mesh (or nurbs).  
You can visualize them by (for example):
* Selecting a skinned mesh and `Deform -> Display Intermediate Objects'.  They'll show up as green (unshaded mesh).
* Once displayed, in the Outliner:
** Check 'Shapes' to see the shape nodes.
** Expand the shapes for that mesh : You'll now see:
** myNode  <-- The parental transform.
*** myNodeShape <-- The output / consumer-facing mesh shape node.
*** myNodeShapeOrig <-- The intermediate object / pre-deformed mesh shape node.
Often times when coding, you'll want to query a hierarchy for mesh shape names, but //not// want to query the corresponding intermediate objects. 
You can test it by skinning a poly sphere to a single joint, and running the below code.

One would think this would do the trick:
{{{
import maya.cmds as mc
print(mc.listRelatives("pSphere1", allDescendents=True, type='mesh', noIntermediate=True))
['pSphereShape1', 'pSphereShape1Orig']
}}}
But.... it most definitely does include the intermediate objects.  This has been a bug forever (at least until 2022).

The fix is to move the {{{noIntermediate}}} arg to the {{{ls}}} command, where it appears to be respected:
{{{
print(mc.ls(mc.listRelatives("pSphere1", allDescendents=True, type='mesh'), noIntermediate=True))
print(childMesh)
['pSphereShape1']
}}}
----
Also see:
* [[How do 'zombie' intermediate objects get created?]]
Maya stores the Channel Box / Attribute Editor/ Tool Settings (etc) dock labels and control names in a global mel string array called {{{$gUIComponentDockControlArray}}}.  From that, you can query which one is currently visible (on top):
{{{
import pymel.core as pm
mayaDocks = pm.language.melGlobals["gUIComponentDockControlArray"]
for i in range(0, len(mayaDocks), 2):
    label = mayaDocks[i]
    ctrlName = mayaDocks[i+1]
    print label, ctrlName, pm.dockControl(ctrlName, query=True, r=True)
}}}
Which prints something like:
{{{
Channel Box / Layer Editor MayaWindow|dockControl1 True
Channel Box MayaWindow|dockControl2 False
Layer Editor MayaWindow|dockControl3 False
Attribute Editor MayaWindow|dockControl4 False
Tool Settings MayaWindow|dockControl5 False
}}}
Note that if they're floating, they'll return True.
[[dockContol|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/CommandsPython/dockControl.html]]s are great for making docked windows that can be torn off.  But it's not entirely easy to track then state they're in.  Here's one solution I've come up with for tracking if they're torn off (floating) or not, and to track when it's visible\hidden.  

When making your {{{dockControl}}}, pass in callback arguments to its {{{visibleChangeCommand}}} and {{{floatChangeCommand}}} parameters:
*{{{visibleChangeCommand}}} : This is executed whenever a dock is made visible \ invisible (different from delete).
*{{{floatChangeCommand}}} : This is executed whenever a dock is torn off (made to float) or docked.
Note that when you close a {{{dockControl}}} via the little {{{[x]}}} in the corner, you're really just hiding it:  You can RMB on the menubar of any other dock to get a popup menu of all available docs.
{{{
from functools import partial as callback
from pymel.core import pm

def visibleChangeCallback(*args):
    dockName = args[0]
    if pm.dockControl(dockName, query=True, isObscured=True):
        # Do stuff when dock is hidden
    else:
        # Do stuff when dock is visible

def dockFloatCallback(*args):
    dockName = args[0]
    if pm.dockControl(dockName, query=True, floating=True):
        # Do things when dock floats
    else:
        # Do things when dock is docked

awesomeLayout = # some layout already made, to be put in the dock...

dockName = "awesomeDock"
pm.dockControl(dockName, allowedArea=['left','right'], area='right',
               floating=False, content=awesomeLayout, label="Awesome Dock", visible=True,
               visibleChangeCommand=callback(visibleChangeCallback, dockName),
               floatChangeCommand=callback(dockFloatCallback, dockName))
}}}
The key to the visibility check is to query the {{{isObscured}}} flag, rather than the {{{visible}}} flag:  Visibility always seems to return True, even if it's hidden.

----
Also see:
*[[How can I make a window dockable?]]
I do this often, but I always seem to forget the syntax.

I want to load a {{{optionVar}}} preset that may or may not exist:
{{{
# Python code:
import maya.cmds as mc

value = mc.optionVar(query='someOptionVarName')
if value is not 0:
    # do work with value
}}}
If an {{{optionVar}}} doesn't exist, it returns zero.  So the above example works great, unless the value you're expecting //is// zero ;)
----
You can also test for existence first.  Which is fine, it's bust a bit more code:
{{{
# Python code:
import maya.cmds as mc

if mc.optionVar(exists='someOptionVarName'):
    value = mc.optionVar(query='someOptionVarName')
     # do work with value
}}}
Maya has had it's own for several versions, but it seems tightly coupled specifically with the blendshape workflow.
* RBF : Radial Basis Function : Super powered SDK (set driven key).
* PSD : Pose Space Deformer : Most commonly uses RBF's to drive blendshapes.
----
* https://github.com/chrisevans3d/poseWrangler
> "`~PoseWrangler is a simple UI to create and edit pose-driven relationships in Maya using the {{{MayaUE4RBF}}} plugin. This plugin is distributed by Epic Games and installed by Quixel Bridge, initially with ~MetaHuman source files."
----
Here's a pretty good one by Hans Godard that is much more generic (although needs a quite a bit of higher level wrapper code to make functional)
* https://vimeo.com/120117324
* https://apps.autodesk.com/MAYA/en/Detail/Index?id=3400459002389744225&appLang=en&os=Win32_64
----
Chad Vernon has released source for a c++ complied plugin here (I've yet to use it):
* https://github.com/chadmv/cmt/tree/master/src
* https://github.com/chadmv/cmt/blob/master/src/rbfNode.h
* https://github.com/chadmv/cmt/blob/master/src/rbfNode.cpp
Maya has no built-in (that I can find) serial libraries for intercepting external serial data.  Python does ([[pySerial|http://pyserial.sourceforge.net/]]), but as of this authoring date it's still compiled to 32-bit, and I'm running 64-bit Python.  However, you can fake this type of connection via the {{{commandPort}}} command, since the {{{commandPort}}} simply wrappers a network socket.  From Maya's docs:
>"The command port comprises a socket to which a client program may connect."  
I have this code scattered in other subjects on the wiki, but I wanted to update it for Python usage, and put it in one place.

These examples were done on 64-bit Windows7.
!Open a {{{commandPort}}} in Maya
{{{commandPort}}}s act like doorways (sockets) that allow external data to come into Maya.  I've found the best result by opening a couple of them.  Sometimes not doing that stops the data transmission from happening.  Not sure why, I'm not a network programmer.

In addition, you can pass the {{{commandPort}}} a //mel// global proc name that can intercept the string values and do what you want with them.  If you don't use this wrapper-proc, Maya will try to execute the passed in values directly.  In the below code, we author a mel wrapper that will be passed to the {{{commandPort}}}, and that wrapper code will in turn call to our Python function that can do whatever you want with the results:
{{{
# In Maya, via Python:
import maya.cmds as mc
import maya.mel as mm

# Our mel global proc.  
# To work with the terminal (PuTTY) example below, strip off return chars
# and encode with escape chars.  If just using the pure-python example,
# those extra two lines aren't needed.
melproc = """
global proc portData(string $arg){
    string $data = strip($arg);
    string $encoded = `encodeString $data`;
    python(("portData(\\"" + $encoded + "\\")"));
}
"""
mm.eval(melproc)

# Our Python function that can be changed to do whatever we want:
def portData(arg):
    """
    Read the 'serial' data passed in from the commandPort
    """
    print "Recieved: ", arg

# Open the commandPort.  The 'prefix' argument string is calling to the defined
# mel script above (which then calls to our Python function of the same name):
mc.commandPort(name="127.0.0.1:7777", echoOutput=False, noreturn=False, 
               prefix="portData", returnNumCommands=True)
mc.commandPort(name=":7777", echoOutput=False, noreturn=False, 
               prefix="portData", returnNumCommands=True)
}}}
Notes: 
*When I first authored this code (Maya 2010), I needed to open two {{{commandPort}}}s to make it work.  Working with Maya 2012, I only open one.  In fact, trying to execute both the bottom lines will cause an error for me now.
*{{{127.0.0.1}}} : This is the local address of your machine.  Also known as [[localHost|http://en.wikipedia.org/wiki/127.0.0.1]].
*{{{:7777}}} This port number can really be anything (I think).  Feel free to change.
*When passing in port-names like '{{{name="127.0.0.1:7777"}}}', you can actually leave off the local address: it will be presumed.  Meaning, you can just pass in the port number like '{{{:7777}}}`.
!Send Data To Maya:
!!!From Python
Launch an external Python shell external to Maya, and connect to Maya's open socket.  Be sure to use the same IP address and port numbers from above. 
{{{
c:\Users\akeric\ python
}}}
''{{{socket}}}'' Example:
For this example, you need to setup the {{{commandPort}}}s based on the above Maya code example.
{{{
>>> import socket
>>> maya = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> maya.connect(("127.0.0.1", 7777))
>>> maya.send('print "a message!"') # Send data to Maya
}}}
Based on the example, you should see Maya print:
{{{
Recieved:  a message!
}}}
From this point, you can embed the external Python code into any externally running Python application to send data to Maya, and you can update the {{{portData()}}} to parse that data however you see fit.
<<<
It's worth showing [[telnetlib|http://docs.python.org/library/telnetlib.html]] as well.  You can make it as complex as the above example, but on the other hand you can actually make it a lot more simple.
For this example, you can setup a {{{commandPort}}} in Maya as easy as this:
{{{
import maya.cmds as mc
mc.commandPort(name=':8999')  
}}}
Then, in Python (external to Maya):
{{{
>>> import telnetlib
>>> maya = telnetlib.Telnet("localhost", 8999)
>>> maya.write('print "a message!"')
}}}
Here, we're passing the //mel// {{{print}}} command over the socket.
<<<
!!!!Example Python Module:
Here is a simple Python module that will stream a series of numbers into Maya.  You can use this as an example to see how Maya intercepts the data (and prints in the Script Editor using the above code) while you continue to work:
{{{
# sendToMaya.py
import time
import socket
maya = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
maya.connect(("127.0.0.1", 7777))

for i in range(100):
    maya.send(str(i))
    time.sleep(.05)
maya.send("DONE!")
}}}
!!!From a terminal
Windows comes with {{{telnet}}}, but I've never got it to properly broadcast to Maya properly:  It seems to send a return character after each character you type, so you can't ever send more than one character at a time.  However, you can freely download [[PuTTY|http://www.chiark.greenend.org.uk/~sgtatham/putty/]] which is a great alternative.  I'm not too well versed in other terminal apps, but I presume they should be similar to the instructions below.
*Launch ~PuTTY. 
*Make sure you're in the "Session" Category.
*Change the "Connect Type" to "Telnet"
*In the "Host Name (or IP address)" field, type the localHost address from above, {{{127.0.0.1}}}
*In the "Port" field, type the port from above, {{{7777}}}
*Press the "Open" button at the bottom, which should open the {{{PuTTY}}} terminal window.
*Start issuing strings to the terminal.  When you press enter, they should be sent to Maya and print in the script editor (using the above example code).
The {{{sceneEditor}}} command makes an editor that shows the references in the scene.  It's the bottom part of the 'Reference Editor' UI.
The name of Maya's {{{sceneEditor}}} is stored in:
{{{
global string $gReferenceEditorPanel;
}}}
From the docs, make a new one:
{{{
# Python code
import maya.cmds as mc
window = mc.window()
mc.paneLayout()
mc.sceneEditor()
mc.showWindow(window)
}}}
We use proxy attributes in our rigging solution to provide 'instanced attrs' on our rig controllers, so that a single attr can be expressed on multiple nodes to define a node state, like IK/FK switching (see [[Working with proxy attrs]]).  Meaning, for an arm, a single 'ikFk' proxy attr exists on all the controllers (shoulder, elbow, wrist, for both ik and fk) to define their state.  It's very convenient.
My animation team ran into a bug with it however:
* If you have a proxy attr on a node (say, an anim controller)...
* And if you have reference that scene (the rig into an anim file)...
* and you keyframe that attr...
* If later you //delete the connection on that attr//, it becomes locked and no longer editable.
Some interesting discoveries:
* In the original (non referenced file) this behavior doesn't happen:  You can break the connection and the attr does not get locked.
* In the referenced file, if you *delete* the channel instead of break it, the behavior doesn't happen.
This has been logged as a bug with Autodesk as of Dec 2019.
This is an overview of the docs found [[here|http://download.autodesk.com/us/maya/2010help/index.html?url=WS73099cc142f48755f2fc9df120970276f7-2158.htm,topicNumber=d0e183276]] that explain how to turn a Python function into a mel procedure.

There are still certain commands that expect incoming //mel//, rather than Python.  For example, when calling to the [[commandPort]] command from Python, it has a {{{prefix}}} string parameter that expect a //mel procedure// to be passed in as an argument (rather than a Python function).   Or other things like marking menus, or in the container node the RMB Command attribute under the Context Properties section only accepts MEL procedures.

In the past I've actually made mel scripts as strings in Python and then evaluated them via {{{maya.mel.eval(someString)}}}, which works.  But I wanted to understand the below method as well.
!!!Make a Python module and a function:
For this system to work, it appears that the python function being called to must live in a module:  No execution via the interactive interpreter.  Below is a module that has two functions:  {{{myFunc}}} is the function we want to 'turn into mel', and {{{createWrapper}}} is the function that will do this work.
It's important to note a few things:
*In {{{pyFunc}}}, we convert our incoming args into float values:  When the mel is called, it appears that the arguments are actually passed in as strings (I've tried messing with the {{{types}}} parameter to the {{{createMelWrapper}}} function, but haven't been able to get it to work successfully), so for our math to work properly, we need to convert them back to float.
*Note in {{{createMelWrapper}}}, we have to specify the return type from our Python function, since mel is strictly typed.
{{{
# pyModule.py
import maya.mel as mm

def pyFunc(argA, argB):
    return float(argA) + float(argB)

def createWrapper():
    mm.createMelWrapper(pyFunc, retType='float')
}}}
!!!Create our mel command wrapper
Now, from a differet module, we can run our {{{createWrapper}}} function to create the mel procedure:
{{{
import pyModule

pyModule.createWrapper()
}}}
This will actually create a mel script named {{{pyFunc.mel}}} living here (on my Windows 7 box):
{{{
C:\Users\<userName>\Documents\maya\scripts\pyFunc.mel
}}}
The contents of which look like:
{{{
global proc float pyFunc (string $argA,string $argB) {
    python("from pyModule import pyFunc");
    string $tmp = ( "\"" + $argA + "\"" + "," + "\"" + $argB + "\"" ); 
    return `python(("pyFunc(" + $tmp + ")"))`;
}
}}}
{{{createMelWrapper}}} can be found in this module (on my Windows 7 box):
{{{
c:\Program Files\Autodesk\Maya20XX\Python\Lib\site-packages\maya\mel\melutils.py
}}}
And it has this parameter signature:
{{{
createMelWrapper(fn, types = [], retType='void', ignoreDefaultArgs=True, returnCmd=False, outDir=None):
    \param fn the function to wrap, must be a function.
    \param types string list of mel argument types to use, 
    defaults to all 'string'.
    \param retType mel return type of the function, must be convertible to
    what fn actually returns. None means return type is 'void'.
    \param ignoreDefaultArgs  True means arguments with default values will be
    ignored when creating the mel wrapper proc.
    \param returnCmd True means return the generated mel code that defines the 
    wrapper proc, False means write it to a mel file.
    \param outDir The directory to write the generated file to.  None means 
    use the default script directory.
    \return path to generated mel proc OR generated mel code, depending on
    returnCmd.
}}}
It should be noted that after the mel script has been created, each time you run {{{pyModule.createWrapper()}}}, it will pop up a modal dialog asking if you want to replace it.  The only way to get around this behavior (so far) would be to hack the above module.
!!!Execute the mel:
Now we can run the mel command (or pass it as an arg to an expectant command):
{{{
float $added = pyFunc(1, 2);
print $added;
3
}}}
Success!
Starting in Maya 2018, they had some 'hidden' commands/nodes to do remesh and retopo (up until 2019 only works on Windows).
Example video's here:
* https://www.youtube.com/watch?v=_165NFY5vwc
* https://www.youtube.com/watch?v=sRkNIUBmLS4
But it's as easy as:

Select your mesh.  Run the mel:
{{{
polyRemesh;
}}}
Then:
{{{
polyRetopo;
}}}
They both have editable history that lets you adjust the results.
@@Note:@@  Wing can interact with Maya in three different ways.  This is one of the three.  See an overview here on the whole system:  [[Interaction between Wing and Maya]]
----

I currently do all my Python authoring in [[Wing|For Python: Maya 'Script Editor' style IDE]] (professional).  As explained in other subjects (see note above) I have it setup so I can execute commands in Wing, and send them to Maya, which allows me to prettymuch bypass the script editor.  More or less.

There are some instances where I need to code in Wing, but have it act on a physical Maya file in Maya.  To do this, Wing lets you setup 'remote debugging':  What does this give you?  You set break-points in your code in Wing. You execute the same Python code in Maya.  When the break-point is hit, Maya halts, and you can then start debugging in the Wing window, but physically querying the data from the open Maya session.  
>Since I've authored this, I found that Maya's official documentation has been updated as well to show this process, [[here|http://download.autodesk.com/us/maya/2010help/index.html?url=WS73099cc142f48755f2fc9df120970276f7-2158.htm,topicNumber=d0e183276]] (under 'Using Wing IDE with Maya ')
Here's how to set it up:
(Find the official Wing docs for this [[here|http://wingware.com/doc/howtos/maya]])
----
*Copy file to the directory that the module you want to debug lives in.  OR, you can simply put it somewhere in your Python path, and import them into your module from there (both methods work equally well it seems).
{{{wingdbstub.py}}} from the Wing IDE installation directory
{{{
XP:
C:\Program Files\Wing IDE X.X\wingdbstub.py
Vista:
C:\Program Files (x86)\Wing IDE X.X\wingdbstub.py
}}}
*It is important that in {{{wingdbstub.py}}} on around line 90, you set '{{{kEmbedded = 1}}}'.  Otherwise, things just may not work so well....
*In the module you want to debug, you need to import {{{wingdbstub.py}}}, since this tells Wing where to start debugging:
{{{
# myModule.py

import wingdbstub
# If you are using Wing 4.0, they recommend you then call to this:
wingdbstub.Ensure()

def main():
    # code...
}}}
*In your Wing preferences, under 'Debugger: Listening', you need to turn ''on'':
**'Enable Passive Listen'
**'Kill Externally Launched'
*You may also need to also turn ''on'' 'Enable Passive Listen' in the Wing 'Debugger' menu, which is the "bug-looking button" at the very bottom-left of the UI (better safe than sorry)
*In your Wing Project Properties UI, make sure that you have the Python Executable set to Maya's version of Python.  For Maya 2008, it lives here:
{{{
c:\Program Files\Autodesk\Maya2008\bin\mayapy.exe
}}}
----
To start the debugging, set some breakpoints in your module in Wing.
In Maya, then import and execute your code:
{{{
import myModule
myModule.main()
}}}
*Note:  Do //not// {{{import wingdbstub}}} in this code.
At which point Maya should 'hang' at each breakpoint Wing hits, and you can start checking the Stack Data (etc) in Wing while running the debugger.

That's more or less it.  The connection between the two apps seems a bit twitchy though, so it often times requires a restart of Maya to make the systems reconnect again. 
Notes:
*If you "Stop Debugging" in Wing, it will shut down Maya (based on the above set prefs).  If debugging finishes successfully, Maya will stay open (in theory).  
*Really bad errors can break the connection to Maya, at which point you'll need to restart Maya, to get the connection back.
----
Wing has an awesome tool called ''Debug Probe'':  If you enable that tool while debugging, you can insert code into the current debugging session.  Meaning, you can tell Maya to do things while it's debugging.
For example, while Wing is remote deubgging, Maya will be locked up, and the 3d view won't refresh.  If you wanted to see the state of things in Maya at any point, in the Debug Probe tool, you could enter:
{{{
>>> import pymel.core as pm
>>> pm.refresh(force=True)
}}}
And you'd see the 3d view suddenly 'snap' to the current state of things.
* Make sure in the VS options under Debugging -> General you have 'Enable Just My Code' //unchecked//.
* Set a breakpoint in VS.
* Debug Menu -> Attach to process : 
** Under Attach To: -> Select... : Choose 'Debug These Code Types': Native only
** Then choose Maya from the list.
* If you don't see exceptions you expect being caught //in the debugger//:
** Debug -> window -> Exception Settings -> Check on all "C++ Exceptions".
** You may want to 'restore to defaults' when you're done.
* Run your code in Maya, wait for breakpoint to be hit, bam: Debug!
If you want to reorient the local axis of a joint or transform, but not move its children in space:
{{{
import maya.cmds as mc
mc.rotate(xVal, yVal, zVal, "myNode.rotateAxis", relative=True, objectSpace=True, forceOrderXYZ=True)
}}}
The secret sauce is applying the {{{rotate}}} command directly to the {{{rotateAxis}}} attr:
If the node is a transform, then the rotateAxis & rotate values will be simultaneously modified to keep the shape node in space.
If the node is a joint, then the rotateAxis and jointOrient values will be modified similarly.
As I run across it...
* https://github.com/patcorwin/fossil
I run across cool rigging demos from time to time, figured I should start saving some links to them:
*[[Jeremy Ernst -- Gears of War 3 Rigging Reel|http://vimeo.com/32220149]] - video
**[[Fast and Efficient Facial Rigging(in Gears of War 3)|http://www.unrealengine.com/files/downloads/Jeremy_Ernst_FastAndEfficietFacialRigging2.pdf]] - pdf for 2011 GDC
**Corresponding movie for the pdf can be found [[here|http://dl.dropbox.com/u/10939513/PoseLibTool.mov]], showcasing the facial system in action.
*[[Gears of War 3: Building a better pipeline|http://www.youtube.com/watch?feature=player_embedded&v=64wpXowpvIU]] -video
*[[Aardman Animations 'Pirates' animation tools|http://www.martintomoya.com/tools_info.html]] - web
*[[Naughty Dog's "The Last of Us" body and facial rigging|http://www.youtube.com/watch?feature=player_embedded&v=myZcUvU8YWc]] - video
@@Note@@: So... this uh... doesn't exactly work:  Works fine when stepping keys, but when scrubbing a keyed motion  or playing the animation it doesn't update properly.  But I typed it, so I'll leave it here... maybe I'll still figure this out...
----
It's easy to move the pivot point of an object (we'll call it the 'target node') by entering the translate tool, hitting the insert key, and moving the pivot.  This actually modifies the {{{rotatePivot}}} and {{{scalePivot}}} attributes of the transform.  One would deduce from that if they simply connected the translate channel from another node to those attrs, one could use that 'pivot node' to now control the pivot point of our target node.  Turns out, this doesn't work so well:  Once the target node is rotated, any future pivot node translations cause our target node to translate off-axis, a complete mess. 

So what do we know:  Using the insert tool to move the pivot works fine:  The target node can be translate and rotated with no issue post-operation.  But if you setup a 'pivot node' via connects to those attrs, any future rotations on the target node will cause it's translations to wig-out.

A solution I came up with is to author a {{{scriptNode}}} that executes on attribute change, that emulates what the insert-key functionality provides.  In the below example, a NURBS circle is created that will act as our 'target node', and a locator is created that acts as the 'pivot node'.  The locator is parented to the circle so it will follow along with the circle is translated (just like the pivot normally does).  You'll also notice that we set the {{{rotatePivotTranslate}}} attr on our target node to zero:  This is needed, otherwise this operation will physically shift our node in space as it runs.
Hierarchy:
{{{
nurbsCircle1
    |---- locator1
}}}
{{{
# Python code
import maya.cmds as mc
targetNode = 'nurbsCircle1'
pivNode = 'locator1'
sjCmd = ("pivPos = mc.pointPosition('"+pivNode+".rotatePivot', world=True);"+
         "mc.move(pivPos[0], pivPos[1], pivPos[2], '"+targetNode+".rotatePivot', absolute=True, worldSpace=True);"+
         "mc.move(pivPos[0], pivPos[1], pivPos[2], '"+targetNode+".scalePivot', absolute=True, worldSpace=True);"+
         "mc.setAttr('"+targetNode+".rotatePivotTranslate', 0, 0, 0)")
mc.scriptJob(attributeChange=['%s.translate'%pivNode, sjCmd])
}}}
{{{
// Mel code
string $targetNode = "nurbsCircle1";
string $pivNode = "locator1";
string $sjCmd = ("vector $pivPos = `pointPosition -world "+$pivNode+".rotatePivot `;"+
                 "move -absolute -worldSpace ($pivPos.x) ($pivPos.y) ($pivPos.z) "+$targetNode+".rotatePivot;"+
                 "move -absolute -worldSpace ($pivPos.x) ($pivPos.y) ($pivPos.z) "+$targetNode+".scalePivot;"+
                 "setAttr "+$targetNode+".rotatePivotTranslate 0 0 0;");
scriptJob -attributeChange ($pivNode+".translate") $sjCmd;
}}}
Now, when the locator is moved the scriptJob fires, snapping the rotatePivot and scalePivot (optionally) to the location of the locator, emulating what the insert key does.
I wanted to create an angled suspension system.  Imagine a right-triangle in the shape of an L, with a cross member connecting the top to bottom corner ('H' in the below diagram):  The top of the cross-member would be where the suspension mounts to the frame, and the bottom of the cross-member would be where the axle would run through it:  I wanted to be able to raise the up and down the body of the vehicle, and the cross-member would pivot on the frame always keeping the axle the ground.  Or I could move the ground, and the axle would stick to it by adjusting the rotation of the cross-member.

I figured this would be a good time to brush up on some maths, and decided I'd author this as a Maya expression.  One thing I've learned is that if you want the expression to be interactive, meaning, it updates as you transform objects, you need to do direct obj.attr assignments:  If you start assigning to variables in the expression, the interactivity is broken.  So I created an expression that works all on one line (but visually split into multiples below) to maintain the interactivity.

Here is the mel expression we want to end up with:
{{{
jointStart.rotateZ =  asind( sqrt( pow( (jointPosShape.worldPosition[0].worldPositionX-closestPointOnSurface1.positionX), 2 ) +
                                   pow( (jointPosShape.worldPosition[0].worldPositionY-closestPointOnSurface1.positionY), 2 ) +
                                   pow( (jointPosShape.worldPosition[0].worldPositionZ-closestPointOnSurface1.positionZ), 2 ) ) / 24) * -1;
}}}
But everything we do below will be in Python.
----
How do we get there?

With help from a local engineer, I figured out the trig to make this happen.  Rough overview:
{{{
So, the "L" is backwards here from our description above...
           ?
         / |
        /  | 
       /   |
      /    |
     /     |
    H      O
   /       |
  /        |
 /         |
A---------90
}}}
*The top of the triangle (?) is where the suspension mounts to the body.  The angle defined by '?' is what we're ultimately after, since this is what will rotate the cross-member ('H') sticking 'A' to the ground.
*The 'A', or angle we need to calculate, is where the 'axel' lives.  The long leg, or H (hypotenuse), our 'cross-member' must always maintain a fixed length, no matter what length O (opposite) becomes as the body of the vehicle bounces up and down.
*Since this is a right triangle, we know the corner is 90 degrees at all times.
*As the lengh of O changes (as the vehicle travels up and down) we need to recalculate A.  If we get A's angle, we can then easily get ?'s angle, since 180 - 90 - A = '?'.
*With our trig mnemonic 'SOH' from 'soh-cah-toa', we can find the Sin of our angle by Opposite\Hypotenuse.
*From there, we can run a {{{asind}}} function (arcsin, in degrees, or reversed sin) to get the degrees of A.
* Let's use some sample #'s:
**O len is .5
**H len is 1.0 (and never changes).
**sin A is o\h, or .5\1.0 = .5
**asind(.5) = 30 degrees.
**So if A's angle is 30, '?'s angle would be: 180 - 90 - 30 = 60.
**This tells you you'd need to rotate ? by 60 deg to place A on the 'ground'.
*Another example:
**The suspension drops, O is now .25.
**H is still 1.0 (suspension never changes in length).
**sin A is o\h, or .25\1.0 = .25.
**asind(.25) = 14.477512 deg. (let's just round to 14.5)
**So if A's angle is 14.5, '?'s angle would be: 180 - 90 - 14.5 = 75.5.
----
Let's make this happen in Maya:
*Create a joint chain perpendicular to the ground plane.  The goal of this tool is to rotate the chain so that the end joint will be placed exactly on the ground.  It does that via an interactive expression.
Start off by making objects \ defining some variables:
*{{{groundShape}}} :  Make a large NURBS plane, defining the ground plane.  This is the name of its //shape// node.
*{{{startJoint}}} : Create a 2-joint chain perpendicular with the ground.  This is the start joint.
*{{{jtnRotAxis}}} : This is the axis that will rotate the chain so that {{{endJoint}}} will touch the ground.
*{{{endJoint}}} : This is the name of the end joint.
*{{{posLocShape}}} : Create a locator, point constrain it to your {{{startJoint}}}.  This is the name of the locator's shape node.
*{{{cpos}}} : Create a {{{closestPointOnSurface}}} node.  This is its name.
*{{{jntLen}}} : using the {{{distBetween}}} function, this is the distance between the start and end joint.
*{{{flip}}} can be used to reverse the direction of the suspension, which may be needed based on the orientation you drew the joints.
{{{
import maya.cmds as mc

from math import sqrt

def distBetween(nodeA, nodeB):
    posA = mc.xform(nodeA, query=True, worldSpace=True, rotatePivot=True)
    posB = mc.xform(nodeB, query=True, worldSpace=True, rotatePivot=True)
    dist = sqrt( sum( map( lambda x:pow(x[0]-x[1], 2), zip(posA, posB) ) ) )
    return dist

groundShape = 'groundShape'
startJoint = 'jointStart'
jtnRotAxis = 'rotateZ'
endJoint = 'jointEnd'
posLocShape = 'jointPosShape'
cpos = 'closestPointOnSurface1'
jointLen = distBetween(startJoint, endJoint)
flip = -1
}}}
Now do this work:
*Connect the nurbs groundShape nodes 'worldSpace' attr othe cpos's 'inputSurface'
*Connect the posLocShape's 'worldPosition' to the cpos's 'inPosition'
The cpos node will now accurately track how far away the start joint is from it.
Finally, create the expression:
{{{
# Create the expression string, using Python's string formatting:
mappings = {'jnt':startJoint,
            'jntAxis':jtnRotAxis,
            'locShape':posLocShape,
            'cpos':cpos,
            'jntLen':jointLen,
            'flip':flip}
expressionStr = """
%(jnt)s.%(jntAxis)s =  asind( sqrt( pow( (%(locShape)s.worldPosition[0].worldPositionX-%(cpos)s.positionX), 2 ) +
                                    pow( (%(locShape)s.worldPosition[0].worldPositionY-%(cpos)s.positionY), 2 ) +
                                    pow( (%(locShape)s.worldPosition[0].worldPositionZ-%(cpos)s.positionZ), 2 ) ) / %(jntLen)s) * %(flip)s;
"""%mappings

# Finally, create the mel expression:
mc.expression(name='rotate_%s'%startJoint, string=expressionStr)
}}}
If everything was done correctly the endJnt should snap to the ground plane via rotations placed on the startJnt.  If you move the startJnt or ground up\down, the rotations should update live, constantly placing endJnt on the ground.
While writing a scripted plugin node for manipulating UV's, I needed a way to 'rotate them'.  Since this is a 2d transformation, I came up with the below function.  It's based around the [[OpenMaya.MVector|http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html]] class.
{{{
# Python code
from math import radians
import maya.OpenMaya as om

def rotateVector(vec, center, angle):
    """
    Rotate a given 2d vector around a center-point, by a number of degrees.

    Parameters:
    vec : OpenMaya.MVector : The vector to rotate.
    center : OpenMaya.MVector : The vector to rotate about.
    angle : float : The angle in degrees (counter-clockwise is positive).

    Return: OpenMaya.MVector : The rotated vector.
    """
    rads = radians(angle) # convert angle to radians
    offsetA = vec - center # move relative to origin
    rotated = offsetA.rotateBy(om.MVector.kZaxis, rads) # Rotate around origin
    reset = rotated + center # offset back to orig position rel to center
    
    return reset
}}}
{{{
vec = om.MVector(.25, .5)
center = om.MVector(.5, .5)
angle = 90
rotated = rotateVector(vec, center, angle)
print rotated.x, rotated.y
}}}
{{{
0.5 0.25 # yes, this is correct ;)
}}}
This is based on the set of [[SOuP|http://www.soup-dev.com/]] tools.

I've always like the look of [[voronoi diagrams|http://en.wikipedia.org/wiki/Voronoi]].  They can be expressed in 2d, or 3d.  Soup's 'shatter' node uses this system to 'fracture mesh into pieces'.  What a great look.  The below is a simple step-by-step to get this set up.

Soup nodes used:
*{{{scatter}}}  -- Used to populate points inside of a volume.
*{{{shatter}}}  -- Used to shatter a mesh based on a list of points.
Instructions:
*Create the polygonal mesh you want fractured.  Call it 'sourceMesh'.
*Duplicate it, and call it 'finalMesh'.
*Create a Soup {{{scatter}}} node.
*Connect: 
**{{{sourceMeshShape.outMesh}}} -> {{{scatterShape1.inGeometry}}}
**You should see a bunch of points appear inside the mesh.
*Create a Soup {{{shatter}}} node.  To start, you may want to turn down the "point density" to something lower, like 100.
*Connect:
**{{{scatterShape1.outGeometry}}} -> {{{shatter1.inGeometry}}}
**{{{scatterShape1.outPositionPP}}} -> {{{shatter.inPositionPP}}}
*Now hook up the shatter to the 'finalMesh'.  Connect:
**{{{shatter1.outGeometry}}} -> {{{finalMeshShape.inMesh}}}
*You still won't see anything yet:
**Hide the 'sourceMesh'
**On the 'shatter' node, turn "auto evalute" on.  After a pause, you should see the shattered mesh.
From this point, you can adjust the scatter "point density" to higher or lower values, and adjust the shatter "distance".
If you need to adjust the shape of your object, go unhide the 'sourceMesh', and make adjustments to it.  To speed up evaluation, you should turn off "auto evaluate" on the shatter node.
----
Page 3 (currently) of the Soup examples shows many different uses for the shatter:  http://www.soup-dev.com/examples2_3.htm
Notes on ~SOuP
----
Official:
*[[Homepage|http://www.soup-dev.com/]]
*[[Forums|http://soup-dev.websitetoolbox.com/]]
Docs:
*[[Google Docs|https://docs.google.com/document/pub?id=1k1cLO2ukmgEDYvO7-wDq3f2d-sF_5KBAUMKq8Ms08Ik]], [[Forum|http://soup-dev.websitetoolbox.com/post/Docs-v02-5591258]]
Tutorials:
*Group Node :  Good introductory tutorial : http://lesterbanks.com/2010/06/soup-plugins-for-maya-tutorial/
*[[Tokeru maya_wiki|http://www.tokeru.com/mayawiki/index.php?title=Soup]] has a bunch of good stuff.
----
Notes:
*Don't use Maya 2013+ Node Editor to select nodes, and then use soup's 'connect' shelf button:  The nodes don't seem to be selected properly.  Use the Hypergraph\Hypershade to pick them instead.
In Maya 2016, I tried using their new 'Create -> SVG' tool.  Unfortunately I was unable to get it to load anything but the most simple svg.  
Forum post on my troubles [[here|http://forums.autodesk.com/t5/modeling/importing-svg-error-unexpected-number-of-points-for-svg-path/m-p/6066538]].
It appears that it's the internals of the {{{svgToPoly}}} node itself that it failing, not any of the wrapper commands:
{{{
setAttr svg1.svgFilepath -type "string" "c:/path/to/my/file.svg"
}}}
{{{
// Error: Unexpected number of points for SVG path.
}}}
Thanks to a tip by user 'mhyworld' on the above link they suggested in Inkscape (what I use) to do a 'Path -> Union' operation.  While it doesn't appear that anything in Inkscape happens when you do that, the newly exported svg //does// actually load in Maya.
Also, if there are multiple ungrouped parts in the svg, in Inkscape they must all be selected and an 'Object -> Group' ran before the Union.
While writing a scripted plugin node for manipulating UV's, I needed a way to 'scale them'.  The below functions work equally well in 2d or 3d (although the examples are in 2d).  They're based around the [[OpenMaya.MVector|http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html]] class.
----
In this example, 'percent' is a single value, doing a uniform scale on the vector:
{{{
# Python code
import maya.OpenMaya as om

def scaleVector(vec, center, percent):
    """
    Scale a given vector around a center-point, by a percent.

    Parameters:
    vec : OpenMaya.MVector : The vector to rotate.
    center : OpenMaya.MVector : The vector to rotate about.
    percent : float : The percent to scale the vector.

    Return: OpenMaya.MVector : The rotated vector.
    """
    offsetA = vec - center # move relative to origin
    scaled = offsetA * percent # scale by the percent about the origin
    reset = scaled + center # offset back to orig position rel to center

    return reset
}}}
{{{
vec = om.MVector(.25, .5)
center = om.MVector(.5,.5)
percent = .5
scaled = scaleVector(vec, center, percent)
print scaled.x, scaled.y
}}}
{{{
0.375 0.5 # yes, this is correct :)
}}}
----
In this example, scale is a vector, allowing for xy(z) scaling, or non-uniform scaling.
{{{
def scaleVector(vec, center, scale):
    """
    Scale a given vector around a center-point, by another vector.

    Parameters:
    vec : OpenMaya.MVector : The vector to rotate.
    center : OpenMaya.MVector : The vector to rotate about.
    scale : OpenMaya.MVector : The vector defining the xyz scale values.

    Return: OpenMaya.MVector : The rotated vector.
    """
    offsetA = vec - center
    scaled = om.MVector(offsetA.x * scale.x, offsetA.y * scale.y, offsetA.z * scale.z)
    reset = scaled + center

    return reset
}}}
{{{
vec = om.MVector(.25, .5)
center = om.MVector(.5,.5)
scale = om.MVector(.5,.5)
scaled = scaleVector(vec, center, scale)
print scaled.x, scaled.y
}}}
{{{
0.375 0.5 # yes, this is correct :)
}}}
Listed in no particular order...
----
''JEDIT''
jEdit is a free (and full featured) text editor, which has tools authored to let it interface directly with Maya (via the Maya's {{{commandPort}}}), //irregardless// of Maya version.  That's pretty nice.  As of this date, I've started using it with some success.
*Download jEdit: http://www.jedit.org/
*Get mel and 'python mel' context sensitive highlighting for it at [[Highend3d.com|http://highend3d.com/maya/downloads/tools/syntax_scripting/2464.html]]
*Get the jEdit plugin that lets it talk to Maya at [[Highend3d.com|http://highend3d.com/maya/downloads/tools/syntax_scripting/jEdit-Maya-Editor-Plugin-3732.html]]
*You'll need the jEdit [[InfoViewer plugin|http://plugins.jedit.org/plugins/?InfoViewer]] too.
*And a tutorial on how to get it setup on [[Highend3d.com|http://highend3d.com/maya/tutorials/using_tools_scripts/Configuring-jEdit-with-Maya-319.html]] (you //really// need to read and follow this)
----
''MAPY''
Mappy isn't a text editor, but rather a free set of tools that let 'any' (so it states) text editor interface with Maya.  Similar to what jEdit is doing above.  I have yet to test this.
*Official mappy homepage:  http://rodmena.com/mapy/ (was down last I checked)
*Download mappy here: http://www.highend3d.com/f/mapy
----
''MEL STUDIO PRO''
I've been using [[Mel Studio Pro|http://www.maxliani.com/technology/MelStudioPro_MainEn.html]] for years.  Purchase and download it [[here|http://www.pluginz.com/product/11765]].  It is in my opinion the best and easiest option for scripting in Maya.  It doesn't have all the debugging features of a full blown text editor, but it's convenience can't be beat.

It is a plugin in Maya (which costs $$$), that provides a Script Editor replacement, that has many features of a full-blown text editor, but lives in Maya.  Check the features from the link.  I can't work without it now...

[img[http://www.maxliani.com/technology/images/MelStudioProUI1.jpg]]
----
''Maxya Scripter''
http://www.tarzworkshop.com
I had this one recommended, but I haven't had a chance to test it yet.  Looks like another standalone IDE with hooks into Maya, for both mel and Python.  There is both a free, and $$$ version.
[img[http://www.tarzworkshop.com/images/maxya/editor_billboard3.gif]]
----
''Also see'':
*[[For Python: Maya 'Script Editor' style IDE]]




''Scripts'':  (WIP)
*[[scripts|Scripts]]
** __Can contain:__
***[[commands|Commands]]
***//executed// [[procedures|Procedures]] (//global// procedures defined in //other// scripts, or //local// procedures defined //previously// in the //current// script) 
***[[procedure|Procedures]] //defintions//

*A 'script' is an actual file on disk, that ends in {{{.mel}}}.  Like {{{foo.mel}}}.  Or it could be a button on the shelf.
*Scripts can hold these things:
**commands  ({{{xform}}}, {{{ls}}}, {{{move}}}, etc...)
**procedure //definitions// (whether 'local' or 'global').  Often times people can mistake commands for procedures, and vice vrsa.
****If there is a //global// procedure inside of a script, with the same name as the script name, when the script is executed, so will the procedure.  This is a very common way of working.
**//executed// procedures
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'] 
}}}
[[MelGlobals|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.language/pymel.core.language.MelGlobals.html]] Docs.
----
Also see:
*[[Sending variable data from Python to Mel]]
With the inclusion of ~PyMel, this has gotten a lot easier:

You want to create a mel global {{{int}}} named {{{$foo}}}, with the value of {{{23}}}:
{{{
import pymel.core as pm
pm.language.melGlobals.initVar('int', 'foo')
pm.language.melGlobals['foo'] = 23
}}}
The type you pass in must be: 'string', 'string[]', 'int', 'int[]', 'float', 'float[]', 'vector', 'vector[]'
[[MelGlobals|http://help.autodesk.com/cloudhelp/2015/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.language/pymel.core.language.MelGlobals.html]] Docs.
----
Also see:
*[[Sending variable data from Mel to Python]]

----
Older info:

I've recently ran across a nasty... bug, in the ~Maya-Python integration.  It appears, that if you have a Python module return a list, Maya will convert it to an array. //That's// as expected.  However, if the Python list is //empty//, Maya converts it to a //string// with two brackets inside of it: "{{{[]}}}", rather than converting it to an empty array.
<<<
I should note, as an update, that this initial presumption (that this is a 'bug') is wrong:  Maya variables are always typed (''{{{string}}}''{{{ $foo = "word";}}}), while Python's aren't, they're just pointers ({{{foo = "word"}}}).  Maya has to auto-detect what type of data (string, float, etc) exists in the passed-in Python object and make a determination for what the conversion should be.  If it just gets an 'empty list', it really has no idea what type of Maya array (float, string, int, vector) it should be.
<<<
Obviously, this can cause some real headaches if you're trying to capture expected data from Python in Maya.  

I found a fix, that I'm... not.... very... happy with.  Explained below the example.

Example:  First, a Python module that will return different types of data, based on the passed in arg:
{{{
# Python code
def variableReturn(varType):
	if varType == "str":
		return "string"
	if varType == "listSingle":
		return ["value1"]
	if varType == "listMult":
		return ["value1", "value2"]
	if varType == "listNone":
		return [None]
	if varType == "listEmpty":
		return []
}}}
Now, some mel code to capture that data.  I've already setup the Maya variable types to be the correct ones based on what the Python module spits out... but you can see that one of them is clearly wrong:
{{{
// Mel code:
string $str = python("variableReturn('str')");
string $lstSingle[] = python("variableReturn('listSingle')");
string $listMult[] = python("variableReturn('listMult')");
string $listNone[] = python("variableReturn('listNone')");
string $listEmpty = python("variableReturn('listEmpty')");

print ("Type of 'str' : " + `whatIs "$str"` + "\n");
print ("Type of 'listSingle' : " + `whatIs "$lstSingle"` + "\n");
print ("Type or 'listMult' : " + `whatIs "$listMult"` + "\n");
print ("Type of 'listNone' : " + `whatIs "$listNone"` +"\n");
print ("Type of 'listEmpty' : " + `whatIs "$listEmpty"` + "  <--- aaaagh!!!\n");
}}}
Prints:
{{{
Type of 'str' : string variable
Type of 'listSingle' : string[] variable
Type or 'listMult' : string[] variable
Type of 'listNone' : string[] variable
Type of 'listEmpty' : string variable  <--- aaaagh!!!
}}}
As you can see, the workaround is to have your Python code return a list with a 'None' object in it:
{{{
# Python
return [None]
}}}
Presumably, you'd do some sort of test before hand in your Python code to know to return things this way:
{{{
# Python
if len(myVal) == 0:
    myVal = [None]
return myVal
}}}
Of course, in Maya you'd then have to detect if the first item of the list was empty, if it's size was 1:
{{{
// mel
string $result[] = python("myPyFunc()");
if(size($result) && $result[0] != ""){
    // start doing something...
}
}}}
----
''Update:''
After reading through the Maya docs on Python integration, this may make a bit of sense:
| ''Python Return Value'' | ''MEL Conversion'' |
| string | string |
| unicode | string |
| int | int |
| float | float |
| list containing numbers, including at least one float | float[] |
| list containing only integers or longs | int[] |
| list containing a non-number | string[] |
| anything else | string |
I'm guessing an 'empty list' falls into the 'anything else' category, and thus converts it to a "string".  Even though our above example returns an empty list, and we //presume// to have string data, it could actually contain float data, int data, etc.  But how is Maya to know this if all it gets is an un-typed empty list?  It doesn't, so it turns it into a string.
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/
To set a node's UUID:
{{{
import maya.cmds as mc

# The node's string name:
node = 'null1'
# Give it a new UUID:
mc.rename(node, "6173DE1A-4AB5-0D87-E788-E6962341D1FC", uuid=True)

# Query the uuid for the node:
uuid = mc.ls(node, uuid=True)
print uuids
# [u'6173DE1A-4AB5-0D87-E788-E6962341D1FC']
}}}
Note I did this in regular Python, since using {{{rename}}} in ~PyMel would raise an exception for some reason.
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...')
}}}
* https://clamdragon3d.com/blog/2018/3/22/python-virtualenvs-and-maya
* http://www.sonictk.com/start-using-virtual-environments-instead-of-mayapy/
<<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<tiddler TspotSidebar>><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
<<tabs txtMainTab  Tags 'All tags' TabTags >>
<<tabs txtMainTab  Dummy Dummy TabDummy Timeline Timeline TabTimeline All 'All tiddlers' TabAll More 'More lists' TabMore>>
{{{scriptCtx}}} are great ways to provide greater interaction between users and tools.  But to get them working in Python takes a bit more hoop-jumping than mel, since the scripts the context calls to must be in mel, not Python.

Another interseting side-effect is that the {{{scriptCtx}}} command borrows flags from the {{{selectType}}} command:  Notice the {{{allObjects=True}}} parameter\argument below:  You'll find no docs for it in the {{{scriptCtx}}} page, but they elude to the fact you can borrow them from {{{selectType}}}.  Crazy!
{{{
import maya.cmds as mc
import maya.mel as mm

#------------------------------------------
# Create functions to do work based on what's picked in the context:

def doStuff(argA, argB):
    """
    Python function to do stuff, based on our context.
    """
    print "Context Data:"
    print argA, argB

def exampleScriptCtx():
    """
    Main function to both create and execute the scriptCtx.
    """

    # The scripts called to from the scriptCtx are in mel, not Python, so we
    # need to make a little mel wrapper to pass along the goods to our doStuff()
    # Python function:
    mel ='''
    global proc doStuff(string $arg[]){
        python( "doStuff(\\""+ $arg[0] +"\\", \\"" + $arg[1] + "\\");" );
    }
    '''
    mm.eval(mel)

    #---------------------------------------------
    # If the context already exists, delete it.  This is helpful when developing
    # the context (since changes are being made and you need an easy way to see
    # the updates), but isn't necessary once the context is finalized.
    context = "exampleScriptCtx" # This defines our new context's name:
    if mc.contextInfo(context, exists=True):
        mc.deleteUI(context, toolContext=True)

    #----------------------------------------------
    # Create the context:
    mc.scriptCtx(context,
                 title="Example scriptCtx",
                 totalSelectionSets=1, # Keep things simple here...
                 finalCommandScript='doStuff($Selection1)', # What is executed when the tool finishes.
                 setAutoToggleSelection=True, # Picking acts as toggle, to make it easier.
                 setSelectionCount=2, # Max things you can pick before tool finishes.
                 setAutoComplete=True, # Auto finish when max things picked is reached?
                 allObjects=True, # A selection filter pulled from the selectType command.
                 setNoSelectionPrompt="Select two nodes, target first.",
                 setNoSelectionHeadsUp="Select two nodes, target first.",
                 setSelectionPrompt="Select a second node.",
                 setSelectionHeadsUp="Select a second node.")

    #--------------------------------------------
    # Clear our selection, and set the context as the active tool:
    mc.select(clear=True)
    mc.setToolTo(context)
}}}
So running:
{{{
exampleScriptCtx()
}}}
Will create the context, set the current tool to it, and when the context completes, it will call to {{{doStuff()}}} for final parsing of the selected data.
- Your Maya : Mel : Python : ~PyMel : API : scripting resource on the web -
[img[mel wiki|http://farm1.static.flickr.com/177/480356457_cb8f44a0f3.jpg?v=0][Welcome]] mel wiki
This subject is about getting source analysis \ auto-completion functionality setup in Wing IDE for Maya's {{{maya.cmds}}} and {{{maya.OpenMaya}}} (and associated API) packages.

''Note'',  Wing has their own tutorials on this, take a look at them [[here|http://www.wingware.com/doc/howtos/maya]].  Now adays, I use their methods, but historical methods are below.

@@IMPORTANT@@ : You also need to point your Wing 'Project Properties' 'Python Executable' to Maya's. This, for example:
{{{
c:\Program Files\Autodesk\Maya2018\bin\mayapy.exe
}}}
Without Maya's mayapy.exe in the mix, the source analysis won't work correctly in Wing.

! Easy Way
I started implementing this in Maya 2013 (Win7), but it should probably work a few versions back.
Note that one newer versions of Maya, you have to download the devkit here for your version of Maya:
https://www.autodesk.com/developer-network/platform-technologies/maya
I then unzip it here (entering your version of Maya for the dir):
{{{
C:\Users\USERNAME\Documents\maya\2018\
}}}
Which should give you this, plus a bunch of subfolders:
{{{
C:\Users\USERNAME\Documents\maya\2018\devkitBase
}}}
Then  'Edit → Preferences -> Source Analysis -> Advanced', press the 'Insert' button under 'Interface File Path', and point it to where the 'pi' files live:
{{{
C:\Users\USERNAME\Documents\maya\2018\devkitBase\devkit\other\pymel\extras\completion\pi
}}}
Restart wing, everything should be a go:  You will get auto-completion for both the {{{maya.cmds}}} package, along with all the associated {{{maya.OpenMaya}}} stuff.
! Slightly More Complex Way
!!!API
A few notes that go along with Wing's above tutorial:
*You need to compile the .pi files with Maya's version of Python, not your system level one.
*Here are examples of how to generate one from the command line, Win10:  The shell must be launched with ''admin privileges''.
{{{
C:\Program Files\Autodesk\Maya2018\Python\Lib\site-packages\maya>"C:\Program Files\Autodesk\Maya2018\bin\mayapy.exe" "C:\Program Files (x86)\Wing IDE 5.1\src\wingutils\generate_pi.py" OpenMaya OpenMaya.pi
}}}
*Note you need to call to Maya's version of python, but be in the same dir as the {{{OpenMaya.pyc}}} file.
* you'll want to do that for the other .pyc files in there:  OpenMayaAnim.pyc, OpenMayaUI.pyc, etc.
*After you compile them in that dir, you need to copy them to your pi-files dir:
{{{
C:\Users\<userName>\AppData\Roaming\Wing IDE 4\pi-files\maya
}}}
!!!maya.cmds
To get auto-completion working for {{{maya.cmds}}}, point Wing to this dir:
{{{
C:\Program Files\Autodesk\Maya2013\devkit\other\pymel\extras\completion\pi
}}}
Via its {{{Edit -> Preferences -> Source Analysis -> Advanced ->}}} section, and "Insert" the path.
Often times when using or authoring tools/commands, there's a concept of 'source' and 'target' nodes.  
Funny enough, I can never keep them straight in my head:
* Is the 'source' node what is being effected by the target?
* Is the 'target' node what is being effected by the source?
In Maya terminology:
* {{{source}}} : The node being effected / driven.  The one being moved/transformed/changed.
* {{{target}}} : The node(s, sometimes more than one) doing the affecting / driving. 
''For example'', when setting up a constraint, if you want a cube to be constrained to a bunch of spheres, the cube is the 'source' (being effected), while the spheres are the 'targets' (doing the affecting).
This is made clear via Maya's docs:
{{{
pointConstraint( [target...] [object] , ...) 
}}}
The (possibly multiple, based on the '...') {{{[target...]}}} / driver is listed first, and then the single {{{[object]}}}, aka 'source' / driven node, is listed after that.
In my head, driver/driven is much easier to understand:
* driver = target
* driven = source
----
You can also think about it in the terms of shooting a weapon:  You're moving the projectile from the weapon (the source) to the physical target off in the distance.
----
This same paradigm is also used in things like ICP on Wikipedia:
https://en.wikipedia.org/wiki/Iterative_closest_point
<<<
In the Iterative Closest Point or, in some sources, the Iterative Corresponding Point, one point cloud (vertex cloud), the reference, or ''target'', is kept fixed, while the other one, the ''source'', is transformed to best match the reference. 
<<<
String formatting has been available in //Python// for a while.  Maya (as of 2010, maybe earlier?) has it as well via the {{{format}}} command.  
Example time:
!!!Mel:
*Docs on Maya ''mel'' [[format command|http://download.autodesk.com/us/maya/2010help/Commands/format.html]]
{{{
string $myStr = "Formatting with position ^1s and ^2s vals.";
string $updateStr = `format -stringArg "one" -stringArg "two" $myStr `;
print $updateStr ;
}}}
{{{
Formatting with position one and two vals.
}}}
!!!Python:
*Docs on Maya ''Python'' [[format command|*http://download.autodesk.com/us/maya/2010help/CommandsPython/format.html]]
Using Maya's {{{format}}} command:
{{{
import maya.cmds as mc

myStr = "Formatting with position ^1s and ^2s vals."
updateStr = mc.format(myStr, stringArg=('one', 'two') )
print updateStr
}}}
{{{
Formatting with position one and two vals.
}}}
----
But, if you're going to be using the {{{format}}} //concept// in Python, you might as well use it's own system, since it's much more powerful once you get into it.
*Docs to Pythons own [[string formatting|http://docs.python.org/library/stdtypes.html#string-formatting]]
{{{
myStr = "Formatting with position %s and %s vals." %("one", "two")
print myStr
}}}
{{{
Formatting with position one and two vals.
}}}


* Substance Plugin: https://docs.substance3d.com/integrations/maya-172825672.html
Also see:
* [[Mesh from point cloud using Open3D]]
----
This will generate a convex hull around a point cloud.  
The heavy work is done by [[scipy.spatial.ConvexHull|https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.ConvexHull.html]]
{{{
import maya.cmds as mc
import maya.mel as mm
import maya.api.OpenMaya as om2

from scipy.spatial import ConvexHull

points = [[1,2,3], [4,5,6], etc...] # Need at least 3

hull = ConvexHull(points)
convexPts = []
for simplex in hull.simplices:
    # Each simplex is the vert ID for the three points that makes up this tri:
    convexPts.append( [ points[simplex[0]], points[simplex[1]],  points[simplex[2]] ] )

# Now build our mesh:
# A single triangle! :)
tri = mc.polyCreateFacet(constructionHistory=False, point=convexPts[0])[0]
convexHull = mc.listRelatives(tri, shapes=True, type='mesh')[0]
selList = om2.MSelectionList()
selList.add(convexHull)
dagPath = selList.getDagPath(0)
mFnMesh = om2.MFnMesh(dagPath)
for point in convexPts[1:]:
    mFnMesh.addPolygon(point, mergeVertices=True)
mc.polyNormal(convexHull, constructionHistory=False, userNormalMode=0, normalMode=2)

mc.select(convexHull)
mm.eval("CenterPivot")

# Figure out if we need to flip normals or not
centerPt = om2.MVector(mc.pointPosition(f"{tri}.rotatePivot", world=True))
vertSamplePt = om2.MVector(mc.pointPosition(f"{convexHull}.vtx[0]", world=True))
vertNormalSample = om2.MVector(mc.polyNormalPerVertex(f"{convexHull}.vtx[0]", normalXYZ=True, query=True)[:3])
outVec = vertSamplePt-centerPt
dot = outVec * vertNormalSample
if dot < 0:
    mc.polyNormal(convexHull, normalMode=0, userNormalMode=1, constructionHistory=False)  
}}}
----
Other examples
* Good python examples using [[open3d|http://www.open3d.org/]] and numpy
** https://towardsdatascience.com/5-step-guide-to-generate-3d-meshes-from-point-clouds-with-python-36bad397d8ba
* Describes the maths behind HRBF's, sort of metaball based surfaces. Provides source c++ code
** http://rodolphe-vaillant.fr/?e=12
* pytetgen, a wrapper around, tetgen, "A Quality Tetrahedral Mesh Generator and a 3D Delaunay Triangulator"
** https://github.com/Marcello-Sega/pytetgen
** http://wias-berlin.de/software/tetgen/
* pypoisson, a wrapper around.... poisson:  Looks like it actually tries to shrink-wrap point clouds rather than making convex hulls.
** https://github.com/mmolero/pypoisson
** http://www.cs.jhu.edu/~misha/Code/PoissonRecon/Version9.011/
* http://pointclouds.org/
** Looks really robust, supports kinect
* Based on my own work, you can wrapper meshlabserver in Python, then call to it on the commandline form Maya, executing any script it can generate. Say for example, a convex hull around a pointcloud.  See general instructions here:
** https://www.mankier.com/1/meshlabserver
* Other meshlab tutorials on this: These could be turned into a script, thus executed from the commandline.  
** http://fabacademy.org/archives/2014/tutorials/pointcloudToSTL.html  : Calls to Possion.
** http://gmv.cast.uark.edu/scanning/point-clouds-to-mesh-in-meshlab : Calls to Possion.
** http://meshlabstuff.blogspot.com/2009/09/meshing-point-clouds.html : Calls to Possion.
WikiEditingInfo
Maya 2016 introduced the ''Evaluation Graph''.  
!Bugs:
!!!The {{{MPxNode.schedulingType}}} method isn't supported in 2016 ~OpenMaya Python API
''Issue''
You can't do this, it's not supported:
{{{
    def schedulingType(self):
        return self.kParallel
}}}
''Solution''
Instead, you can do this:
{{{
def initializePlugin(mobject):
    # codez
    mc.setNodeTypeFlag("myPluginNodeType", threadSafe=True)
}}}
''Note'' : After learning this, it may not matter:  All since Python scripted plugins use.... Python, and Python itself isn't multi-threaded, they always, //always// evaluate in "globally serial" mode only.  And should best be avoided in the EG.
!!!~TdeformerEvaluator Warnings in the Output Widow:
''Issue:''  Tons of this stuff prints in the Output Window, slowing everything down during playback & batch operations.  Example:
{{{
Warning: TdeformerEvaluator using dynamic mesh data source :polyColorPerVertex19 of type polyColorPerVertex, for final mesh :myMeshShape
}}}
''Solution:''
None yet, other than turning off 'GPU Override' disables all the spam.
!!!GPU Override not working:  Deformer 'not part of a chain'.
''Issue:''  Using my ~NVidia Titan card, I couldn't get GPU Override to do anything, with VP2.0 turned on, not expressions in my scene, bacface culling disabled, etc.  The HUD always says "Enabled (0k)".   On a simple asset with mesh skinned to joints, when I run the Bonus Tools “GPU Acceleration Status: Check Common Deformers” on that scene, I get the below prints for all the deformers:
{{{
// Node skinCluster15 is supported but not a part of a chain.  Checking if related downstream nodes are supported. // 
}}}
@@''Solution:''@@
Based on the type of graphics card, GPU acceleration will only help with skinned mesh //over// a certain poly limit.  Since we break our mesh up into many small parts, no one single part was over the threshold to actually start the optimization.  From the docs, you need greater than 500 verts on AMD, and 2000 verts on ~NVidia cards to have any one mesh make use of optimization.
By combining all the small mesh into one large mesh and copying across the old weights, I saw a 1.5x speedup in framerate, and finally got the HUD to say "Enabled (17k)".
Here's an example of happy happy 'deformer chain':
{{{
// Deformer Chain: skinCluster1GroupParts.outputGeometry->:skinCluster1->:pCylinderShape1 // 
}}}
!!!GPU Override not working:  Backface Culling Fail:
''Issue:''
{{{
// Mesh :fooShape not supported by deformerEvaluator because backfaceCulling attribute is enabled. // 
}}}
@@''Solution:''@@
Turn off backface culling on all mesh in the scene:
{{{
import pymel.core as pm
map(lambda m: m.backfaceCulling.set(0), pm.ls(type='mesh'))
}}}
!!!GPU Override not working:  Group Component Fail:
''Issue:''
This was on a {{{cluster}}} node, not a {{{skinCluster}}}.
{{{
Cluster :cluster8 not supported by deformer evaluator since the group component is not complete.
}}}
''Solution:''
None yet...
----

!Notes:
Notes below on its specifics, taken from [[Parallel_Evaluation_Maya2016.pdf|http://download.autodesk.com/us/support/files/maya_documentation/Parallel_Evaluation_Maya2016.pdf]].
!!!Expressions
*If your scene contains expression nodes that use the {{{getAttr}}} command the DG graph will be missing explicit dependencies, resulting in an incomplete EG. In addition to impacting correctness, expression nodes will also reduce the amount of parallelism in your scenes.
*Maya currently defaults to scheduling expression nodes as 'untrusted' (see Thread Safety below).
!!!Animation
*When manipulating your rig, you may notice that performance improves once you’ve added at least 2 keys on a controller. By default, only animated nodes are included in the EG. This helps keep the EG compact, making it fast to build, schedule, and evaluate. Hence, if you’re manipulating a controller that hasn’t yet been keyed, Maya relies on legacy DG evaluation. When 2 or more keys are added, the EG is rebuilt to include the newly keyed nodes, permitting parallel evaluation via the EM.
!!!Evaluation Modes:
| Mode Type |Purpose |
| ''DG'' |Uses the legacy ''Dependency Graph''-based evaluation of your scene. This was the default evaluation mode prior to Maya 2016. |
| ''Serial'' |''Evaluation Manager Serial'' mode. Uses the EG but limits scheduling to a single core. Serial mode is a troubleshooting mode to pinpoint the source of evaluation errors. |
| ''Parallel'' |''Evaluation Manager Parallel'' mode. Uses the EG and schedules evaluation across all available cores. This mode is the new Maya 2016 default. |
*When using either Serial or Parallel EM modes, you can also activate the ''GPU Override'', which accelerates deformations on your GPU. You must be in Viewport 2.0 to use this feature.
*If you’ve enabled GPU Override and the HUD reports Enabled (0 k), this indicates that no deformations are happening on the GPU (which is probably weird\bad).
!!!Evaluation Graph Correctness
*In the event that you see evaluation errors, first try to test your scene in ''Serial'' evaluation mode.  This eliminates threading as the possible source of differences.
*If errors persist even after transitioning to Serial evaluation this indicates that the EM is building an incorrect EG for your scene.
*Custom Plugins:  If your scene uses custom plug-ins that rely on the {{{MPxNode::setDependentsDirty}}} function to manage attribute dirtying, this may be the source of problems.
*Errors in Autodesk Nodes. Although we’ve done our best to ensure that all out-of-the-box Autodesk Maya nodes correctly express dependencies, it’s possible your scene is using nodes in an unexpected manner.
!!!Thread Safety:
*Developers working in Maya 2016 will need to update plug-ins to ensure correct behavior during multi-core evaluation. Although individual calls to nodes don’t need to be thread-safe (since we evaluate a node’s dirty plugs sequentially from a single thread), plugs values may be read concurrently once evaluated. 
*We’ve added new scheduling types that instruct the EM regarding how to schedule nodes. Scheduling types provide a straightforward migration path, so you don’t have to pass up opportunities for parallelizing some parts of your scenes, because a few nodes still need work.
| Scheduling Type |What are you telling the scheduler? |
| Parallel |Asserts that the node and that all third-party libraries used by the node are thread-safe. |
| Serial |Asserts it is safe to run this node with instances of other nodes. However, all nodes with this scheduling type should be executed sequentially within the same evaluation chain. |
| ~GloballySerial |Asserts it is safe to run this node with instances of other nodes but only a single instance of this node should be run at a time. |
| Untrusted |Asserts this node is not thread-safe and that no other nodes should be evaluated while an instance of this node is evaluated |
*By default nodes are scheduled as Serial, which provides a middle ground between performance and stability/safety.
*When testing your plug-ins with parallel Maya, a simple strategy is to schedule nodes with the most restrictive scheduling type (i.e., Untrusted), validate that the evaluation produces correct results, raise individual nodes to the next scheduling level, and repeat the experiment.
There are two ways to alter the scheduling level of your nodes:
1 : Mel/Python Commands:
{{{
evaluationManager -nodeTypeParallel on "transform"; // Parallel 
evaluationManager -nodeTypeSerialize on "transform"; // Serial
evaluationManager –nodeTypeGloballySerialize on “transform”; // GloballySerial 
evaluationManager -nodeTypeUntrusted on "transform"; // Untrusted
}}}
2 : C++/Python API methods:
Individual nodes can also be scheduled at compile time by overriding the {{{MPxNode::schedulingType}}} function. Functions should return one of the enumerated values specified by {{{MPxNode::schedulingType}}}. See the [[Maya 2016 MPxNode|http://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__cpp_ref_class_m_px_node_html]] class reference for more details.
!!!Safe Mode
On rare occasions you may notice that during manipulation or playback, Maya changes from Parallel to Serial evaluation. This is due to Safe Mode, which attempts to trap errors leading to instabilities, such as crashes. If Maya detects that multiple threads are attempting to simultaneously access a single node instance at the same time, the evaluation is forced to Serial execution to prevent problems.
You can manually set this:
{{{
// Disable the EM:
evaluationManager –mode “off”;
// Disable EM-accelerated manipulation
evaluationManager -man 0;
}}}
While Safe Mode catches many problems, it cannot catch them all. Therefore, we’ve also developed a special Analysis Mode that performs a more thorough and costly checks of your scene.
!!!Custom Evaluators
Once the EG has been created, Maya can target evaluation of node sub-graphs.  Currently it’s not possible for users to author new custom evaluators. In the future, we may extend OpenMaya to support such extensions.
Use the [[evaluator|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/evaluator.html]] command to query the available evaluators and query or modify evaluators that are currently active.
{{{
import maya.cmds as cmds
# Returns a list of all evaluators currently available
cmds.evaluator( query=True )
# Result: [u'dynamics', u'pruneRoots', u’deformer’, u’cache’, u’null’] #

# Returns a list of all evaluators currently enabled.
cmds.evaluator( query=True, enable=True )
# Result: [u'dynamics', u'pruneRoots'] #
}}}
!!!GPU Override
To accelerate deformations in Viewport 2.0, Maya 2016 contains a custom deformer evaluator that targets mesh deformations on the GPU using OpenCL.
We’ve included GPU implementations for 6 of the most commonly-used deformers in animated scenes:
#skinCluster
#blendShape
#cluster
#tweak
#groupParts
#softMod
Even if your scene uses only supported deformers, it’s possible the GPU override may not be enabled due to unsupported node features. 
For example, with the exception of softMod, deformers must currently apply to all vertices and there is no support for incomplete group components.
Additional deformer specific limitations are listed below:
|Deformer |Limitation |
|skinCluster |bindMethod basePoints bindPose bindVolume dropOff driverPoints envelope heatmapFalloff influenceColor lockWeights maintainMaxInfluences maxInfluences nurbsSamples paintWeights paintTrans smoothness useComponents weightDistribution weightList |
|blendShape |baseWeights baseOrigin icon inputTarget inputTargetGroup inputTargetItem targetWeights normalizationId normalizationGroup origin parallelBlender supportNegativeWeights targetOrigin topologyCheck useTargetCompWeights |
|cluster |n/a |
|tweak |Only relative mode is supported. relativeTweak must be set to 1. |
|groupParts |n/a |
|softMod |Only volume falloff is supported when distance cache is disabled.  Falloff must occur in all axes. Partial resolution must be disabled |
There are a few other reasons that will prevent GPU override from accelerating your scene:
*Meshes are not sufficiently dense. 
**For deformations to happen on the GPU, your mesh needs over 500/2000 vertices, on AMD/NVIDIA hardware respectively. You can change the threshold by using the {{{MAYA_OPENCL_DEFORMER_MIN_VERTS}}} environment variable.
*Downstream nodes in your graph read deformed mesh results. 
**No node, script, or viewport can read the mesh data computed by the GPU override. This means that GPU override won’t be able to accelerate portions of the deformation chain upstream of nodes, such as {{{follicle}}} or {{{pointOnPolyConstraint}}}, as it requires information about the deformed mesh.
*Meshes have animated topology changes. 
**If your scene animates the number of mesh edges, vertices, and/or faces during playback, corresponding deformation chains will be removed from the GPU deformation path.
*Maya Catmull-Clark Smooth Mesh Preview is used. 
**We’ve included acceleration for OpenSubDiv (OSD)-based smooth mesh preview but there is currently no support for Maya’s legacy Catmull-Clark.
*Unsupported streams are found
**Depending on the drawing mode you’ve selected for your geometry (e.g., shrunken faces, hedge-hog normals, etc) and the material assigned to your geometry Maya will need to send different information to the graphics card
*Back face culling is enabled
*Driver-related issues.
To troubleshoot factors limiting use of GPU override for your particular scene, use the [[deformerEvaluator|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/CommandsPython/deformerEvaluator.html]] command.
{{{
deformerEvaluator; // For each selected node print the chain or a reason it is not supported
deformerEvaluator -chains;// Print all active deformation chains
deformerEvaluator -meshes; //Print a chain for each mesh or a reason it is not supported
}}}
!!!Dynamics Evaluator
Maya 2016 has limited support for animated dynamics. Although scenes with Bullet rigid bodies and Bifrost fluids should evaluate correctly, attempting to playback or manipulate scenes with animated legacy (particles, fluids) or nucleus (nCloth, nHair, nParticles) nodes will disable the EM and revert to DG-based evaluation.
(As of Maya 2010)
Many UI elements support the '{{{backgroundColor}}}' argument. which lets you assign them custom colors.  But if you change the color of one of the controls after the ui has been created and displayed, Maya won't alway update the color.  How can one make this happen?

I've found a few solutions: 
*If possible, you can delete and rebuild the ui with the new colors.  This only works if the ui is built via a class in Python with the colors stored so they can be rebuilt... or if the color values are saved on some node in the scene for retrieval.
*You can author 'refresh' code for each ui element that will 'disable' and then 're-enable' it, via the {{{enable}}} argument.  I've found that this will force the new colors to display.
However, there appear to be certain ui elements, the {{{checkBox}}} is an example of one, that refuse to update color, no matter what:  Once the color is assigned, it won't change until Maya is closed and restarted.  I have yet to find a way around that one :(
The {{{rebuildCurve}}} command is great for turing one type of curve into another
For example, you can use this process to turn a deg1 curve into a deg3 curve, that exactly matches the deg1 curve's cv's:
*Draw deg1 curve -> {{{fitBspline}}} = b-spline curve -> {{{rebuildCurve}}} = deg3 curve.
However, when the deg1 curve is at certain extreme shapes, can cause the rebuilt deg3 curve to "freak out".  Weird sine-waves can form in it, and it can really look horrible.

To fix, when using the {{{rebuildCurve}}} command, be sure to set {{{-fitRebuild 0}}}.  This seems to just "fix" the problem:
{{{
rebuildCurve -fitRebuild 0 "myCurve";  // plus all the other args you'd call to...
}}}
While the thread is old:
http://www.3dbuzz.com/vbforum/showthread.php?157484-Maya-Demystified-The-Hidden-Marking-Menus
It still seems to be valid in 2013:  Talks about all the secret marking menus Maya has, what's in them, how to access them.
We have encountered quite a few bugs in Maya 2014 that go like this:
*Rig an asset:  Joints + skinning + rigging.
*Animator references it into their scene.  All is well, animation is performed on it.
*Iterate on rigged version:  New mesh, new joints, new skinning, all is well.
*One day, when a new version is submitted, when the animator opens their animation scene, all the skinning on the asset is broken:  Stretching, moving with the wrong joints.
Doing research, the reference appears to be remapping which joints influence which verts:  All the rigging and animation is ok, but the skinning is broken.
Reloading references doesn't fix.
A solution, as found by Mason Sheffield, is to simply rename the {{{skinCluster}}} nodes in the original rigged scene:  This 'breaks' any connections the reference tried to make to them, and 'fixes' the skinning in those scenes.  
Example code:
{{{
# Rename all skinClusters to get around nasty reference bug.
import pymel.core as pm

skinClusters = pm.ls(type='skinCluster')
for sc in skinClusters:
    oldName = sc.nodeName()
    newName = sc.rename("%s#"%oldName)
    print "Renamed %s -> %s"%(oldName, newName)
}}}
And while this works, I //still// found an issue:
Had another example where sceneA had a vehicle referenced in, and animated.  sceneB referenced sceneA (as... reference).  sceneA's skinning looked ok after a rig update, but sceneB was all missed up, with missing mesh, and misnamed mesh:  A steering-wheel was now called 'rope' (for example).  Super crazy reference chaos.
The fix was to go into sceneA and reload the vehicle ref, then resave sceneA:  Upon opening sceneB, the vehicle looked correct.  //Really// Maya?
I jumped from __Maya 2008 \ ~WinXP \ 32bit__ to __Maya 2010 \ Vista \ 64bit__.  
Suddenly, code I was using to communicate to Maya from an [[external Python session|How can I have Wing send Python or mel code to Maya?]] quit working.
Based on [[this post|http://area.autodesk.com/forum/autodesk-maya/python/commandport-doesnt-work-in-vista-32/]], I figured out how to address.

When in the past, I could open a socket like this in Maya:
{{{
commandPort -name ":6000" -echoOutput;
}}}
I now had to call to {{{commandPort}}} twice, with slightly different syntax:
{{{
commandPort -name "127.0.0.1:6000" -echoOutput;
commandPort -name ":6000" -echoOutput;
}}}
Now in Python in my [[external application|How can I have Wing send Python or mel code to Maya?]], I could reconnect to the socket and start sending commands again:
{{{
import socket
maya = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
maya.connect(("127.0.0.1", 6000))  # this is where it would fail
maya.send('python("print \'foo\'")')
}}}
It should be noted I still can't //open// the {{{commandPort}}} via Python in Maya:  I still have to do that via mel.  If I do it in Python, I get no errors, I can query its existence, but I can't send commands to it... :(
----
That post has an update about setting a Maya environment variable like so, for correcting ~IPv6 & ~IPv4 issues (for which I have no concept)
{{{
MAYA_IP_TYPE = ipv4 
}}}
Testing new syndication to my blog.  Please ignore.
MEL Wiki
Niec visual review of new Maya commands added since Maya 2012:
http://www.garyfixler.com/MayaPythonCommands.html
----
Also see:
* [[Timeline of Maya features]]
Nice visual review of new Maya features added since Maya 2016
https://area.autodesk.com/timelines/maya/
----
Also see:
* [[Timeline of Maya commands]]
This used to be a standalone plugin, but at some point this because a real menu command:
Mesh -> Transfer Vertex Order
https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=GUID-8CF82D97-203C-4056-BEF2-EA0CD27336ED

If two mesh, with the same vert counts, and same topology have different //vert orders//, you can use this to fix them.

To use it, select three verts on the source mesh, and the same three verts on the target mesh.
It calls to the runttimeCommand
{{{
TransferVertexOrder
}}}
Which wrappers the proc:
{{{
global proc int performTransferVertexOrder()
// in performTransferVertexOrder.mel
}}}
Which loads the {{{meshReorder}}} plugin, and calls to it based on the above selection.
----
Also see:
* [[Transfer vertext order by matching UVs]]
Here's an easy, although not perfect solution for matching the topology of meshB to meshA, presuming they have the same UVs.  Their vert count/topo can be completely different.
*Dupe the orig meshes, hide them.
*Picking both mesh at the same time, apply new UV's to them, so they both get the exact same UV data.
*Select meshA, add-select meshB.
*Access the 'Mesh -> Transfer Attributes' tool options.
**Set 'Vertex position: On'
**Set 'Sample space:' to UV
**Press 'Transfer'
*meshB should not match the topology of meshA.  You can then transfer meshB's original UV's back onto it from the hidden copy.
On sphere's, this works great :)  Talking with modelers, they've had less successful conversions on complicated character heads.
----
Also see:
* [[Transfer vertex order on mesh with the same vert counts]]
*http://inventwithpython.com/blog/2012/07/18/using-trigonometry-to-animate-bounces-draw-clocks-and-point-cannons-at-a-target/
**Great visual reference using Python and Pygame to show how to use trig.
*[[Autodesk University: ICE Design Tools|http://area.autodesk.com/userdata/blogs/marks/ToddAkita_ICE_Design_Tools.pdf]] (pdf) - This is for Softimage's ICE, but the reference it has for trig and vectors is great.
There can come a time where something goes wrong with your shelves:  Maybe one is empty, when it should be full.  Here's a way to sort of 'reset' your shelves to get them working again.

Let's say your shelf is called "{{{myAwesomeTools}}}":
First off, close Maya.  Maya saves its prefs when it closes, and we need to modify them.
Second, (after making a backup of it...) open in your favorite text editor:
{{{
C:\Users\<userName>\Documents\maya\<mayaVersion>\prefs\userPrefs.mel
}}}
Do a text search for the name of your shelf.  You should find it at minimium in two different locations, looking like this:
{{{
 -sv "shelfFile34" "shelf_myAwesomeTools"
}}}
and this:
{{{
 -sv "shelfName34" "myAwesomeTools"
}}}
These are both calls to {{{optinVar}}}s:  The first one stores the name of the {{{shelf_myAwesomeTools.mel}}} script that holds all your script contents.
The second one stores the actual name of the shelf you see on-screen, "{{{myAwesomeTools}}}".
It's possible if there are problems, you'll find these on other lines as well:  ''REMOVE THEM ALL FROM THAT FILE'', save, and restart Maya.

From there, your shelf will be missing:  You can now 'Load Shelf...' from the menu to bring it back.  It should be a happy shelf.

These problems can also happen when calling to the mel {{{loadNewShelf}}}, that is trying to load a shelf from some network location at startup:  Using this system to clean out the old {{{optionVar}}}s, you can get your networked shelf back.
/***
Required by Tiddlyspot
***/
//{{{

config.options.chkHttpReadOnly = false; // make it so you can by default see edit controls via http

if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false; // disable autosave in d3

config.tiddlyspotSiteId = 'mayamel';

// probably will need to redo this for TW 2.2
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[Welcome to Tiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[Welcome to Tiddlyspot]] ");
}

merge(config.shadowTiddlers,{

'Welcome to Tiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),

'TspotControls':[
 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<<br>>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n")

});
//}}}
https://www.youtube.com/watch?v=CysqH3gw9TI&feature=youtu.be&t=1
https://www.youtube.com/watch?v=CbxE5AUh1IM
As of Maya 2019, USD is not yet built in.  Autodesk does supply the source you can build here:
* https://github.com/Autodesk/maya-usd
* https://github.com/Autodesk/maya-usd/blob/master/doc/build.md
Also this:
* https://github.com/vfxpro99/usd-build-club
* https://github.com/vfxpro99/usd-build-club/wiki/USD-on-Windows
If you want to comment, please see the post over my my [[main blog|http://www.akeric.com/blog/?p=304]].

Python has the ability to create generator functions.  See Python docs here:
http://docs.python.org/tutorial/classes.html#generators
http://docs.python.org/reference/expressions.html#generator-expressions
http://docs.python.org/reference/expressions.html#yieldexpr
http://docs.python.org/reference/simple_stmts.html#yield

What are they?  From the Python docs (above):
<<<
"Generators are a simple and powerful tool for creating //iterators//. They are written like regular functions but use the //yield// statement whenever they want to return data. Each time //next()// is called, the generator resumes where it left-off (it remembers all the data values and which statement was last executed)..."
<<<

You can imagine that they are like a normal function looping over a list of items, or doing some work on each of the items in the list.  The difference is, rather than looping over the //whole list// when the function is executed, the function pauses after each loop, waiting for you to tell it to continue.

This allows you the ability to make tools that can execute over a list of items, only executing on the next item when you tell it too.

Example below.  In this example, the user writes a generator function that will create a locator placed at each vert passed into the function.  But the locators are created one by one, only when we call to the generator function:
{{{
# Python code

import maya.cmds as mc

// Define our generator function
def generateLocs(verts):
    for v in verts:
        pos = mc.pointPosition(v, world=True)
        loc = mc.spaceLocator(position=pos)
        // Use 'yield' instead of 'return':
        yield loc[0]
}}}
Now put the code to use:
{{{
# First, select a polygonal object.  Then convert to verts:
verts = mc.ls(mc.polyListComponentConversion(toVertex=True), flatten=True)

// create our generator object called "loc":
loc = generateLocs(verts)

// each time we call to loc.next(), a new locator is created, base on
//  the next vert in the list:
print loc.next()
# locator1
print loc.next()
# locator2
print loc.next()
# locator3

# etc...
}}}
For some reason my brain has a hard time remembering how layered textures work whenever I use them:
* Layered texture, in the Attribute Editor, stack from left to right, in order.  If a real stack, this would be top to bottom.
* The Blend Mode for a given texture dictates how it will react with the texture to its right (or, under it) ->  The Blend Mode for the right (bottom) most texture has no impact, since there is nothing for it to blend with.
* Setting a blend mode to "None" will not display any textures to the right of that one.
* Values can go over 255 if you use "Add".  Use "Lighten" to keep them at 255 max.
Docs
*[[Maya 2019 Module docs|http://help.autodesk.com/view/MAYAUL/2019/ENU/?guid=__developer_Distributing_Maya_Plug_ins_Maya_module_paths_folders_and_html]]
*[[Older Maya blog post|http://around-the-corner.typepad.com/adn/2012/07/distributing-files-on-maya-maya-modules.html]]
Modules are a way to distribute custom plugins, but can also be used to configure global Maya script & plugin paths in general.
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.  A Maya 'module' defines a whole directory structure based around certain conventions discussed below.

Check out the {{{moduleInfo}}} command as well.

This will print all the module info on your machine:
{{{
import os
import pymel.core as pm

print "MAYA_MODULE_PATH:"
for pth in sorted(os.getenv("MAYA_MODULE_PATH").split(";")):
    print pth
for mod in sorted(pm.moduleInfo(listModules=True)):
    print mod
    print "\t", pm.moduleInfo(moduleName=mod, path=True)
    print "\t", pm.moduleInfo(moduleName=mod, definition=True)
}}}
!userSetup.py
Each module can have its own unique userSetup.py file in it, placed in the 'scripts' folder (see below).
For these to execute properly on Maya startup, a common method inside userSetup.py is this:
{{{
import maya.cmds as mc
def main():
    # put all your code in here
mc.scriptJob(event=['DagObjectCreated', main], runOnce=True)
}}}
Maya's module system will auto-execute every userSetup.py it can find in every module it finds, and the scriptJob will wait for a dag object to be made before it runs it.  This helps to get Maya fully initialized.
Note, for some reason I've had really mixed results. //Sometimes// I need to add this to my module for it to work:
{{{
PYTHONPATH += scripts
}}}
And other times not.  Very confused.
Also note:  If there are any bugs in the {{{userSetup.py}}} code, it will fail //silently//.

It should also be noted that userSetup.py files added via modules may not be accessible on the path once Maya starts:  Since Python can't have conflicting module names, my //guess// is the last module loaded is the one Maya sees.  Even though I have my own unique userSetup.py, this is what I get when I print it:
{{{
import userSetup
print userSetup.__file__
C:\Program Files\Autodesk\Maya2018\plug-ins\MASH\scripts\userSetup.pyc
}}}
!.mod files
Maya looks for {{{.mod}}} files living in paths defined by the {{{MAYA_MODULE_PATH}}} env var, which can be customized by the user.  
* ''Note'':  It appears that Maya will read any text file as a 'module file' based on the defined path:  Even if it doesn't have a {{{.mod}}} extension, it will be treated a a module file.  Meaning, if you rename a module file to test something, thinking Maya will ignore it, it won't.
By default, Maya looks for modules here:
{{{
C:\Program Files\Autodesk\Maya20XX\modules
}}}
If you install an external application, often times they'll place their .mod files in that dir.
The syntax per line is:
{{{
+ [Conditions] ModuleName ModuleVersion ModulePath
}}}
With additional path modifications listed below it.
!Conditions
Optional, but include:
* {{{PLATFORM}}} : win32/win64/mac/linux
* {{{LOCALE}}} : en_US/js_JP/zh_CN
* {{{MAYAVERSION}}} : Version of Maya.
!~ModuleName
This can seemingly be anything, just a string that distinguishes this module from others.
!~ModuleVersion 
Some value that can tick up over time?  I've seen 1.0, 1.0.1, etc...
!~ModulePath : 
While I've read:
<<<
This is either the relative path to the root of the module relative to this .mod file location, or it is the full path to the root of the module.  
<<<
By default, it will look for the below subfolders relative to it, and add them to the appropriate Maya paths with no extra work on your side:
*{{{/icons}}} <- any required icons go in here.  Presmably modifies Maya's icon path.
*{{{/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.  Updates Python's {{{sys.path}}} var, and Maya's {{{MAYA_SCRIPT_PATH}}} var.
*{{{/presets}}} <- any (mel?) presets go here.
*{{{/plug-ins}}} <- plugins go here.  Updates Maya's {{{MAYA_PLUG_IN_PATH}}}.
So for example, if this was all you had in your module (the name and 'module path'):
{{{
+ myModule 1.0.1 ../../../someOtherDir/thisDir
}}}
It will take the //current location of the module// go up 3 subfolders, then back down into /someOtherDir/thisDir, and then add the above list of subdirs to the appropriate paths

You can also use windows env vars, and add to them with relative pathing:
* {{{=}}} is single variable assignment
* {{{+=}}} is to append to an existing variable
* {{{:=}}} is single variable assignment, but for a path relative to the module location
* {{{+:=}}} is to append a path ''relative to the module location'' to an existing variable
So you could do something like this:
{{{
PYTHONPATH+=c:/someDir/someAbs/path
PYTHONPATH+:=../../someRelative/path
}}}
@@Very Important@@ : It appears (based on experimentation) that empty lines in the module file will stop it from evaluating. So for example, this will properly update the {{{PYTHONPATH}}}:
{{{
+ my_module 1.0.0 C:\path\to\the\module
PYTHONPATH+=C:\custom\python\path
}}}
But this will not:
{{{
+ my_module 1.0.0 C:\path\to\the\module

PYTHONPATH+=C:\custom\python\path
}}}
My guess is the empty line designates a new set of module conventions.
----
You can also embed windows env vars into the path name, like:
{{{
+ myTools 1.0.1 %TOOLS_ROOT%\path\to\my\stuff\
}}}
!Recursively searching subfolders
''Note'' : These rules only applies to the hard-coded paths, like {{{scripts}}}, {{{presets}}}, {{{plug-ins}}}, etc:  They do //not// apply to any environment variable updates (like, appending to the {{{MAYA_SCRIPT_PATH}}}).

By default, Maya ignores all subfolders included in a module. In order for Maya to consider subfolders of a module, you must request recursive behavior for that folder using the {{{[r]}}} keyword; for example, as follows:
{{{
[r] scripts: ..\..\CommonFiles\scripts
}}}
This is handy specifically for the {{{\scripts}}} dir, since that's where mel scripts go:  By making it recursive, you can make all the subfolders you need, put your mel in there, and not have to mess with the {{{MAYA_SCRIPT_PATH}}}.
@@HOWEVER@@ : If you store //Python// modules in the {{{/scripts}}} dir, and set that up as your root package, you would //not// want to make it recursive with the {{{[r]}}} syntax, since it would add every subfolder to the Python path ({{{sys.path}}}), defeating the purpose of the root package, and possibly creating module name conflicts with other packages.
----

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.
{{{
+ MAYAVERSION:2016 PLATFORM:win64 LOCALE:ja_JP CustomModule 3.10.5 ..\CustomModule\Modules\win64
}}}
----
! Personal Examples

This is what I do at home.
High level concepts:
* {{{userSetup.py}}} must live in the {{{\scripts}}} dir to auto-execute on Maya start.
* But I want  the {{{\scripts}}} dir to be next to all of my other Python packages. So, I setup something that looks like this:
* {{{C:\path\to\warpcatpy}}} : The root package for all my code, and the root namespace when imported into Maya.
** {{{\scripts}}} : {{{userSetup.py}}} lives in here.
** {{{\some_package}}} : This is where I start adding all my other packages.
** {{{\etc...}}}
* I create the below module at this location, since it's auto-seen by Maya on starup:
** {{{C:\Users\Warpcat\Documents\maya\modules\warpcatpy.mod}}}
* Inside it (first line), I store the absolute path to the root folder of my module
* The second line, I store the relative to the {{{\scripts}}} dir.
* The third line I modify Maya's Python path, so that the directory //above// my module is added to the path.  Because of that, I can now import my module into it's own namspace in Maya:
{{{
import warpcatpy.some_package.some_module as awesomeSauce
}}}
The module {{{warpcatpy.mod}}} : 
{{{
+ WarpcatPy 1.0 C:\path\to\tools\maya\warpcatpy
scripts: .\scripts
PYTHONPATH+:= ..
}}}





On the top menubar (which can be hidden from view) Maya exposes a 'Workspace:' section, with a lock icon to its right. 
I've never quite understood the point of the lock icon, but now that it's become clear, I'm writing it down since I know I'll forget again:
* Lock icon gray : Unlocked.
* Lock icon blue : Locked.
* Locked behavior:
** Can't dock or undock panels: They're locked in place.
** Opening a new scene won't switch to the workspace stored in that scene.
* Unlocked behavior:
** Can dock/undock panels.
** When opening a new scene, Maya can switch to the workspace in that scene.
!User Defined Colors:
Maya has the {{{color()}}} command, which can be used to apply 'user defined' colors, or arbitrary RGB colors, to an objects 'default' wirefreame color.  
>An object has two types of wireframe colors:  'Default' and 'Override'.  It displays the 'default color' by default (yah!).  The {{{color()}}} command can change the //default color// to a 'user defined color' (but even when changed it's still considered the 'default color' of the object).  It can be 'overridden' later with an 'override color' (see below...)
!!!By User Defined Color Index
There are a total of eight 'user defined colors' that the default can be set to:
{{{
# Color our sphere:
import maya.cmds as mc
mc.color('pSphere1', userDefined=4)
}}}
It should be noted that calling the color command on an object //without// a userDefined value will remove the 'user defined' color form it, and set it back to the //original default value//, which is whatever color the user has set in their color preferences for the 'active' and 'inactive' wireframe colors:
{{{
# Remove any user-defined colors on the object:
mc.color('pSphere1')
}}}
Behind the scenes, the {{{color}}} command is setting the values on the {{{dagNode}}} {{{useObjectColor}}} (enum, 1/0?) and {{{objectColor}}} (short, 0-7):
{{{
setAttr ".useObjectColor" 1; // 1 = by index, 2 = rgb
setAttr ".objectColor" 3; // the user-defined index
}}}
* Interesting #1 :  If you apply the color command to the //transform//, it will set those attrs on both the transform //and// shape node.
* Interesting #2 : If you use the {{{color}}} command to set  the color, then {{{useObjectColor=1}}} in  the ma file (above).  But if you use the same command to remove the color, Maya physically removes the {{{useObjectColor}}} command entirely from the ma, which presumably sets it to it's default, 0.

User defined colors can be changed via: 
*'Window -> Settings/Preferences -> Color Settings -> General -> User Defined'
This action calls to the {{{displayRGBColor()}}} command, which physically saves them on disk.  For example:
{{{
# Make userDefined4 red:
mc.displayRGBColor('userDefined4', 1, 0, 0)
}}}
If you want to print a list of all the user-defined colors, you can go about it this way:
{{{
for c in mc.displayRGBColor(list=True):
    if c.startswith('userDefined'):
        print c,
}}}
{{{
userDefined1 0.055 0.445758 1
userDefined2 0 1 1
userDefined3 0 1 0
# etc...
}}}
You can make new colors as well, and even label them 'userDefined':
{{{
mc.displayRGBColor('userDefined9', 1,0,1, create=True)
}}}
However, the {{{color()}}} command won't recognize them :-(
{{{
mc.color('pSphere1', userDefined=9)
# Error: RuntimeError: line 1 of <maya console>: The valid range of user defined colors is [1-8]. # 
}}}
When you make a new {{{displayRGBcolor}}}, or modify an existing one, Maya saves these in the mel script: 
{{{
\documents\maya\20XX\prefs\userRGBColors.mel
}}}
Note that 'override colors' (below) will override what the {{{color}}} command does.
!!! By Arbitrary RGB Color
Running this command:
{{{
mc.color(rgbColor=(0,0,1))
}}}
Will update the node like so:
{{{
setAttr ".wireColorRGB" -type "float3" 0 0 1 ;
setAttr ".useObjectColor" 2; // 1 = by index, 2 = rgb
}}}
----
!Color Assignment Priority
Note, that this is the priority order of how Maya will draw colors, from highest priority to lowest:
* shape node color
** transform node color
*** color applied via the {{{color}}} command (above).
----
!Override colors
All nodes, transform or shape level have the ability to have their 'Drawing Overrides' turned on:  
*Pick Node -> Attribute Editor -> Object Display -> Drawing Overrides -> Enable Overrides
Which supports by indexed color, or by rgb.
!!!By Index
This gives the mesh the ability to set it's {{{overrideEnabled}}} & {{{overrideColor}}} attributes via the Attribute Editor (from [[dagNode|https://help.autodesk.com/cloudhelp/2020/ENU/Maya-Tech-Docs/Nodes/dagNode.htm]] inheritance).  To do it with code:
{{{
mc.setAttr('pSphereShape1.overrideEnabled ', True) # Must do first
mc.setAttr('pSphereShape1.overrideColor', 3)
}}}
'override color' 0 is the 'default color' (which could be set to one of the 'user defined colors') described above.  Any other value applied will override the default.

There are a total of 32 'override colors', index 0-31.  It should be noted that the last 8 map directly to the 'user defined' colors described above.  So subtracting 24 from an 'override color' will give you the corresponding 'user defined color'.

This raises the question:  How are the other 24 'override colors' values set?  I haven't found them in the 'displayRGBColor' list.  But you can //see// them (index 1-23) in the Color preferences:
*Window -> Settings/Preferences -> Color Settings -> Active -> Active Index Palette
*Window -> Settings/Preferences -> Color Settings -> Inactive -> Inactive Index Palette
They're also visible in the Layer Editor, when you change the color of a layer (since the layer node wires itself into the overrideColor attr of the nodes it controls...)
But I can't figure out how to //change// them.  Hard coded in Maya somewhere?  They're //not// in {{{userPrefs.mel}}}...

And again, if you try and set an 'override color' attribute past its maximum (even if you've tried to make a new userDefined color, like in the example above), Maya gets angry:
{{{
mc.setAttr('pSphereShape1.overrideColor', 32)
# Error: RuntimeError: line 1 of <maya console>: Cannot set the attribute 'pSphereShape1.overrideColor' past its maximum value of 31. # 
}}}
!!! By RGB
Like above, you can enable them via the Attribute Editor, or via code:
{{{
mc.setAttr('pSphereShape1.overrideEnabled ', True) # Must do first
mc.setAttr('pSphereShape1.overrideRGBColors', True) # to switch from index to RGB
mc.setAttr('pSphereShape1.overrideColorRGB ', 1,0,0, type='float3' )
}}}
----
Also see:
* [[How can I set the RGB wireframe color of the selected nodes?]]
Starting in Maya 8.5, Maya has Python integration via a 'maya Python site-package'.  How exactly is that accessed?
>See [[How is Python initialized in Maya?]]
In the Script Editor, in a //Python// tab, the way to access the data at the topmost level is:
{{{
import maya
}}}
The {{{maya}}} //site-package// actually points to a lot of other data, and you'd rarely access it that way.  To understand more about how packages work, see my Python wiki [[here|http://pythonwiki.tiddlyspot.com/#Packages]]
The {{{maya}}} site-package is located here (Vista, Maya 2009):
{{{
C:\Program Files\Autodesk\Maya2009\Python\Lib\site-packages\maya
}}}
The most common usage of the mel scripter would be to import the {{{cmds}}} sub-package, and put it in a different namespace in the process (to something shorter, since you'll be typing it a lot):
{{{
import maya.cmds as mc
# other common imports:
import maya.mel as mm
import maya.OpenMaya as om
# etc...
}}}
In the maya site-package, there are other sub-packages and modules.  What do they do?
{{{
help(maya) # generated data for the below list (or browse do the dir shown above).
}}}
*/{{{app}}}  :  Sub-package.  In addition to having modules, it has its own sub-packages.Researching.  Seems to have to do with UI, Mental Ray, startup code, and a few other odds and ends. 
*/{{{cmds}}}  :  Sub-package.  Gives the user full access to all the mel //commands//, through Python.  __Probably the most common one used for the mel scripter.__  However, if you look at this packaged on disk, you'll notice there are no sub-modules are packages:  To understand how this works, please see this subject: [[How does Maya populate maya.cmds?]]
*/{{{mel}}}  :  Sub-package.  Provides the {{{eval}}} function, allows the user to evaluate mel //code// inside of Python;
*{{{standalone}}}  :  Module:  Allows the usage of Maya's Python in external IDE's.  Allows the execution of "Maya Python" outside of Maya.  However, if you look at this packaged on disk, you'll notice there are no sub-modules are packages:  Maya works some voodoo to (my guess) generate the function calls on the fly when Maya starts.
*{{{utils}}} : Module:  'General utility functions that are not specific to the Maya Command or the ~OpenMaya API'.  Currently, maya.utils contains three routines relevant to threading.  This module will likely expand in future versions (writing as of version 2008)
*{{{OpenMaya}}}  :  Module:  Provides Maya API access, via Python.  Contains fundamental classes for defining nodes and commands and for assembling them into a plug-in.
*{{{OpenMayaAnim}}}  :  Module:  Contains classes for animation, including deformers and inverse kinematics.
*{{{OpenMayaCloth}}}  :  Module:  Presumably contains classes for Cloth
*{{{OpenMayaFX}}}  :  Module:  Contains classes for Dynamics.  
*{{{OpenMayaMPx}}}  :  Module:  Used for authoring 'scripted plugins'
*{{{OpenMayaRender}}}  :  Module:  Contains classes for performing rendering functions.
*{{{OpenMayaUI}}} :  Module:  Contains classes necessary for creating new user interface elements such as manipulators, contexts, and locators.
Once a package\sub-package\module is loaded, you can access the functions via dot notation:
{{{
import maya.cmds as mc
everything = mc.ls()
}}}
The custom cut of Python that Maya runs is located here:
{{{
C:\Program Files\Autodesk\Maya<version>\bin\mayapy.exe
}}}
----
Also see:
*[[How can I execute Maya's version of Python outside of Maya?]]
(WIP)
----
*[[Scripts]]
**[[Procedures]] (defined)
***[[Commands]]
***[[Arguments]]
***[[Return Values]]
***[[Procedures]] (executed)
**[[Commands]]
***[[Flags]]
****[[Arguments]]
***[[Arguments]]
***[[Return Values]]
[[Script Paths]]
[[Scope]]
To make a hotkey (all via Python code), you first must follow these steps:
# Make a {{{runTimeCommand}}} wrappering your Python code.
# Make a {{{nameCommand}}} wrappering the {{{runTimeCommand}}}.
# Make a {{{hotkey}}} wrappering the {{{nameCommand}}}.
----
!{{{runTimeCommand}}}
The [[runtTimeCommand|http://download.autodesk.com/global/docs/maya2012/en_us/Commands/runTimeCommand.html]] command will, from the docs: "Create a MEL command given the specified name. Once the command is created you can invoke it like any other MEL command."  
Note that for some reason there are only mel docs for it, even though it is exposed in Python.
If coding the command, if the 'category' flag is set to "User" (and it probably should), the commands will be saved in this mel script:
{{{
\Documents\maya\<version>\prefs\userRunTimeCommands.mel
}}}
In the Hotkey Editor, when the user interactively creates a command in the 'User' category, they are saved via this method in the above mel script as well.
Simple example:
{{{
# The runTimeCommand name is always uppercase per Maya conventions.
rtc = mc.runTimeCommand("MyAwesomeCommand", commandStr="import myModule; myModule.main()", commandLanguage="python", category="Custom Scripts")
}}}
This is then passed to the 'nameCommand' below.
!{{{nameCommand}}}
[[nameCommand|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/nameCommand.html]] is a command that from the docs "...creates a {{{nameCommand}}} object. Each {{{nameCommand}}} object can be connected to a hotkey."
When the user creates one of these from mel or Python, they are saved in this mel script:
{{{
\Documents\maya\<version>\prefs\userNamedCommands.mel
}}}
It should be noted that the command can accept either a mel or Python command to execute via it's {{{-sourceType}}} and {{{-command}}} flags.  That being said, when I pass in Python commands directly, I get errors: Which is why we wrapper the Python call first in a {{{runTimeCommand}}}. 
Example using the {{{RunTimeCommand}}} above:
{{{
# The 'name' should be the name of the RunTimeCommand + "NameCommand" on the end, per Maya conventions:
nameCommandCmd = mc.nameCommand("MyAwesomeCommandNameCommand", command=rtc)
}}}
!{{{hotkey}}}
The [[hotkey|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/hotkey.html]] command will, from the docs "...sets the single-key hotkeys for the entire application."
When the //user// creates a hotkey in the Hotkey Editor, or via this command, they are stored in this mel script:
{{{
\Documents\maya\<version>\prefs\userHotkeys.mel
}}}
Example, using the nameCommand, from above:
{{{
mc.hotkey(keyShortcut="x", name=nameCommandCmd )
}}}
After the hotkey has been created, you'll need to call to the command separately to save it (via {{{autoSave=True}}}).
*UV's are assigned to "UV sets" on a mesh. The terminology is misleading though. They aren't assigned to a DG node "set", but are actually attributes on the mesh itself. The attributes themselves are derived from the "controlPoint" DG node that is one of many that defines a polygonal mesh. So, if you want to write a script dealing with UV's, then you need to understand how the attributes relate.
*All meshes have a UV set by default. It is expressed by an attribute called ".uvSet[0]". Since there can be multiple UV sets on a mesh, the ".uvSet[]" attr is an array attribute, allowing multiple connections. Below, I'll illustrate the various components of this multi-attribute:
*object.uvSet[0] : This is the base multi-attribute, won't return anything specifically, you must query the below attrs:
*object.uvSet[0].uvSetName : This is a string that defines the name of the ".uvSet[0]" attribute.
*object.uvSet[0].uvSetPoints[0] : A float array attribute that returns the two floating point UV values of the ".uvSetPoints?[]" value specified.
*object.uvSet[0].uvSetPoints[0].uvSetPointsU : A float attribute that returns the U value of the current ".uvSetPointsp?[]" attr.
*object.uvSet[0].uvSetPoints[0].uvSetPointsV : A float attribute that returns the V value of the current ".uvSetPointsp?[]" attr.
*object.uvSet[0].uvSetTweakLocation : can't figure this one out
*object.currentUVSet : A string attribute, that returns the name of the "current" UV set.

Mel commands relating to UV's organization:
*getAttr : for getting the above values
*polyUVSet
*polyEditUV
*polyClipboard
*polyTransfer
----
Also see:
*[[How can I handle uv sets?]]
It seems on a more regular occurrence than it should be, polygonal mesh normals get "screwed up".  Black splotches at certain camera angles, white splotches, look funny when exported to the game engine, etc.
*Maya has an internal concept of "locking\freezing" \ "unlocking\unfreezing" normals.  The UI calls it {{{Lock Normals}}} \ {{{Unlock Normals}}}, but the actual mel command {{{polyNormalPerVertex}}} calls it {{{-freezeNormal}}} and {{{-unfreezeNormal}}}.  
*To the best of my ability, it appears that when normals are "locked\frozen", and something happens to change the polygonal mesh, you can //possibly// end up with "bad normals", since they no longer are oriented correctly to the surface's topology.  
*Usually when this happens, simply unlocking\unfreezing the normals fixes the issue.  
*In addition, these concepts are decoupled from the ideas of "smooth" and "hard" normals, which will also be discussed in a bit.
Some peculiarities:
*If you unlock\unfreeze the normals, Maya does some mojo in the mayaAscii file:  For each normal "unlocked\unfrozen", it sets it's .n (.normal) attr to an obscure float of {{{1e+020}}}.  See some example code:
{{{
setAttr ".n[0:165]" -type "float3"  1e+020 1e+020 1e+020 1e+020 1e+020 1e+020 etc...
}}}
*Apparently this tells Maya to calculate the normal on the fly (thus they are "unlocked\unfrozen").
*If the user applies any modifications to the normals, then these absolute values are stored directly in the .ma, and the 1e+020's are removed from those normals .  But at any point, if the user "unlocks" the normal values, they will change back to a 1e+020 looking state again.
It gets stranger:
*However, "smooth\hard" looking normal can be the result of two different modifications:  
**Softening\Hardening the polygonal //edges// (via the {{{polySoftEdge}}} mel command).  This modifies the edge values, but //not// the normal values.
**Directly modifying the normals themselves like {{{polySetToFaceNormal}}} command.  However this command sets both the edges //and// the normals.
*What's confusing about this, is that one is an //edge// operation, and one is a //normal// operation //and// an edge operation.  The end result always looks like locked\unlocked normals, but that's really not the case.  To add even more confusion, the Maya UI sticks the {{{polySoftEdge}}} command in the 'Edit Polygons -> Normals -> Soften/Harden' UI.  So even the //user// thinks this is a normal operation.  But if you closely inspect the .ma file, you'll see that in fact, it's modifing the edge attributes (see below):
** To make sense of the .ed (.edge) attribute values, they go in three-number sequences:
**First number = polygon first point of edge. Second number = polygon second point of edge.  Third number = polygon the hard or smooth information of edge.
After executing this code (via the above-mentioned 'Soften/Harden'UI'), to make our "normals soft":
{{{
polySoftEdge -a 180;
}}}
Our mayaAscii looks like this (sample line):
{{{
// notice every 3rd number is 1:
setAttr ".ed[0:165]"  0 1 1 1 2 1 2 3 1 etc...
}}}
After executing this code, to make our "normals hard:"
{{{
polySoftEdge -a 0;
}}}
Our mayaAscii looks like this (sample line):
{{{
// notice ever 3rd number is 0:
setAttr ".ed[0:165]"  0 1 0 1 2 0 2 3 0 etc...
}}}
*As you can tell, the .ed attr is being modified, and //not// the .n (.normal) attribute (take my word for it, will save on how much I have to paste in here).
Things to consider:
*{{{unlocking}}}\{{{unfreezing}}} normals always sets them back to their "Maya interpreted" version, which is usually a "smooth" looking version.
*If after unlocking the normals, some still look "hard", then it's probably the edge values themselves that are "hard", and they need to be set as well.
Just some notes as I fight my way through using these ui elements...
!Takeaways
From the below notes, this is what I've learned:
*{{{modelPanel}}} is a //command// that creaets both a {{{modelEditor}}} with the same name, and a menubar.
*You can then use the {{{modelEditor}}} command on the {{{modelEditor}}} name the {{{modelPanel}}} command returns.
*When combined with 'isolate selection', Maya gets buggy fast.
*The UI elements the {{{modelPanel}}} command generates don't delete from the scene when their parent UI does:  They stay persistent.  You need to delete them just like windows before you rebuild them (if building by hard-coded name).
*Making a {{{modelEditor}}} without a {{{modelPanel}}} is doable, but you won't be able to change transform manips in that panel, or undo, etc.
**This also saves you from not having to worry about deleting the {{{modelPanel}}}.  But the lack of functionality is annoying.
*The {{{modelPanel}}} can be though of as the frame with menu around the view.  The {{{modelEditor}}} is the 3d view itself.
!Notes
*You can use {{{lsUI}}} and {{{getPanel}}} commands to query {{{modelPanels}}}.
*{{{modelPanel}}} : 
**Controls what camera is used.
**Can query it's {{{modelEditor}}}.
**Can set the vis state of the menu bar.
**Can be 'torn off'
**It's existence allows you to 'undo' in the panel, change transformation tools via hotkeys, etc.
*{{{modelEditor}}} : 
**Automatically created when a {{{modelPanel}}} is.  But can be made by itself as well.
**Controls the look and display of objects\items in the 3d view.  Many options for this.  It //is// the 3d view:  If there is a {{{modelEditor}}} associated with a {{{modelPanel}}} (that has its menu-bar visible), if you delete the {{{modelEditor}}} the {{{modelPanel}}}'s menu bar will still be there.
**Controls how 'isolate select' works (which can get buggy...)
**Can query it's parent layout (which is actually the parent layout of the {{{modelPanel}}}).
**Can query the {{{modelPanel}}} its parented to, presuming it is parented.
**Can work with {{{selectionConnection}}}s.
**Can change or query the camera.
**Can parent itself to a {{{modelPanel}}}?  This doesn't seem to work based on my tests.
**If //not// parented to a {{{modelPanel}}}, user can't change manipulator\transformation tools via hotkeys, can't undo while focus is in the panel.
*It appears you can use the {{{modelEditor}}} command directly on a {{{modelPanel}}} panel and it will work on the associated {{{modelEditor}}} editor.  Because of that, you really don't need to every query the {{{modelEditor}}} editor directly to work on it.
Hierarchy Option A:
*{{{modelPanel}}}
**{{{modelEditor}}}
Hierarchy Option B:
*{{{modelEditor}}}
Some confusing things:
*Making a {{{modelPanel}}} will also create a {{{modelEditor}}} with the same name.  This seems to be a true statement.
*Making a {{{modelEditor}}} will //not// create a {{{modelPanel}}}:  It will be 'unparented'(?).  But still seems to work just fine (other than the limitations discussed at the top).
*Creating a {{{modelPanel}}} first, and a {{{modelEditor}}} second, and when making the {{{modelEditor}}} specifying the previously made {{{modelPanel}}} as the '{{{panel}}}' //''doesn't'' seem to parent them together//:  Querying the {{{modelPanel}}}'s {{{modelEditor}}} will return the name of the {{{modelEditor}}} created at the same time as the {{{modelPanel}}} (which seems to have the same name).
*If you want a {{{modelPanel}}} //and// associated {{{modelEditor}}}, just make a {{{modelPanel}}}, then query it's {{{modelEditor}}} and modify it.
*As mentioned above, if a {{{modelEditor}}} is //not// parented to a {{{modelPanel}}}, user can't change manipulator\transformation tools via hotkeys, can't undo while focus is in the panel.
*{{{modelPanel}}}'s, when created, __won't be removed when the parental window is__: They stay persistent in the scene.
**To delete them, you need to use {{{deleteUI}}} with the {{{panel}}} argument.
**If building {{{modelPanel}}}'s by name, since they won't auto-delete, if you try to rebuild a window with the same {{{modelPanel}}} name you will get a name-clash.  You'll need to delete the old {{{modelPanel}}} first.  To do this, you need to delete it before you run your 'window delete code'.  For example:
{{{
# Delete modelPanel first:
if mc.modelPanel("myModelPanel", query=True, exists=True):
    mc.deleteUI("myModelPanel", panel=True)
# Then delete window:
if mc.window("myWindow", exists=True):
    mc.deleteUI("myWindow")
}}}
**If you don't follow this order of operation, Maya will get plenty angry.
Bugs (Maya 2010, Win7) :
*In some cases, if you make a custom {{{modelPanel}}}, after that window is displayed, making a new scene will crash Maya.  Writing the same code without a {{{modelPanel}}} (just using a {{{modelEditor}}}) causes the crash to go away.
*If you have 'isolate select' turned on in a custom {{{modelEditor}}} and try and save the scene with the window open, it will hang Maya on save.  If 'isolate select' is turned off, Maya will save just fine.  See this subject:
**[[Maya crash on save with isolate select in custom window?]]
More 'isolate select' chaos:
*If you have a {{{modelPanel}}} that has isolate selection enabled (via it's {{{modelEditor}}}) then close the window, like described above the modelPanel will stick around in memory.  Furthermore, the sets in the Outliner that were used for the isolate selection will stay around as well, and they're non-deletable.  To clean up everything properly, you need to author a callback that executes on window close that will first turn off {{{isolateSelect}}} on the panel, then delete the panel itself.
*I have a subject to author such callbacks [[here|How can I execute code when a window is deleted?]].  Note for it to work with the modelPanels you need to follow the steps for using the {{{OpenMayaUI}}} callback system:  The {{{scriptJob}}} system won't work for some reason.  My guess is the {{{OpenMayaUI}}} system is executed first, or maybe even before the window exits.
It should first be said that {{{source}}} isn't a //mel command//.  From the docs:
>"It is a directive that tells the interpreter what to compile and execute."
So there you go:  {{{source}}} is a //directive//, not a //command//.
And while there are docs on {{{source}}}, you can only find them by browsing to them through the help UI (By Category -> Language -> Scripting).  Executing either of these won't work:
{{{
help -doc source;
// Error: help  source; // 
// Error: Line 1.12: Syntax error // 

help -doc "source";
// Result: "source" is a keyword in the MEL language.  
Please see the manual "Using Maya: MEL" for more information // 
}}}
----
''All about {{{source}}}'':
More info from the docs:
<<<
If a script has "source" directives in it, then all sourced files will be compiled at the same time as the script. This will happen regardless of where the source directive appears in the file. And, if the script with the "source" directives is re-run, the sourced files will not be recompiled. It is possible to override this behavior by enclosing the source directive inside of an "eval" statement. 
<<<
You '{{{source}}}' a script.  This has several telling benefits:
*If you've modified a script since Maya has opened, this exposes the updates to Maya.
*If there are any {{{global procs}}} in the script, those global procs are now visible outside of the script ({{{local procs}}} are never seen outside of a script).
*The contents of a script that //aren't// in any procedures is executed.
----
''Sourcing, and relative sourcing'':
When you run {{{source}}}, it's expecting the script to be part of the script path.  Pretend his has been added to the {{{MAYA_SCRIPT_PATH}}}:
{{{
c:/scripts/mel
}}}
When you {{{source}}}, it will look in that dir (and every other dir in the path) for the script in question.  
However, you can use relative paths when sourcing.  Say you make this subdir, but it //is not// part of the path:
{{{
c:/scripts/mel/test
}}}
And in that new dir, you make a script called {{{tester.mel}}}.  You can still source that script like so:
{{{
source "test/tester.mel"
}}}
----
''Some more practical examples.''  Presume they all live in Maya's script path:
----
Here is a script called {{{foo.mel}}} that has no procedure declarations, but it does call to an external proc called {{{shoe()}}}.
{{{
// C:/scripts/foo.mel
print "I'm foo.mel\n";
shoe();
}}}

Here is a script called {{{goo.mel}}} that has a local proc called {{{myLoc()}}}, and two {{{global procs}}}: {{{shoe()}}} and {{{goo()}}}.
{{{
// C:/scripts/goo.mel
proc myLoc()
    {
    print "I'm local\n";
    }
global proc shoe()
    {
    print "shoe!\n";
    }
global proc goo()
    {
    print "goo!\n";
    }
}}}
Since there is no {{{global proc foo()}}} in {{{foo.mel}}}, it means you can't execute {{{foo();}}} to get any result.  You need to source the script instead:
{{{
source foo;

// I'm foo.mel
// Error: file: C:/scripts/foo.mel line 3: Cannot find procedure "shoe". // 
}}}
As you can see, source executes all the commands in {{{foo.mel}}}.  But {{{shoe()}}} fails even though it's a {{{global proc}}} in {{{goo.mel}}}, why?  As mentioned above, until a script is {{{source}}}d, any global procs inside of it won't be seen.  Try:
{{{
source goo;
// nothing seems to happen...
}}}
Nothing seems to happen because there are no //commands//, or //executed procedures// in the root of {{{goo.mel}}}.  However, {{{source}}} has now declared all the //defined global procedures// (not local procedures) in {{{goo.mel}}}, so they can be seen outside of {{{goo.mel}}}.  So now we can re-try {{{foo.mel}}}:
{{{
source foo;

// I'm foo.mel
// shoe!
}}}
Sourcing {{{foo.mel}}} executes the print command, and it also executes the procedure call to {{{shoe()}}}.
----
''{{{source}}} Gotcha's'':
When you {{{source}}} a script, it triggers a "chain of sourcing" persay.  Every {{{source}}} statement in every script that is called to is executed.  For example, presume you have 4 scripts, and each script has a {{{source}}} directive calling to the next:
{{{
source script1;
}}}
This then sources {{{script2}}}, which sources {{{script3}}}, which sources {{{script4}}}.
However, at any point if any of those scripts tosses an error, it'll bring the whole system to a halt.
On one hand, it's good because it identifies broken code.  On the other hand, in a shared codebase, it can introduce some nasty consequences:
Say you have a user interface script, that is updated by multiple people.  One of the people puts {{{source}}} in the UI script to look to some external code.  If that external code ever breaks, the whole UI script will fail to execute until the issue is resolved.




In Maya 2017, they changed how 'docking windows' worked.  Up until then it used a {{{dockControl}}} command.  But that all changed to start using {{{workspaceControl}}} command.
<<<
Check out this newer subject: [[Creating a dockable window via PySide and workspaceControl]]
<<<
However, as expected, it was fraught with many issues and hacks to get working correctly.  Below is a nugget of data that distills the important stuff.
{{{
import pymel.core as pm

wcName = "myAwesomeWorkspaceCtrl"
layoutCommand = "import someModule; someModule.buildSomeDock()"

if pm.workspaceControl(wcName, exists=True):
    # Already exists, just reopen it.
    pm.workspaceControl(wcName, edit=True, restore=True, r=True, visible=True)
else:
    # This defines where it should dock by default:
    targetControl = pm.mel.eval('getUIComponentDockControl("Channel Box / Layer Editor", false);')
    # If we put 'uiScript=layoutCommand' in this line, Maya errors.
    wc = pm.workspaceControl(wcName, loadImmediately=True,
                             initialWidth=256, minimumWidth=256)
    # Have to split this into two lines to have docking work  :S
    # Put the 'uiScript=layoutCommand' in this line, so Maya stores it,
    # but it won't execute it via and edit call...
    pm.workspaceControl(wc, edit=True, tabToControl=(targetControl, -1), r=True,
                        label=dockLabel, uiScript=layoutCommand) 
    # ...so, now we execute the layoutCommand here, which adds itself to
    # the workspaceControl.  Otherwise the repopulated dock can look
    # super weird, untill Maya is restarted.  WTF!?!!?
    exec layoutCommand
    # This may be needed to, to display it correctly, can't remember.
    pm.workspaceControl(wcName, edit=True, restore=True)
}}}
Maya then handles all the state saving:  Is it docked, undocked (floating)?  Is it closed, or open?  All of that is saved in here (by default):
{{{
C:\Users\epavey\Documents\maya\2018\prefs\workspaces\Maya_Classic.json
}}}
Few other odd things:
* It //appears// that Maya loads {{{workspaceControll}}}s before it parses Maya modules and userSetup.py on startup.  What this means is, if you have a custom window the {{{workspaceControl}}} should execute, and that path to that python script is added either via a Maya module or in your userSetup.py, it will fail, since that path doesn't yet exist in Maya.  //However//, it looks like the userSetup.''mel'' //does// get ran before anything else, so if you call to your userSetup.py in your userSetup.mel, the paths will be added before the workspaces are created.  Madness.
* There is a {{{retain}}} creation arg that is True by default.  If True, when you {{{close}}} a {{{workspaceControl}}}, it just 'hides' it, doesn't actually delete it.  However, I've found that if you set {{{retain=False}}}, upon a close/reopen, the contents of the {{{workspaceControl}}} may not populate at all, or, populate very strangely.  I tried also running a {{{deleteUI}}} on the contents of the {{{workspaceControll}}} as well, at the time of closure, but this seemed to make things worse / not help.
* As you'll notice above, I needed to split the creation of the {{{workspaceControll}}} into three separate lines:
** Create it, as an empty shell.
** Edit it, passing in the values to make it unique + where / how to dock.
** Re-execute the {{{layoutCommand}}}, to actually build the contents.
** This combination took many hours to find... other combinations did not work at all.
* {{{workspaceControll}}}s actually act just like windows.  Meaning, you can create them as the 'core container' for your other UI controls.  Note, you do not (and shouldn't) use the {{{showWindow}}} command on them.
----
Other Resources:
* How to do this without the {{{MayaQWidgetDockableMixin}}}: 
** https://gist.github.com/liorbenhorin/69da10ec6f22c6d7b92deefdb4a4f475
* Newer Maya's come with examples:
** {{{C:\Program Files\Autodesk\Maya2019\devkit\devkit\pythonScripts\dockableWorkspaceWidget.py}}}
Commands I've ran into that have no online documentation.  You can still run the {{{help}}} command on them though.
{{{
repeatLast
}}}
{{{
superCtx
}}}
{{{
polySelectSp
}}}
{{{
texSmoothContext
}}}
{{{
dagObjectHit
}}}
----
The {{{file}}} command has an undocumented flag\arg, which is "{{{removeDuplicateNetworks}}}", or "{{{-rdr}}}".  This is what you call to to combine shading networks (and others) when a file is imported or referenced into the scene.  The docs have a single call to the short name {{{-rdr}}}, but fail to expand on it.  Typing {{{help file}}} will give the coresponding long name.
----
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 11/04/2022 17:25:06 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . | ok |
| 11/04/2022 17:44:16 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . | ok |
| 11/04/2022 17:44:29 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . | ok |
| 11/04/2022 17:44:59 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . |
| 04/05/2022 11:33:51 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . |
| 13/05/2022 16:43:30 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . |
| 23/05/2022 12:16:12 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . | ok |
| 23/05/2022 13:46:15 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . |
| 03/06/2022 09:37:32 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . | ok |
| 03/06/2022 09:40:05 | WarpCat | [[/|http://mayamel.tiddlyspot.com/]] | [[store.cgi|http://mayamel.tiddlyspot.com/store.cgi]] | . | [[index.html | http://mayamel.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);			
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}

/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.0|
|''Date:''|May 5, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (#3125)|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 0,
	date: new Date("May 5, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (#3125)'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	refreshOptions: function(listWrapper) {
		var uploadOpts = [
			"txtUploadUserName",
			"pasUploadPassword",
			"txtUploadStoreUrl",
			"txtUploadDir",
			"txtUploadFilename",
			"txtUploadBackupDir",
			"chkUploadLog",
			"txtUploadLogMaxLine",
			]
		var opts = [];
		for(i=0; i<uploadOpts.length; i++) {
			var opt = {};
			opts.push()
			opt.option = "";
			n = uploadOpts[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
}

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,null,null,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		bidix.upload.httpUpload(rssUploadParams,convertUnicodeToUTF8(generateRss()),callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == httpStatus.NotFound)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


/* don't want this for tiddlyspot sites

// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");

*/


//}}}


Starting in Maya 2011, Autodesk changed the UI system to [[Qt|http://qt.nokia.com/]] (made by Nokia), which can be authored in Maya via ~PyQt or ~PySide (see [[Python & Qt versions in Maya]]). I found this reference discussing some of the high level features:
*http://area.autodesk.com/blogs/stevenr/maya_2011_highlight_qt_user_interface
*http://www.creativecrash.com/tutorials/using-qt-designer-for-mel-interfaces
*http://www.creativecrash.com/maya/tutorials/scripting/mel/c/maya-mel-qt-and-you-interfacing-with-the-qt-designer
Qt Designer can be found here:
{{{
C:\Program Files\Autodesk\Maya20XX\bin\designer.exe
}}}
!!!Rough overview:
*Access 'File -> New...' in QT Designer.  
**From the 'New Form' menu, select "Dialog without Buttons", since you want to make a dialog, not a "Main Window" (since Maya is 'the main window').  
**Start draggin' widgets and whatnot onto the canvas.
**To get things like 'radio buttons' to work together, they need to be added to a 'group box'.
**''To add Maya commands to the widgets'', you need to add a 'dynamic property' to them that emulates the corresponding Maya command parameter.
***Select the widget, and press the big green ''+'' button on the right of the Property Editor. 
***The 'Property Name' would be set to the equivalent Maya control parameter, like {{{command}}}, {{{changeCommand}}}, etc.
****What is important is the //prefix of the parameter//: 
*****If you're going to call to a mel command, then the prefix is a dash: {{{-}}}
*****If you're going to call to a Python command, the prefix is a plus: {{{+}}}
****For example, a Maya [[button|http://download.autodesk.com/global/docs/maya2014/en_us/Commands/button.html]] equals a Qt 'Push Button' ([[QPushButton|http://qt-project.org/doc/qt-5/QPushButton.html]]).  The command parameter on a Maya button is {{{command}}}.
****So the Qt button dynamic 'Property Name' would be set to "{{{-command}}}" if calling to //mel//, or "{{{+command}}}" if calling to Python (both without quotes), and the 'Property Type' would be set to "String".
***This will create a new "Dynamic Properties" section at the bottom of the Property Editor.
***The value of this property is the mel or Python code to be executed, surrounded by quotes.  
***For example, to have a button do something via //mel// ({{{-command}}}) , you'd enter:
****{{{"print \"qt button!\\n\""}}}
***For the button to do something via //Python// ({{{+command}}}), you'd enter:
****{{{"import myModule; myModule.main()"}}}
****{{{"import maya.cmds as mc; print mc.ls()"}}}
***Why does it need to be in quotes?  Because when Maya later creates the UI, it executes something like this to hook up the commands:
****Mel: {{{# Executing: button -e -command "print \"qt button!\\n\"" "MayaWindow|Dialog|pushButton" # }}}
****Python: {{{# Executing: import maya.cmds as cmds;cmds.button('MayaWindow|Dialog|pushButton',e=True,command="import maya.cmds as mc; print mc.ls()") #}}}
***You can see Maya print this stuff if you set the {{{loadUI}}} {{{verbose}}} flag to {{{True}}} during execution.
**Save out .ui file.
*Author Maya code (see [[Loading Qt .ui files]] for an example) to load said ui.  If you save the the Python module in the same dir as the .ui file, it makes it easy to find.  
Other Notes:
*Not all Qt Widgets support dynamic properties that Maya can use.  Or at least I haven't figured out how yet.  For example, if you make a 'Spin Box' widget, creating a {{{-changeCommand}}} dynamic property on it will cause an error in Maya.  If you need access, see: [[PySide : Access Qt .ui widget data in Maya]].
*F3 is used to enter 'Edit Widgets' mode (the default), F4 is used to enter 'Edit Signals/Slots', which allows you to have one widget control another.  When a you do something to a widget (like press a button) it can emit a signal.  That signal can be connected to a slot on another widget, to cause some other action.  Behind the scenes, slots are really just instance methods.
!!!Supported Widgets:
From the above link, transcribed and updated list of Qt widgets supported in Maya. Question marks just mean I haven't tested those yet.  But generally, like mentioned, they map to the corresponding Maya control command argument(s):  Check the Maya docs.
If you need to provide for a unsupported widget, check out the docs on [[MQtUtil.registerUIType|http://docs.autodesk.com/MAYAUL/2014/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_qt_util.html,topicNumber=cpp_ref_class_m_qt_util_html37e048f2-39a4-4c09-89cf-1a704d4512c8,hash=aa90598cb1a43550924960d4d7c239b77]]
| Designer Name | Designer Category| Maya Equivalent | Dynamic Property | Supports '#1' argument passing? |
| | | |
| push Button | Buttons | button | -command | No |
| radio button | Buttons | radioButton | -onCommand -offCommand -changeCommand | |
| check box | Buttons | checkBox | ? | |
| list view | Item Views (Model Based) | textScrollList | ? | |
| combo box | Containers | optionMenu | ? | |
| group box | Containers | NONE |  | |
| tab widget | Containers | tabLayout | ? | |
| line edit | Input Widgets | textField | -changeCommand | |
| text edit | Input Widgets | scrollField | -enterCommand, -changeCommand | No |
| spin box | Input Widgets | NONE |  | |
| double spin box | Input Widgets | NONE |  | |
| dial | Input Widgets | NONE |  | |
| horizontal scroll bar| Input Widgets |  NONE |  | |
| vertical scroll bar| Input Widgets |  NONE |  | |
| vertical slider | Input Widgets | intSlider | -dragCommand, -changeCommand | Yes |
| horizontal slider | Input Widgets | intSlider | -dragCommand, -changeCommand | Yes |
| vertical line | Input Widgets | NONE |  | |
| horizontal line | Input Widgets |  NONE |  | |
| label | Display Widgets | NONE |  | |
| progress bar | Display Widgets | progressBar | ? | |
| main window  |  |  window |  | |
----
Note you can also use the [[loadUI|http://download.autodesk.com/global/docs/maya2014/en_us/CommandsPython/loadUI.html]] command to get a list of supported pairs:
{{{
from pprint import pprint
import pymel.core as pm
pairs = sorted([item.split(":") for item in pm.loadUI(listTypes=True)])
pprint(pairs)
}}}
{{{
[[u'CharacterizationTool', u'characterizationToolUICmd'],
 [u'QCheckBox', u'checkBox'],
 [u'QComboBox', u'optionMenu'],
 [u'QDialog', u'window'],
 [u'QLabel', u'text'],
 [u'QLineEdit', u'textField'],
 [u'QListWidget', u'textScrollList'],
 [u'QMainWindow', u'window'],
 [u'QMenu', u'menu'],
 [u'QProgressBar', u'progressBar'],
 [u'QPushButton', u'button'],
 [u'QRadioButton', u'radioButton'],
 [u'QSlider', u'intSlider'],
 [u'QTextEdit', u'scrollField'],
 [u'QWidget', u'control'],
 [u'TopLevelQWidget', u'window']]
}}}
So my //guess// is, if you want to have a widget interact with Maya, it needs to be one of those...
I'm mainly going to focus on doing this in ~PyMel, since it makes it so much easier and less clunky than regular python/mel implementation.

A {{{uiTemplate}}} is simply a way to enforce consistency between later-create ui elements.  For example, if you need to make a hundred buttons, all with a given background color, you can add that color to the {{{uiTemplate}}} button definition, saving you from having to copy that {{{backgroundColor=}}} over and over again.

~PyMel has a good example at the bottom of [[this page|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/ui.html]], but here it is copied over (and slightly modified):
{{{
import pymel.core as pm

template = pm.uiTemplate( 'ExampleTemplate', force=True )
template.define( pm.button, width=100, height=40, align='left' )
template.define( pm.frameLayout, borderVisible=True, labelVisible=False )

with pm.window(menuBar=True,menuBarVisible=True) as win:
    # start the template block
    with template:
        with pm.columnLayout( rowSpacing=5 ):
            with pm.frameLayout():
                with pm.columnLayout():
                    pm.button( label='One' )
                    pm.button( label='Two' )
                    pm.button( label='Three' )
            with pm.frameLayout():
                with pm.optionMenu():
                    pm.menuItem( label='Red' )
                    pm.menuItem( label='Green' )
                    pm.menuItem( label='Blue' )
# add a menu to an existing window
with win:
    with pm.menu():
        pm.menuItem(label='One')
        pm.menuItem(label='Two')
        with pm.subMenuItem(label='Sub'):
            pm.menuItem(label='A')
            pm.menuItem(label='B')
        pm.menuItem(label='Three')
}}}
Docs:
*[[uiTemplate|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/classes/pymel.core.uitypes/pymel.core.uitypes.UITemplate.html]] (used above, via the {{{with}}} statement)
*[[setUITemplate|http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/PyMel/generated/functions/pymel.core.windows/pymel.core.windows.setUITemplate.html]] (the clunkier implementation)
*If you run a system call from Maya, that call is executed in what Maya thinks is the "[[current working directory]]" (see notes).
*You need to change this path to the path that your .bat file lives in, so when it is executed, it operates on the correct files. Presuming you have this file: {{{c:/temp/myBat.bat}}}, and that .bat executes operations on the contents of {{{c:/temp}}}, you'd need to tell Maya that {{{c:/temp}}} is the [[current working directory]] with the {{{chdir}}} command:
{{{
chdir "c:/temp/";
string $result = system("myBat.bat");
}}}
*//If// it is important that the original "[[current working directory]]" isn't changed, query its value before executing the above code (using the {{{pwd}}} command), then using {{{chdir}}} set it back to it's original value after code execution.
*Also realize that if  you use "{{{start}}}" in front of your .bat execution like this: '{{{system("start myBat.bat");}}}', this will cause the .bat file to be executed //external// to Maya.  Meaning, you won't be able to capture the return value (if there is one), nor will Maya wait for the bat file to finish before advancing to the next line of mel.
Also see:
*[[I'm passing a very long command to Windows via the mel system command, but nothing is happening, why?]]
Python seems to have no built in libraries for doing vector math.  Options for doing it in Maya are listed [[here|How can I do vector math via Python in Maya?]].

Based on a pile of info I got from the below website, I made a few functions to do this without the overhead of the external libraries \ Maya's API.  This is not an exhaustive list of vector functions, just some of the more popular ones.
*http://www.geocities.com/SiliconValley/2151/math3d.html
*http://mathworld.wolfram.com/topics/VectorAlgebra.html
*http://en.wikipedia.org/wiki/Euclidean_vector
*http://chortle.ccsu.edu/VectorLessons/vectorIndex.html
*[[Autodesk University: ICE Design Tools|http://area.autodesk.com/userdata/blogs/marks/ToddAkita_ICE_Design_Tools.pdf]] (pdf) - This is for Softimage's ICE, but the reference it has for trig and vectors is great.
Honestly, I'd just use Maya's {{{OpenMaya.MVector}}} class to do most of this, but it's good to understand what's going on under the hood:
{{{
# Simple vector math implemented in straight Python, no Maya API \ mel calls.
import math

def mag(vector):
    """
    Return the length of a vector.
    """
    val = 0
    for v in vector:
        val += pow(v,2)
    m = math.sqrt(val)
    return m

def unit(vector):
    """
    Find unit vector.
    Vector if length = 1
    """
    len= mag(vector)
    u = [vec/len for vec in vector]
    return u
    
def dot(v1, v2):
    """
    Find dot product of two vectors

    Easy description:  If v1 & v2 are pointing the same direction, this value
    is 1.0.  If they're pointing opposite directions, this value is -1.  If they
    are at right angles, the value is 0.  So you can use this test to see how well
    two vectors align.

    More complex description:
    It can be interpreted as the *length* of the projection of 
    the unit of v1 onto v2 when the two vectors are placed so that 
    their tails coincide. 
    http://mathworld.wolfram.com/DotProduct.html

    Your right index finger is V1, your left index finger is V2.  Place
    both knuckles on top of one another, pointing in different directions.
    Find the unit vector of V1 (right finger), and mark that point on your 
    right finger.  Then...
    Draw a perpendicular line from the mark on your right finger (V1) to your
    left finger (V2), and mark a second point.  The dot product is the distance 
    from your left knuckle to that second point.  GOT IT?
    """
    v1 = unit(v1)
    v2 = unit(v2)
    d=0
    for i in range(len(v1)):
        d = d + v1[i]*v2[i]
    return d

def cross(v1, v2):
    """
    Find cross product of two vectors.
    Vector which is perpendicular to the two.
    If v1 and v2 are the vectors defining two edges
    of a triangle (both with the same root), then the
    cross could be considered the normal of the triangle.
    """
    v = [v1[1]*v2[2] - v1[2]*v2[1],
        v1[2]*v2[1] - v1[1]*v2[2],
        v1[0]*v2[1] - v1[1]*v2[0]]
    return v

def normal(p1, p2, p3):
    """Given three 3d points, find the vector normal to those points"""
    va = [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]]
    vb = [p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]]
    n = cross(va, vb)
    return n

def sum(v1, v2):
    """
    Add two vectors together
    """
    ret = []
    for i in range(len(v1)):
        ret.append(v1[i] + v2[i])
    return ret

def difference(v1, v2):
    """
    Subtract v2 from v1.
    """
    ret = []
    for i in range(len(v1)):
        ret.append(v1[i] - v2[i])
    return ret
}}}
{{{
vec1 = [1,0,0]
vec2 = [0,1,0]
point1 = [0,0,0]
point2 = [0,1,0]
point3 = [1,0,0]
    
print "Length vec1:", mag(vec1)
print "Unit vec1:", unit(vec1)
print "Dot vec1, vec2:", dot(vec1, vec2)
print "Cross vec1, vec2:", cross(vec1, vec2)
print "Normal of point1, point2, point3:", normal(point1, point2, point3)
print "Sum of vec1, vec2:", sum(vec1, vec2)
print "Difference of vec1, vec2:", difference(vec1, vec2)

# Length vec1: 1.0
# Unit vec1: [1.0, 0.0, 0.0]
# Dot vec1, vec2: 0
# Cross vec1, vec2: [0, 0, 1]
# Normal of point1, point2, point3: [0, 0, 1]
# Sum of vec1, vec2:  [1, 1, 0]
# Difference of vec1, vec2: [1, -1, 0]
}}}
Vectors variables are nice way to package up triplets of floats.  You can also make arrays out of them, which is about the only way in mel you can get an "array of arrays".  They are accessed a bit differently from other variable types though:
{{{
vector $spam = <<0,1,2>>;
print ($spam.x);
//  0 // success
	
vector $foo[] = {<<0,1,2>>, <<3,4,5>>};
print ($foo[0].x);
// fail
vector $boo = $foo[0];
print ($boo.x);
// 0 // success
}}}
----
More from the Maya docs:

Suppose you have a vector initialized as follows:
{{{
vector $myvector = <<1,2,3>>; 
}}}
To replace the right component of {{{$myvector}}}, 3, with a new value such as 7, use this technique to preserve the other two components:
{{{
$myvector = <<$myvector.x,$myvector.y,7>>; 
}}}
This statement is //incorrect//:
{{{
$myvector.z = 3; 
}}}
When making UI's in Maya, it's nice to be able to //see// what you want to make, //before// you make it.  

Below, is a visual style guide.  The name of the ELF (extended layer framework) //command// required to make the specified //control// is in the window's titlebar.  Listed in a pseudo-categorized-alphabetical order.  

The code I used to make these (for the most part) is actually at the bottom of each command's help page, in the [[Maya Docs|http://download.autodesk.com/us/maya/2008help/wwhelp/wwhimpl/js/html/wwhelp.htm]] (cut, paste, execute, screengrab, upload).  I've also listed the name of each control above the images, for page text-searching purposes.  Some of the command names I've linked to other tiddlers, when I have more detailed info on that subject.

I'll be updating this over time, in alphabetical order.

----
attrColorSliderGrp
[img[http://farm3.static.flickr.com/2004/2179176830_688e16a8ae.jpg?v=0]]
attrControlGrp
[img[http://farm3.static.flickr.com/2011/2178386185_db74568327.jpg?v=0]]
attrFieldGrp
[img[http://farm3.static.flickr.com/2199/2179176868_9fecfc694c.jpg?v=0]]
attrFieldSliderGrp
[img[http://farm3.static.flickr.com/2120/2178386227_3baecbea24.jpg?v=0]]
attrNavigationControlGrp
[img[http://farm3.static.flickr.com/2079/2179176896_9639174130.jpg?v=0]]
----
button
[img[http://farm3.static.flickr.com/2250/2178692575_d583ef2e87.jpg?v=0]]
----
canvas
[img[http://farm3.static.flickr.com/2361/2179482410_4c72a3a903.jpg?v=0]]
----
channelBox
[img[http://farm3.static.flickr.com/2252/2179482426_fd22b3dc1f.jpg?v=0]]
----
checkBox
[img[http://farm3.static.flickr.com/2147/2178692637_323a58fee8.jpg?v=0]]
checkBoxGrp
[img[http://farm3.static.flickr.com/2131/2178692649_1de57f7df6.jpg?v=0]]
----
cmdShell
[img[http://farm3.static.flickr.com/2366/2193178370_7d6274877a.jpg?v=0]]
commandLine
[img[http://farm3.static.flickr.com/2098/2193178442_0254a07798.jpg?v=0]]
----
colorIndexSliderGrp
[img[http://farm3.static.flickr.com/2419/2192390061_273a5f3b91.jpg?v=0]]
colorSliderButtonGrp
[img[http://farm3.static.flickr.com/2218/2192390075_9a25049691.jpg?v=0]]
colorSliderGrp
[img[http://farm3.static.flickr.com/2402/2193178426_6cc2856be1.jpg?v=0]]
----
floatField
[img[http://farm3.static.flickr.com/2318/2196649760_cc3e5faa0e.jpg?v=0]]
floatFieldGrp
[img[http://farm3.static.flickr.com/2093/2195860611_c3f06546c0.jpg?v=0]]
floatScrollBar
[img[http://farm3.static.flickr.com/2212/2196649798_3758e59521.jpg?v=0]]
floatSlider
[img[http://farm3.static.flickr.com/2366/2195860637_e76d00ef1f.jpg?v=0]]
floatSlider2
[img[http://farm3.static.flickr.com/2407/2196649832_9a8f7073d5.jpg?v=0]]
floatSliderButtonGrp
[img[http://farm3.static.flickr.com/2140/2197257833_8e1a6777f3.jpg?v=0]]
floatSliderGrp
[img[http://farm3.static.flickr.com/2358/2197257845_0a5ecdd556.jpg?v=0]]
----
[[gradientControl|How can I author a 'gradientControl' into my UI?]]
[img[http://farm3.static.flickr.com/2067/2197352149_10f912b166.jpg?v=0]]
[[gradientControlNoAttr|How can I author a 'gradientControlNoAttr' into my UI?]]
[img[http://farm3.static.flickr.com/2270/2198047046_1b6111924d.jpg?v=0]]
----
helpLine
[img[http://farm3.static.flickr.com/2268/2197257875_b91d4e71b0.jpg?v=0]]
----
hudButton
[img[http://farm3.static.flickr.com/2179/2197257895_51b76918d4.jpg?v=0]]
hudSlider
[img[http://farm3.static.flickr.com/2360/2198047104_8739cbe494.jpg?v=0]]
hudSliderButton
[img[http://farm3.static.flickr.com/2413/2197257913_d1f585865e.jpg?v=0]]
----
iconTextButton
[img[http://farm3.static.flickr.com/2299/2197923567_0da721f624.jpg?v=0]]
iconTextCheckBox
[img[http://farm3.static.flickr.com/2282/2197923619_7f84042e6a.jpg?v=0]]
iconTextRadioButton & iconTextRadioCollection
[img[http://farm3.static.flickr.com/2008/2198710702_6252c0e3c0.jpg?v=0]]
iconTextStaticLabel
[img[http://farm3.static.flickr.com/2155/2198710716_8e70edb0d8.jpg?v=0]]
----
image
[img[http://farm3.static.flickr.com/2341/2197923601_3f9cf70dc0.jpg?v=0]]
----
intField
[img[http://farm3.static.flickr.com/2039/2228702575_c792f0207d.jpg?v=0]]
intFieldGrp
[img[http://farm3.static.flickr.com/2198/2229496534_019d69c52a.jpg?v=0]]
intScrollBar
[img[http://farm3.static.flickr.com/2412/2229496550_0d2defdc43.jpg?v=0]]
intSlider
[img[http://farm3.static.flickr.com/2115/2229496562_2121818c40.jpg?v=0]]
intSliderGrp
[img[http://farm3.static.flickr.com/2159/2229496588_50b98c74bc.jpg?v=0]]
----
layerButton
[img[http://farm3.static.flickr.com/2369/2228702639_ba2439f1c4.jpg?v=0]]
----
messageLine
[img[http://farm3.static.flickr.com/2133/2229496616_9f7475b945.jpg?v=0]]
----
nameField
[img[http://farm3.static.flickr.com/2045/2229496640_8d3e831b64.jpg?v=0]]
----
//Many// more to come....
Starting with Maya 2012 (?.. I skipped 2011...), I would get this warning when I tried to translate skinned joints:
{{{
Warning: Joint orientation aborted. Cannot orient joints bound to skin.
}}}
It appears there is a new option in the Move tool's 'Tool Settings' window called "Automatically Orient Joints".

Turn.... that.... off....
<<gradient horiz #ffffff  #ddddff #8888ff>>
[img[warpcat|http://farm3.static.flickr.com/2017/2118148943_75636dd96c.jpg?v=0]] Yes, me playing Rock Band back in the day.
''Eric Pavey''
Technical Art Director at 31st Union.
*Email:  -  warpcat {{{(at)}}} gmail {{{(dot)}}} com  -
*[[Blog|http://www.akeric.com/blog/]] - [[LinkedIn|http://www.linkedin.com/in/pavey]] - [[Instagram|https://instagram.com/e_pavey/]]
>>
My other Tiddlywiki's:
*[[CG OpenSource wiki|http://cgoswiki.tiddlyspot.com/]]
*[[Python Wiki|http://pythonwiki.tiddlyspot.com/]]
*[[PyGame Wiki|http://pygamewiki.tiddlyspot.com/]]
*[[Processing Wiki|http://processingwiki.tiddlyspot.com/]]
Maya 2010 (& earlier?) comes with a system plugin allowing web browser -> Maya communication.  You can see docs on it [[here|http://download.autodesk.com/global/docs/maya2012/en_us/files/Interface_overview_Install_the_Maya_Web_browser_plugin.htm]].  It should be noted that Maya has an older [[webBrowser|http://download.autodesk.com/global/docs/maya2012/en_us/CommandsPython/webBrowser.html]] //command//, that will be deprecated.

These are steps I went through to try to get it working.  I have yet to do so... :-S
*The Maya menu 'Help -> Browser Setup Assistant' is designed to setup this process for you. I ran the "Install Automatically" option, but it didn't seem to do anything:  The plugin {{{npmcpplugin.dll}}} wasn't copied to my web browser.  I tried the "Install Manually" button, but it took me to a deal web link. However, I presume that link is the one I pasted above.
So... that didn't work.  I tried the manual method (I'm on Windows 7):
!!!Chrome
I copied the plugin dll from here:
{{{
C:\Program Files\Autodesk\Maya20XX\ExternalWebBrowser\Windows\PlugIn\npmcpplugin.dll
}}}
To here (which appears to be where Chrome keeps its plugins for that version):
{{{
C:\Users\<userName>\AppData\Local\Google\Chrome\Application\18.0.1025.142
}}}
I then ran the bat file here to register the dll:
{{{
C:\Program Files\Autodesk\Maya20XX\ExternalWebBrowser\Windows\COM\Register.bat
}}}
Restarted Chrome, restarted Maya.
In Maya, made sure I had a 'default command port' opened:
{{{
// mel:
commandPort -name "commandportDefault";
}}}
Launched their example file here:
{{{
C:\Program Files\Autodesk\Maya2012\ExternalWebBrowser\Examples\OtherBrowser\Test.html
}}}
But it didn't seem that the browser sent anything to Maya.
In the Chrome address bar I entered:
{{{
about:plugins
}}}
Which listed all the loaded plugins:  {{{npmcpplugin}}} wasn't one of them.... :-S
!!!Firefox
Restarted Maya.  Re-opened the {{{commandPort}}}.
Copied the plugin here:
{{{
C:\Program Files (x86)\Mozilla Firefox\plugins\npmcpplugin.dll
}}}
In the address bar ran
{{{
about:plugins
}}}
And at the very bottom, the {{{MCP PlugIn}}} was listed.  That's good.
Opened the example page:
{{{
C:\Program Files\Autodesk\Maya2012\ExternalWebBrowser\Examples\OtherBrowser\Test.html
}}}
And clicked on any of the buttons.  At the bottom of the screen I got a 
{{{
The MCP plugin has crashed.  Reload the page to try again.
}}}
Which I did... but it kept crashing.
!!!Internet Explorer
I re-registered the dll via the above bat.  Opened the test page... accepted all the active-x permissions.  Tried pressing buttons, and all I got was "error on page" reports at the bottom.

Sigh... I'll need to dig into this later....

<<gradient horiz #ffffff  #ddddff #8888ff >>
[[About|About mel wiki]] 
[[Instructions|Instructions For Use]]
[[Latest Updates|History]]
@@...Always refresh your cache (''F5'' on most PC browsers) to see the latest stuff...@@

<<gradient horiz #ddddff  #8888ff >>''Browse \ Search using'' Ctrl+F:
----
{{{<---}}} Major ''//Categories//'' in the Left column
Key-word ''//Tags//'' in the Right column  {{{--->}}}
----
>>
^^If you find this wiki useful, let the [[author|WarpCat]] know :-)^^
[[Copyright Information|Copyright Information]] 
As of Maya 2008, as I find them....
*[[Tkinter|http://docs.python.org/library/tkinter.html]] : Isn't available.  I guess they really want you to uses Maya's ELF (Extended Layer Format) system.
*{{{raw_input}}} function :  While it does exist in Maya, Maya has it's own gui wrapper around it and you can't provide the 'prompt argument' to it.  Better to just use a {{{maya.cmds.promptDialog}}}.
----
Also see:
*[[What mel commands are 'missing' from the Python integration?]]
Here is a list (as of Maya 7) of all the "shape" nodes in Maya.  FYI, I'm using Maya Complete, and some of these when created come up as {{{unknownDag}}} nodes, presumably because they exist in Unlimited.

"Shape" nodes are DAG nodes that exist as children of transform objects.  They cannot exist in any other form (to my knowledge); they //must// be a child of a transform.

I found this by going into Maya's help -> 'node' documentation, sorting my hierarchy (rather than alphabetically), and grabbing a copy of everything under the "shape" node type.  I took that full list into Maya as an array (after some format editing in a text editor), and looped through the list:  Caught the evaluation of creating each type of node.  If they failed creation(since some types are parental types, and not actual nodes you make), they weren't added to the final list below:
{{{
string $allShapes[] = 
"baseLattice",
"camera",
"clusterHandle",
"deformBend",
"deformFlare",
"deformSine",
"deformSquash",
"deformTwist",
"deformWave",
"angleDimension",
"annotationShape",
"distanceDimShape",
"arcLengthDimension",
"paramDimension",
"dynHolder",
"flexorShape",
"clusterFlexo1rShape",
"follicle1",
"geoConnectable",
"nurbsCurve",
"lattice",
"fluidShape",
"fluidTexture2D",
"fluidTexture3D",
"heightField",
"mesh",
"nurbsSurface",
"subdiv",
"particle",
"directedDisc",
"environmentFog",
"implicitBox",
"renderBox",
"implicitCone",
"renderCone",
"implicitSphere",
"renderSphere",
"locator",
"dropoffLocator",
"hikFloorContactMarker",
"positionMarker",
"orientationMarker",
"sketchPlane",
"renderRect",
"snapshotShape",
"hairConstraint",
"hairSystem",
"ambientLight",
"areaLight",
"directionalLight",
"pointLight",
"volumeLight",
"spotLight",
"lineModifier",
"pfxGeometry",
"pfxHair",
"pfxToon",
"stroke",
"polyToolFeedbackShape",
"rigidBody",
"softModHandle",
"spring"};
}}}
{{{
import maya.cmds as mc
for event in sorted(mc.headsUpDisplay(listEvents = True)):
    print event
}}}
{{{
ActiveViewChanged
ColorIndexChanged
DagObjectCreated
DisplayColorChanged
DisplayRGBColorChanged
DragRelease
MenuModeChanged
ModelPanelSetFocus
NameChanged
NewSceneOpened
PostToolChanged
PreFileNewOrOpened
RebuildUIValues
RecentCommandChanged
Redo
SceneImported
SceneOpened
SelectModeChanged
SelectPreferenceChanged
SelectPriorityChanged
SelectTypeChanged
SelectionChanged
SetModified
ToolChanged
Undo
angularToleranceChanged
angularUnitChanged
animLayerAnimationChanged
animLayerGhostChanged
animLayerLockChanged
animLayerRebuild
animLayerRefresh
axisAtOriginChanged
cameraChange
cameraDisplayAttributesChange
constructionHistoryChanged
currentContainerChange
currentSoundNodeChanged
deleteAll
displayLayerAdded
displayLayerChange
displayLayerDeleted
displayLayerManagerChange
displayLayerVisibilityChanged
glFrameTrigger
gridDisplayChanged
idle
idleHigh
lightLinkingChanged
lightLinkingChangedNonSG
linearToleranceChanged
linearUnitChanged
nurbsCurveRebuildPrefsChanged
nurbsToPolygonsPrefsChanged
nurbsToSubdivPrefsChanged
passContributionMapChange
playbackRangeChanged
playbackRangeSliderChanged
quitApplication
renderLayerChange
renderLayerManagerChange
renderPassChange
renderPassSetChange
renderPassSetMembershipChange
selectionPipelineChanged
setEditorChanged
snapModeChanged
softSelectOptionsChanged
symmetricModellingOptionsChanged
threadCountChanged
timeChanged
timeUnitChanged
workspaceChanged
}}}
*Change the Font size:
**ctrl-shift {{{<}}} or {{{>}}}.
*Suppress warnings, errors, etc?
**{{{scriptEditorInfo}}}
*Clear the upper and lower fields"
**{{{scriptEditorInfo -ch -input;}}}
*Change the line spacing:
**ctrl + 1, 2, or 5
*Add "numbering\bullets":
**ctrl-shift + l (that's an L)
*Add an indent:
**tab, or ctrl+i... don't know if there's a difference.
*Allign left, or right:
**ctrl+l (that's an L), ctrl+r
**ctrl+e is "center" but also seems to execute the script, odd.

Based on Maya 2010
Maya's anim layering system seems heavily based in mel.  One script in particular, {{{animLayer.mel}}} has many global procs that are used by other parts of the application.  But since this is a script, and not a command, there are no docs for them.  So at the very least, I wanted to collect in one place their names.
This is the Python code I used to generate the below list:
{{{
with open('C:\Program Files\Autodesk\Maya2010\scripts\startup\layerEditor.mel', 'r') as f:
    for line in f:
        if 'global proc' in line:
            print line.strip()
}}}
And without further ado:
{{{
global proc string[] getSelectedAnimLayer(string $toolName)
global proc int isAffectedLayer(string $layer, string $affectedLayers[], int $recursive)
global proc string[] getAffectedLayers(string $selected[])
global proc buildAnimLayerArrayRecursive(string $layer, string $layerArray[])
global proc string[] buildAnimLayerArray()
global proc openFloatingAnimLayerEditor()
global proc createAnimLayerEditor(string $parentLayout, string $toolName )
global proc layerEditorBuildAnimLayerMenu(string $parent, string $toolName)
global proc layerModeSubMenu(string $layerModeMenu, string $toolName)
global proc layerAccumulationSubMenu(string $layerAccumulationMenu, string $toolName)
global proc onAnimLayersAnimationChanged(string $toolName)
global proc onAnimLayersLockChanged(string $toolName)
global proc updateEditorFeedbackAnimLayers(string $toolName)
global proc animLayerEditorDoNothingSJ()
global proc animLayerEditorUpdateWeightSJ( string $pTool, string $pWeightAttribute )
global proc setPreferredForAllLayers(int $preferred)
global proc setSelectedForAllLayers(int $selected)
global proc setSelectedLayerRecursive(string $layer, int $selected, int $skipRoot )
global proc animLayerMuteCallBack(string $item,int $buttonState)
global proc animLayerGhostCallBack(string $item,int $buttonState)
global proc animLayerGhostRightCallBack(string $item,int $buttonState)
global proc animLayerSoloCallBack(string $item,int $buttonState)
global proc animLayerLockCallBack(string $item,int $buttonState)
global proc animLayerDragAndDropCallback(string $dropItems[],string $oldParents[],int $oldIndexes[],
global proc layerEditorMoveAnimItem(string $toolName, int $up)
global proc string animLayerEditLabelCallback(string $item,string $newLabel)
global proc animLayerExpandCollapseCallback(string $item,int $expand)
global proc updateAnimLayerEditor(string $toolName)
global proc rebuildAnimLayerEditor(string $toolName)
global proc int animLayerEditorOnSelect(string $item,int $itemSelected)
global proc layerInitialiseParameterDefaults()
global proc layerEditorBuildAnimOptionMenu( string $parentMenu )
global proc layerEditorBuildAnimShowMenu( string $parentMenu )
global proc layerEditorBuildAnimHelpMenu( string $parentMenu)
global proc int layerEditorBuildPopupMenu(string $toolName, string $parentMenu, string $item)
global proc layerEditorAnimLayersPopup(string $layers[], string $toolName, string $parentMenu, int $outline)
global proc string layerEditorCreateAnimLayer( int $addSelection , int $createOverrideLayer)
global proc layerEditorDeleteAnimLayer(string $layers[])
global proc animLayersExport(string $layers[], int $branch)
global proc animLayersSaveExportClbc(string $file, string $type)
global proc deleteEmptyAnimLayers()
global proc packageAnimLayerObjects()
global proc layerEditorAddObjectsAnimLayer(string $objects[], string $layers[],int $showOptions)
global proc string layerEditorExtractObjectsSingleAnimLayer(string $objects[], string $layer )
global proc layerEditorExtractObjectsAnimLayer(string $objects[], string $layers[])
global proc layerEditorMergeAnimLayer(string $layers[], int $pOptions )
global proc layerEditorRemoveObjectsAnimLayer(string $objects[], string $layers[])
global proc layerEditorCopyAnimLayer(string $layers[])
global proc layerEditorCopyAnimLayerNoAnim(string $layers[])
global proc layerEditorSelectObjectAnimLayer(string $layers[])
global proc layerEditorZeroKeyAnimLayer(string $layers[])
global proc layerEditorZeroWeightKeyAnimLayer(string $layers[])
global proc layerEditorFullWeightKeyAnimLayer(string $layers[])
global proc layerEditorWeightAnimLayer(string $layers[], float $value)
global proc layerEditorKeyWeightAnimLayer(string $layers[])
global proc layerEditorExclusiveSoloAnimLayer(string $layers[])
global proc string[] getSelectedRenderItems(string $toolName, string $specified, int $type)
global proc string[] buildRenderLayerArray()
global proc string buildLayerContMapIdent(string $layer, string $map)
global proc string[] parseLayerContMapIdent(string $ident)
global proc renderLayerEditorValidateSelection(string $toolName, int $deselectNonCurrent)
global proc string blendModeOptions_AttrToUI(int $attrVal)
global proc int blendModeOptions_UIToAttr(string $ui)
global proc int isNodeConnectedToContMap(string $obj, string $map, string $attr)
global proc alterNodeContMapConnection(string $obj, string $map, string $attr, int $connect)
global proc alterContMapConnectionsForParentsOfShape(string $shape, string $map, int $connect)
global proc string displayLabelRenderLayer(int $showNamespace, string $layer)
global proc hookShaderOverride(string $layer, string $type, string $shader)
global proc string[] getUnassociatedContMaps(string $layer)
global proc layerEditorRenderLayerManagerChange(string $toolName)
global proc string renderLayerEditorItemOnRename(string $oldName, string $newName)
global proc renderLayerEditorOnSelectionChanged(string $toolName)
global proc layerEditorRenderLayerOnDragDrop(string $toolName, string $dropItems[], 
                string $oldParents[], int $oldIndexes[], string $newParent, int $newIndexes[], 
                string $newItemPrev, string $newItemNext)
global proc renderLayerEditorRenderable(string $toolName, string $layer, string $state)
global proc renderLayerEditorRecycleChange(string $toolName, string $layer, string $state)
global proc renderLayerEditorSettingsOverride(string $toolName, string $layer, string $state)
global proc layerEditorMoveRenderItem(string $toolName, int $up)
global proc layerBlendModeChanged(string $toolName)
global proc renderLayerEditorShowLayersMenu(string $toolName, string $menu)
global proc renderLayerEditorShowContributionMenu(string $toolName, string $menu)
global proc renderLayerEditorShowOptionsMenu(string $menu)
global proc layerEditorCreateRenderLayer(int $contents)
global proc renderLayerEditorCopyLayer(string $toolName, string $inLayer)
global proc setRenderLayerCopyLayerMode(string $parent)
global proc resetRenderLayerCopyLayerMode(string $parent)
global proc renderLayerEditorCopyLayerOptions(string $toolName)
global proc renderLayerEditorDeleteLayer(string $toolName, string $inLayer)
global proc renderLayerEditorSelectObjects(string $toolName, string $layer)
global proc renderLayerEditorRemoveObjects(string $toolName, string $inLayer)
global proc renderLayerEditorEmptyLayer(string $toolName, string $inLayer)
global proc renderLayerEditorAddObjects(string $toolName, string $inLayer)
global proc renderLayerEditorDeleteUnused(string $toolName)
global proc renderLayerEditorLayerAttributes(string $layer)
global proc renderLayerEditorMembership(string $toolName, string $layer)
global proc renderLayerEditorAlterObjectsInContMap(string $toolName, string $layer, string $inMap, int $addObjects)
global proc renderLayerEditorSelectObjectsInContMap(string $toolName, string $inMap)
global proc renderLayerEditorEmptyContMap(string $map)
global proc renderLayerEditorAssocContMap(string $toolName, string $layers[], string $map)
global proc renderLayerEditorCreateContMap(string $toolName, string $inLayer, int $withSelected)
global proc renderLayerEditorCopyContMap(string $toolName, string $inMap, string $inLayer)
global proc renderLayerEditorDeleteContMap(string $toolName, string $map)
global proc renderLayerEditorDeleteUnusedContMaps(string $toolName)
global proc renderLayerEditorContMapMembership(string $toolName, string $map)
global proc renderLayerEditorContMapAttributes(string $toolName, string $map)
global proc renderLayerEditorCreateAndAssignPass(	string $layer, string $map, string $uiName, string $presetPath)
global proc updateEditorFeedbackRenderLayer(string $toolName, string $layer)
global proc updateEditorRenderLayer(string $toolName)
global proc string[] getSortedPassContributionMapList(string $layer)
global proc renderLayerEditorFloatingWindow()
global proc createRenderLayerEditor(string $parentLayout, string $toolName)
global proc layerEditorBuildRenderLayerMenu(string $parent, string $toolName)
global proc layerEditorBuildRenderContributionMenu(string $parent, string $toolName)
global proc layerEditorBuildRenderOptionMenu(string $parent, string $toolName)
global proc int renderLayerEditorBuildPopupMenu(string $toolName, string $parentMenu, string $item)
global proc setLayerToMenuItems( string $parent, string $layers )
global proc layerEditorBuildDisplayLayerMenu(string $parent)
global proc layerEditorBuildDisplayOptionMenu(string $parent)
global proc string [] getLayerSelection(string $type)
global proc layerEditorNewScene()
global proc layerEditorOpenScene()
global proc layerEditorDisplayLayerChange()
global proc layerEditorDisplayLayerManagerChange()
global proc layerEditorLayerButtonSelect(int $modifiers, string $layerButton)
global proc layerEditorQuickEditWindowSave(string $layerArray[])
global proc createLayerEditorQuickEditWindow(string $layerArray[])
global proc layerEditorLayerButtonRename(string $oldName, string $newName)
global proc layerEditorLayerButtonVisibilityChange(string $layer)
global proc layerEditorLayerButtonTypeChange(string $layer)
global proc string [] layerEditorLayerButtonDrag(string $dragControl, int $x, int $y, int $mods)
global proc layerEditorLayerButtonDrop(string $dragControl, string $dropControl, string $messages[], int $x, int $y, int $dragType)
global proc layerEditorShowEditMenu(string $menu)
global proc layerEditorShowOptionsMenu(string $menu)
global proc layerEditorShowPopupMenu(string $menu, string $layerButton)
global proc layerEditorCreateLayer(int $contents)
global proc layerEditorDeleteLayer(string $inLayer)
global proc layerEditorEditLayer(string $layer)
global proc layerEditorSelectObjects(string $layer)
global proc layerEditorAddObjects(string $inLayer)
global proc layerEditorRemoveObjects(string $inLayer)
global proc layerEditorEmpty(string $inLayer)
global proc layerEditorSelectUnused()
global proc layerEditorRemoveFromLayer()
global proc layerEditorLayerAttributes(string $layer)
global proc layerEditorMembership(string $layer)
global proc layerEditorDisplayTypeChange()
global proc layerEditorMoveDisplayLayer(int $up)
global proc string displayLabel( int $showNamespace, string $layer )
global proc updateLayersByType(string $type)
global proc updateLayerOrderByType(string $type)
global proc updateLayerEditor()
global proc int layerEditorVisibilityStateChange(int $newState, string $layout)
}}}
Helping people over there years, there have been some weird edge-case issues where the user saves their file, goes to open it later, and it's empty.  There is no error during save.  It just... doesn't save.  

How can you detect for this?  If it keeps happening to a user, right after they save, have them re-open the file in another Maya.  If it's empty, clearly something bad is happening, and hopefully you can track it down since... they need to close Maya eventually...

Below are culprits I've found. And again to reiterate, there is no error during the save... the file is saved with nothing in it (the than the standard Maya data that an empty scene would have).
!!!Out of hard drive space
* You have no HD space yet, but Maya won't complain even though it fails to save
!!!Corrupt Mesh
* I've encountered files that wouldn't save because of a single, quite low-poly, very simple mesh.  Deleting that mesh will allow it to save correctly.
!!!Bad Hard Drive
* I've seen HD's go bad, that cause files to corrupt when being saved.
!!!Weird characters serialized into a .ma file
*Sometimes, if the user saved as a .ma file, you can find strang non-ascii characters that can be deleted, that allow the file to be reopened. 
There is a {{{scriptJob}}} (in my case, it's {{{scriptJob}}} 1) that executes this code:
{{{
scriptJob -protected -parent AEmenuBarLayout -event SelectionChanged autoUpdateAttrEd;
}}}
{{{autoUpdateAttrEd}}} is a global proc found in the script:
{{{
C:\Program Files\Autodesk\Maya201X\scripts\others\showEditor.mel
}}}
When you 'freeze transforms' (the {{{makeIdentity}}} command) on a transform node, it will zero the translate, rotate attrs, set scale to 1.0.  But what is it doing behind the scenes?
By making two files and diffing them, we can find out.

To do this experiment, I made two transform transform nodes in this hierarchy:
* A
** B
And applied these transformations to each:
!A Transforms
* Translate : 5, 5, 5
* Rotate : 33, 33, 33
* Scale : 3, 3, 3
!B Transforms (relative to A)
* Translate : 10,10,10
* Rotate : 45, 45, 45
* Scale : 2, 2, 2
And saved that out as 'transformed.ma'.
I then 'froze transformations' on the 'B' node it and saved it as 'frozen.ma', and diffed the files.  Here's the important stuff:
!transformed.ma
{{{
createNode transform -n "B" -p "A";		
	setAttr ".t" -type "double3" 10 10 10 ;
	setAttr ".r" -type "double3" 45 45 45 ;
	setAttr ".s" -type "double3" 2 2 2 ;
}}}
!frozen.ma
{{{
createNode transform -n "B" -p "A";
	setAttr ".rp" -type "double3" 10 10 10 ;
	setAttr ".sp" -type "double3" 10 10 10 ;
}}}
!Comparison
By running 'freeze transformations' on node B, what appears to be happening behind the scenes in the //frozen// file is:
* Maya removes storage of the translate, rotate, and scale attrs entirely, thus reverting them to their default (identity) values.
* Applies the local translate values to the {{{rotatePivot}}} and {{{scalePivot}}}.
* However, it's not just 'applies the values': It actually adds the translate values to any existing values on the {{{rotatePivot}}} and {{{scalePivot}}}. Meaning, if custom values had been pre-applied to the {{{rotatePivot}}} and/or {{{scalePivot}}} by the user before the freeze, after freeze they'll have the {{{translate}}} values added to them.
* FYI, you can visualize all this by making the {{{rotatePivot}}} and {{{scalePivot}}} attrs visible in the Channel Box via the 'Windows -> General Editors -> Channel Control' window before the freeze.
* What about rotation and scale?  These attrs get zeroed, and any child mesh shapes (if they exist) have their vert positions updated so as to maintain the original worldspace locations, so it looks like 'nothing moved'.  Because of this, you can technically 'unfreeze' the translation freezing, but not that of the rotate or scale.
!Why do we care?
When querying worldspace matrices, if the node had its transforms frozen, the {{{xform("nodeName", matrix=True, worldSpace=True)}}} / {{{getAttr("nodeName.worldMatrix")}}} commands (both return lists), or the {{{MDagPath.inclusiveMatrix()}}} method (returns {{{MMatrix}}}) will return back incorrect positional information on the node, since those commands/method query the //translate// value of the node (which has been frozen/zeroed relative to their parent) when computing the matrix data, not the {{{rotatePivot}}} for the positional info, and it's the {{{rotatePivot}}} that the translate & rotate manips draw themselves: It's where the node 'really is'.  You need to use a command like {{{pointPosition('nodeName.rotatePivot', world=True)}}} or {{{xform(nodeName, query=True, rotatePivot=True, worldSpace=True)}}} to get that worldspace pivot location, and then insert it //back// into the positional component of the matrix.

BTW, this is even more complicated if your Maya's 'internal units' aren't in 'cm':  Many matrix commands internally store the positional data in cm, regardless of what your 'ui/working units' are (like 'inches'), so you'll need to convert to/from cm during positional storage / retrieval.
----
Also see:
* [[How can I 'unfreeze' a node?]]
* [[How can I test if a transform is frozen or not?]]
{{{
global string $gshowManip; = "ShowManips"
global string $gSelect; = "selectSuperContext"
global string $gLasso; = "lassoSelectContext"
global string $gMove; = "moveSuperContext"
global string $gRotate; = "RotateSuperContext" - note the capital "R"
global string $gScale; = "scaleSuperContext"
global string $gCurrentSacredTool; = whatever context is currently active
global string $gNonSacredToolWidget; = "toolButton1" - actually, a much longer path to the Toolbox UI
global string $gNonSacredTool; = "superCtx1"
global string $gToolBox; = "gridLayout2" - actually, a much longer path to the Toolbox UI 
}}}
*ALL nodes in Maya are DG nodes.  DG stands for Dependency Graph.  All nodes live in the dependency graph, and can connect to one another via the attributes.  Picking a node, opening the Hypergraph, and pressing the "Input and Output Connections" button will show you the dependency graph for that node.
*A subset of DG nodes are DAG nodes.  DAG stands for [[Directed Acyclic Graph|http://en.wikipedia.org/wiki/Directed_acyclic_graph]].  In English it really means 'parent\child relationships'.  While all nodes can connect via attrs in the Dependency Graph, only certain types (transforms, shapes) can have parent\child relationships.  Also see: '[[What are ALL the shape nodes in Maya?]]'
*In the Outliner, by default you see only DAG objects.  There is a filter that allows you to //turn off// "Show Dag Objects Only".  At which point, the Outliner fills with all the DG nodes in the scene (asside from some that are still hidden, by default, another option will display them too).
I update this as I run across them ;)  By no means an exhaustive list!
For the official docs on Python's built-in exception types, go [[here:|http://docs.python.org/library/exceptions.html]].

For any of the below examples to work in Maya, you'd first need to:
{{{
import maya.cmds as mc
}}}
!{{{RuntimeError}}}
For when a command fails, but not due to a syntax problem.
http://docs.python.org/library/exceptions.html#exceptions.RuntimeError

''Example:'' (just one of many...)
{{{string}}} attrs need to be added with the {{{dataType}}} argument, not the {{{attributeType}}} argument:
{{{
mc.addAttr("myObject", longName = "stringAttr", attributeType = "string")
# Error: Type specified for new attribute is unknown.
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# RuntimeError: Type specified for new attribute is unknown.
#  # 
}}}
{{{
mc.addAttr("myObject", longName = "stringAttr", dataType = "string")
# success!  
}}}
So a nice workaround would be (presuming you were procedrually adding attrs, and didn't know what type to expect:
{{{
try:
    mc.addAttr("myObject", longName = "stringAttr", attributeType = "string")
except RuntimeError:
    mc.addAttr("myObject", longName = "stringAttr", dataType = "string")
}}}
Using {{{except RuntimeError:}}} is better than just using {{{except:}}}, since it will only catch {{{RuntimeError}}}s, and still report any other problems.  Like, our next example:
!{{{SyntaxError}}}
For when you make a syntactical goof:
http://docs.python.org/library/exceptions.html#exceptions.SyntaxError

''Example:''
I add "{{{oops!}}}" to the middle of my command:
{{{
mc.addAttr("myObject", oops!, longName = "stringAttr", dataType = "string")
# Error: ('invalid syntax', ('<maya console>', 2, 28, 'mc.addAttr("myObject", oops!, longName = "stringAttr", dataType = "string")\n'))
#   File "<maya console>", line 1
#      mc.addAttr("myObject", oops!, longName = "stringAttr", dataType = "string")
#                                 ^
#  SyntaxError: invalid syntax # 
}}}
And look, it's even nice enough to put a little carrot ({{{^}}}) under the part that has the syntactic mistake.
!{{{TypeError}}}
When you pass the wrong data type into a command.
http://docs.python.org/library/exceptions.html#exceptions.TypeError

''Example:''
Try to pass in a {{{string}}} default value to the new attribute.  It's expecting a float.
{{{
mc.addAttr("null1", longName="test", defaultValue="val")
# Error: Invalid arguments for flag 'defaultValue'.  Expected float, got str
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# TypeError: Invalid arguments for flag 'defaultValue'.  Expected float, got str # 
}}}
!{{{AttributeError}}}
When you try to call to a ~Maya-Python command that doesn't exist
http://docs.python.org/library/exceptions.html#exceptions.AttributeError

''Example'':
Try to find the type of an object:
{{{
print mc.objType("null1")
# Error: 'module' object has no attribute 'objType'
# Traceback (most recent call last):
#   File "<maya console>", line 1, in <module>
# AttributeError: 'module' object has no attribute 'objType' # 
}}}
There is no 'attribute' of the {{{maya.cmds}}} ({{{mc}}}) module called {{{.objType()}}}.
But there //is// an attribute called {{{.objectType()}}}:
{{{
print mc.objectType("null1")
# transform
}}}
!{{{IOError}}}
Usually having to do with file operations, like trying to write to a read-only file.
http://docs.python.org/library/exceptions.html#exceptions.IOError

''Example:''
Forthcoming...

As of Maya 2008, as I find them...
*//Most// {{{string}}} commands\scripts, (like {{{match}}}, {{{size}}}, {{{sort}}}, etc) don't exist.  That's ok of course, since Python has such better [[string handling services|http://docs.python.org/library/string.html]] and [[string methods|http://docs.python.org/library/stdtypes.html#string-methods]].
*//All// of the {{{math}}} commands\scripts, (like {{{abs}}}, {{{fmod}}}, {{{pow}}}, {{{seed}}}, etc).  But again, Python gives you its [[math module|http://docs.python.org/library/math.html]], so no bad there.  
**However, the Python math module has no commands for processing //vectors// or //matrices// like mel does, and it has no built-in types that represent them specifically (but {{{lists}}} can fake it pretty well).  Luckily Maya's Python API has wrappers for Vectors and Matrices:
**[[How can I do vector math via Python in Maya?]]
**[[Working with matrices with Python]] & [[Different ways to access matrix and apply data]]
*The {{{catch}}} 'keyword' is gone.  But it's been replaced by Python's [[exception handling|http://docs.python.org/library/exceptions.html]], which is much better (see Mel Wiki notes [[here|What kind of exceptions does Python raise when a mel command fails?]]).  To understand how they work, see my [[Python Wiki|http://pythonwiki.tiddlyspot.com/#%5B%5BHow%20do%20I%20catch%20%5C%20handle%20exceptions%3F%5D%5D]]
*{{{eval}}} is gone, but Python has it's own [[eval|http://docs.python.org/library/functions.html#eval]] function for doing the same thing with //Python// expressions.  And ~Maya-Python has {{{maya.mel.eval}}} for evaluating //mel// expressions.
*{{{evalDeferred}}}, gone.  But, you can use {{{maya.utils.executeDeferred}}} in Python.
*{{{scriptJob}}} :  Possibly gone in 8.5, but appears to be back in 2008 and newer.
*{{{trace}}}  :  Gone.  Replaced by Python's [[traceback|http://docs.python.org/library/traceback.html]].  Also see Python's [[sys.exc_info|http://docs.python.org/library/sys.html#sys.exc_info]] to help with the traceback.
*{{{env}}}  :  Gone.  Replaced with Python's [[locals|http://docs.python.org/library/functions.html#locals]] and [[globals|http://docs.python.org/library/functions.html#globals]] functions.  However, you can actually gather this info in Python, see my post [[here|How can I get a Python dictionary of every mel global variable?]].
*{{{putenv}}} and {{{getenv}}}.  But Python has it's own via the {{{os}}} module: {{{os.putenv}}} & {{{os.getenv}}}.  It should be noted I've not been able to update Maya's env vars with {{{os.putenv}}}, instead I needed to directly modify {{{os.environ}}}.
*{{{whatIs}}}  :  Gone.  You have to wrapper the mel with {{{maya.mel.eval}}}.  Or in Python, you can just use {{{type()}}} or {{{inspect.getfile()}}}
*{{{thumbnailCaptureComponent}}} : But it appears to be missing in mel, too!
*{{{error}}} : Gone.  You should use Python's [[exception handling|http://docs.python.org/library/exceptions.html]] instead.  Serves the same purpose, only better.  But if you really want to see the 'red' command-line error color, you can use this examples:
<<<
{{{
import maya.OpenMaya as om
om.MGlobal.displayError("this is an error")
}}}
{{{
import maya.mel as mm
mm.eval('error "This is an error"')
}}}
{{{
import pymel.core as pm
pm.displayError("This is an error")
}}}
<<<
*{{{warning}}} : Gone.  You can use Python's [[warnings|http://docs.python.org/library/warnings.html]] instead.  However, this doesn't give Maya //color feedback//.  If you really need that 'purple' command-line warning color, you can use these two systems:
<<<
{{{
import import maya.OpenMaya as om
om.MGlobal.displayWarning("this is a warning")
}}}
{{{
import maya.mel as mm
mm.eval('warning "This is a warning";')
}}}
{{{
import pymel.core as pm
pm.displayWarning("This is a warning")
}}}
<<<
* {{{rehash}}} : Gone, but you can {{{eval}}} through Python this way:
<<<
{{{
import maya.mel as mm
mm.eval('rehash;')
}}}
<<<
----
Also see:
*[[What Python modules are missing from Maya's implementation?]]
{{{curveInfo}}}
{{{
# Python code
import maya.cmds as mc

lenCalc = mc.createNode("curveInfo")
mc.connectAttr("myCurveShape.worldSpace[0]", lenCalc+".inputCurve")
}}}
You can then query, or connect to the {{{curveInfo}}} node's "{{{.arcLength}}}" attribute	
----
Also check out the {{{arclen}}} command.
{{{\Autodesk\Maya20XX\scripts\startup\channelBoxCommand.mel}}}
For example, if you RMB on an entry and choose "Freeze -> Translate", the command for that is in here.
Running a {{{lsUI}}}, it returns back {{{tabLayout2}}} :  This name doesn't seem very save to use in code. and I've found that it corresponds to the mel global string {{{$gAETabLayoutName}}}.
Maya has some built-in ones you can use immediately:
*{{{ikRPsolver}}} : Rotate Plane IK
*{{{ikSCsolver}}} : Single Chain IK
*{{{ikSplineSolver}}} : Spline IK
*{{{hikSolver}}} : Human IK
But it also has some 'hidden' ones, which you can create:
*{{{ikMCsolver}}} : Mult-chain IK.  Highly unpredictable, hard to get back to bind-pose.
*{{{ikSpringSolver}}} : Spring IK.  Similar to {{{ikRPsolver}}}, but distributes rotation values between the joints evenly.
*{{{ik2Bsolver}}} : ~Two-Bone solver (three joints max), Maya provides the source-code for it.  
{{{
createNode ikMCsolver;
createNode ikSpringSolver;
createNode ik2Bsolver;
}}}
Docs for all the solvers are [[here|https://help.autodesk.com/view/MAYAUL/2020/ENU/?guid=GUID-952FC4B3-19A6-4055-B034-3A7D15EC66D6]]

Once generated, you can create new IK handles with them via the {{{ikHandle}}} command (the docs say these new solvers should show up in the tool UI, but I've not seen them do that).
{{{
import maya.cmds as mc
mik = mc.createNode("ikMCsolver")
mc.ikHandle(solver=mik, startJoint="joint1", endEffector="joint9")
}}}
{{{#1}}} (#2, #3, etc)
*Example:
{{{
floatFieldGrp -nf 1 -cc "myProc(#1)";
}}}
*In this example, some proc "myProc" is executed, using the value from the floatFieldGrp as its argument. The number "#" represents the field to grab the data from, so if there was more than one field, you could use #2, #3, etc.
This doesn't work on all kinds of UI controls.
Based on the Maya 2008 docs, you can find the documentation for it here:
**Using Maya -> General -> Mel and Expressions -> Creating Interfaces -> Attaching commands to UI elements
Recently ran into several files that when I'd open them, I'd get these errors:
{{{
// Error: line 1: No object matches name: |someNamespace:someNode_parentConstraint1 // 
// Error: line 0: Error reading file. // 
}}}
The biggest side effect of which was Maya not logging what the scene name was.  See notes on that subject [[here|Why can't I query the scene name?]].
But what was causing this?  There are probably more than one reason, but this is what I ran into:
*In the file, there were referenced nodes.
*Certain referenced nodes had been constrained to other non-referenced nodes.
*The created constraint nodes were parented to the referenced nodes being constrained (as expected).
The problem however, as you can see from the error line, is that the 'missing constraint' was being queried from the //root of the scene// (based on the pipe {{{|}}} before its name) when in fact it was parented to a referenced node.  So that query path is invalid.

The fix was actually quite easy:  I unparented all the constraint nodes so they were children of the root, re-saved the scene.  When I reopened the scene, not only had the errors gone away, but all the constraints had re-parented themselves back to their previous referenced parents.  Wacky, but true.
This deals with Maya's interactive display, not prerendered situations:
*If you have a non-planar n-sided faces (quad or greater) being deformed on a mesh, Maya will "on the fly" change the triangulation to best match the non-planar shape.  If there are textures on the object, this is made visible by the texture "flickering" (as the UV's are subtly changed, based on the new triangulation I presume).  While there is no harm done in this, it is visually bothersome to some people.....
*To disable this "flickering", //enable// the mesh's (shape node) {{{.reuseTriangles}}} attr:
{{{
setAttr ($myMesh + ".reuseTriangles") 1;
}}}
From the docs:
*"Should the triangles be reused. If set to ON, the triangulation that existed before the tweaking, or deformation will be re-used. Setting this to true helps interactive tweaking and deforming of objects. This setting helps objects with N-sided faces. This setting is meant for display modes that require triangles, as in the case of shaded mode display, or when the displayTriangles attribute is turned on. Note that the displayed triangles may not be accurate, since the pre-deformed, pre-tweaked triangles are re-used."
Thanks to Eric Vignola for finding this ;)
This is the best resource I can find:
https://gist.github.com/tm8r/faac10a7660213e57da363fa3d5813d6
Since the {{{shaderfx}}} command appears to be undocumented, and has no help.
''Update'' : This info is relevant for Maya 2018 & earlier.  In 2019, they (thankfully) now give you a dialog to choose exactly where to save the file.
----
When you use the ~HumanIK 'Character Controls' window's 'Save Skeletal Definition' option, it doesn't give you a a filebrowser dialog, it has a secret hard-coded path.  Where does it go?
{{{
C:\Users\<USERNAME>\AppData\Roaming\Autodesk\HIKCharacterizationTool5\template
}}}
Just replace {{{<USERNAME>}}} with the current user name.

It should be noted that the //Windows// env var {{{APPDATA}}} stores this path:
{{{
import os
print os.getenv("APPDATA")
# C:\Users\<USERNAME>\AppData\Roaming\
}}}
And Maya just appends the rest to the end.


On Windows:
*{{{C:\Users\<userName>\AppData\Local\Temp\dumpdata.zip}}}
Which contains these file living in the same folder, but zipped up:
*{{{MayaCrashLog*.dmp}}}  :  Open in Visual Studio, gives an exception that was triggerd, and the various modules (dlls) loaded.
*{{{dmpuserinfo.xml}}} : Date, time, graphics, user info.
*{{{MayaCrashLog*.log}}}  : Text, human readable, shows the call stack during the crash.
{{{
C:\Users\<USERNAME>\Documents\maya\<MAYAVERSION>\prefs\scriptEditorTemp\commandExecuter*
}}}
Each {{{commandExecuter}}} is a text file that corresponds to a tab in the script editor.
It should be noted that these aren't updated / saved until Maya exits: Even if you 'save all prefs', these remain unaffected.
Maya ships with it's own version, found here:
{{{
C:\Program Files\Autodesk\Maya20XX\bin\designer.exe
}}}
I was wondering, which is faster:  The {{{MPoint.distanceTo}}} method, or just doing the 'actual math' to compute the distance between two points?

Short version:  They're really close.   And since we're dealing with random numbers, get get non-consistent results.

You'll have to roll your own Timer context manager:
{{{
from math import sqrt
import random

import maya.cmds as mc
import maya.api.OpenMaya as om2

import Timer

def distBetween(posA, posB):
    return sqrt( sum( map( lambda x:pow(x[0]-x[1], 2), zip(posA, posB) ) ) )

numPoints = 1000000
samplePoints = [ (random.randrange(-100, 100), random.randrange(-100, 100), random.randrange(-100, 100) ) for i in range(numPoints) ]
sourcePoint = (random.randrange(-100, 100), random.randrange(-100, 100), random.randrange(-100, 100) )

with Timer(name="Math Dist"):
    dists = []
    for samplePoint in samplePoints:
        dist = distBetween(sourcePoint, samplePoint)
        dists.append(dist)

with Timer(name="MPoint"):
    dists = []
    sourcePoint = om2.MPoint(sourcePoint)
    for samplePoint in samplePoints:
        samplePoint = om2.MPoint(samplePoint)
        dist = sourcePoint.distanceTo(samplePoint)
        dists.append(dist)
}}}
Multiple results with 100000 points
{{{
# Timer 'Math Dist' : Ran for 0.281 seconds
# Timer 'MPoint' : Ran for 0.219 seconds

# Timer 'Math Dist' : Ran for 0.25 seconds
# Timer 'MPoint' : Ran for 0.313 seconds

# Timer 'Math Dist' : Ran for 0.296 seconds
# Timer 'MPoint' : Ran for 0.235 seconds

# Timer 'Math Dist' : Ran for 0.391 seconds
# Timer 'MPoint' : Ran for 0.328 seconds
}}}
So, out of four runs, they each win an equal number of times.  And considering we're instancing ~MPoint's in the loop, the actual {{{distanceTo}}} code may be faster.
Note, behind the scenes, {{{animLayers}}} track //attrs//, not actual nodes.  So when you 'add a node', you're actually adding all it's attrs.  
Because of that, there can be some strange behaviors when adding nodes that have proxy attrs: If a node has a proxy attr, and you add that node:  If the node doesn't have the 'parent' proxy attr, then the node //with// the parent proxy attr will have it's attr added instead:  Basically, 'child proxy attrs' aren't added to layers, only 'parent proxy attrs are'.  So later, when you go to select 'nodes in that layer', you can possibly get another node that you never added, since it is the owner of the parent proxy attr.  I wouldn't consider this a bug, but more of a feature of how proxy attributes work. 
Had an issue where any FBX data we'd sync to in P4 would be corrupted. However, the person who had initially submitted it could still use the data successfully.

As it turns out:  If you're using version control (like P4) and storing your FBX files there, make sure that P4 and your FBX file format are both the same type.  Meaning, if you're saving binary FBX files, make sure P4 is storing them as binary files.  If you're saving ascii FBX files, make sure P4 is storing them as ascii files.  If there is any unalignment here, the FBX files will be corrupted on the server.

This is what had happened to us:  We were saving FBX as binary, but P4 was storing them as ascii, and thus wrecking their content in the process :S
I've ran into (quite frustrating) instances where after I make a UI, all the elements in the UI are either hidden, or stacked on top of each other, and they don't 'appear' until I resize the UI:  Upon resize, the 'spring' into place.  I've jumped through a lot of hoops writing callbacks to try to automatically resize the UI, but I didn't get any luck that way.  Finally tracked it down:

To display a window after creation, you call to the {{{showWindow()}}} command.  I've learned that if you call to this command //before// the window has been completely made, it won't display the window elements properly until the window is resized (which must make call to the windows geometry manager and clean things up).  Here are two examples illustrating the issue:
!!!Good Example:
This example displays successfully:  {{{showWindow()}}} is execute after the window is created.  The two {{{frameLayout}}}s and their {{{button}}}s are properly placed in the window:
{{{
# Python code
import maya.cmds as mc

if mc.window("testWinA", exists=True):
    mc.deleteUI("testWinA")
mc.window("testWinA", resizeToFitChildren=True)
topCol = mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5), rowSpacing=5)
mc.frameLayout(label="test frame1", collapse=False, collapsable=True, borderStyle='out')
mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
mc.button(label='but1')
mc.setParent(topCol)
mc.frameLayout(label="test frame2", collapse=False, collapsable=True, borderStyle='out')
mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
mc.button(label='but2')

# call to showWindow at the end, display is correct:
mc.showWindow()
}}}
!!!Bad Example:
However, the next example fails:  The window is shown immediately after it is created, then the UI elements are added to it.  The result has the two {{{frameLayout}}}s stacking on top of one another, hiding the second {{{button}}}.  If you resize the UI, the layout will 'spring' into place:
{{{
# Python code
import maya.cmds as mc

if mc.window("testWinB", exists=True):
    mc.deleteUI("testWinB")
mc.window("testWinB", resizeToFitChildren=True)

# call to showWindow at the beginning, final display is incorrect:
mc.showWindow()

topCol = mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5), rowSpacing=5)
mc.frameLayout(label="test frame1", collapse=False, collapsable=True, borderStyle='out')
mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
mc.button(label='but1')
mc.setParent(topCol)
mc.frameLayout(label="test frame2", collapse=False, collapsable=True, borderStyle='out')
mc.columnLayout(adjustableColumn=True, columnAttach=('both', 5))
mc.button(label='but2')
}}}
!But....
But what if you want to 'refresh' the contents of the ui after it's created?  Say, without closing the window, delete and rebuild its contents:  The same problem comes back.  

Another hack I've found is setting the window's {{{iconify}}} flag on and off in succession:  This will turn the window into an icon, then bring it back again, and in the process totally 'refresh' the contents.
As it turns out (as of Maya 2018) bullet physics expect Maya to work in //meters//.  If you're in cm, or inches, or anything else, this can throw it off. Some notes from the developers.
<<<
Bullet's internal units are in metric (meters) and do not perform correctly when Imperial measurements are used. If the Imperial system is required, maintain system units in centimeters and then change the physics settings to compensate, such as changing Maya Gravity settings from 9.8 to 980 kg for mass changed from 1kg to 1000kg.
<<<
Other limitations can be found [[here|https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-95DDBE62-A2D8-49CC-9E80-AA3248BFE03C-htm.html]]
Ran into a reoccurring bug recently in Maya 2019 that I was able to repro across machines:   On //certain// animator files, when they'd change between translate and rotate manips, the pivot position for the manip would 'hop around'.  I thought maybe this had to do with our custom tools, but it did not.  However, after more troubleshooting, if I wiped prefs it would go away.  Finally tracked it down.
* Evaluation Graph on
* Rig controller in an animation layer.
* Auto key on
All three of those things causes the bug.  Remove any one of them (switch to DG / delete the anim layers / turn off auto-key) the bug goes away.
This has been logged with Autodesk.
I recently had to access files from another studio that had references mapped to paths I didn't have.  As expected, when the file opened, Maya complained it couldn't find the files, and I 'skipped' passed them.
However, in the reference editor, I realized Maya was physically missing some 'missing references'. Meaning, the 'unloaded reference box/path' to a known ref'd file physically wasn't there.
Troubleshooting, I opened the ma file in a text editor, and could copy-paste all the reference code into the script editor, and in a new scene, all of it executed correctly:  The missing reference was now brought in.

That's when I noticed this error when I would normally open the file:
{{{
// Warning: file: C:/path/to/a/file.ma line 34: You cannot form a reference to the root scene file. : c:/some/other/path/to/a/file.ma // 
}}}
Checking opening the file in a text editor, I saw something like this (pseudo code):
{{{
//Maya ASCII 2018ff07 scene
//Name: file.ma
file -rdi 1 -ns "myFile" -rfn "myFileRN" -typ "mayaAscii" "c:/some/other/path/to/a/file.ma";
file -r -ns "myFile" -dr 1 -rfn "myFileRN" -typ "mayaAscii" "c:/some/other/path/to/a/file.ma";
}}}
And that's where I noticed an issue: The file I was opening, and the file that was being referenced had the same name ({{{file.ma}}}), but lived in different dirs.
Now this //shouldn't// be a problem.  But it seemed like Maya thought it was trying to ref a file into itself by leaf name only.  Which is why I could execute the same code in a new scene (no name) and Maya didn't error.  Furthermore, I was able to //import// the file, since again, it was importing into a new scene with no name.

The kicker is, I had two other studios //open// this file, and it worked for them.  //AND// I reverted Maya back to factory defaults so that no custom code was running... and I //still// got this bug.

Currently have no solution, but putting these notes down to help my future self.
Let's say you add a matrix attr to a node via:
{{{
mc.addAttr(longName="myMatrix", dataType='matrix')
}}}
And open the attribute editor: All you'll see is its name, not it's values.
So, you try this next:
{{{
mc.addAttr(longName="myMatrix", attributeType='matrix')
}}}
Same result.

But then you do:
{{{
mc.addAttr(longName="myMatrix", attributeType='matrix', hidden=False)
}}}
//Now// it shows up : The combination of using {{{attributeType}}} instead of {{{dataType}}}, //and// {{{hidden=False}}} is the secret sauce.  It should be noted that you can only use {{{attributeType}}} on certain attrs.

This is because, in part, {{{dataType}}} is an atomic entity, meaning you can only access the data as a whole, not the individual parts.  You can read more on this in the Maya [[addAttr docs|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/addAttr.html]]
----
Also see:
*[[attributeType vs dataType]]
We had an issue with an animator where whenever they'd look at keyframe values on multiple anim layers in the Graph Editor, they weren't the same values as reflected in the Channel box:  For example, the graph editor key was at a value of .75, but in the Channel Box it was 13.52.

The issue was they had accidentally turned on the Graph Editors 'View -> Display Normalized' option, that forces all curves into a 0->1 space.  Turning this off got all the values back to what they expected.
Update #2 : It appears this error has shown up again in Maya 2014, working on 'older' data.  This code will find all the 'bad' attrs, and 'fix' them to have both long and short names:
{{{
import pymel.core as pm

shortAttrStr = "liw"
longAttrStr = "lockInfluenceWeights"
attrs = pm.ls("*.%s"%longAttrStr)
for attr in attrs[:]:
    node = attr.node()
    shortName = attr.shortName(fullPath=False)
    if shortName == longAttrStr:
        print attr, ": No short name :("
        attrVal = attr.get()
        attr.delete()
        pm.addAttr(node, longName=longAttrStr, shortName=shortAttrStr)
        newAttr = node.attr(shortAttrStr)
        newAttr.set(attrVal)
        print "\tFIXED! "
}}}
----

Update #1:  This appears to be local to my machine.  While I've not modified any of Maya's default scripts in this area, other people are not reporting the problem.  I guess I'm just... lucky..... :-S

----
!The BUG:
Switching from Maya 2010 to Maya 2012, I started getting a bunch of errors regarding the {{{.liw}}} (lock influence weights) attr on skinned joints.  Long story short:  It looks like the {{{liw}}} attr's long name is {{{lockInfluenceWeights}}}.  But scripts that try to access the short name  ({{{liw}}}) fail.  I did a check:  The attr has both it's long and short name, and it appears the attr is created when the physical {{{skinCluster}}} command is executed, so this isn't some typo in mel.  I'm not sure why it's breaking in external scripts, but it is.

When I was using this in 2012, it seemed to be fixed in a 2013 test I did.  But now that I'm in 2013, it's broken again.  Argh!

This causes problems in a variety of places (I've found so far...):
----
The "Paint Smooth Skin Weights" tool settings will toss errors and not properly work until you do this fix:
In the global proc {{{skinClusterInflFindInfluenceNodes()}}} in the script 
{{{
C:\Program Files\Autodesk\Maya2012\scripts\others\skinClusterInflMenu.mel
}}}
throws all sorts of errors.
Changing line 568 of that script from this:
{{{
if ( `getAttr ($infl + ".liw")`) 
}}}
to this:
{{{
if ( `getAttr ($infl + ".lockInfluenceWeights")`) 
}}}
Fixed the problem
----
After a mesh has been skinned and you are painting weights, you can RMB on a joint to access the marking menu item "Select Influence".  However, until you do the below fix, that menu item won't show up at all....
In the global proc {{{populateMMForPaintSkinWeights()}}} in the script:
{{{
C:\Program Files\Autodesk\Maya2012\scripts\others\dagMenuProc.mel
}}}
Change these lines:
704:
{{{
if (`attributeQuery -n $item -ex liw`)
}}}
716:
{{{
if ( `getAttr ( $item + ".liw" )` == 0 )
}}}
To:
{{{
if (`attributeQuery -n $item -ex lockInfluenceWeights`)
}}}
{{{
if ( `getAttr ( $item + ".lockInfluenceWeights" )` == 0 )
}}}
To get the proper menu items back.
----
Just go replace all the {{{liw}}} calls (four of them) with {{{lockInfluenceWeights}}} in this script:
{{{
C:\Program Files\Autodesk\Maya2012\scripts\others\artAttrSkinCallback.mel
}}}
Otherwise you can't hold\unhold (lock\unlock) joints from the smooth skin weights tool options.
The {{{outlinerEditor}}} command has a {{{selectCommand}}} argument which takes the name of a function to execute when things are selected.  If you're authoring all this via //mel// there is no problem.  But if you're authoring this via Python, there is a bug, pre-Maya 2011, that even though you call to a Python function, it's still expecting a mel command:
{{{
import maya.mc as mc

def selCommand():
    print "Selected"
    
# Create a new regular outliner in its own window #
mc.window()
mc.frameLayout( labelVisible=False )
panel = mc.outlinerPanel()
outliner = mc.outlinerPanel(panel, query=True,outlinerEditor=True) 
mc.outlinerEditor( outliner, edit=True, 
                     mainListConnection='worldList', 
                     selectionConnection='modelList', 
                     selectCommand='selCommand()' )
mc.showWindow()
}}}
Results in Script editor from interactive pick in the new Outliner:
{{{
select -r side ;
// Error: line 1: Cannot find procedure "selCommand". // 
}}}
To get around this bug, you have to make a ''mel'' global proc:
{{{
global proc selCommand(){
	print "Selected\n";
}
}}}
Results in Script editor from interactive pick in the new Outliner:
{{{
select -r front ;
Selected
}}}
Again, talking with Autodesk, this has been fixed in Maya 2011.
If you're on Windows10, and you're running Maya in 'administrator mode', Windows disables drag&drop "because reasons to do with UAC" (Universal Access Control).
{{{#windowsfail}}}
I've not tried this yet, but from [[this thread|https://answers.microsoft.com/en-us/windows/forum/windows_10-start/how-to-fix-drag-and-drop-in-windows-10/0ea16377-dfcf-4a28-9e38-c35f4113202b?page=4]]:
# Start -> Run -> regedit
# Navigate to ~HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\~CurrentVersion\Policies\System
# Change "~EnableLUA" from "1" to "0"
# Reboot
But it seems bad to kill that entirely?
It's common to query the Maya scene name via:
{{{
string $name = `file -q -sn`;
}}}
And //normally//, it'll print the name in the process.  But somtimes it won't, and just return an empty string.  Why?
A couple reasons I've ran into:
#__The scene has been imported, or 'dragged' into Maya__:  If Maya has a new unsaved scene, in which you import a new scene into, Maya still won't know the name of the active scene, since it hasn't been saved.  Save the scene, and how it has a name.
#__There are Errors on file open__:  I've ran into occurrences when the Script Editor reports Errors on open, it will fail to recognize the scene name.  It also won't add the file to the 'Recent Files List'.  If you resave the scene file after open, it will 'set' the name, and the {{{ls}}} command will start working again.
##In one instance, I found the reason for the file-open errors:  [[When I open a scene I get 'No object matches name:" errors, how to fix?]]
Sometimes when filling variables, either in Maya, or when looking at a mayaAscii file, you'll see "non-standard" characters \ symbols like:
{{{
€     // what is that, the Euro symbol?
#     // pound symbol
1.#QNAN  // found this when a camera's positions corrupted
-1.#IND // same camera problem 
1.#INF // same camera problem
1.#QO // showed up in the camera's tras channels when framing corrupt geo...
others?  (see 'e-' * 'e+' below)
}}}
Why does this happen?  My best guess is that it has something to do with 'order of operation' and writing to memory:  Sometimes mel commands take longer to execute than maya expects, so it will keep running code before the past command has finished.  When it tries to capture the data, or fill new data while a command is still executing, problems can occur, and these weird symbols can pop up.  I've see them in skin weights and path names most commonly.
How to fix?  Some ideas:
*Try breaking your procedure into multiple smaller procedures, each that has a return value.  Call to them in order capturing the return value, which //should// make Maya stop and smell the roses.
*Use {{{evalDeferred -lp "command..."}}} to make Maya wait until the processor becomes free to execute the command.  However, this starts a slippery slope of making everything run in {{{evalDeferred}}}, so use sparingly.
----
Notes on {{{€}}}:
*I've seen this come up in a few cases (in addition to what I list below):
**When I try to execute Python code in a mel tab of the Script Editor (oops...).
**When the previous line didn't have a semicolon on the end:  {{{;}}}
**When I was trying to capture the return value of a command, and in that command, it was being passed an arg by capturing the return value of some other command via the back-tick \ back-quote {{{`}}}.  Like: {{{float $val = `command -argA `getVal` -argB 4`;}}}
*I had a script that was filling each element of a vector in a vector array with the return value from some code that maintains float precision.  It was all happening on one line.  
*On days 1-2, code worked fine.  On day 3, it stopped working:  
*When I would interactively select-execute the code in the Script Editor, I would get errors in the history:  On each of the vector declaration lines, at either 125 or 126 characters, it would truncate the line, and insert a {{{€}}} character at the end (shown in the history of the Script Editor).
*The only solution I came up with (restarting Maya, and my machine didn't help) was to put each call to the precision code on a single line, filling a single variable, //then// passing that to the vector array.
*The kicker is the code that was failing was duplicated in another proc in the script, the only difference was the data was being passed to a string, rather than a vector.  :-S
*I've had this show up in other places, and in those cases I found the problem:  I was evaluating a string, and escaping some variable names I'd passed inside.  I had one too many escape characters '{{{\}}}', and for some reason the extra escape char was causing this symbol to appear.
*Another example:  I was sourcing a script, and in that script was a procedure being called to with arguments.  The arguments were concatenated strings, but in on case, I forgot to add the end parenthesis, and got this as part of Maya's error.  As you can see, there is a missing parenthesis in there, causing the next argument to really get confused...
**The source line being eval'd looked like this:
{{{
...rigDir, (assetDir+"/build/", "S:/assets/rigs/shared/")});
}}}
**Notice, the mistake with the parenthesis '{{{)}}}' missing after {{{/build/}}} and an extra '{{{)}}}' after {{{/shared/}}}?
**This is the error code that Maya spit out:
{{{
...$rigDir,  ($assetDir+"/build/", "S:/a€ // 
}}}

----
Here's an example of bad data, from a script I was writing:
{{{
file  -f -chn 1 -con 0 -exp 1 -ch 1  -typ "mayaAscii" -es c:/temp/asse€
}}}
The end asset path was a variable I had passed into this command.  But for whatever reason due to previous evaluation issues, it wasn't able to store the full path value, so it failed.
It should be said that sometimes you'll see numbers like this:
{{{
8.881784197e-018
8.881784197e+018
}}}
The {{{e-}}} isn't a "bug" it's saying that it's 18 decimal places over:  A very very small number!:
If I detect {{{e-}}} in my number, I usually just set it to zero.
{{{e+}}} is the same thing, but it's saying it's a HUGE number.
Recently one of my modelers would consistently loose uv's on one of two mesh objects being combined.
As it turns out, one of the mesh was from a much older version of Maya (2007?), and it's uv's would just vaporize when combined with the 'newer' mesh.  To fix, he had to export the old mesh as an obj, re-import it into Maya.  Then it would combine and not loose uv's.

what....?
Had a nasty case of certain Mya preference (specifically Maya's autosave) states being reset when Maya was reopened.
Come to find out, a function my {{{userSetup.py}}}  was calling to, was executing this line of code:
{{{
import maya.cmds as mc
mc.savePrefs(general=True)
}}}
It appears that trying to save prefs while Maya is opening is a __very bad idea__.
Good question.  Possibly related to verts with bad values.
See here:
* https://knowledge.autodesk.com/support/maya/troubleshooting/caas/sfdcarticles/sfdcarticles/Default-Maya-Camera-shows-NaN-as-Tranlate-values.html 
* https://forums.autodesk.com/t5/maya-forum/persp-camera-nan/td-p/6510048
I tried making a nan detector, but curently I don't know if it's working nor not since the one bad file I had seemed to auto-fix itself:
{{{
badVerts = []
meshes = mc.ls(type='mesh')
for mesh in meshes:
    numVerts = mc.polyEvaluate(mesh, vertex=True)
    for i in range(numVerts):
        pntVal = mc.getAttr('%s.pnts[%s]'%(mesh, i))[0]
        for val in pntVal:
            if math.isnan(val):
                badVerts.append('%s.vtx[%s]'%(mesh, i))
                break
print "bad verts:"
print badVerts
}}}
When we switched to 2016, some of our older files, generally ones used by the animation staff with multiple references could take minutes to open, compared to seconds.
Not only that, newer ~PySide windows would suffer huge refresh issues: Just clicking in them would cause the "wait" icon to show up, griding operations to a halt.

While I never tracked down the specific issue behind this, it's preference related:  By resetting back to default prefs, everything started opening correctly.Basically, quit Maya, then rename this:
{{{
C:\Users\<userName>\Documents\maya\2016
}}}
To this:
{{{
C:\Users\<userName>\Documents\maya\_2016
}}}
Restart Maya: When it asks you if you should 'make new' or 'copy old' prefs, say 'make new'.  Then you can selectively copy over the old data you had from the renamed folder.
Found a bug where starting in Maya 2017 (and this persists in Maya 2019) if you try to retarget assets with twist joints, they can flip out.  And, your asset can pop into the air, with feet off the ground.  

Working with Autodesk, this is a hack fix you can apply in the file that's doing the retarget:  Doing this work on the individual scenes (mocap files for example) will fix the twist, but not the hop;  Just run it once you connect the skeletson in the HIK tool.
{{{
# Bugfix the HIK Nodes
import maya.cmds as mc
 
hikProperty2States = mc.ls(type='HIKProperty2State')
for hikProperty2State in hikProperty2States:
    print "Working on:", hikProperty2State
    # Fix the 'twist':
    hikAttr = mc.listAttr(hikProperty2State)
    for attr in hikAttr: 
        if 'Param' in attr and 'Mode' in attr: 
            mc.setAttr("%s.%s"%(hikProperty2State, attr),1)
    print "\tFix twist"

    # Fix the 'hop':
    mc.setAttr("%s.HipsHeightCompensationMode"%hikProperty2State, 0)
    mc.setAttr("%s.FootBottomToAnkle"%hikProperty2State, 0)
    print "\tFix hop" 

print "HIK Fix applied"
}}}
''Update'': Working with Autodesk support, this has been traced back to a 2018 bug with the 'Bifrost' plugin.  If you unload all the bifrost plugins (+ save prefs & //must// restart Maya), this issue goes away. 

It appears Maya 2018->2018.5 has a new bug that can show up in these conditions:
! Condition #1:
* You have a file that has file nodes pathed to missing textures.
* In your existing Maya session, you call out to another version of Maya in batch mode, via a shell.
* After that batch, you try to import that file:
* Maya silently crashes: Just 'disappears'.
! Condition #2:
* From 'UI Maya' you call out to Maya in a shell in batch mode.  In that shell you do some form of work.
* After you return to 'UI Maya' you then open a file that has missing texture paths.
* Maya crashes.  Something about launching Maya in batch mode from a UI Maya, then opening a file with missing texture paths causes the crash.  Just opening the offending file before the batch works fine.
! Condition #3 : 
* Sometimes even if you think you've fixed all the texture paths, just calling out to Maya in batch mode per the above conditions and then opening \ importing a file (could be a .anim file) will cause Maya to crash.
This has been reproduced on multiple machines and at Autodesk. 
Same process/code didn't cause these crashes in 2016. 
Here's a snippet that will set the bad paths to empty paths, that works around the bug.
{{{
import os
import pymel.core as pm
# 'shader' is for dx11shader nodes, and 'fileTextreName' is for file nodes:
for attr in pm.ls("*.shader")+pm.ls("*.fileTextureName"):
    fpath = attr.get()
    if fpath and not os.path.isfile(fpath):
        attr.set("")
        print "Removing bad path from: %s : %s"%(attr.node(), fpath)
}}}
When connecting attribute to attribute, sometimes you'll see Maya introduce a {{{unitConversion}}} node.  For the longest time (until today, using Maya 2013) I've always wondered why this is.  For example, if I do this connection:
*nodeA.rx -> multiplyDivide.inputX
*multiplyDivide.outputX -> nodeB.rx
A unit conversion node will be inserted where each of those arrows are, looking like so:
*nodeA.rx -> unitConversionA -> multiplyDivide.inputX
*multiplyDivide.outputX -> unitConversionA -> nodeB.rx
Furthermore the values on those {{{unitConversion}}} nodes  would be:
* unitConversionA = 57.296
* unitConversionB = 0.017
What is going on?

Maya nodes internally work in 'internal units', and by default these are:
*Linear : cm
*Angular: radian
But the ui the user interacts with is in 'ui units', who's defaults are:
*Linear : cm
*Angular : degrees
While different studios will often change the linear ui units to something else (meters, inches, etc), I've never heard of anyone changing from degrees to radians.  And this is the key piece of the mystery:

When you connect node to node, you're connecting based on the //internal units//:  When you connect nodeA.rx -> multiplyDivide.inputX, it's connecting a //radian// attribute to a (in this case) 'degree attribute'.  And for the values to read correctly, it needs to mutiply them by some value.  What are those conversion values anyway? (Google will back me up)
*Radian to degrees: 57.296
*Degree to radian: 0.017
The same values as above.  First, Maya needs to convert from the radian values of the rotate attr to the 'degree' values of the multiplyDivide.  Then, it needs to convert back from the 'degree' values of the multiplyDivide to the radian values nodeB's rotate attr.

If you go into Maya's preferences and change your angular units to radians and redo the above connections, no {{{unitConversion}}} nodes will be generated, since the 'internal units' and 'ui units' are now the same.
In Maya 2012, I've seen an instance that when you do File->Open, File->Import etc (or a custom use-created dialog), Maya will lock up\freeze.

As it turns out, the file dialog is appearing behind the Maya main ui.  To fix, hit 'esc' to cancel the operation.  Move the main Maya window, redo the operation.  Once you can click on that dialog for the first time, it should start behaving normal from that point on.
Recently I was trying to hijack Maya's main 'File' menu, to add my own items.  What I found was, when Maya launches, the menu is empty until you physically open it (click on it), at which point it is populated with the various {{{menuItem}}}s.  If you try to add your own items to the menu before it's been opened, when you open it, only your item will be shown.  This can be shown by this example:
{{{
import maya.cmds as mc

fileMenu = 'mainFileMenu'
fileMenuItems = mc.menu(fileMenu, query=True, itemArray=True)
if fileMenuItems:
    for menuItem in fileMenuItems:
        print menuItem
else:
    print "The menu is unpopulated: %s"%fileMenu
}}}
If you haven't opened the File menu before running that code, you'll get the print statement.

How to get this working?  Thanks to a tip from David Moulder, I've come up with this solution:
{{{
import maya.cmds as mc
import maya.mel as mm
    
def addToFileMenu():
    # If Maya is in batch mode, just exit, no point to update a menu:
    if mc.about(batch=True):
        return
    # Get the global mel variable name for the File menu:
    gMainFileMenu = mm.eval('string $temp_string = $gMainFileMenu')
    # If the contents of the file menu don't exist, build them:
    if mc.menu(gMainFileMenu, query=True, numberOfItems=True) == 0:
        mm.eval('buildFileMenu()')

    # Add our menu to the very top:
    mc.menuItem(label="My Awesome Menu", parent=gMainFileMenu, insertAfter='')
}}}
After you execute that function, you get an awesome new menu item at the top of Maya's File main menu.

The mel proc {{{buildFileMenu()}}} is found in this script, which is sourced when Maya launches in gui mode:
{{{
C:\Program Files\Autodesk\Maya20XX\scripts\startup\FileMenu.mel
}}}
Looking in that directory, there are other scripts for the other menus: {{{EditMenu.mel}}}, {{{DisplayMenu.mel}}}, etc, that can be inspected to find the corresponding code needed to build their contents.
----
Also see:
*[[How can I update Maya's menus with my own entry?]]
*[[How to add a submenu to Maya's File menu]]

We've had several repro cases where if the top transform of a rig is parented to a new transform (grouped), all the IK on that rig just 'turns off'.  Suddenly all the limbs are sticking out straight.  And it's not 100% reproable.  Reopening the file and repeating the process can have different results (success\failure) when picking multiple rigs.

I figured out that if you undo the parenting operation, it can fix broken rigs, and the undo operation can actually break rigs that previously parented just fine.

I've looked into the Ik, and the attrs don't change: they're fully enabled.  So, how to fix?

As it turns out, 'reloading the reference' will fix this.... or crash Maya, so save first.  I've also found simply reopening the file after save will fix it too.

In Maya 2019, I had a random issue where when I'd do a 'frame selected' or 'frame all' in a model panel, the camera would zoom out super far away.  No idea why.

Two different fixes:
! Fix 1:
The default {{{f}}} hotkey is bound to the runtimeCommand {{{FrameSelected}}}, which just calls to this mel:
{{{
fitPanel -selected;
}}}
{{{fitPanel}}} is a mel script living here:
{{{
C:/Program Files/Autodesk/Maya2022/scripts/others/fitPanel.mel
}}}
But this will frame what you have selected, and //all of its children//.  If you only want to frame the 'specific thing you have selected' and 'not all its children', change the hotkey to (or make a new runtimeCommand called something like {{{FrameSelectedNoChildren}}} and assign it to {{{f}}}):
{{{
fitPanel -selectedNoChildren;
}}}
! Fix 2:
But to fix it, you need to hack the:
{{{
C:/Program Files/Autodesk/Maya2022/scripts/others/fitPanel.mel
}}}
... script (you'll need to launch your editor with administrator privileges to do this).
In the code inside this scope (starting line round 46):
{{{
if ($type == "modelPanel") 
}}}
On any line you see the {{{viewFit}}} command, you'll want to add this arg to it:
{{{
-fitFactor 1.0
}}}
So, this line:
{{{
viewFit -panel $panel -animate $animate `lookThru -q $panel`;
}}}
Becomes:
{{{
viewFit -panel $panel -fitFactor 1.0 -animate $animate `lookThru -q $panel`;
}}}
Then re-source the mel script in the Script Editor:
{{{
source fitPanel;
}}}
Your 'framing' hotkey should work a lot better now.
I've seen if there is a 'preferred angle' set on an intermediate joint, that when an IK handle is created on the chain the chain can change shape.  Zero out the preferred angle, rebuild.
Update:
I've been told, but not yet tried, that you can execute this code after your loop, and it may also stop the crash:
{{{
mc.evaluationManager(invalidate=True)
}}}
----
A number of tools that loop over frames that worked in 2015 & earlier will now hard-lock Maya in 2016.  I have yet to find the direct cause, but the fix is to exit 'parallel' evaluation mode (if you were in it) before the loop is hit.
Example code:
{{{
import maya.cmds as mc
MAYA_VER = int(mc.about(version=True).split(" ")[0])

# Inside main code:
# Check to see what version of Maya we're using, disable parallel mode:
if MAYA_VER >= 2016:
    evalMode = mc.evaluationManager(query=True, mode=True)
    mc.evaluationManager(mode="off")

# Loop code goes here
# loop loop loop....

# Reset previous evaluation mode:
if MAYA_VER >= 2016:
    mc.evaluationManager(mode=evalMode[0])
}}}
It should be noted this crash happens with video cards both approved, and not approved by Autodesk, so I don't think it's graphics related.

And here is a decorator to do that:
{{{
import pymel.core as pm

class RunDG(object):
    """
    Create a funcion decorator to turn off Parallel mode if it's on, run the
    function, then return parallel mode to its previous state.
    """
    def __init__(self, f):
        self.f = f
    def __call__(self, *args, **kwargs):
        evalMode = pm.evaluationManager(query=True, mode=True)
        pm.evaluationManager(mode="off")
        self.f(*args, **kwargs)
        pm.evaluationManager(mode=evalMode[0])
}}}
{{{
@RunDG
def foo():
    print "foo!"
}}}


A {{{motionPath}}} node can report a different 'length' than other curve querying nodes like {{{nearestPointOnCurve}}}, or even the curve itself.  Here is a breakdown of what I've found:
*{{{nurbsCurve.minValue}}}, {{{nurbsCurve.maxValue}}} : 'internal units'
*{{{motionPath.uValue}}} : If {{{motionPath.fractionMode}}} is {{{True}}} (in the Attribute Editor, this is the 'Parametric Length' field), the uValue is the 'ui unit' equivalent of the {{{curveShape.maxValue}}} attr (which is stored in 'internal units').  If {{{fractionMode}}} is {{{False}}}, {{{uValue}}} is some smaller value, I have yet to figure out how this relates.
*{{{curveInfo.arcLength}}} : 'ui units'
*{{{nearestPointOnCurve.parameter}}} : 'internal units' (cm) - This is based on the {{{nurbsCurve}}} shape's {{{maxValue}}} attr, which also appears to be stored in internal units.
*{{{nearestPointOnCurve.position}}} :  'ui units'
Chaos.
Maya gives you the ability to right-mouse-button on a set in the Outliner, and select the members inside the set.  However, on multiple occasions (reported to me), that functionality has simply quit working.  After doing research, it has come down to this:

The {{{userPrefs.mel}}} somehow got corrupted:
(on Windows)
{{{
C:\Documents and Settings\<user>\My Documents\maya\<version>\prefs\userPrefs.mel
}}}
When the user deleted (or renamed) this file, it started working correctly again.  I havn't spent the time figuring out //what// in that file is causing the issue.
I found what I would consider a bug in Maya 20222:  Per the docs here:
https://help.autodesk.com/view/MAYAUL/2022/ENU/?guid=GUID-FD9D539B-BEC0-4F2F-B4E1-E96AFC88756B
I could change the shape of a base mesh that had blendshapes applied, but the 'Bake Topology To Targets' tool had no effect.

After reaching out to Autodesk support, it was exposed that:  This tool only promotes topo changes that apply history to the base mesh.  So 'just picking verts and moving them interactively' won't leave any history.  but if you use a deformer \ transform that does leave history, they will bake properly.

This feels like a bug to me, but at least there is a valid workaround.
Below are old notes when I didn't understand this.  Maya support finally got back with me : For a referenced 'Character' to accept HIK data, it's Definition must be 'locked' : Just press the little 'padlock' icon on it's definition in it's source (non-referenced) file, save it that way, and now it will be usable when referenced.  The default is unlocked :S  I asked Autodesk support (Feb 2020) about why it's unlocked by default, since it appears to limit the system.  This was their reply:
<<<
This mayaHIK code has not been touched for a while.  The developer wanted to be careful and avoid cases where user would try to mix edits done directly in the reference file and edits done in the main scene. Developer thinks those wouldn't play nice together.  This is why they blocked it that way but again this is very old. However the condition now prevents any edit in the main scene which is probably too restrictive(probably they were expecting all edits to occur on directly the reference scene. Feel free to truy but please test carefully. There are not much regression tests on mayaHIK and probably none on mayaHIK+references
<<<
The main reason to 'lock' a definition is to allow you to initially create it in a T-pose, 'lock' it in that pose, then put it back in an A-pose (if needed). If it wasn't locked, the definition would break since it's no longer in that T pose.  

@@This ''lock'' step is really important!@@, but I can't find it called out in any significant way in the documentation.  Feels like it's one of those fundamental things the author just presumed you should know.

Old notes below anyway:
----
By default, ~HumanIK won't work if the 'Character' (thing being driven) is referenced.  Not sure why this is a limitation.  It appears however to be a simple hack to make this work.  It should be stated though, I'm not sure if there are other wider-reaching complications this may incur.  Do at your own risk.

You'll want to edit this file (insert your version of Maya below):

C:\Program Files\Autodesk\Maya2019\scripts\others\hikDefinitionOperations.mel

Around line 26 you'll find the {{{proc LockSkeletalDefinition()}}}:  Edit it to comment out the lines as  shown below (or copy/paste this block on top of it):
{{{
proc LockSkeletonDefinition()
{
	if ( ! hikIsCharacterizationToolUICmdPluginLoaded() )
		return;

	// Get the button lock state and set the attribute
	string $currentCharacterName = hikGetCurrentCharacter();
	if ( size($currentCharacterName) > 0 )
	{
		// !!!! Edit to remove reference restriction.
		//if( `referenceQuery -isNodeReferenced $currentCharacterName` )
		//{
			//warning (uiRes("m_hikDefinitionOperations.kLockReferencedCharacterWarning"));
		//}
		//else
		//{
			int $newLockState = ! hikIsDefinitionLocked( $currentCharacterName );
			hikCharacterLock( $currentCharacterName, $newLockState, 1 );
		//}
	}

	// Update the button states -- the lock may have failed
	hikUpdateDefinitionButtonState();
}
}}}
Then restart Maya:  This should allow you to retarget onto referenced characters.
Maya 2013:  It looks like there is a bug where if your camera's Near Clip Plane is set to anything greater than .78 (in inches, presumably cm is a different value), any viewable {{{headsUpMessage }}}'s will disappear.
The Node Editor, like many editors, has options allowing you to change the display of nodes in it.  It has two ways to do this:  Via it's menubar (with labeled menus), and via it's icon-bar.  However (as of Maya 2019), changing the //menus// doesn't seem to have any effect, even after restart.  Why is this?

It appears the Node Editor is 'special', in that even though it gives you menu options to change the values, in fact, those menus are only showing you //what the current options are// : You can't actually change them via that menu.

To change them, you need to either:
* Use the menubar on top if the editor (presuming it's visible)
** These will take effect immediately.
* Windows -> Settings/Preferences -> Preferences -> Settings -> Node Editor
** You can make the changes there. But even then, //they won't take effect//: @@You need to restart Maya@@.
** I'm not sure why Maya made this so hard to do, but that's how it's done via  that method.
In a nutshell, use the icon-bar to make changes if you want them to be done immediately.
You can make your own timeslider via the {{{timePort}}} command.  But when you pick a keyframed object their key-ticks don't appear in it, why?

There is a control called {{{timeControl}}} that is very similar to the {{{timePort}}}.  However, accodring to its docs, //it's// the one that can show key-ticks in the time-slider, and, there can only be one of them in the scene.

So there you go.
Normally, the {{{F8}}} hotkey puts Maya both into component mode, and makes the verts on that mesh visible.

I've ran into instances when.... that stops happening.  You have to manually click the mesh //again// after you enter that mode to pick them.  Non-standard.

I tracked it down to the below {{{optionVar}}} settings in the {{{usePrefs.mel}}}:  
----
The presence of this line causes it, it doesn't seem to be  in relationship of any of the arguments provided to it:
{{{
optionVar -sv "sculptMeshCacheContext" "sculptMeshCacheCtx -e ...bunch of options... sculptMeshCacheContext"
}}}
Removing that {{{sculptMeshCacheContext}}} {{{optionVar}}} fixes this issue.  My guess this is triggered when using Maya's Sculpting shelf.
----
Having {{{selectAffectsActive}}} set to {{{0}}} will cause this.  Removing the line (with the {{{0}}}), or setting it to {{{1}}} fixes it:
{{{
optionVar -iv "selectAffectsActive" 1;
}}}
----
Also see:
* [[Why has 'display affected' stopped working in 2019?]]
Appears a new 'feature' was added that disabled a core feature that has been around since Maya 1:  When you pick something, anything it affects is highlighted.  Adding the option to disable it is fine, but don't set that as the new normal :S
To re-enable it:
In your userSetup.mel:
{{{
displayPref -displayAffected true;
}}}
Or via the menu:
{{{
Display -> Animation -> Highlight Affected
}}}
Talking with Autodesk support, they were able to provide this reason:
<<<
Short version: it was done to improve performance for more complex rigs where the dependency highlighting could noticeably slow things down.

A bit more detail: While the animation team was reviewing opportunities to boost performance, this defect was logged, where selecting objects in some complex scenes was taking very long to process. Selecting an object with tens of thousands of connected shapes would end up highlighting every one of those shapes, causing a significant performance hit because in some cases the entire rig needed to be re-evaluated during that selection process. In reviewing this, the internal thinking was that this default behavior was “of minimal use” for riggers and “a hindrance” for animators; “More often than not when you have affect highlighting on and you select something 3/4 of the rig turns pink. Animators frequently rely on the colors of NURBS curves to differentiate controllers in their rigs. If they select something and most things turn pink, they have to deselect everything in order to see what controller to select next.”. It was then decided to not only disable this behavior by default, but to no longer save the state of the display option in the Preferences.
<<<
I personally as a rigged completely disagree with this:  I //need// to see what is effected by what, and disabling this makes me feel like I'm working blind.  I could see an advantage for animators:  I have no issue with this feature, but it should be left off by default, so that individual users can turn it on if needed.
----
Also see:
* [[Why don't my verts display when pressing F8?]]
As it  turns out, as of Maya 2022.2:  If you have a node with a 'proxy' attribute (child or parent proxy attr type) assigned to an anim layer (see [[Working with proxy attrs]]):  If you select that layer, and access the 'Edit -> Keys -> Bake Simulations' tool, setting 'Bake To' to 'New Layer', this will outright crash Maya.  Baking it to the 'Base Animation' works.
Note the below code is in ~PyMel, but the same problem happens when using {{{maya.cmds}}} Python.

Have had weird instances where code like this:
{{{
import pymel.core as pm

fFilter = "*.txt"
theFile = pm.fileDialog2(caption="Save skin data", fileMode=0, fileFilter=fFilter, startingDirectory=startDir)
}}}
Would crash Maya after a file was selected, and the save button was pressed (Windows 7).

I found that changing the file filter to the below code fixed it:
{{{
import pymel.core as pm

fFilter = "Text (*.txt)"
theFile = pm.fileDialog2(caption="Save skin data", fileMode=0, fileFilter=fFilter, startingDirectory=startDir)
}}}
What's weird is both work on my machine, but on other's machines it would crash.  
The big difference was (using the broken top code), in the save dialog, the "Save as type" for them was //completely empty//, while I would see "*.txt".  When I changed it to the bottom code, I would see "Text (*.txt)", but they'd only see "Text".  But at least it stopped crashing.
A number of people have reported their Script Editor windows taking a 'long time' to open, and once open, seem to have poor performance.

Two culprits have shon this to happen:
# It looks like they're running out of hard drive space, and this seems to be an odd side effect.
# They have 'Echo All Commands' turned on, and it is filled with a massive amount of data that takes time to display.
This weird 'bug' started poppup up out of the blue:  Certain anim controllers, while pickable in the 3d viewport, weren't in the Outliner.
After saving the scene file as .ma, I tracked it down:  For reasons beyond my undersatanding the {{{.hio}}} attr ({{{.hiddenInOutliner}}}) was being set True on certain shapes.  Not only that, it was being locked in that state as well.
Simple fix to get them back:
{{{
import pymel.core as pm
for attr in pm.ls("*.hiddenInOutliner"):
    attr.unlock()
    attr.set(False)
}}}
What's weird is, I couldn't find docs on the {{{.hio}}} attr on the {{{transform}}} node, or any of it's superclassses.
In 2014, we found a reproducable bug:
Select two mesh and:
{{{
// clears history and messes up normal
polyUnite –ch 0;
}}}
{{{
// clears history and messes up normal
polyUnite;
delete –ch;
}}}
{{{
// clears history but does not mess up normal
polyUnite;
refresh;
delete –ch;
}}}
Maya behaving badly :(
I've tried xpm, and jpg, but nothing works?  I seems that the {{{iconTextButton}}} (or {{{iconTextRadioButton}}}, or probably anything with 'icon' in it) really wants a ''bmp'' file format:  Pass in the absolute path to the image, and it shows right up.
{{{
# Python code:
mc.iconTextButton(style='iconOnly', image="c:/full/path/to/image.bmp")
}}}
AND, the plot thickens:  It appears that Maya possibly has reserved names?  I'd made an icon called:
{{{
c:/temp/local.bmp
}}}
And the icon wouldn't load.  Kept getting warnings:
{{{
# Warning: File not found: c:\temp\local.bmp
}}}
Changing it's name to anything else however, made it work:
{{{
c:\temp\_local_.bmp
}}}
Can you explain that?
----
FURTHERMORE, it seems that sometimes Maya just decides it doesn't like a name.   For example, I had a .bmp with this name:
{{{
v_smooth_whiteToBlack.bmp
}}}
but Maya wouldn't load it.  All of these things got it working:
*Removing a single character from it.
*Changing the uppercase B to lowercase.
*Changing the uppercase T to lowercase.
*Making any other character uppercase.
But for some reason, if both the B & T were uppercase, it wouldn't load. @@>grrrrr<@@
!!!The Fix?
In one case, simply restarting Maya fixed the issue:  Suddenly it could see all the images that were 'missing' before.  Crazy....
Some links:
*http://zoomy.net/2010/04/11/wii-nunchuk-to-maya/
*http://www.jfernsler.com/blog/?p=29
Notes on editing wiki:
*[[Add custom graphics to the header|http://www.tiddlywiki.org/wiki/How_To/Header_Macro/Plugin_%28for_Custom_Graphic_Header%29]]
*[[text formatting]]
*[[list of macros|http://www.tiddlywiki.org/wiki/What_macros_are_built_in_to_TiddlyWiki%3F]]
*[[DateFormatString arguments|http://www.tiddlywiki.com/#DateFormatString]]
You can tag DAG objects as 'controllers' in Maya.  These appear to be designed with ~HumanIK, since they are part of that documentation.
* See docs on how to 'tag' animation ctrl nodes as them [[here|http://help.autodesk.com/view/MAYAUL/2019/ENU/index.html?guid=GUID-17AF3B37-8A57-46DD-8808-D4BFF23AD285]].
* You can set them to control the visibility based on mouse position: [[How can I interactively set a controllers visibility based on mouse proximity?]]
** Note, this has significant performance playback issues, up to 10% FPS slowdown.
The {{{type}}} node is created via the {{{Type.mll}}} plugin.  It creates pretty strings of characters on-screen.

@@NOTE@@ : It //appears// that the type is created in some other background thread:  Not a big deal if being accessed via the UI, but if you're trying to script it, the calling code will continue executing before the type is finished being created, which can be problematic...
----
!Opening the tool
When you, via the menu '{{{Create -> Type}}}', this series of code launches:
{{{
createTypeCallback
}}}
is executed, living in this mel script:
{{{
C:/Program Files/Autodesk/Maya20XX/scripts/startup/ModCreateMenu.mel 
}}}
Which in turn calls to :
{{{
typeCreateText
}}}
Which lives here:
{{{
C:/Program Files/Autodesk/Maya20XX/scripts/others/typeCreateText.mel
}}}
And //it// in turn calls to this, to load the plugin if it's not alreayd:
{{{
typeInitPlugin;
}}}
Which lives in this mel script:
{{{
C:/Program Files/Autodesk/Maya20XX/scripts/others/typeInitPlugin.mel
}}}
And finally executes this Python code:
{{{
import maya.app.type.typeToolSetup
maya.app.type.typeToolSetup.createTypeTool()
}}}
That Python module lives here:
{{{
C:\Program Files\Autodesk\Maya2018\Python\Lib\site-packages\maya\app\type\typeToolSetup.py
}}}
And the first thing it does is, create a {{{type}}} node, from the loaded plugin:
{{{
typeTool = cmds.createNode( 'type', n='type#', skipSelect=True )
}}}
----
!Setting values:
There appears to be no official docs for the {{{type}}} node, but the {{{typeToolSetup.py}}} shows a lot of ways to do things:
Set the font:
{{{
cmds.setAttr( typeTool+'.currentFont', defaultFont, type="string" )
}}}
Set the style (bold, italic, etc)
{{{
cmds.setAttr( typeTool+'.currentStyle', defaultStyle, type="string" )
}}}

Also see my other subject [[Matrix info]] for non-Python related matrix stuff, and an overview of how matrices in Maya work.
----
By default Python has no "matrix" data object.  But //mel does// ({{{matrix[][]}}}), and through mel you can add, multiply (etc) matrices.  One way I've found to do this in Python is through the {{{OpenMaya}}} api calls (reference I pulled a bunch of this info from [[here|http://www.rtrowbridge.com/blog/2008/11/05/vectors-and-matrices-in-python/]]).
----
To make things easier, first define some helper functions that will process matrices based on how Maya deals with them.  Specifically, if you query a matrix attr on a node, it returns back a list of 16 floats.  And, using the {{{xform}}} command, if you try to apply a matrix back to a node, it also expects a list of 16 floats.  But to do any kind of matrix //math// between matrices (using operators like {{{+}}}, {{{*}}},{{{ -}}}, etc) you need to turn those float lists into actual API {{{MMatrix}}} objects that support the given operators.
*Docs for the API [[MMatrix|http://download.autodesk.com/us/maya/2010help/API/class_m_matrix.html]] class.
{{{
# Python code
# matrixutils.py

import maya.cmds as mc
import maya.OpenMaya as om

def listToMMatrix(mList):
    """
    Convert a list of 16 floats into a MMatrix object
    Mainly a helper for getMMatrix()
    """
    if len(mList) != 16:
        raise Exception("Argument 'mList' needs to have 16 float elements")
    m = om.MMatrix()
    om.MScriptUtil.createMatrixFromList(mList, m)
    return m

def mMatrixToList(matrix):
    """
    Convert a MMatrix object into a list of 16 floats.
    Mainly a helper for matrixXform()
    """
    return [matrix(i,j) for i in range(4) for j in range(4)]

def getMMatrix(node, matrixType):
    """
    Get matrix data from a node, and return as MMatrix object.
    """
    good = ["matrix", "inverseMatrix", "worldMatrix", "worldInverseMatrix",
            "parentMatrix", "parentInverseMatrix", "xformMatrix"]
    if matrixType not in good:
        raise Exception("Argument 'matrixType' is an invalid matrix attr type."+
                        "  Please choose from: " + ' '.join(good))
    return listToMMatrix(mc.getAttr(node+"."+matrixType))

def matrixXform(node, matrix, spaceType):
    """
    Transform a Maya DAG (transform, joint) object based on a given matrix value,
    via the mel 'xform' command.

    matrix : either a maya.OpenMaya.MMatrix object, or a list with 16 entries.
    spaceType :  either 'worldSpace' or 'objectSpace'
    """
    mList = None
    if type(matrix) is type(om.MMatrix()):
        mList = mMatrixToList(matrix)
    elif type(matrix) is type(list()) and len(matrix) == 16:
        mList = matrix
    else:
        raise TypeError("Arg 'matrix' either needs to be a maya.OpenMaya.MMatrix object, or list with 16 entries")

    if(spaceType == "worldSpace"):
        mc.xform(node, worldSpace=True, matrix=mList)
    elif(spaceType == "objectSpace"):
        mc.xform(node, objectSpace=True, matrix=mList)
    else:
        raise ValueError("spaceType arg is either 'worldSpace' or 'objectSpace', passed value is '%s'"%spaceType)
}}}
And some example usages:
*[[Finding and applying the difference between two matrices]]
Create two objects in Maya, objectA, and objectB.  Transform both of them in space away from each other.  Then, grab their worldMatrix info:
{{{
matrixA = getMatrix("objectA", "worldMatrix")
matrixB = getMatrix("objectB", "worldMatrix")
}}}
Matrices are applied in the order multiplied.  Order is very important!  The order could be considered a transform hierarchy, with parents on the left, and children descending to the right.  In the below example, we create a new matrix that could be thought of as being the same position if you had parented objectB to objectA (matrixA (the 'parent') is on the left, matrixB (the 'child') is on the right) maintaining objectB's current offset from the origin.  When it is applied to objectB, it will jump to that new location as defined by the 'parental' transformations on objectA:
{{{
offsetMatrix =  matrixA * matrixB
matrixXform("objectB", offsetMatrix, "worldSpace")
}}}
To query an item in a {{{MMatrix}}} object requires some hoop-jumping.  This example will query the second row ({{{[1]}}}), and the first item in that row ({{{0}}}):
{{{
a = om.MScriptUtil.getDoubleArrayItem(matrixA[1], 0)
}}}
----
The book [[Beginning Game Development with Python and PyGame: From Novice to Professional|http://www.apress.com/book/view/9781590598726]] describes a module, and a specific class for dealing with matrices:
*It has a {{{matrix44}}} class in its [[gameobjects|http://code.google.com/p/gameobjects/]] module.  [[gameobjects documentation|http://www.willmcgugan.com/gameobjects/docs/index.html]].  [[matrix44 documentation|http://www.willmcgugan.com/gameobjects/docs/gameobjects.matrix44.Matrix44-class.html]].  It stores its translation values along the bottom row, which is the same way Maya does it. 
Here's an example using it, and some of the above code, to transform object "test2" to match positions with object "test":
{{{
import maya.cmds as mc
# matrixXform() is defined in matrixutils.py in the above example.
from matrixutils import matrixXform
from gameobjects import Matrix44

# Get our matrix as a list of 16 items:
mtxList = mc.getAttr("test.matrix")
# Make a Matrix44 object from that list:
mtx44 = Matrix44.from_iter(mtxList)
# Transform object 'test2' by object 'test's matrix:
matrixXform("test2", mtx44._m, 'worldSpace')
}}}
----
I've also found a Python module called '~PyEuclid' that cas a matrix class, but I have yet to do any research into it.  However, it does seem to store its matrix data differently from Maya:  It appears to store its transforms down the right column, rather than the bottom row (Maya).
*Main Page: http://partiallydisassembled.net/euclid.html
*Documentation: http://partiallydisassembled.net/euclid/
*Source: http://code.google.com/p/pyeuclid/
I can't believe how long I've been working in Maya without knowing about this feature.
In a nutshell, '{{{proxy}}}' flagged attributes act like bi-directional attrs.  Or to put it another way, they're like 'instanced' attributes:  One attr that lives on multiple nodes.  They are similar to how the {{{connectControl}}} command connects attrs to UI controls.

Below is a simple example:  We create three locators, and add a custom attr to each (called 'blendSwitch').
We need to make a 'non proxy' version on at least one node, then point the other nodes attrs back to it as proxies.
From reading the docs, the attrs must all be keyable as well.
{{{
import pymel.core as pm

proxyAttr = "blendSwitch"

loc1 = pm.spaceLocator()
loc2 = pm.spaceLocator()
loc3 = pm.spaceLocator()

loc1.addAttr(proxyAttr, keyable=True)
loc2.addAttr(proxyAttr, proxy=loc1.attr(proxyAttr), keyable=True)
loc3.addAttr(proxyAttr, proxy=loc1.attr(proxyAttr), keyable=True)

loc1.attr(proxyAttr).set(23.3)
print loc3.attr(proxyAttr).get()
# 23.3
}}}
At the end you can see how we change the value of the 'proxyAttr' on the first locator, and the 3rd locator now has the same value.

Looking at the connection editor shows some interesting things:  loc1.blendSwitch output connects to both loc2.blendSwitch & loc3.blendSwitch as inputs.  But it's a non-blocking input, and in fact appears to now act bi-directional.
Furthermore, if you keyframe the attr on any of the nodes, the keyframe connects to loc1, since it was the first instance of the attr, it appears.
{{{
animCurve.output -> locator1.blendSwitch -> locator2(&3).blendswitch
}}}
Finally, there is also a {{{usedAsProxy}}} arg, but I'm not able to really figure out what it does yet.  If I plug it in as True on the first locator attr creation, while all the connections are made, I can't change any of the values.
----
Also see:
* [[Referenced proxy enum attrs become locked when connections are broken]]
Wrap deformers never seem to work right when I first try.  Here's some notes for my future self:
* You select the thing to be wrapped first, and the thing doing the wrapping second.  This is backwards to most deformers.
* ''Exclusive bind'' :
** Maya notes:  When on, the wrap deformer’s target surface will behave like a rigid bind skin and Weight Threshold is disabled. Each surface point on the wrap deformer’s target surface will only be affected by a single wrap influence object point.
** My notes:  
***This is the most optimized way to wrap something, providing the best performance.
*** It appears that 'Max Distance' still seems to have an effect with this, mainly just causing it to break at random values.
*''Auto Weight Threshold''
** Maya notes: When on, the wrap deformer automatically sets the optimal weight of the wrap influence objects’ shapes by calculating the smallest Max Distance value that will ensure every point on the mesh is affected by an influence object. This option is on by default.
** My notes:  This can take a looooong time to calculate on complex assets.  Disables 'Weight Threhold' and 'Max Distance'.
*''Weight Threshold'' : 
** Maya Notes:  Lets you manually specify the influence of the wrap influence objects’ shapes based on the proximity of their components to the objects being deformed. Depending on the point density (for example, the number of CVs) of the wrap influence objects, changing the Weight Threshold can change the overall smoothness of the deformation effect. Values range from 0.000 (smooth) to 1.000 (sharp). Use the slider to select values from 0.000 to 1.000. Default is 0.000.  The Weight Threshold takes effect within the influence area defined by the Max Distance value (below).
** My notes:  
*** Low values tend to make the wrappee's points stick better to the wrapper.
*''Max Distance'' : 
** Maya Notes: Specifies the influence area of wrap influence object points. By limiting the influence area with Max Distance, you can manually limit how much memory Maya requires to perform the deformation. The less memory required, the better the performance. Using Max Distance is especially useful when you are working with high-resolution wrap influence objects.  The Weight Threshold takes effect within the influence area defined by the Max Distance value.
** My notes:  Use this to speed things up, make it as low a value as possible.
Warpcat
Per the Maya [[addAttr docs|http://help.autodesk.com/cloudhelp/2019/ENU/Maya-Tech-Docs/CommandsPython/addAttr.html]]:
----
To add a non-double attribute the following criteria can be used to determine whether the {{{dataType}}} or the {{{attributeType}}} flag is appropriate:
* Some types, such as double3 can use either. 
* In these cases the {{{-dt}}} flag should be used when ''you only wish to access the data as an atomic entity'' (eg. you never want to access the three individual values that make up a double3). 
* In general it is best to use the {{{-at}}} in these cases ''for maximum flexibility''. 
* In most cases the {{{-dt}}} version //will not display in the attribute editor// as it is an atomic type and you are not allowed to change individual parts of it.
So, 'Attribute Type' attrs will show up in the things like the Attribute Editor, but 'Data Type' attrs won't. 
| Attribute Type | Data Type | Info |
| bool | | |
| long, long2, long3 | long2, long3 | 32 bit integer | |
| short, short2, short3 | short2, short3 | 16 bit integer |
| byte | | 8 bit integer |
| char | | |
| enum | | |
| float, float2, float3 | float2, float3, floatArray | |
| double, double2, double3 | double2, double3, doubleArray | |
| doubleAngle | | |
| doubleLinear | | |
| | string, stringArray | |
| compound | | An 'attribute container', like a {{{struct}}} var in {{{c++}}} |
| message | | no data (just object name) |
| time | | |
| fltMatrix  | matrix | 4x4, matrix is double, float matrix is float  | |
| reflectance | reflectanceRGB |  | |
| spectrum | spectrumRGB |  | |
| | ~Int32Array | Array of 32 bit Ints | |
| | vectorArray | |
| | nurbsCurve | |
| | nurbsSurface | |
| | mesh | |
| | lattice | |
| | pointArray | array of double 4D points |


----
Also see:
* [[Why are some custom attributes hidden in the Attribute Editor?]]
* [[Difference between multi and compound attrs]]
There are different types of caches in Maya.  Here are a few notes on the {{{cacheFile}}} node & associated commands.  I got interested in this when I was looking into how Maya does it's "delete non-deformer history" operation.
*Node: [[cacheFile|http://download.autodesk.com/us/maya/2010help/Nodes/cacheFile.html]]
*Commands (Python links):
**[[cacheFile|http://download.autodesk.com/us/maya/2010help/CommandsPython/cacheFile.html]] - Used for the creation of the above {{{cacheFile}}} node.
**[[cacheFileCombine|http://download.autodesk.com/us/maya/2010help/CommandsPython/cacheFileCombine.html]]
**[[cacheFileMerge|http://download.autodesk.com/us/maya/2010help/CommandsPython/cacheFileMerge.html]]
*Scripts:
**{{{C:\Program Files\Autodesk\Maya20XX\scripts\others\findExistingCaches.mel}}}
**{{{C:\Program Files\Autodesk\Maya2010\scripts\others\getGeometriesToCache.mel}}}
*API (Not sure if these are related or not yet...)
**[[MGeometry|http://download.autodesk.com/us/maya/2010help/API/class_m_geometry.html]]
**[[MGeometryManager|http://download.autodesk.com/us/maya/2010help/API/class_m_geometry_manager.html]]
There are two terms that sound a lot a like, but are in fact different:
*''current //workspace// directory'' :  Where the Maya 'project' defaults too, and is modified by the {{{workspace}}} command.  We're not dealing with this concept in this example, but check notes on it [[here|How can I modify the current project \ workspace?]].
*''current //working// directory''  :  From the Maya docs: "This starts out as the directory where Maya was launched, but will change in response to {{{chdir}}} commands as well as some file browsing operations. This directory is where system-level commands such as {{{system}}}, {{{fopen}}}, and {{{popen}}}. The working directory is not related to the [[workspace directories|How can I modify the current project \ workspace?]] where scene data is stored, and should not be used for accessing or creating scene- or project-specific files."
The @@{{{pwd}}}@@ command simply prints the 'current working directory', while the @@{{{chdir}}}@@ command sets it.
Since I always seem to forget, the {{{heightField}}} node is what's used to preview material displacements, by connecting the out displacement of the material/texture to the in displacement attr on the heightField.
http://help.autodesk.com/cloudhelp/2016/ENU/Maya-Tech-Docs/Nodes/heightField.html
The {{{maya.app.general}}} package contains a lot of additional Python scripts that can fly under the radar:
* ~CommandPort.py
* executeDroppedPythonFile.py
* fileTexturePathResolve.py
** getFilePatternString
** findAllFilesForPattern
** computeUVForFile
** computeUVForFiles
* mayaMixin.py
** For working with ~PySide windows & ~workspaceControls
* nodeEditor.py
* pointOnPolyConstraint.py
* positionAlongCurve.py
* resourceBrowser.py
* ~ServerRegistryMMap.py
** Managing commandPorts
* shelfEditorWindow.py
* zipScene.py 
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 add a multi attr to my node, query its connections?]]

When running the 'Modify -> Convert -> nParticle to Polygon' tool, why doesn't the new mesh show up?
Select the {{{nParticle}}} node in the Attribute Editor, and under 'Output Mesh', lower the 'Threshold' until they start to show up.  I'm guessing this is based on a worldspace size thing : Small particles need a small threshold to show up.
Real docs on the matter:
http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=GUID-6213F506-D7C3-4B0E-AEE9-1B2526C474AB

Like most wikis, TiddlyWiki supports a range of simplified character formatting:
| !To get | !Type this |h
| ''Bold'' | {{{''Bold''}}} |
| --Strikethrough-- | {{{--Strikethrough--}}} |
| __Underline__ | {{{__Underline__}}} (that's two underline characters) |
| //Italic// | {{{//Italic//}}} |
| Superscript: 2^^3^^=8 | {{{2^^3^^=8}}} |
| Subscript: a~~ij~~ = -a~~ji~~ | {{{a~~ij~~ = -a~~ji~~}}} |
| @@highlight@@ | {{{@@highlight@@}}} |
<<<
The highlight can also accept CSS syntax to directly style the text:
@@color:green;green coloured@@
@@background-color:#ff0000;color:#ffffff;red coloured@@
@@text-shadow:black 3px 3px 8px;font-size:18pt;display:block;margin:1em 1em 1em 1em;border:1px solid black;Access any CSS style@@
<<<

//For backwards compatibility, the following highlight syntax is also accepted://
{{{
@@bgcolor(#ff0000):color(#ffffff):red coloured@@
}}}
@@bgcolor(#ff0000):color(#ffffff):red coloured@@
*Beginners Guide:
**http://www.giffmex.org/twfortherestofus.html
*TW Help
**http://tiddlyspot.com/twhelp/
*GoogleGroup: TiddlyWiki
**http://groups.google.com/group/TiddlyWiki
*GoogleGroup: TiddlyWikiDev
**http://groups.google.com/group/TiddlyWikiDev
*TiddlyWiki Development Community:
**http://www.tiddlywiki.org/