チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第113回:同じUIを別のウィンドウで使い回す標準的な方法

  • Maya
  • UI・ビューポート
  • ゲーム
  • コラム
  • スクリプト・API
  • チュートリアル
  • 初心者・学生
第113回:同じUIを別のウィンドウで使い回す標準的な方法

Mayaでは色々なツールで同じUIを再利用しています。詳しい方法についてはヘルプドキュメントのスクリプトのガイドにも載っていないので、今回取り上げてみます。

例えばOrient Joint。選択したジョイントの軸を再設定するためのあのオプションを開いてみると、Primary Axisなど軸を指定するオプションが出てきます。
でもこれ、他の場所でもなんとなく見かけたことがありませんか?

実は軸を指定するオプションは3カ所で出てきます。
・Create Jointsのオプション(ツール設定)
・Orient Jointのオプション
・Move Toolのツール設定。

同じオプションのUIに見えます。

同じオプションのUIに見えます。

チェックボックスとかを変えると、そのUIが属しているオプション設定が変わるものの、他のウィンドウに出ているオプションには影響しません。

こういう使いまわし、コードを複製せずに、カッコよくキメてみたいですよね!

Mayaではこういう使い回しの部分で、同じスクリプトを使ってUIを作成しています。
具体的な作成方法を見ていきましょう。

普通に実装すると、思ったように動かない

このままちょっとだけ、Orientation Settingsを参考に見ていきましょう。
Orient Joint to Worldのチェックボックスを変えると、その下にあるラジオボタンが有効になったり無効になったりします。

Orientation Settings

これを実装する方法を考えてみます。

「チェックボックスのchangeCommandで、ラジオボタンのEnabledを変える」ようにすればいけるはず!
というわけで、コードにすればこんな感じです。

global proc setRadioButtonEnable()
{
    $value = `checkBoxGrp -q -v1 myCheckBoxGrp`;
    radioButtonGrp -e -en (!$value) myRadioButtonGrp;
}

$win = `window`;
columnLayout layout1;
checkBoxGrp -label "Check" -ncb 1 -label1 "Enabled?" -changeCommand "setRadioButtonEnable" myCheckBoxGrp;
radioButtonGrp -label "Radio" -nrb 2 -label1 "A" -label2 "B" myRadioButtonGrp;
showWindow;
print($win+"\n");

デバッグ用に変数や出力をしているのはお気にせず…。このあとの説明のための伏線です。

実行するとこんなウィンドウが出てきます。チェックボックスをクリックすると、Radioと書かれたところの有効/無効が連動します。

実装してみたウィンドウ

処理の流れとしては、checkBoxGrpをクリックすると、changeCommandで指定した関数が実行されます。その関数内で、チェックボックスの状態を見て、ラジオボタンを更新しています。
いずれもUIに名前をつけて管理しています。

ある程度の経験者であれば、ここでUIの名前を動的に決めて、changeCommandに引数として渡せば、いくつ同じUIを作っても動作するぜ!と考えるでしょう。実際それでも動きます。
ですが、UI名を取ってコールバックコマンド用の関数の引数にするために、UI名を受け取る記述、UIを作成したあとに改めてコールバックコマンドを設定するなど、思いの外コードが読みにくくなります。
今回紹介する方法はMayaの実装でもよく使う方法で、コードがスッキリして読みやすくなります。

では同じスクリプトを追加で2回実行して、ウィンドウを3つ作ってみてください。
チェックボックスを変えると、あれ?最後に作ったウィンドウのチェックしか正常に動かなくなります。

同じコードを3回実行してみると、最後のウィンドウのチェックしか動かない

これが使いまわしで起きる問題です。

どうしようかと慌てて、とりあえず簡単な解決方法として、UIに個別の名前をつけて、関数も何もかもコピペして使い回す方法へ走って行ってはもったいない。

もうちょっと具体的な原因を検討してみましょう。

なぜUIの使いまわしが期待通り動作しないのか?

チェックボックスやラジオボタンは、UI名前を直接指定して作成しています。同名のUIが複数の場所に登場しますが、これ自体はMayaで問題とはなりません。

そして冷静に挙動を見てみれば、いつも最後に作ったウィンドウのチェックボックスだけ機能しているようです。

何が起きているか整理すると、こんな感じになります。
・チェックボックスのチェックを変えると、setRatioButtonEnalbe関数を呼び出す。(ここは大丈夫なはず。)
・myCheckboxGrpとかのUIを照会・設定する。
  → このときMayaのコマンドは、現在設定されている親UI内にmyCheckboxGrpがあるか確認します。なければそこから検索範囲を広げて、UIを見つけようとします。

ウィンドウを作るときに、printがウィンドウの名前を出力していたはずです。3つ作ったウィンドウでは、myCheckBoxGrpのフルパスはそれぞれこんな感じになっています。
window1|layout1|myCheckBoxGrp
window2|layout1|myCheckBoxGrp
window3|layout1|myCheckBoxGrp

UIのフルパスの例

ウィンドウの名前は個々で違う名前となりますが、その中に含まれるUI要素は同じ名前で通用します。
これは、シーンにオブジェクトを作るときと同じですね!UIにも親子関係が存在します。

で、現在親となっているUI以下からの相対的な位置で名前を見つけようとします。

親を変えるにはsetParentコマンドを使います。
Mayaに含まれているスクリプトを見たことがある方の中には、「なんでかやたらとsetParentや親を引数で渡すことが多いな」と薄々気づいているかもしれません。これが理由の一つだったりします。

これで必要なことはすべてわかりました!
早速改良したコードを書いてみましょう。

使い回せるUIを作ってみる

同じUIを作成するコードを実行するときに、setParentで親を変えてから実行すれば、触っているウィンドウ内だけで処理を完結できます。やってみましょう。

global proc setRadioButtonEnable(string $parent)
{
    setParent $parent;
    $value = `checkBoxGrp -q -v1 myCheckBoxGrp`;
    radioButtonGrp -e -en (!$value) myRadioButtonGrp;
}

global proc createMyOptions( string $parent )
{
    setParent $parent;
    checkBoxGrp -label "Check" -ncb 1 -label1 "Enabled?" -changeCommand ("setRadioButtonEnable " + $parent) myCheckBoxGrp;
    radioButtonGrp -label "Radio" -nrb 2 -label1 "A" -label2 "B" myRadioButtonGrp;
}

window;
$parent = `columnLayout`;
createMyOptions( $parent );
showWindow;

何度か実行して、複数のウィンドウを作成してください。
チェックボックスを変えると、そのチェックボックスのあるウィンドウでだけラジオボタンの有効/無効が切り替わります!

複数のウィンドウ

ポイントとなるのは、常にどのUIが親となって、今から扱うUIはどこに属しているか、明確にすることです。今回の場合は常にウィンドウ名を引数として渡して、なにかする前にはsetParentで設定しています。

その後実行するcheckBoxGrpなどは、setParentしたUI以下にある名前を指定すればよい、というわけです。

コードも見やすく、使い回しも確実に行える。これがMayaでのUI使いまわしの方法です。

重要なのでもう一度まとめますと、
・UIの各要素を作るときに、まず今の親となるUIをしっかり設定。
・次に、コントロールを照会・編集するときもsetParentで今の親をしっかり設定。

これだけで、どこでも使い回せるUIとなります!

興味のある方は、ぜひJoint Orientのオプションを実際に見てみてください。
C:\Program Files\Autodesk\Maya20##\scripts\others\jointOrientUtils.mel
このスクリプト内の、次の関数がオプションを作る部分です。

global proc jointOrientOptionsSetup( string $parent )

この jointOrientOptionsSetupを呼び出している部分が、次のスクリプト内にあります。
「performJointOrient.mel」(Orient Jointオプション)
「jointToolProperties.mel」(Create Jointsオプション)
「manipMoveProperties.mel」(Move Toolオプション)

なんなら次のようにすれば、自作のUI内に組み込むことも出来ます。

// jointOrientOptionsSetupを使うために、一度実行します。jointOrientUtils.mel内のグローバル関数が使えるようになります。
jointOrientUtils();

window -t "My Custom Tool";
$parent = `columnLayout`;
jointOrientOptionsSetup($parent);
showWindow;

こんなウィンドウが出来ます。

ウィンドウに組み込んでみると、こんな感じになります。

ん、作ったはいいけど、この値を取るには?
そこもsetParentです。次のようにすれば、狙ったウィンドウのPrimary Axisを取れます。
(Primary Axisのラジオボタンの名前は、jointOrientOptionsSetup関数内で確認しておきます。)

setParent $parent;
$selected = `radioButtonGrp -q -sl ojPrimaryAxisGrp`;
print($selected + "\n");

こんなふうに、自由自在にUIを使いこなしてください!

まとめ

今回は曖昧になりがちなUIの使いまわしについて紹介しました。
Orient Jointの機能追加でコードを見ていて、改めて紹介すべきではと思ったので取り上げてみました。

PySideだとまた違ってくるとは思いますが、MayaのコマンドでUIを作っているなら、MELでもPythonでも同じ方法が通用しますので、ぜひ今回ご紹介したアイディアを制作現場で活かしてみてください!

製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。