【Maya/Python】OpenMaya API2.0 の MCommandMessage のコールバックタイミング解説

この記事は Maya Advent Calendar 2025 シリーズ2 の25日目の記事です。

 

自分の中での毎年のルーティンになりつつある、API2.0 の Message 系クラスの解説がなんとか間に合いました。

今回は OpenMaya API 2.0 の maya.api.OpenMaya.MCommandMessage あるコールバックのタイミングについてまとめていこうと思います。

 

この記事は Maya 2024.2.4 で検証したものです。

 

 ちなみに過去にコールバック系で

【Maya/Python】OpenMaya API2.0 の MAnimMessage のコールバックタイミング解説

【Maya/Python】OpenMaya API2.0 の MSceneMessage クラス内のコールバックタイミング解説

【Maya/Python】OpenMaya API2.0 の Message 系クラス内のコールバックタイミング解説 1

という記事も書いておりますのでご興味あればご覧下さい。


OpenMaya.MCommandMessage

OpenMaya.MCommandMessage.addCommandCallback()

MEL コマンドが実行されるたびに呼ばれます。このコールバックはスクリプトの実行時ではなく、実際の MEL コマンドが実行されたときに呼び出されます。
また、公式のヘルプにも書かれていますが、ちょっと操作するだけでもかなりの数の呼ばれて Maya のパフォーマンスが低下するので、使うタイミングは限定的にした方が良いです。
渡される内容は実行された MEL コマンドそのままが取得できます。
例えば、メニューからプリミティブの Cylinder を作成すると「polyCylinder "-r" 1 "-h" 2 "-sx" 20 "-sy" 1 "-sz" 1 "-ax" 0 1 0 "-rcp" 0 "-cuv" 3 "-ch" 1;」という文字列などが取得できます。

 

# Sample Code ===================================

import maya.api.OpenMaya as om2

 

def addCommandCallbackMethod(*args):

    print(args[0]) # 実行された MEL コマンドが文字列で渡されます

 

om2.MCommandMessage.addCommandCallback(addCommandCallbackMethod)

 

OpenMaya.MCommandMessage.addCommandOutputCallback()

上の addCommandCallback と少し似てはいますが、こちらはスクリプトエディタに文字が出力されるたびに呼ばれます。

なので、Python 側で print 文を実行しても呼ばれるので、どういう情報が取れるのかテストしようと print したら、無限ループになってしまったので、サンプルコードではテキストファイルに内容を出力するようにしています。

コールバックの args[0] にスクリプトエディタに出力されているメッセージ、args[1] にメッセージのタイプ ( int ) が渡されます。

メッセージタイプの番号が何かといいますと、公式ヘルプに載っていますがこんな感じになっています。

int     kDisplay = 1
int     kError = 4
int     kHistory = 0
int     kInfo = 2
int     kResult = 5
int     kStackTrace = 6
int     kWarning = 3

 

このコールバックがどんなときに使えるかといいますと、なるほど!と思った例がこちらの BigRoy さんのノードのサイクルチェックツールで、17 行目でメッセージタイプが kWarning かどうかをチェック、21 行目でメッセージが "Cycle on" という文字列で始まっているかどうかをチェックして処理を実行されています。

このように何か特定のエラーや警告が出たタイミングをキャッチして任意の処理を走らせたいときに使えます。

 

# Sample Code ===================================

import maya.api.OpenMayaAnim as oma2

from datetime import datetime

 

def addCommandOutputCallbackMethod(*args):

    log_file_path = "D:/Maya_addCommandOutputCallbackMethod_Log.txt"
    dt = datetime.now()
    dataStr = dt.strftime("%Y-%m-%d_%H%M%S")
    try:
        with open(log_file_path, mode = "a") as f:
            f.write(f"addCommandOutputCallbackMethod {dataStr} ====================================================\n")
            print(f"    message = {args[0]}")
            print(f"    messageType(int) = {args[1]}")

    except:
        pass

 

om2.MCommandMessage.addCommandOutputCallback(addCommandOutputCallbackMethod)

 

OpenMaya.MCommandMessage.addCommandOutputFilterCallback()

上の addCommandOutputCallback と基本は同じなのですが、こちらは関数名に Filter とついているように、出力されるログをスクリプトエディタに出力するかどうかをフィルタリングしたいときに使用します。

必ず bool 値を返す必要があり、True を返すと出力されなくなり、False を返すと通常通り出力されます。

例えば、

 

if args[0].startswith("poly"):
        return True

 

みたいに書いておくと、ポリゴンのプリミティブを生成したときの「polySphere -r 1 -sx 20 -sy 20 -ax 0 1 0 -cuv 2 -ch 1;」のようなログがスクリプトエディタには出力されなくなります。

 

# Sample Code ===================================

import maya.api.OpenMayaAnim as oma2

from datetime import datetime

 

def addCommandOutputFilterCallbackMethod(*args):

    log_file_path = "D:/Maya_addCommandOutputFilterCallbackMethod_Log.txt"
    dt = datetime.now()
    dataStr = dt.strftime("%Y-%m-%d_%H%M%S")
    try:
        with open(log_file_path, mode = "a") as f:
            f.write(f"addCommandOutputFilterCallbackMethod {dataStr} ====================================================\n")
            for arg in args:
                f.write(f"    arg = {arg}, type = {type(arg)}\n")

    except:
        pass

 

     return False

 

om2.MCommandMessage.addCommandOutputFilterCallback(addCommandOutputFilterCallbackMethod)

 

OpenMaya.MCommandMessage.addProcCallback()

こちらは1つ目の addCommandCallback と似ていて、MEL プロシージャが実行されるたびに呼ばれます。

 

例えば、下のような MEL とそれを呼び出す Python があったとして、その MEL の呼び出し前に addProcCallback を設定しておき、MEL を実行後にすぐにそのコールバックを削除してみると、どうなるか。

 

# Sample Code (MEL) ====================================

proc LocalProcTest1()
{
    print("< Local Proc Test 1 >");
}
proc LocalProcTest2()
{
    print("< Local Proc Test 2 >");
}

global proc GlobalProcTest()
{
    print("< Global Proc Test Start >");
    LocalProcTest1();
    LocalProcTest2();
    print("< Global Proc Test End >");
}

 

# Sample Code (Python) ===================================

import maya.api.OpenMaya as om2

import maya.mel as mel

 

def addProcCallbackMethod(*args):

    print("\n" + "addProcCallbackMethod ====================================================")

    print(args[0]) # プロシージャ名

    print(args[1]) # プロシージャの呼び出しの ID

    print(args[2]) # 実行したプロシージャの呼び出し直前は True, 呼び出し後は False

    print(args[3]) # MEL プロシージャなら 0, MEL コマンドなら 1 

 

addProcCallbackID = om2.MCommandMessage.addProcCallback(addProcCallbackMethod)
mel.eval("GlobalProcTest;")
om2.MEventMessage.removeCallback(addProcCallbackID)

 

# ===============================================

 

これを実行したあとにスクリプトエディタに出力されるのはこんな感じです。

 

 

addProcCallbackMethod ====================================================
GlobalProcTest
0
True
0
< Global Proc Test Start >
addProcCallbackMethod ====================================================
LocalProcTest1
1
True
0
< Local Proc Test 1 >
addProcCallbackMethod ====================================================
LocalProcTest1
1
False
0

addProcCallbackMethod ====================================================
LocalProcTest2
2
True
0
< Local Proc Test 2 >
addProcCallbackMethod ====================================================
LocalProcTest2
2
False
0
< Global Proc Test End >
addProcCallbackMethod ====================================================
GlobalProcTest
0
False
0

 

 

こちらも1つ目同様、公式ヘルプに書かれているようにコールバックを設定したままにしておくと、Maya 操作時の MEL 実行によって大量に呼び出されて Maya のパフォーマンスが落ちるので、必要な場面以外ではなるべく早くコールバックの設定解除した方が良いようです。

ただ、呼び出す MEL の前後のタイミングをキャッチして、任意の処理を挟むことができるので、処理のチェックなどに使えるかもしれませんね。