2008/12/10

Sukio Sukio Sukio Python - VI

リアクション: 
さてさて、6回目です。
大分経ちましたが、いかがお過ごしでしょうか?
最近、サボっている気がしますね・・・。
忙しいと思ってくださいね。
更新がんばります!
とはいいつつ、今年最後の更新な気がしますね・・・。

そういえば、読んでますメールを初めて戴きました。
ウレシイデスー。
見てくれる人がいらして感謝ですね。
(某Pさんありがとうございます)
これからも精進いたしますね。

では本題に入りましょう。
やっとか、という感じですが、とうとうPythonの使い方に入ります。
いままでは、XSIの使い方しかご紹介してませんでしたので序の口です。
でも、ワタクシもPythonは、初歩的な使い方にとどまっていますが
Stringでの簡略の指定法、Listの使い方、強力なモジュール群、などなど紹介したいと思います。

Pythonでは、Stringの指定も楽々に行えます。
全てを解説すると、1回で終わらなくなってしまうので、良く使うやつをざっくり抜き出して使ってみますね。

例えば、人骨には左右が存在します。
右手の骨、'IK_Rhand'というオブジェクトを選択して
対応する左手の骨'IK_Lhand'を取得したいときどうすれば良いでしょうか?
そして、ほかの名前にも対応する書き方はどうすれば良いでしょう?

回答は、LとRが対応しているものを探せば良いのです。

Rを選択していて、実行したらそれに対応するLを選択するというものを書いてみます。

こんな感じでしょうか。

app = Application
sSel = app.Selection.GetAsText()
app.Selection.SetAsText(sSel.replace('R','L'))

これだけで、選択が全てL側に移ると思います。
少々脱線してしまいますが、良く使うSelectionをちょっとだけ解説しますね。

Selectionオブジェクトは、アプリケーションのグローバルセレクションです。』

とマニュアルには、書いてありますが、なんのこっちゃ良く分かりませんが、Selectionは、XSI特有のオブジェクトです。
mayaやMotionBuilderなどの主要ソフトは、オブジェクトごとにSelectフラグというものが用意されているのですが、XSIは、Selectionという変数に属しているものが選択されるというちょっと一風変わった仕様なのです。
ようするに、Selectionにオブジェクトをぶち込めばSelect状態になります。逆に空にすれば全て選択が外れます。
変な感じですが、意外と使いやすいです。
この説明でも良く分からないかもしれませんが、String解説に戻ります。

sSel = app.Selection.GetAsText()

これで、String型としてsSelに入れます。
どういう形で返るかというと

null
null1
null2

と選択していたら

"null,null1,null2"

といった具合に返ってきます。
LogMessageは、Stringしか受け付けませんから、エラーチェックのときは、GetAsText()を多用します。

app.Selection.SetAsText(sSel.replace('R','L'))

SetAsTextは、SelectionをStringに書かれている通りに選択します。
そして文字列メソッドのreplaceを使いRからLに変えています。
replaceは、置換です。

'IK_Rhand,IK_Rarm'

でしたら

'IK_Lhand,IK_Larm'

に置き換えます。

けれども、これだけでは、Lを選択してもRに行きません。寂しいです。
ですので

Lを選択のときは、Rを選択する。
Rを選択のときは、Lを選択する。

というちょっとした仕込を入れましょう。

import string
app = Application
sSel = app.Selection.GetAsText().encode()
table = string.maketrans('LR','RL')
sSel = sSel.translate(table)
app.Selection.SetAsText(sSel)

翻訳ツール(translate)を使って、LとRを逆に認識させるというコードです。
ステキなモジュール、stringを呼びました。
これは、文字列とは違います。stringというモジュールです。
モジュールを呼ぶときには、importとして、classdefを外部ファイルから呼ぶときに使います。
おまじないみたいなもんです。
そして通常翻訳するときには、'hello'は'こんにちは'に変換するみたいな感じでやってますが、同じ事を文字列で実行します。

まず、変換テーブルというものを作ります。

table = string.maketrans('LR','RL')

これは

'L'は'R'にしなさい
'R'は'L'にしなさい

という翻訳機が使う変換テーブルを作成します。
中身を見てみますと・・・

'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'


・・・ワケ分かりません。
ギャボンヌです。
Application.ES_Speak('gabonnu')

まあ、コンピュータが理解出来ればいいので
こんな感じですよ。って教えてあげます。

そして、変換テーブルに沿って文字列を翻訳します。

sSel = sSel.translate(table)

'IK_Larm'という文字列なら'IK_Rarm'に翻訳します。
そうそう。translateは、str型でないと機能しないようです。
XSIは、str型でも結構unicode型で返してくるファンクションが多いので、少々注意です。

encode()で、str型に変換しています。

sSel = app.Selection.GetAsText().encode()

そして最後に

app.Selection.SetAsText(sSel)

として、Selectionをセットしなおします。
これで、反転セレクトツールが出来ましたね。

『やったよ!ママン!ボクにも出来た!』

しかし、喜ぶのはまだ早いです。
ここでもまだワナが潜んでおります。
キャラクターなどは、Model階層化で作られることがほとんどです。
キャラクター'ROB'というModelに属していたらどうでしょうか?

・・・上手く取得出来ません。

'ROB.IK_Rarm'を翻訳すると、'LOB.IK_Larm'になってしまいます・・・。
目標が見つからなくエラーが出てしまいます。

ああ、これじゃクソスクリプトじゃんか。
これは、どうしよう・・・。
悩む・・・。
悩むなぁ・・・。

なんて感じでスクリプト制作はまだまだ続きます。
というわけで、この続きは次回までのお楽しみです。
完成目指して、がんばってみますね。
ではでは、しばらくの沈黙をいただきます。

冬休み早くこねーかなー。

2 件のコメント:

junki さんのコメント...

どもども。

奇遇ですね、おれも昨日、リグの作業中にブチ切れて、L から R を、R から L を選択するスクリプトを書いたところでした。



SearchStrR = 'R_';
SearchStrL = 'L_';

var rtn = GetKeyboardState();
var oSelCol = XSIFactory.CreateObject('XSI.Collection');
var oSel = Selection;

for (var i=0; i < oSel.count; i++)
{
ObjName = oSel(i).name;
IsMatchR = ObjName.match(SearchStrR);
IsMatchL = ObjName.match(SearchStrL);
NewName = '';

if (IsMatchR == null && IsMatchL != null)
{
NewName = ObjName.replace('L_','R_');
}
else if (IsMatchL == null && IsMatchR != null)
{
NewName = ObjName.replace('R_','L_');
}

if (NewName != '')
{
var oSelObj = Dictionary.GetObject( oSel(i).Model + '.' + NewName,false);
oSelCol.Add(oSelObj);
}
}
if (oSelCol.count != 0)
{
modifier = rtn(1);
if ( 1 & modifier )
{
AddToSelection(oSelCol);
}
else
{
SelectObj(oSelCol);
}
}



ううむ、いかにも行き当たりばったりの、美しくないコードですね。牌損はスヴァらしいですね。文字列の処理って、いつも汚いコード書いてる気がします俺。


また飲みにでも行きましょうよ。
っていつも言ってる気がするw

garu さんのコメント...

どもどもです。

いやぁ、変なメールのVimeo見たですけど、junkiさん、やさしいっすね!
マジで感激しました。そしてちょっと面白かった。
いいチュートリアルだと思いました。
言いたいこといっぱい言ってくれた感じですよ。
エラー処理とかいろいろ。
助かりますね。

そして、コード、ありがとうございます。
でも実は今後の展開同じこと考えてて、やっぱそんなんしか無いって感じですよね。
でもあれですわ。GetKeyboardStateの使い方知らなかったんで、書いてもらって良かったっす!
ウヒャヒャ。

文字列の処理っていっつもぐちゃぐちゃして嫌になりますよね。
Pythonなら、というほどではないですが、割とキレイに書けます。
ただ、他がイマイチ煩雑というか余計なものが増えるというか・・・。
XSIのPythonは、正直言って100%完全な能力を引き出す事に成功してないですねぇ。
変な宣言しないとちゃんと動かないとか、データ型が違うとか細かいところがいろいろ沢山あってちょっとだけ微妙。
まあ、さらにその先を行くものがあるから楽しいんですが。