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 プログラミングで使用可能なクラスが含まれています。これらのクラスは異なるカテゴリに分類され、その関連を示すための適切な命名規則を持っています。クラスには次のような種類があります。
このプリフィックスを含むクラスは、特定タイプの MObject
に対して演算を行う関数セットです。
このクラスはイテレータで、MObject
に対する動作は関数セットの場合とほぼ同じになります。たとえば MItCurveCV
は、個々の NURBS カーブ CV に演算を行う場合や(MFnNurbsCurveCV
はありません)、カーブのすべての CV に演算を繰り返し行う場合に使用します。
このプリフィックスを含むクラスはすべて「プロキシ」です。これは、ユーザ独自のオブジェクト型を作成したり、そこから派生させたりするために設計された API クラスです。
これらのクラスのほとんどは「ラッパ(wrapper)」ですが、それ以外もあります。このクラスの例には、MVector
、MIntArray
などがあります。
ラッパ クラスと関数セット クラスを使用して、次のようなスクリプトを作成できます。
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()
メソッドを使用して作成できます。MString
ラッパ クラスの代わりに渡され、返されます。注:わかりやすくするため、上の例ではエラー チェックを省略しています。
スクリプト プラグインは、Maya に強固に統合されたソリューションの作成を可能にします。スクリプト プラグインにより開発者はコマンドのアンドゥ、Maya シーン ファイルへの適切な requires 行の構築などの機能をサポートすることができます。スクリプト プラグインを使用するもう 1 つの利点は、MEL と Python の両方で機能を使用きることです。
Maya プラグイン マネージャ(Plug-in Manager)が拡張され、スクリプト プラグインのロードとアンロードがサポートされるようになりました。
MAYA_PLUG_IN_PATH
上にあり、.py
拡張子で終わるファイルが、プラグイン マネージャ(Plug-in Manager)に表示されます。ロード(Loaded)チェック ボックスまたは自動ロード(Auto Load)チェック ボックスのどちらかを選択して、スクリプト プラグインをロードまたは自動ロードします。
注:
.py
にプラグイン スクリプトではないMAYA_PLUG_IN_PATH
スクリプトがあっても構いませんが、これらの項目はロードされません。エントリ ポイントが見つからないという内容の警告メッセージが発行されます。
プラグインは、プラグイン マネージャから、または MEL あるいは Python のコマンド タブからロードできます。 MEL では、loadPlugin()
コマンドを使用します。Python では、maya.cmds.loadPlugin()
コマンドを使用します。
helixCmd.py
などのサンプルを実行するには、プラグインをロードし、Python エディタ(Python Editor)のタブに次を入力します。
import maya
maya.cmds.spHelix().
この Python スクリプトを起動すると、次が実行されます。
cmds
モジュールが使用可能になります。spHelix()
が起動されます。注:
sp
プリフィックスは、「スクリプト プラグイン」に使用します。- スクリプト プラグインは、loadPlugin コマンドを使用してロードする必要があります。スクリプト プラグインのソースを実行してもロードされません。
このプラグインは、Python コマンド maya.cmds.unloadPlugin("helixCmd.py")
を使用してアンロードすることもできます。
ロードと実行の手順は、MEL エディタ内でも次を使用して起動できます。
loadPlugin helixCmd.py;
spHelix();
スクリプト プラグインの作成には、プラグイン内で専用の関数を定義することが必要です。スクリプト プラグインは次を行う必要があります。
initializePlugin
および uninitializePlugin
エントリ ポイントを定義します。以下のセクションで、これらの項目についてサンプルとともに詳しく説明します。
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 の例外を使用します。ほとんとのケースでは、あるメソッドに戻り値があるとクラスのマニュアルに示されていても、例外が使用されます。例外が発生しうる状況にはいろいろあります。
コールが失敗し、失敗の状態を保持する必要がある場合。
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 に例外を返してプラグインのアンロードが失敗します。
コールが失敗し、失敗の状態をクリアする必要がある場合。
このコードを修正してエラーをキャッチし、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 では、不明なパラメータ戻り値を使用してメソッドが特定のケースを処理できないことを示します。この操作を処理するかどうかは、コール元が決定します。このようなメソッドの 1 つに MPxNode::compute()
があります。この状況では、Python コードは OpenMaya.kUnknownParameter.
を返します。
前のセクションで説明したとおり、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
を返す場合、この戻り値は Python で次のように処理されます。
MS::kUnknownParameter
である場合は、文字列 unknown
が Python に返されます。MS::kSuccess
である場合は、何も返されず、例外も発生しません。RuntimeError
例外が発生します。MS::kUnknownParameter
を特別に処理するのは、MPxNode::compute()
に対応するためです。
API 固有の特別な例外はありません。Maya は単に Python の標準 RuntimeError
を使用し、エラー文字列を引数として渡します。
API メソッドがパラメータ リストのポインタ変数を通じて MStatus
を返す場合、この MStatus
は次のように処理されます。
MS::kSuccess
である場合、例外は発生しません。RuntimeError
例外が発生します。つまり、C++ 言語でプラグインを記述する場合に、コードを呼び出しているのが C++ であるか Python であるかに関係なく、これまでどおり MStatus
コードを返すことができます。Maya は必要に応じて、これらのコードを Python 例外に変換します。
Python でプラグインを記述する場合は、MStatus
値を返すのではなく、例外を発生させる必要があります。ただし、compute()
メソッドでプラグを処理しないことを指示する場合を除きます。この場合、このメソッドは maya.OpenMaya.kUnknownParameter
を返します。
すべての数値配列(MIntArray
、MUintArray
、MUint64Array
、MFloatArray
、および 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] #
プロキシ クラスは、開発者に対し、使用されているノードについての標準的な情報を提供します。これには、ノードの定義に使用するアトリビュート オブジェクトが含まれます。Maya Python API でスタティック クラス MObject
にアクセスするために、類似のコードを使用できます。
envelope = OpenMayaMPx.cvar.MPxDeformerNode_envelope
この呼び出し後、エンベロープは MPxDeformerNode::envelope
の MObject
になります。
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 リファレンス マニュアルの 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 内では、保護されたメソッドの使用の要件を守ってください。
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)
カスタム ノード タイプ(MPxNode
、MPxSurfaceShape
など)の定義には、追加作業が必要です。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 値は、次のように moduleName.className.value 表記法でアクセスすることができます。
OpenMaya.MSyntax.kDouble
サポートするすべてのプラットフォームで、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