※記事に関しましてご注意下さい
普段使用する手法を「よりシンプルで柔軟な方法がないか」調査した内容になります。
あくまでも個人調査のメモです。間違い・勘違いが含まれている可能性もありますので予めご了承ください。
※ 結論だけ見たい方は 最後尾 へどうぞ。
調査のきっかけ
maya 内の python で mel 変数の値を取得する場合、一時的な代入を使い
from maya import mel
progress_bar = mel.eval('$tmp = $gMainProgressBar')
のように取得する方法がよく使われると思います。
ここで個人的にずっとモヤモヤしている箇所がありました。
1. $tmp という変数はどこに作成されるのか (global 変数か local 変数か)
2. 1 が global 変数の場合、既存変数との型違いでエラーは起きないのか
という疑問です。
この確認と、よりトラブルを起こさない記述について調べることにしました。
調べた結果
1. $tmp は global 変数として作成される(そのまま後に残される)
2. 別の型の $tmp 変数が既にあった場合にはエラーが起こる
そのままでした。
つまり、頭に書いた記述には使用条件があり、
・変数 $tmp が初めて作成される場合 (mel が型を振ってくれる)
・変数 $tmp が同じ型で宣言されている場合
のどちらかである必要があります。
この問題を起こさない記述は 最後尾 にメモしています。
トラブルが起こる場面
この穴をついてエラーが起きる・エラーを起こす方法は簡単です。
思いつく場面として以下があると思います。
型違いの同名変数を mel で "先に" 準備した場合
int $tmp = 1;
を mel で実行した後、python 文を実行すると
from maya import mel
progress_bar = mel.eval('$tmp = $gMainProgressBar')
progress_bar
# Result: 0L #
この場面では文字を変数に変換しようとして 0 という数字になります
上は int $tmp;
でしたが、int $tmp[];
のように配列だった場合など、
型変換すら行われずにエラーになる可能性大です。
型違いの同名変数を mel で "後から" 使用した場合
※こちらは python 側のエラーにはなりませんがのちの mel でエラーが起きます
from maya import mel
progress_bar = mel.eval('$tmp = $gMainProgressBar')
progress_bar
# Result: MayaWindow|toolBar3|MainHelpLineLayout|helpLineFrame|formLayout16|mainProgressBar #
を python で実行した後、mel 文を実行するとエラーがでます。
int $tmp = 1;
// Error: int $tmp; //
// Error: Line 1.10: Invalid redeclaration of variable "$tmp" as a different type. //
基本的に mel での global 変数使用は最小限にするのが暗黙のルールであるためおそらく頻発はしないとは思います。が、そうはいいつつも起きそうな場面としては scriptEditor でのテスト時、数行のスクリプトを Shelf などに登録している場合などがあると思います。
実際にトラブルが発生した例
[2022/12 追記]
身近でトラブルが起こる場面に遭遇したため例を追記しました。
公式ツール、有名ツールだとしても意識していないものが多いようです。
MayaScanner + mgear を共に使用した場合
[2022/12 追記]
MayaScanner は Maya 起動時に発動し、File Open などでスクリプトが動作しますが、そこで以下のコードが実行されます。
232: if (mel.eval('whatIs "$autoUpdateAttrEd_aoto_int"') != "Unknown"):
233: a = int(mel.eval('$temp=$autoUpdateAttrEd_aoto_int'))
ここで $temp
が global 変数として自動定義されることになりますが、この場合 global int $temp
相当だと意識しておく必要があります。
一方 Maya 起動後に File Open し、mgear を使用してSynoptic > Space Transfer などのツールを使用すると (一時的に再描画を止めるため) 以下のコードが実行されます。
185: gMainPane = mel.eval('global string $gMainPane; $temp = $gMainPane;')
ここで mgear としては $temp
の想定は global string $temp
ですが、事前に MayaScanner によって global int $temp
で先に作られてしまっているため、エラーが発生します。
エラーメッセージ的にはこんな感じです。
# RuntimeError: paneLayout: Object '0' not found.
まさに上で書いたことがそのまま発生し、エラーが起こっています。
どう修正すべきかについては、
MayaScanner はこう ↓ するべきですし、
if (mel.eval('whatIs "$autoUpdateAttrEd_aoto_int"') != "Unknown"):
a = int(mel.eval('$autoUpdateAttrEd_aoto_int = $autoUpdateAttrEd_aoto_int'))
mgear はこう ↓ 書くべきだと思います。
gMainPane = mel.eval('$gMainPane = $gMainPane')
$temp
は自分だけのものではない、と改めて感じる例です。
トラブルを起こさない記述
いろいろ試しつつ情報収集していたところ、
studiolibrary 内になるほどの記述がありました。
https://github.com/krathjen/studiolibrary/blob/master/install.py#L68
それは 自分自身に代入する方法
です。
from maya import mel
progress_bar = mel.eval('$gMainProgressBar = $gMainProgressBar')
先に変数の型を知る必要がなく、代入が自分自身なので型は当然同じ、そして代入で値を変化させず、かつ無駄な global 変数を作成しない
、非常にスマートな方法です。
一方的に勉強になりました。。。感謝。
この手法のもう一つの利点
頻繁に使用・問合せする以下はその多くが string ですが、
$gMainWindow
$gMainProgressBar
$gChannelBoxName
$gMainWindowMenu
$gMainPane
例えば以下は int の配列です。
$gCustomSelPriority
ですが、この場合もその型を事前に知る必要がなく
from maya import mel
gCustomSelPriority = mel.eval('$gCustomSelPriority = $gCustomSelPriority')
gCustomSelPriority
# Result: [10, 10, 2, 9, 2, 2, 2, 2, 2, 2, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 10, 10, 9, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 11, 11] #
のように取得することができます。