2010-12-13

python on Nuke 3

前回は長々となってしまったので、分割しました。
引き続き、保存されたnukescript(.nk)のファイルパスは

C:/test/nukeScripts/teapot_composit_001.nk

Readノードにおけるファイルパスは

C:/test/cgRender/101209/teapot/teapot_%04d.exr

です。前回は、このReadノードが選択されている状態で、

---------------------------------------------------------
readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
        i = i + 1
--------------------------------------------------------- 

とするとこで、お互いのファイルパスのどこまでが一致しているかを判定しました。
前回も説明しましたが、

[file dirname [value root.name]]


という文字列を用いると用いると保存されたnukescript(.nk)のファイルパスから見たファイルパスで置き換えることができます。

[file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

で、これがどういう利点があるかというと、Readノードにおけるファイルパスを相対パスに書き換えることができるということで、そうすることで、環境の違いやOSの違いにも対応しやすくなり、もちろんネットワークレンダリングする場合などにとても便利です。

「..」はひとつ階層があがるということです。[file dirname [value root.name]] は保存されたnukescript(.nk)のファイルパスを示しており、この場合

[file dirname [value root.name]] =  C:/test/nukeScripts/

ということになり、Readノードにおけるファイルパス「C:/test/cgRender/101209/teapot/teapot_%04d.exr」と比べると、「C:/test/」までしか一致しないので、その一致する階層に移動するために、[file dirname [value root.name]] =  C:/test/nukeScripts/ から一つあがる必要があります。

その一つあがるが

[file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

での「..」となります。
毎度毎度くどい説明ですが、そんなです・・・
ちなみに、前回用いたリスト、「listroot」と「listread」をわかりやすくすると、



となります。ここでこれらタプル型のリストの文字列の数は、

len()

で示すことができて、たとえば、listrootに含まれる文字列の数は、

len(listroot)

で表すことができます。この場合、

print len(listroot)

をすると



となり、「4」という数字が帰ってきます。


で、わかるようにリストの構成文字列は4個です。最後の listroot[3] に相当する、「teapot_composit_001.nk」は階層ではなくファイルそのものなので、階層だけで考えると、3個の階層で、さらに、前回、頭から2個目までが一致しているという結果を得ているので、

(4 - 1) - 2 = 1

をして、[file dirname [value root.name]] =  C:/test/nukeScripts/ から上がる階層としては、1階層分となります。なので [file dirname [value root.name]] にその回数分(上がる階層分) 「..」を追加します。またここで、リスト「listroot」とリスト「listread」に含まれる文字列の一致回数と、「..」の追加回数を適当に、「sametimes」、「uptimes」として、そのコードとし、前回分のコードからの続きで・・・

---------------------------------------------------------

readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
        i = i + 1

sametimes = i


uptimes = len(listroot) - sametimes - 1
---------------------------------------------------------

と書き表すことができます。ループ文(この場合whileループ)などは段がえを抜けると、次のブロックになるので、whileループは「i = 2」で終わり、「sametimes」にそれが適応されてます。

で、ここで得たuptimes分「..」を [file dirname [value root.name]] に付け加えます。
[file dirname [value root.name]]を一旦「reppath」という文字列で置き換えて・・・

---------------------------------------------------------
readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
        i = i + 1

sametimes = i
uptimes = len(listroot) - sametimes - 1

reppath = '[file dirname [value root.name]]' 

for i in range(uptimes):
   
reppath = reppath + ('/..')

print reppath
 --------------------------------------------------------- 

としてやります。すると結果として・・・


 となり、

# Result: [file dirname [value root.name]]/..

を得ることができます。
ここではforループというものを用いています。

for i in range (数字):
    行いたい処理

としてやると、「数字」の回数分だけ行いたい処理を行います。たとえば数字に「5」を入れて、print i とかすると・・・


--------------------------------------------------------
for i in range(5):
    print i 
--------------------------------------------------------


となり、

# Result: 0
1
2
3
4

を得ます。気をつけたいのは、「数字」を「5」を入れると「0,1,2,3,4」を順番に「 i 」に代入していき、「行いたい処理」を実行するということです。今回の場合はあまり関係がないのですが・・・
この場合、

for i in range(uptimes):
    reppath = reppath + ('/..')


で「uptimes」は「1」なので、一回だけ

reppath = reppath + ('/..')

の処理がされます。たとえば、もし「2」なら、一回目で
reppath = reppath + ('/..')  =  '[file dirname [value root.name]]/..'

となっている、reppathに対して、

reppath = reppath + ('/..')

が行われますので、

reppath = reppath + ('/..')  =  '[file dirname [value root.name]]/../..'

となり、その分、階層を上がっていくことになります。ちょっとそれましたが・・・

続けていきましょう。さらに、ここまでで得た、

reppath = reppath + ('/..')  =  '[file dirname [value root.name]]/..'

に、Readノードにおけるファイルパス C:/test/cgRender/101209/teapot/teapot_%04d.exr をリスト化した「listread」から必要な分だけ、文字列を抽出して追加していきます。この追加回数を、適当に「addtimes」とします。で「addtimes」は、「listread」に含まれる文字列の数から、リスト「listroot」とリスト「listread」に含まれる文字列の一致回数「sametimes」を引いたものになり、さらに、「listread」から抽出したいリストの番号を「ordtimes」として、

---------------------------------------------------------
readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
     i = i + 1

sametimes = i
uptimes = len(listroot) - sametimes - 1

reppath = '[file dirname [value root.name]]' 

for i in range(uptimes):
    reppath = reppath + ('/..')

addtimes = len(listread) - sametimes

for i in range(addtimes):
   
ordtimes = sametimes + i
 
    reppath = reppath + '/' + listread[ordtimes]

print reppath
 ---------------------------------------------------------

とすると、



となり、

# Result: [file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

を得ました。


ですので、

addtimes = len(listread) - sametimes

で導かれる数字「4」を用いて、

for i in range(addtimes):
    ordtimes = sametimes + i
    reppath = reppath + '/' + listread[ordtimes] 

で4回同じ処理をするのですが、「i」 には 「0、1、2、3」 が順番に代入されます。ここで、前に定義した「reppath」の後ろに listread[2]、listread[3]、listread[4]、listread[5] を順に追加していきたいので、一致回数「2」の「sametimes」を用いて、

ordtimes = sametimes + i

として、i = 0 の時

reppath = reppath + '/' + listread[ordtimes]  =  '[file dirname [value root.name]]/../' + listread[2] 

となり、その時、

reppath = '[file dirname [value root.name]]/../cgRender'

となります。あとはループですので、この文字列に追加すべく文字列が追加されていきます。

で結果、

# Result: [file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

を得ることができるので、あとは、これを用いて、選択しているReadノードのファイルパスに置き換えてやればいいので、

---------------------------------------------------------
readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
     i = i + 1

sametimes = i
uptimes = len(listroot) - sametimes - 1

reppath = '[file dirname [value root.name]]' 

for i in range(uptimes):
    reppath = reppath + ('/..')


addtimes = len(listread) - sametimes

for i in range(addtimes):
    ordtimes = sametimes + i
    reppath = reppath + '/' + listread[ordtimes]

nuke.selectedNode().knob('file').setValue(reppath)

---------------------------------------------------------

です。


ここでやっているように、

nuke.selectedNode().knob('file').setValue(reppath)

とすると、実際にReadノードのファイルパスが 「.setValue(reppath)」によって、「reppath」である

[file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

によって置き換わります。


と、こんな感じで、自前でファイルパスを変更するツールができました。
更に関数化することで、シーン内のReadノードに対して一気に処理できたり、色々とより便利なものへとしていくことも可能です。


・・・ しかし、くどい説明・・・

逆に理解しづらいかも・・・ わかり難いところあれば、コメントいただけると幸いです。

補足:
前回も何事も無く触れてないのですが、文字列を扱う時は「' '(シングルクオート)」で囲わなければなりません(「" "(ダブルクオート)」でもOKです)。
たとえば適当なリスト「ls」をでっち上げたとして、そのリストの中に「apple」、「gorilla」、「trumpet」を格納したい場合は

ls = ['apple' , 'gorilla' , 'trumpet']

です。数字なら、「' '(シングルクオート)」を用いずに、

ls = ['apple' , 'gorilla' , 'trumpet' , 0 , 1 , 2]

となります。

2010-12-09

python on Nuke 2

ってことで続きです。

前回「タプル」という言葉を出しましたが、「タプル」アマチュアのボクが考えるに、「タプル」はまさにリストです。

話はいきなり飛びますが、Nukeはファイルパスをそのnukescript(.nk)のある場所から見たパスに置き換えることができます。いわゆる、相対パスです。相対パスに置き換えれれば、環境が変わっても、ディレクトリ構造が一緒であれば、ファイルパスに関しては問題なく同じように扱えます。
また、たとえば、ローカルで作業して、ファイルサーバー上に同じディレクトリ構造で同じようにファイルを持っていれば、ネットワークレンダリングなどで、ファイルパスを気にせず分散処理ができます。

実際どういうことかというと・・・
今ココに、適当な連番ファイルを読み込んだReadノードを持つ「teapot_composit_001.nk」というnukescript(Nukeのプロジェクトファイル)があるとします。





図は模式図ですが、こんな階層構造になっていて、 nukescriptのファイルパスは

C:/test/nukeScripts/teapot_composit_001.nk


 であり、Readノードに記されたファイルパスは

C:/test/cgRender/101209/teapot/teapot_%04d.exr


です。
で、この場合、 このReadノードのファイルパスを

[file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr


 と相対パス表示に置き換えることができ、これはディレクトリ構造的にnukescriptのファイルパスから見て、Readノードの相対パスが保たれていれば、どこへ持っていってもファイルが見つからないということはありません。win、mac、linux どれであっても問題なくファイルパスが通ります。

[file dirname [value root.name]]

というのは、そのnukescriptが存在するディレクトリを指しています。この場合、「nukeScripts」ディレクトリの一階層上にある「cgRender」ディレクトリからたどらないといけないので、「一階層上」を表すため、

[file dirname [value root.name]]/../cgRender/101209/teapot/teapot_%04d.exr

「..」が挿入されています。


で、この方法を用いてpythonで、この部分を自動処理させます。

まずは、Readノードのファイルパスを取得します。そのスクリプトの運用方法にもよりますが、ひとまず、Readノードを選択した状態で、その連番のファイルパスがpython上ではどうなるかを見ていきます。

まず、選択したノードは前回にも説明しましたが、単一選択の場合は

nuke.selectedNode()

で表されます。
さらに、そのファイルパスは・・・

nuke.selectedNode().knob('file').value()


で表されます。 実際にはこのファイルパスは

C:/test/cgRender/101209/teapot/teapot_%04d.exr

です。さらに、これと、 nukescriptのファイルパスを見比べて、クダンの文字列と入れ替えるのですが、そのnukescriptのファイルパスは、

nuke.root().name()



で表されます。これはNukeの中ではこういうものみたいです。
また、これらのファイルパス(文字列)を見比べたいのですが、階層の数などを考慮するため、「/(スラッシュ)」を取り除いてリスト化しようと思います。ここでまさにpython的効力が発揮されるわけですが、一旦ファイルパスを扱いやすいように、

readpath = nuke.selectedNode().knob('file').value()

として ファイルパスを一旦「readpath」で置き換えます。さらに同じく、nukescriptのファイルパスも


rootpath = nuke.root().name()


として、同様に「rootpath」で置き換えます。でこの文字列が格納された、「readpath」、「rootpath」をpythonの機能(関数)を用いて、、「/(スラッシュ)」を取り除いてリスト化するという処理を施します。用いる関数は「split」というもので、

readpath.split('/')

とすると、スラッシュを取り除いてできる文字列群のリストが出来上がります。
実際に、


print readpath.split('/')


とすると、


# Result: ['C:', 'test', 'cgRender', '101209', 'teapot', 'teapot_%04d.exr']



と返ってきます。これがタプル型に格納された文字列のリストになります。
このままでは扱いづらいので、

listread = readpath.split('/')

として、「listread」というものでこのリストを表します。またリストは頭から数えて何番目かの文字列を取り出す、ということができます。たとえば、

print listread[3]

とすると、

# Result: 101209


が返ってきます。1番目は[0]なので、[3]とすることで、4番目の文字列が返ってきました。

print listread[5]

にすれば、6番目の文字列である

# Result: teapot_%04d.exr

と返ってくるはずです。

じゃあ、ここで、Readノードのファイルパス と nukescriptのファイルパス を見比べて、リストの何個目まで文字列が一致するかを調べてみます。whileループというものを使います。

whileループは条件を指定し、その条件が満たされている限りは、指定した処理を行うというものです。実際には

while (条件文):
        行いたい処理

となります。今回の場合リストに格納されている文字群がどこまで一緒かを調べたいので、「i」という適当な変数を用いて、Readノードが選択されている状態で、

---------------------------------------------------------
readpath = nuke.selectedNode().knob('file').value()
listread = readpath.split('/')

rootpath = nuke.root().name()
listroot = rootpath.split('/')

i = 0
while (listread[i] == listroot[i]):
        i = i + 1
---------------------------------------------------------

とします。この場合条件文が

listread[i] == listroot[i]

で、行いたい処理が

i = i + 1

です。
あらかじめ、 i = 0 と宣言しているので、
まず最初に、

listread[0] == listroot[0]

が成り立っているか否かをしらべます。成り立っている場合は、

i = i + 1

の処理が行われ、今一度、条件文の判定が行われます。すると、最初は i = 0 でしたが、 i = i + 1 によって、 次の条件文では i = 1 となり

listread[1] == listroot[1]

が成り立っているか否かを調べることになります。このケースの場合、まだ、この時点では成り立っているので、i = i + 1 処理が行われ、この時点で i = 2 となります。whileはその条件文が成立しなくなるまで処理を続けるので、この時点での条件文、


listread[2] == listroot[2]

が成立しなくなります('cgRender' と 'nukeScripts')。なので、 i = i + 1 処理は行われず、 i = 2 という結果を得ます。
つまり、頭から二つ目までが一致しているということになります。



長くなっているので、今回はここまでにします。

補足:pythonでは(?)、ループ処理などで「条件文」と「行いたい処理」を「:(コロン)」で区切って、改行し、さらに「段違い」にします。処理が複数個あるときはその「段違い」で頭をそろえる必要があります。

たとえば、

--------------------------------------------------------- 
while (listread[i] == listroot[i]):
        i = i + 1
        i = i - 1
---------------------------------------------------------
というような感じです。