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

If Maya crashes, and if it saves a backup, where does it save it?

$
0
0
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])

Source Analysis in Wing

$
0
0
NOTE: Wing has been updating their own tutorials on this, take a look at them here:
http://www.wingware.com/doc/howtos/maya
A some of their tutorial came from my troubleshooting with their support team (which is great by the way), and actually links back to this wiki :-)

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, Windows7:
C:\Program Files\Autodesk\Maya2013\Python\Lib\site-packages\maya>"C:\Program Files\Autodesk\Maya2013\bin\mayapy.exe"  "C:\Program Files (x86)\Wing IDE 4.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.
  • 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\maya
Via its Edit -> Preferences -> Source Analysis -> Advanced -> section, and "Insert" the path.

Old stuff. You should really follow the above tutorial on the Wing site.

Tutorial done on Windows Vista.
This will get 'source analysis' of the Maya API working in Wing. Will allow for such nice things as auto-completion. Here are some links I found on the subject, but none of them got me exactly to where I needed to be. But they all helped ;)

Get the API OpenMaya calls working:

  • Make 'pi file' dir here:
C:\Users\epavey\AppData\Roaming\Wing IDE 3\pi-files\maya
  • Copy the Wing-made .pi files to that dir from here:
C:\Users\epavey\AppData\Local\Wing IDE 3\pi-cache\2.6\C\Program Files\Autodesk\Maya2010\Python\Lib\site-packages\maya
  • Remove all the underscores from the front of the .pi files:
    • _OpenMaya.pi rename to OpenMaya.pi
  • In Wing's prefs, go : Source Analysis -> Advanced -> Interface File Path -> Insert.
    • And add the directory you made above.
  • Restart Wing
  • Now, when you enter the below code, Wing should start to auto-populate the command list:
import  maya.OpenMaya as om
om. # <-- Wing should now populate that attribute list.
Note, it seems like the below won't work. You need to use the above example:
import maya.OpenMaya
maya.OpenMaya. # <-- no auto-complete :(

Get the mel.cmds calls working:

At the time of this authoring, it appears that it's a 'bug' in Wing that doesn't let it see the maya.cmds pacakge. However, after working with Wing they made a patch, that should be available in future versions. So the below data really doesn't have any effect... yet...
import maya.cmds as mc

filepath = 'c:/temp/cmds.pi'
f = open(filepath, mode='w')

for k in sorted(mc.__dict__.keys()):
    f.write('def %s():\n'%k)
    f.write('    pass\n')
    f.write('\n')
f.close()
  • Copy the cmds.pi file to the directory you made above under 'Get the API OpenMaya calls working'.
  • And then.... (currently not working.... )

API : How can I access c++ structs?

$
0
0
There are edge-cases where certain API calls will want a c++ struct passed in. For example, the OpenMayaRender.MRenderView class's updatePixels method expects a pointer to a RV_PIXELS 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?

How can I find the namespace of something?

$
0
0

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.

Authoring Scripted Plugins via Python

$
0
0
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
Also see official examples online here:
http://download.autodesk.com/us/maya/2011help/API/examples.html
Search for .py extensions.
Or you can find examples on your local drive:
C:\Program Files\Autodesk\Maya20XX\devkit\plug-ins\scripted\*.py

Autodesk has made a nice video of how to setup a simple scripted plugin... HERE.

Collection of simple example scripted plugins I've been authoring for the wiki can be shown by clicking on this tag:

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. 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
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.
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

How can I trigger an event when the scene saves?

$
0
0
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?

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";
    }

Qt versions & downloads

$
0
0
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.

This is not to be confused with PyQt, which is a Python (not Maya) implementation of Qt. You can also get PyQt working in Maya, if you jump through some hoops (shown in the PDF below).

If you actually need to download the Qt source, Autodesk has its own custom cuts for Maya downloadable here: http://www.autodesk.com/lgplsource

Qt Designer can be found here (2012):
C:\Program Files\Autodesk\Maya2012\bin\designer.exe
Posts:

Installing PyQt4 and running in Maya (this appears to be the same docs from 2008->2012):
C:\Program Files\Autodesk\Maya20XX\devkit\other\PyQtScripts\Windows_QT_Installation.pdf
Good posts on getting PyQt (and PySide) running in Maya:
Other PyQt posts:

Python & Qt versions in Maya

$
0
0
MayaPythonQtPyQt
8.52.4.3
20082.5.1
20092.5.1
20102.6.1
20112.6.44.5.3?
20122.6.44.7.1?
20132.6.44.7.14.9.1
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.

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

$
0
0
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:
This installation is for Maya 2013 on Windows 7, and I mainly followed the above pdf.

  1. Downloaded these zips, and extracted to a temp folder:
    1. PyQt-win-gpl-4.9.4.zip -> \PyQt-win-gpl-4.9.4
    2. qt-4.7.1-modified_for_maya_2013.zip - "Maya's modified version of the Qt source code" (from the above pdf) -> \Qt-4.7.1-Maya
    3. sip-4.13.3.zip -> \sip-4.13.3
  2. \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.
  3. 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
    1. 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:
  1. Unzip to temp folder
  2. 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):
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!!!

MainMenu

Qt Designer

$
0
0
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.

This is not to be confused with PyQt, which is a Python (not Maya) implementation of Qt. You can also get PyQt working in Maya, if you jump through some hoops, see Installing PyQt.

C:\Program Files\Autodesk\Maya20XX\bin\designer.exe
Posts:

PyQt : Docs & reference

$
0
0
Official PyQt docs by Riverbank Computing:
For Qt itself, look here:

Other tutorials from the web:
For Maya:
PyQt specific:

Qt widget galleries:

PyQt : How can I get a pointer to the main Maya window?

$
0
0
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 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's, and then parent them to this main window.

Docs:

PyQt : Embedding a Maya widget into a PyQt UI

$
0
0
Really good blog post here, by Nathan Horne
Copied below (and slightly changed) for redundancy:
# http://nathanhorne.com/?p=381
import maya.cmds as mc
import maya.OpenMayaUI as omui
from PyQt4 import QtGui, QtCore
import sip

def getMayaWindow():
    ptr = omui.MQtUtil.mainWindow()
    return sip.wrapinstance(long(ptr), QtCore.QObject)

def toQtObject(mayaName):
    '''
    Given the name of a Maya UI element of any type,
    return the corresponding QWidget or QAction.
    If the object does not exist, returns None
    '''
    ptr = omui.MQtUtil.findControl(mayaName)
    if ptr is None:
        ptr = omui.MQtUtil.findLayout(mayaName)
    if ptr is None:
        ptr = omui.MQtUtil.findMenuItem(mayaName)
    if ptr is not None:
        return sip.wrapinstance(long(ptr), QtCore.QObject)

class MayaSubWindow(QtGui.QMainWindow):
    def __init__(self, parent=getMayaWindow()):
        super(MayaSubWindow, self).__init__(parent)
        self.executer = mc.cmdScrollFieldExecuter(sourceType="python")
        qtObj = toQtObject(self.executer)
        #Fill the window, could use qtObj.setParent
        #and then add it to a layout.
        self.setCentralWidget(qtObj)

myWindow = MayaSubWindow()
myWindow.show()
Here is another example:

PyQt : How can I get the PyQt widget for a named Maya control?

$
0
0
# Code snippet pulled from:
# http://nathanhorne.com/?p=381

import maya.OpenMayaUI as omui
import sip
from PyQt4 import QtCore

def toQtObject(mayaName):
    '''
    Given the name of a Maya UI element of any type,
    return the corresponding QWidget or QAction.
    If the object does not exist, returns None
    '''
    ptr = omui.MQtUtil.findControl(mayaName)
    if ptr is None:
        ptr = omui.MQtUtil.findLayout(mayaName)
    if ptr is None:
        ptr = omui.MQtUtil.findMenuItem(mayaName)
    if ptr is not None:
        return sip.wrapinstance(long(ptr), QtCore.QObject)
Here's another example, showing how you can get a QWidget for Maya's 'timeline':
# Snippet pulled from here:
# http://dannywynne.com/scripts/timeline_markers.zip

import maya.OpenMayaUI as omui
import sip

ptr = omui.MQtUtil.mainWindow() # QtGui.QWidget pointer to the QtGui.QMainWindow
maya_window = sip.wrapinstance(long(ptr), QtCore.QObject) # Returns QtCore.QObect
maya_timeline = maya_window.findChild(QtGui.QWidget, "timeControl1")
Docs:

How can I have Wing send Python or mel code to Maya?

$
0
0
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

Note: The below code worked up until Maya 2013: At that point, it looks like Maya did something with it's commandPort interactions, and everything quit working. I have a new solution using a completely different method using threads and server\client code in Python, but have yet to update the below tutorials :-S


I using Wing IDE for 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 you highlight in Wing execute in Maya (aka 'evaluate selection'). This lets me completely replace the Script Editor when authoring Python or mel, which we all know doesn't have the best Python integration... :-S
I've added some updated code below that now allows you to publish mel code from Wing to Maya as well.
Note that you can also 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 few 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

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.

Also, 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: userSetup.mel and the function send_to_maya() in wingHotkeys.py.

#1 - userSetup.mel

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:
  1. Save the text selected in Wing as a temp file on disk.
  2. 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
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.
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

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 commandPort you opened in userSetup.mel.  Make sure this matches!
   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 += ';'
   # This saves a temp file on Window.  Mac\Linux will probably need to
   # change this to support their OS.
   tempFile = os.path.join(os.environ['TMP'], '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)
   #mSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) # Works!
   # 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))
      #mSocket.connect(("::1",commandPort))  #works!
      mSocket.connect(("localhost", commandPort))

      # Send our code to Maya:
      mSocket.send('python("import executeWingCode; executeWingCode.main(\'%s\')")'%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 execPythonCode.py is the one Wing's send_to_maya() function (above, step 2) triggers. 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.
"""
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 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'

    """
    tempFile = os.path.join(os.environ['TEMP'], 'wingData.txt').replace("\\", "/")
    if os.access(tempFile , os.F_OK):
        # open and print the file in Maya:
        f =  open(tempFile , "r")
        lines = f.readlines()
        for line in lines:
            print line.rstrip()
        f.close()
        print "\n",

        if codeType == "python":
            # execute the file contents in Maya:
            f = open(tempFile , "r")
            exec(f, __main__.__dict__, __main__.__dict__)
            f.close()

        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 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
  • What's so special about '127.0.0.1'? Check Wikipedia HERE

How can I append to the Time Slider's marking menu?

$
0
0
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";

Vector Math

$
0
0
Python seems to have no built in libraries for doing vector math. Options for doing it in Maya are listed here.

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.
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]

Trigonometry references

API: Simple scripted plugin deformer node

$
0
0
I wanted to build a bare-minimum framework for authoring a deformer node via a scripted plugin. Maya comes with a simple example here,
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

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.
Viewing all 610 articles
Browse latest View live