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

PyMel : Find the center of a face

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

Qt & PySide: Docs & reference

$
0
0
For Qt itself, look here:

Other tutorials from the web:

PySide docs for Maya:

PyQt docs for Maya:
PyQt specific:

PyMel : Logging

$
0
0
Nice overview:
PyMel has it's own logger menuyou can enable:
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.

How can I query the name of my rendered image?

For Python: Maya 'Script Editor' style IDE

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

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 Mayahere (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:

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.

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
Another Note: : The below examples I've gotten to work repeatedly on Windows. Still having a hard time getting them to work on Mac (commandPort problems).


I use Wing 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 Wingexecute in Maya (aka 'evaluate selection'). Also known as REPL. 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.pyand 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 mayaWingServer

mayaWingServer.startServer()
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:
  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
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

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 += ';'
   # 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) # 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))

      # 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 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):
        # 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:
                exec(f, __main__.__dict__, __main__.__dict__)
        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
  • What's so special about '127.0.0.1'? Check Wikipedia HERE

Loading Qt .ui files

$
0
0
The Qt Designer app gives you a wysiwyg window authoring environment. Maya provides a loadUI 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)
        gMainWin = pm.mel.eval("$g = $gMainWindow")
        pane = pm.paneLayout(configuration="single", parent=gMainWin)
        pm.dockControl(docName, allowedArea="all", area=docArea, width=dockWidth,
                       floating=dockFloat, content=pane , label="My Custom Dock")
        pm.control(qtWin, edit=True, parent=pane)

    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 : Convert a Maya control into a widget

$
0
0
Really good blog post here, by Nathan Horne
I made my own version below
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:

Also see:

PySide : Add Maya controls to PySide layout\widget

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

API: Access Qt widget data

$
0
0
Newer docs here:

Starting with Maya 2011 they introduced a new API class MQtUtil.
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.

PySide : Access Qt .ui widget data in Maya

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

How can I get a list of face-shells on a given mesh?

$
0
0
Normally I loath picking things in code, but the polySelect 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 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
    """
    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

API : How to compile a plugin

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

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

Questions:

Maya compiler versions

Python & Qt versions in Maya

$
0
0
MayaPythonQtCompatible PyQtCompatible PySideWin CompilerLinux CompilerMac Compiler
8.52.4.3
20082.5.132bit & 64bit vc8.0 + SP1 + qtHotfix, Intel 9.1.03432bit & 64bit, gcc 4.1.2, Intel 9.1.03932bit XCode 2.4.1, gcc 4.0.1, Intel 9.1.037*
20092.5.132bit XP SP2 & 64bit XP x64 SP2, vc8.0+SP1+qtHotfix, Intel 10.1.01364bit RHEL4.4, gcc 4.1.2 Intel 9.1.03932bit Tiger 10.4.11, XCode 2.4.1, gcc 4.0.1, Intel 10.1.007*
20102.6.132bit Xp SP2 & 64bit Xp x64 SP2, Visual Studio 2008 SP1, Intel 10.1.02264bit RHEL5.1 gcc 4.1.2, Intel 11.0.08332bit & 64bit Leopard 10.5.6, XCode 3.0, gcc 4.0.1, Intel 11.0.064
20112.6.44.5.3 (it begins...)??32bit Xp SP2 & 64bit Xpx64 SP2, Visual Studio 2008 SP1, Intel 11.1.05164bit RHEL5.1, gcc 4.1.2, Intel 11.1.05932bit & 64bit Leopard 10.5.x, XCode 3.1.2, gcc 4.0.1, Intel 11.1.076
20122.6.44.7.1??32bit XP SP2 & 64bit XPx64 SP2, Visual Studio 2008 SP1 + ATL security update, Intel 11.1.06764bit RHEL5.1, gcc 4.1.2, Intel 11.1.07364bit Snow Leopard 10.6.4, XCode 3.2.1, gcc 4.2.1, Intel 11.1.089
20132.6.44.7.14.9.1?32bit Win7 & 64bit Win7x64, Visual Studio 2010 SP1, Intel 12.0.4.19664bit RHEL 6.0, FC14 gcc 4.1.2, Intel 11.1.07364bit SnowLeopard 10.6.8, Xcode3.2.1 gcc 4.2.1, Intel 11.1.089
20142.7.34.8.24.101.1.2 - Ships with Maya!64bit Win7x64, Visual Studio 2010 SP1, Intel Composer XE 201164bit RHEL 6.0, FC14, gcc 4.1.2, Intel Composer XE 2011 Update 1164bit Lion 10.7.4, Xcode 4.3.3 with SDK 10.6 (SnowLeopard), gcc 4.2.1, Intel Composer XE 2011 Update 11
20152.7.34.8.5?1.264bit Win7x64, Visual Studio 2012 Update 4, 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)
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, if using versions before 2014: Starting in 2014, PySide started shipping with Maya: No more needing to compile PyQt.

Also see:

Getting pywin32 working in Maya

$
0
0
Had to do some hoop-jumping to get "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:
  1. Download the pywin32-216.win-amd64-py2.6.exe cut: This is the 64-bit cut of the package that will match up with Maya's 64-bit cut of Python.
    1. http://sourceforge.net/projects/pywin32/files/pywin32/Build216/
  2. 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.
  3. Run the downloaded package executable. When complete, the new C:\Python26\Lib\site-packages dir will be populated with the new (messy) package contents.
  4. Copy all of that new stuff to Maya's site-package dir here: C:\Program Files\Autodesk\Maya20XX\Python\Lib\site-packages
  5. Delete the new C:\Python26\Lib\site-packages dir.
  6. Rename the old dir C:\Python26\Lib\site-packages-old back to C:\Python26\Lib\site-packages.
  7. 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:
  • 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 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:

How can I remove a namespace, and all of its child-namespaces?

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

How can I remove a namespace by renaming its nodes?

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

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

Offset animCurve data

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

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


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