はじめに
私は展示系インスタレーションなどのシステム開発をTouchDesignerで行うことがありますが、状態管理や外部との通信等のためにPythonを書くことが多いです。
(以前は Table DAT
に必要な情報を全部入れていたのですが、システムが複雑になると管理がしんどくなってしまいました。)
そんな中、Python Class内の変数を、ネットワーク側のoperatorにも反映したくなることが多く発生します。
この場合、例えば Evaluate DAT
などでClass内の変数を引っ張ってくることができますが、これだけだと、変数の値が変更されたときに、ネットワーク側でcookが走らず、値が同期できなくなってしまいます。(なんて不便だ…)
これまでは、下記みたいにPython側に直接オペレータを変更する処理を書いていましたが、
op('text1').text = self.property
これだとノードが繋がらないため、あとでネットワークを見返した時に
- このoperatorが一体どこのスクリプトで変更されているか?
がわかりにくく、確認に時間がかかってしまいます。
数個だけなら良いですが、大量に発生すると、メンテナンスする場合にとても大変です。
解決策
すばり、 Dependency Objects
というものがあります。
Because Python does not inherently have a procedural mechanism, Dependency Objects in TouchDesigner allow python data to cause downstream cooking when that data is changed.
Dependency Objects
を使うことで、Python側の数値が変わったときにダウンストリームのcookが走るようになる。と書いてあります。ありがたい。
サンプルプログラム
こちらにアップしました。
https://github.com/genkitoyama/TD_Dependency_sample
使い方
ネットワーク側で参照したい変数に、 tdu.Dependency(初期値)
と入れればOKです。これだけ。
例えば、
-
int
ならtdu.Dependency(0)
-
string
ならtdu.Dependency('')
-
list
ならtdu.Dependency(['hoge', 'fuga'])
などなど。
中身の値を get/set するには、 val
メンバを使います。
hoge = tdu.Dependency(0)
print(hoge) #type:Dependency val:0
print(hoge.val) #0
hoge.val += 1
print(hoge.val) #1
下図は、サンプルプログラムをキャプチャしたgifです。
ボタンを押すと、Class内の変数がインクリメントされ、Textportに出力されます。
右下に Evaluate DAT
が2種類あり、Dependencyの使用有無で比較をしています。
-
parent().ValueA
(Dependencyを使っていない)- ❌ ボタンを押しても数値が増えず、値の同期ができていません。
-
parent().ValueB.val
(Dependencyを使っている)- ✅ ボタンを押すと数値が増え、値の同期ができています。
callbacks
TouchDesigner2022以降では、新たに callbacks
メンバが追加されたので、値が変更された時に、簡単にコールバック関数を設定することができます。
まだあまり触れていないので、これから触ってみようと思います。
注意点
list
, dictionary
, set
などの mutable なオブジェクトに関しては、個々の要素を変更しただけでは、cookが発生しません。
その場合は、 modified()
というメソッドを呼ぶ必要があります。
以下、公式からの引用です。
Dictionaries, lists, sets, and other objects with changing contents are called "mutable" objects. If the contents of a mutable object changes, a dependency wrapping it does not automatically register as being modified! You must either use a deeply dependable collection or call the .modified() function manually as in the example below:
op('comp1').Scale.val = [1,2,3]
op('comp1').Scale.val[1] = 15 # changing the contents doesn't update dependency!
op('comp1').Scale.modified() # this call notifies TouchDesigner that the value has changed
おわりに
ぶっちゃけ、きっちりPython書かずとも、ノードベースで実装してしまえば良いのでは?ってなるのですが、
例えば常設のアプリケーションなど、後々のメンテナンスが必要なものにおいては、
なるべくコードベースで実装した方が、メンテナンスやデバッグが楽になるのでは?という気持ちです。
(遥か昔にエイヤで作ったノードまみれのシステムの不具合修正をしようとした時は、なかなかに絶望を感じました、、)
時と場合に応じて、使い分けると良いのかなと思いました。
おまけ
ちなみに、上記デモでは、TDの Comp Extension
という機能を使っています。
個人的にはわかりやすいです。
詳しくは下記を参考にしてください。