Maya Python API を使用する

Maya API のラッパ(wrapper)、イテレータ、関数セット クラスを使用する基本的なスクリプトを作成できます。これらのスクリプトは、Maya モデルを照会し操作することはできますが、Maya に完全に統合されているわけではありません。スクリプト プラグインは、Maya に強固に統合された、より複雑なソリューションを提供します。このセクションでは、基本のスクリプト、スクリプト プラグイン、そしてスタンドアローン型スクリプトを作成する方法について説明します。

Maya Python API は Python ベースの API なので、Python の知識が必要です。

モジュールを読み込む

Maya Python API は、いくつかの Python モジュールに内蔵されています。作成するスクリプトで使用する機能を読み込む必要があります。また、Maya Python API は Maya ネームスペース内に存在します。したがって、追加のプリフィックスが必要です。OpenMaya モジュールを読み込むには次を実行します。

import maya.OpenMaya

モジュールまたはクラスのヘルプ

help コマンドを使用して、任意のモジュールまたはクラスについての情報を表示できます。たとえば、MVector についてのクラス情報を表示する場合、次を使用します。

help(maya.OpenMaya.MVector)

また、モジュール全体についての情報を表示することもできます。

help(maya.OpenMaya)

OpenMaya モジュールは非常に大きいので、この操作が値を返すには時間がかかります。

スクリプトを作成する

Maya Python API モジュールには、Python プログラミングで使用可能なクラスが含まれています。これらのクラスは異なるカテゴリに分類され、その関連を示すための適切な命名規則を持っています。クラスには次のような種類があります。

MFn

このプリフィックスを含むクラスは、特定タイプの MObject に対して演算を行う関数セットです。

MIt

このクラスはイテレータで、MObject に対して演算を行う関数セットとほとんど同じように動作します。たとえば MItCurveCV は、個々の NURBS カーブの CV に対して個別に(MFnNurbsCurveCV は存在しません)、あるいは繰り返し実行することでカーブのすべての CV に対して演算を行うために使用されます。

MPx

このプリフィックスを含むクラスはすべて「プロキシ」です。これは、ユーザ独自のオブジェクト型を作成したり、そこから派生させたりするために設計された API クラスです。

M クラス

これらのクラスのほとんどは「ラッパ(wrapper)」ですが、それ以外もあります。このクラスの例には、MVectorMIntArray などがあります。

ラッパ クラスと関数セット クラスを使用して、次のようなスクリプトを作成できます。

import maya.OpenMaya
vector1 = maya.OpenMaya.MVector(0,1,0)
vector2 = maya.OpenMaya.MVector(1,0,0)
vector3 = maya.OpenMaya.MVector(0,0,2)
newVector = vector1 + vector2 + vector3
print "newVector %f, %f, %f " % (newVector.x, newVector.y, newVector.z)

import コマンドを修正して、使用されているシンボル名を短くすることが可能です。

import maya.OpenMaya as OpenMaya
vector1 = OpenMaya.MVector(0,1,0)

スクリプトは、Maya Python API クラスを使用してディペンデンシー グラフの情報にアクセスすることができます。次のスクリプトでは、persp ノードを検索し、その translateX アトリビュート値を出力します。

# import the OpenMaya module
import maya.OpenMaya as OpenMaya

# function that returns a node object given a name
def nameToNode( name ):
    selectionList = OpenMaya.MSelectionList()
    selectionList.add( name )
    node = OpenMaya.MObject()
    selectionList.getDependNode( 0, node )
    return node

# function that finds a plug given a node object and plug name
def nameToNodePlug( attrName, nodeObject ):
    depNodeFn = OpenMaya.MFnDependencyNode( nodeObject )
    attrObject = depNodeFn.attribute( attrName )
    plug = OpenMaya.MPlug( nodeObject, attrObject )
    return plug

# Find the persp camera node
print "Find the persp camera"
perspNode = nameToNode( "persp" )
print "APItype %d" % perspNode.apiType()
print "APItype string %s" % perspNode.apiTypeStr()

# Print the translateX value
translatePlug = nameToNodePlug( "translateX", perspNode )
print "Plug name: %s" % translatePlug.name()
print "Plug value %g" % translatePlug.asDouble())

上の例は次のことを示します。

  • クラスのインスタンス化には、fn = OpenMaya.MFnFunctionSet() 表記を使用します。
  • MObject は、node = OpenMaya.MObject() を使用して作成することができます。
  • Python は型のない言語ですが、クラスのパラメータとして型を渡すには、正しい型をインスタンス化する必要があります。
  • Python 文字列は MString ラッパ クラスの代わりに渡され、返されます。
    注:

    わかりやすくするため、上の例ではエラー チェックを省略しています。

スクリプト プラグイン

スクリプト プラグインは、Maya に強固に統合されたソリューションの作成を可能にします。スクリプト プラグインにより開発者はコマンドのアンドゥ、Maya シーン ファイルへの適切な requires 行の構築などの機能をサポートすることができます。スクリプト プラグインを使用するもう 1 つの利点は、MEL と Python の両方で機能を使用きることです。

スクリプト プラグインを使用する

Maya プラグイン マネージャ(Plug-in Manager)が拡張され、スクリプト プラグインのロードとアンロードがサポートされるようになりました。

MAYA_PLUG_IN_PATH 上にあり、.py 拡張子で終わるファイルが、プラグイン マネージャ(Plug-in Manager)に表示されます。ロード(Loaded)チェック ボックスまたは自動ロード(Auto Load)チェック ボックスのどちらかを選択して、スクリプト プラグインをロードまたは自動ロードします。

注:

MAYA_PLUG_IN_PATH にプラグイン スクリプトではない .py スクリプトがあっても構いませんが、これらの項目はロードされません。エントリ ポイントが見つからないという内容の警告メッセージが発行されます。

プラグインは、プラグイン マネージャ(Plug-in Manager)から、または MEL あるいは Python のコマンド タブからロードできます。MEL では、loadPlugin() コマンドを使用します。Python では、maya.cmds.loadPlugin() コマンドを使用します。

helixCmd.py などのサンプルを実行するには、プラグインをロードし、Python エディタ(Python Editor)のタブに次を入力します。

import maya
maya.cmds.spHelix().

この Python スクリプトを起動すると、次が実行されます。

  • Maya モジュールが読み込みされ、cmds モジュールが使用可能になります。
  • カスタム コマンド spHelix() が起動されます。
    注:
    • sp プリフィックスは、「スクリプト プラグイン」用に使用します。
    • スクリプト プラグインは、loadPlugin コマンドを使用してロードする必要があります。スクリプト プラグインのソースを実行してもロードされません。

このプラグインは、Python コマンド maya.cmds.unloadPlugin (「helixCmd.py」) を使用してもアンロードできます。

ロードと実行の手順は、MEL エディタ内でも次を使用して起動できます。

loadPlugin helixCmd.py;
spHelix();

スクリプト プラグインを作成する

スクリプト プラグインの作成には、プラグイン内で専用の関数を定義することが必要です。スクリプト プラグインは次を行う必要があります。

  • initializePluginuninitializePlugin エントリ ポイントを定義します。
  • これらのエントリ ポイント内で、プロキシ クラスを登録および登録解除します。
  • 必要に応じて、プロキシ クラス構築のためにコールされる creator メソッドと initialize メソッドを実装します。
  • プロキシ クラスで必要な機能を実装します。これには、必要なモジュールの読み込みが必須です。

以下のセクションで、これらの項目についてサンプルとともに詳しく説明します。

読み込み

Python は import キーワードを使用して、機能をモジュールからスクリプトにインクルードします。例:

import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys

スクリプト プラグインが複数のファイルに分割されていてもかまいません。import コマンドは、2 次ファイルの機能をスクリプト プラグインにロードするために使用します。

import polyModifier

すべての 2 次スクリプトは、スクリプト プラグインと同じディレクトリに配置する必要があります。

スクリプト プラグインを初期化する

スクリプト プラグインがロードされると、Maya は定義内の initializePlugin() 関数を検索します。この関数にすべてのプロキシ ノードが登録されます。

# Initialize the script plug-in
def initializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.registerCommand( kPluginCmdName, cmdCreator )
    except:
        sys.stderr.write( "Failed to register command: %s\n" % kPluginCmdName )
        raise

initializePlugin() 関数が見つからない場合、スクリプト プラグインのロードは失敗します。また、ロード時、Maya は uninitializePlugin() 関数も検索します。これが見つからない場合、スクリプト プラグインのロードは失敗します。

スクリプト プラグインの初期化を解除する

Maya では、プラグインのアンロード試行時にあらかじめ見つけておいた uninitializePlugin() 関数がコールされ、プラグインのリソースがアンロードされます。

def uninitializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.deregisterCommand( kPluginCmdName )
    except:
        sys.stderr.write( "Failed to unregister command: %s\n" % kPluginCmdName )
        raise

クリエータ関数

クリエータ関数は、派生したバージョンのプロキシ クラスを Maya に返すために使用します。バーチャル メソッドが Maya からコールする派生クラスに実装されます。以下は、クラス定義とクリエータ関数の例です。

class scriptedCommand(OpenMayaMPx.MPxCommand):
    # ...
def cmdCreator():
    return OpenMayaMPx.asMPxPtr( scriptedCommand() )

新しく作成したプロキシ オブジェクト上で OpenMayaMPx.asMPxPtr() をコールすることが非常に重要です。このコールにより、オブジェクトの所有権が Python から Maya に移行します。このコールを行わなければ、Python でこのオブジェクトの参照が解除され破棄されることがあるため、プログラム エラーが発生します。これにより、Maya にダングリング ポインタが残ります。

クラスの実装

プロキシ クラスの実装には、Maya Python API オブジェクトからの派生が必要です。

class scriptedCommand(OpenMayaMPx.MPxCommand):
    def __init__(self):
        OpenMayaMPx.MPxCommand.__init__(self)
    def doIt(self,argList):
        print "Hello World!"

scriptedCommand クラスは、OpenMayaMPx.MPxCommand から派生します。コンストラクタまたは __init__ メソッドは、親クラスの __init__ メソッドをコールする必要があります。すべてのクラス メソッドには、1 番目のパラメータとして self が必要です。この後、通常の引数リストが続きます。このコマンドの doIt() メソッドは「Hello World!」と出力します。

初期化関数

初期化関数は、MPxNode クラスを使用して新しいプロキシ ノードを定義するスクリプト プラグイン内で使用します。次は、その出力がサイン関数である単純なスクリプト プラグイン ノードを作成する方法を示すサンプルです。

import math, sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
kPluginNodeTypeName = "spSineNode"
sineNodeId = OpenMaya.MTypeId(0x8700)

# Node definition
class sineNode(OpenMayaMPx.MPxNode):
    # class variables
    input = OpenMaya.MObject()
    output = OpenMaya.MObject()
    
    def __init__(self):
        OpenMayaMPx.MPxNode.__init__(self)
        
    def compute(self,plug,dataBlock):
        if ( plug == sineNode.output ):
            dataHandle = dataBlock.inputValue( sineNode.input )
            
            inputFloat = dataHandle.asFloat()
            result = math.sin( inputFloat ) * 10.0
            outputHandle = dataBlock.outputValue( sineNode.output )
            outputHandle.setFloat( result )
            dataBlock.setClean( plug )
# creator
def nodeCreator():
    return OpenMayaMPx.asMPxPtr( sineNode() )
    
# initializer
def nodeInitializer():
    # input
    nAttr = OpenMaya.MFnNumericAttribute()
    sineNode.input = nAttr.create( "input", "in", OpenMaya.MFnNumericData.kFloat, 0.0 )
    nAttr.setStorable(1)
    # output
    nAttr = OpenMaya.MFnNumericAttribute()
    sineNode.output = nAttr.create( "output", "out", OpenMaya.MFnNumericData.kFloat, 0.0 )
    nAttr.setStorable(1)
    nAttr.setWritable(1)
    # add attributes
    sineNode.addAttribute( sineNode.input )
    sineNode.addAttribute( sineNode.output )
    sineNode.attributeAffects( sineNode.input, sineNode.output )
    
# initialize the script plug-in
def initializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.registerNode( kPluginNodeTypeName, sineNodeId, nodeCreator, nodeInitializer )
    except:
        sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
        raise
        
# uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.deregisterNode( sineNodeId )
    except:
        sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
        raise

nodeInitializer() 関数は、initializePlugin() 関数内で registerNode() に渡されます。プラグインのロード時、Maya では nodeInitializer() 関数がコールされてノードのアトリビュートが作成されます。

エラー条件

Maya Python APIでは、スクリプトの状態の照会と設定に Python の例外を使用します。ほとんとのケースでは、あるメソッドに戻り値があるとクラスのマニュアルに示されていても、例外が使用されます。例外が発生しうる状況にはいろいろあります。

  1. コールが失敗し、失敗の状態を保持する必要がある場合。
    def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)
        try:
            mplugin.deregisterNode( sineNodeId )
        except:
            sys.stderr.write( "Failed to deregister node: %s" % kPluginNodeTypeName )
            raise
    

    この例では、deregisterNode() コールが失敗した場合、uninitializePlugin() コールは Maya に例外を返してプラグインのアンロードが失敗します。

  2. コールが失敗し、失敗の状態をクリアする必要がある場合。

    このコードを修正してエラーをキャッチし、deregisterNode() コールが失敗した場合でもプラグインのアンロードを許可することができます。

    def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)
        try:
            mplugin.deregisterNode( sineNodeId )
        except:
            sys.stderr.write( "Failed to deregister node: %s" % kPluginNodeTypeName )
            pass
    

    唯一の違いは、raise キーワードが pass に変更されたことです。この技法は、正しくないオブジェクトを審査した場合に失敗するイテレータ コードを作成する場合に便利です。

    3. 不明なパラメータ戻り値。

    Maya Python API では、不明なパラメータ戻り値を使用してメソッドが特定のケースを処理できないことを示します。この操作を処理するかどうかは、コール元が決定します。このようなメソッドのひとつに MPxNode::compute() があります。この状況では、Python コードは OpenMaya.kUnknownParameter を返します。

例外と MStatus

前のセクションで説明したとおり、Maya Python API では、ステータス情報をやり取りするために、MStatus の値ではなく例外が使用されます。C++ ドキュメントから API メソッドの Python 的な動作を推測する場合、下記のルールが適用されます。

C++ API メソッドが MStatus 値を返す方法には、メソッドの返り値として返す方法と、

MStatus someMethod(Type arg1, Type arg2, ...) 

パラメータ リストの MStatus 変数(通常、最後のパラメータ)へのオプション ポインタとして返す方法の 2 通りがあります。

Type someMethod(Type arg1, Type arg2, ..., MStatus* ReturnStatus = NULL) 
メソッドが関数値として MStatus を返す場合

メソッドが関数値として MStatus を返す場合、この戻り値は Python で次のように処理されます。

  • ステータスが MS::kUnknownParameter である場合、文字列 unknown が Python に返されます。
  • ステータスが MS::kSuccess である場合、何も返されず、例外も発生しません。
  • それ以外のステータスの場合、何も返されませんが、RuntimeError 例外が発生します。

MS::kUnknownParameter を特別に処理するのは、MPxNode::compute() に対応するためです。

API 固有の特別な例外はありません。Maya は単に Python の標準 RuntimeError を使用し、エラー文字列を引数として渡します。

メソッドがポインタ変数を通じて MStatus を返す場合

API メソッドがパラメータ リストのポインタ変数を通じて MStatus を返す場合、この MStatus は次のように処理されます。

  • ステータスが MS::kSuccess である場合、例外は発生しません。
  • それ以外のステータスの場合、何も返されませんが、RuntimeError 例外が発生します。

つまり、C++ 言語でプラグインを記述する場合に、コードを呼び出しているのが C++ であるか Python であるかに関係なく、これまでどおり MStatus コードを返すことができます。Maya は必要に応じて、これらのコードを Python 例外に変換します。

Python でプラグインを記述する場合は、MStatus 値を返すのではなく例外を発生させる必要があります。ただし、compute() メソッドでプラグを処理しないことを指示する場合を除きます。この場合、このメソッドは maya.OpenMaya.kUnknownParameter を返します。

クラスでスライスをサポートする

すべての番号配列(MIntA rayMUintArrayMUint64ArrayMFloatArray、および MDoubleArray)は Python スタイルのスライシングをサポート。 たとえば、次のようになります。

import maya.OpenMaya as OpenMaya 
array = OpenMaya.MUintArray() 
for i in range(0,9):
   array.append( i )
array[2:8:2]

# Result:[2, 4, 6] #

MPx クラスのスタティック MObject にアクセスする

プロキシ クラスは、開発者に対し、使用されているノードについての標準的な情報を提供します。これには、ノードの定義に使用するアトリビュート オブジェクトが含まれます。Maya Python API でスタティック クラス MObject にアクセスするためには、類似のコードを使用できます。

envelope = OpenMayaMPx.cvar.MPxDeformerNode_envelope

このコール後、エンベロープは MPxDeformerNode::envelopeMObject になります。

メッセージ

Maya Python API では、メッセージ クラスをサポートしています。Python の関数が、コールバック用に渡されます。この関数は、コールバック メッセージが必要とするのとちょうど同じ数のパラメータを持つ必要があります。そうでない場合、メッセージ起動時に例外が発生し、コンソールに情報が書き出されます。ほとんどのメッセージと一緒に、Python オブジェクトの形式のクライアント データを渡すことができます。次はメッセージのサンプルです。

# Message callback
def dagParentAddedCallback( child, parent, clientData ):
    print "dagParentAddedCallback..."
    print "\tchild %s" % child.fullPathName()
    print "\tparent %s" % parent.fullPathName()
    print "\tclient data %s" % clientData

# Create the mesage
def createParentAddedCallback(stringData):
    try:
        id = OpenMaya.MDagMessage.addParentAddedCallback( dagParentAddedCallback, stringData )
    except:
        sys.stderr.write( "Failed to install dag parent added callback\n" )
        messageIdSet = False
    else:
        messageIdSet = True
    return id
# Call the message creator
messageId = createParentAddedCallback( "_noData_" )

割り当てを使用する代わりにパラメータ値を修正する

Python では、割り当てを使用する代わりにパラメータを修正するほうが適切です。 次のコードには割り当てが含まれており、その場合にどのようにエラーが発生しうるかを示します。

import maya.OpenMaya as OpenMaya 
def vectorTest(v): 
    lv = OpenMaya.MVector(1,5,9) 
    v = lv 
    print "%g %g %g" % (v.x,v.y,v.z) 
v = OpenMaya.MVector() 
vectorTest(v) 
print "%g %g %g" % (v.x,v.y,v.z)

2 番目の print コマンドは(0,0,0)を出力します。Python で、パラメータ値を修正するかコードを作成して、新しい値が返されるようにします。次のように vectorTest() 関数を書き換えます。

def vectorTest(v): 
    lv = OpenMaya.MVector(1,5,9) 
    v.x = lv.x
    v.y = lv.y
    v.z = lv.z
    print "%g %g %g" % (v.x,v.y,v.z) 

基本型へのリファレンス

Maya Python API には、int&、char&、float& など、基本型へのリファレンスとなる値またはパラメータを返すコールがたくさんあります。Maya Python API では、すべてのリファレンスはポインタとして扱われます。結果として、これらの項目の値を作成、設定、アクセスするには、特別なコールが必要になります。

OpenMaya.py モジュールにあるMScriptUtil というユーティリティ クラスは、これらのタイプのパラメータで動作して値を返す手段を提供します。このクラスでは、データを作成してからそのデータへのポインタを取得してリファレンスを必要とするクラス メソッドに情報を渡すことができます。API Reference マニュアルの MScriptUtil を参照してください。

MScriptUtil の使用法については、以下の例を参考にしてください。MFnLattice.getDivisions() へのコールから x、y、z の値を取得する方法を示します。

import maya.OpenMaya as OpenMaya
import maya.OpenMayaAnim as Anim
xutil = OpenMaya.MScriptUtil()
xutil.createFromInt(0)
xptr = xutil.asUintPtr()
yutil = OpenMaya.MScriptUtil()
yutil.createFromInt(0)
yptr = yutil.asUintPtr()
zutil = OpenMaya.MScriptUtil()
zutil.createFromInt(0)
zptr = zutil.asUintPtr()
it = OpenMaya.MItDependencyNodes(OpenMaya.MFn.kFFD)
while not it.isDone():
    latDefFn = Anim.MFnLatticeDeformer( it.thisNode() )
    latFn = Anim.MFnLattice( latDefFn.deformLattice() )
    latFn.getDivisions(xptr, yptr, zptr)
    x = xutil.getUint(xptr)
    y = yutil.getUint(yptr)
    z = zutil.getUint(zptr)
    doSomethingUseful(x, y, z)
    it.next()

getUint()MScriptUtil のスタティック メソッドであるため、クラスから直接コールしても最終値を取得できます。たとえば、次のようになります。

x = OpenMaya.MScriptUtil.getUint(xptr)

列挙型へのリファレンスは short int へのリファレンスとして扱う必要があります。

フラグ付きコマンド

フラグ付きコマンドは、MSyntax オブジェクトを使用して構文を指定し、MArgParser クラスまたは MArgDatabase クラスを使用してフラグを解析する必要があります。そうしないと、Python からコマンドを使用するときにフラグにアクセスできません。これは、Python と C++ のどちらで記述されたかに関係なく同じです。次は、フラグ付きコマンドを実装したプラグインの記述例です。

import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import sys, math
kPluginCmdName="spHelix"
kPitchFlag = "-p"
kPitchLongFlag = "-pitch"
kRadiusFlag = "-r"
kRadiusLongFlag = "-radius"

# command
class scriptedCommand(OpenMayaMPx.MPxCommand):
    def __init__(self):
        OpenMayaMPx.MPxCommand.__init__(self)
    
    def doIt(self, args):
        deg = 3
        ncvs = 20
        spans = ncvs - deg
        nknots = spans+2*deg-1
        radius = 4.0
        pitch = 0.5
        
        # Parse the arguments.
        argData = OpenMaya.MArgDatabase(self.syntax(), args)
        if argData.isFlagSet(kPitchFlag):
            pitch = argData.flagArgumentDouble(kPitchFlag, 0)
        if argData.isFlagSet(kRadiusFlag):
            radius = argData.flagArgumentDouble(kRadiusFlag, 0)
        controlVertices = OpenMaya.MPointArray()
        knotSequences = OpenMaya.MDoubleArray()
        
        # Set up cvs and knots for the helix
        for i in range(0, ncvs):
            controlVertices.append( OpenMaya.MPoint( radius * math.cos(i),
                pitch * i, radius * math.sin(i) ) )
        for i in range(0, nknots):
            knotSequences.append( i )
        
        # Now create the curve
        curveFn = OpenMaya.MFnNurbsCurve()
        
        nullObj = OpenMaya.MObject()
        try:
            # This plugin normally creates the curve by passing in the
            # cv's. A function to create curves by passing in the ep's
            # has been added. Set this to False to get that behaviour.
            if True:
                curveFn.create( controlVertices, knotSequences, deg, OpenMaya.MFnNurbsCurve.kOpen, 0, 0, nullObj )
            else:
                curveFn.createWithEditPoints(controlVertices, 3, OpenMaya.MFnNurbsCurve.kOpen, False, False, False)
        except:
            sys.stderr.write( "Error creating curve.\n" )
            raise
# Creator
def cmdCreator():
    # Create the command
    return OpenMayaMPx.asMPxPtr( scriptedCommand() )

# Syntax creator
def syntaxCreator():
    syntax = OpenMaya.MSyntax()
    syntax.addFlag(kPitchFlag, kPitchLongFlag, OpenMaya.MSyntax.kDouble)
    syntax.addFlag(kRadiusFlag, kRadiusLongFlag, OpenMaya.MSyntax.kDouble)
    return syntax

# Initialize the script plug-in
def initializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
    try:
        mplugin.registerCommand( kPluginCmdName, cmdCreator, syntaxCreator )
    except:
        sys.stderr.write( "Failed to register command: %s\n" % kPluginCmdName )
        raise
        
# Uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.deregisterCommand( kPluginCmdName )
    except:
        sys.stderr.write( "Failed to unregister command: %s\n" % kPluginCmdName )
        raise

このサンプルでは、クラスの doIt() メソッドに、シンタックス クリエータ関数とパース処理が含まれます。

保護されたメソッド

Maya Python API には、メソッドが所属するクラスからのみコールされるメソッドがいくつか含まれています。Maya では、この保護がメソッドに適用されることを示すため、Python の指定に従って _ を付加しています。MPxNode クラスに、このようなメソッドのサンプルがいくつかあります。

_forceCache()
_setMPSafe()

Maya Python API 内では、保護されたメソッドの使用の要件を守ってください。

MPx ベースのオブジェクトから派生したオブジェクトを取得する

API メソッドによっては、カスタム オブジェクトを派生クラスのインスタンスではなく、MPx ベース クラスのインスタンスとして返します。たとえば、MPxContext。_newToolCommand() はカスタム ツール コマンドを MPxToolCommand オブジェクトとして、MPxSurfaceShapeUI.surfaceShape() はカスタム サーフェス シェイプを MPxSurfaceShape オブジェクトとして返します。これらのベース インスタンスはオリジナルの派生インスタンスと同じ内部 Maya オブジェクトを参照しますが、派生インスタンスに設定されたメンバー変数の値をまったく保持しない個別の Python オブジェクトです。このため、返されたオブジェクトのメンバー変数をコールまたはアクセスすると、不正な結果が生じることがあります。

この問題は、OpenMayaMPx.asHashable() 関数のプロパティを利用して解決できます。OpenMayaMPx.asHashable()MPx オブジェクトを取り、Maya の基本オブジェクトを一意に識別するハッシュ値を返します。これは、オブジェクトが MPx ベース クラスのインスタンスであるか派生クラスのインスタンスであるかには関係ありません。両方が Maya の基本オブジェクトを参照していれば、同じハッシュ値を返します。これにより、ベース インスタンスを該当する派生インスタンスに翻訳できる辞書を設定することができます。

これにはまず、空の辞書をクラスに追加します。

class MoveToolCmd(OpenMayaMPx.MPxToolCommand):
    kTrackingDictionary = {}

このクラスの __init__ メソッドで、辞書にエントリを追加し、新しいオブジェクトのハッシュ値をオブジェクト自体にマッピングします。

def __init__(self): 
    OpenMayaMPx.MPxToolCommand.__init__(self)
    MoveToolCmd.kTrackingDictionary[OpenMayaMPx.asHashable(self)] = self 

クラスの __del__ メソッドで、ディクショナリから削除されたオブジェクトを除去します。

def __del__(self): 
    del MoveToolCmd.kTrackingDictionary[OpenMayaMPx.asHashable(self)] 

これでトラッキング ディクショナリができたので、これを利用して該当するベース オブジェクトから派生したオブジェクトを読み出すことができます。

class MoveContext(OpenMayaMPx.MPxSelectionContext): 
    ... 
    
    def doPress(self, event): 

        # Create an instance of the move tool command. 
        baseCmd = self._newToolCommand() 
        derivedCmd = MoveToolCmd.kTrackingDictionary.get( 
                        OpenMayaMPx.asHashable(baseCmd), None 
                     ) 

        # Call its methods.
        derivedCmd.setVector(0.0, 0.0, 0.0) 

カスタム ノード タイプ(MPxNodeMPxSurfaceShape など)の定義には、追加作業が必要です。Maya でファイルを開く、または新規ファイルの操作を行うと、各ノードで __del__ method を実行しなければシーンが破損します。つまり、これらのノードのエントリはトラッキング ディクショナリからは削除されないということです。これを回避するには、initializePlugin メソッドでコールバックをセットアップし、ファイルのオープンと新規ファイルの作成のトラッキング ディクショナリをクリアする必要があります。

fileOpenCB = 0
fileNewCB = 0

def clearTrackingDictionaries(unused):
    MyNode.kTrackingDictionary = {} 
    MySurfaceShape.kTrackingDictionary = {}

def initializePlugin(plugin): 
    ... 
    fileOpenCB = OpenMaya.MSceneMessage.addCallback(
                     OpenMaya.MSceneMessage.kBeforeOpen, 
                     clearTrackingDictionaries 
                 )
    fileNewCB  = OpenMaya.MSceneMessage.addCallback(
                     OpenMaya.MSceneMessage.kBeforeNew, 
                     clearTrackingDictionaries 
                 )
    ...
}

def uninitializePlugin(plugin): 
    ... 
    if fileOpenCB != 0:
        OpenMaya.MSceneMessage.removeCallback(fileOpenCB) 
        fileOpenCB = 0 

    if fileNewCB != 0: 
        OpenMaya.MSceneMessage.removeCallback(fileNewCB)
        fileNewCB = 0

オペレーティング システム タイプ

Maya Python API の一部のメソッドでは、<iosteam> オペレーティング システム タイプが必要です。これは Python に含まれていないので、このようなタイプのオブジェクトの作成と使用を行うための MStreamUtils クラスが使用可能です。このクラスの使用法を示すサンプルについては、Developer Kit を参照してください。

親クラスへのコール

MPx プロキシ クラスを作成する場合、スクリプトで親クラスへのコールが必要になることがあります。これは次のような表記法で行います。

matrix = OpenMayaMPx.MPxTransformationMatrix.asMatrix(self)

Enum 値

Enum 値は、次のように moduleName.className.value 表記法でアクセスすることができます。

OpenMaya.MSyntax.kDouble

OpenGL を使用する

サポートするすべてのプラットフォームで、OpenGL 機能をスクリプトで使用するためのラッパ(wrapper)クラス MGLFunctionTable を提供しています。このクラスへのリファレンスを取得するには、次のコードを使用します。

glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()

ネストされたイテレータとガベージ コレクション

ネストされたイテレータを記述する場合、通常、外側のループで現在選択されている項目は、内側のループに渡されます。たとえば、MItSelectionList で現在選択されている項目は MItSurfaceCV イテレータに渡されます。内側のイテレータに、無効になった情報が保持されている場合には、ガベージ コレクションの問題が発生する可能性があります。この問題を回避するには、内側のループが完了したら、内側のイテレータをなし(None)にリセットします。この結果、外側のループを継続する前に、内側のイテレータの情報がリリースされます。現在選択されている項目をリビルドまたは更新している場合にはこれが重要です。

スタンドアローン型スクリプト

ラッパ クラスと関数セットを使用して Maya モデルを修正するためのスタンドアローン型スクリプトを作成できます。このスクリプトはコマンドラインから実行します。単純なスタンドアローン型「hello world」スクリプトは次のとおりです。

import maya.standalone
import maya.OpenMaya as OpenMaya
import sys
def main( argv = None ):
    try:
        maya.standalone.initialize( name='python' )
    except:
        sys.stderr.write( "Failed in initialize standalone application" )
        raise
    sys.stderr.write( "Hello world! (script output)\n" )
    OpenMaya.MGlobal().executeCommand( "print \"Hello world! (command script output)\\n\"" )
    maya.standalone.uninitialize()
    
if __name__ == "__main__":
    main()

スタンドアローン型スクリプトが初期化された後、Maya コマンドと API クラスを使用して Maya モデルの作成や修正をすることができます。終了後、スタンドアローンは非初期化され、その後に Maya がクリーンアップできるようになります。initialize()を呼び出す前、または uninitialize()を呼び出した後に Maya コマンドまたは API クラスの使用を試みないでください。その場合、動作が不安定になる可能性があります。

スタンドアローン スクリプトの実行では、Maya と共に提供される Python 実行可能ファイルを使用する必要があります。たとえば、次のようになります。

$MAYA_LOCATION/bin/mayapy helloWorld.py