[TouchDesigner] Custom Compでイベントを使う！：CallbacksExtの導入ガイド

Custom Compでイベントを使う方法。依存性を下げたCustom Compを実装するのに便利でです。

TouchDesignerでは、`CallbackExt`というPython callback systemを使って実装します。詳細は、公式ドキュメントに記載されています。

[TouchDesigner公式ドキュメント callbacksExt Extension](https://docs.derivative.ca/CallbacksExt_Extension)
[TouchDesigner公式ドキュメント CallbacksExt Examples](https://docs.derivative.ca/CallbacksExt_Examples)

OP同士をネットワークでつないでいくよりも、よりコードベースでComp同士を連携でき、`Base COMP`もしくはtox単位での開発がしやすくなります。

### サンプルファイル
https://github.com/arkwknsk/touchdesigner/tree/master/tips/callbacks-ext

# Custom Compへのライブラリの追加方法
Custom Compに、TouchDesignerが用意しているライブラリを`component's Extension Object`へ追加することで実現できます。

ここでは、イベント処理機能を持った`userComp`というCustom Compの作り方を説明します。


`userComp`という`Base Comp`をつくります。

`userComp`の中に入り、<img alt="1" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/e1bf660c-e249-c2a5-4714-f508440b8b48.png">`Select DAT`を追加。DATの名前を`CallbacksExt`に変更します。この名前は、別なもにはしないでください。

![callbacks-ext_00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/24005510-394d-57eb-8b63-f9293e4cea08.jpeg)

追加した`Select DAT`のプロパティの、「Select」→<img alt="2" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/7a37b4a2-f05e-5a27-512c-d2f9100d2ac1.jpeg">`DAT`をexpressionモードに変更し、以下のexpressionを入れます。

```
op.TDModules.op('TDCallbacksExt')
```

すると、`Select DAT`に、`TDCallbacksExt`というTouchDesignerが用意しているクラスが参照されます。

このコードを`userComp`に登録します。

![callbacks-ext_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/2f556a09-7c13-f077-0fe1-dad3958321d3.jpeg)

1つ上の階層に戻り、`userComp`のプロパティを表示し、「Extensions」> <img alt="3" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/817d085b-389f-134a-7e46-aa65f8d2ae7c.jpeg">
Objectパラメータを
```
op('./CallbacksExt').module.CallbacksExt(me)
```
と設定します。

`TDCallbacksExt`に、外部（`userComp`ではないCompもしくはDATなど）からアクセスできるようにするには、<img alt="4" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/eb417c37-4800-322e-2361-132dad1149c4.jpeg">Promoteパラメーターを`On`にします。

`TDCallbacksExt`がデフォルトで参照するパラメーターを追加します。`userComp`のComponent Editorを表示します。(`userComp`を選択、右クリック > Custoimize Componentを選択）


<img width="395" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/33ab3995-d0d7-0cc5-debd-48264db4c0ee.jpeg">

「Custom Parameters」で、`userComp`ページを作成します。

続いて、次の表のようなパラメーターをつくります。

|parameter|style||
|:-|:-|:-|
|Callback DAT|DAT|
|Print Callbacks|Toggle|
|Toggle|Toggle|※|
|String|Toggle|※|
|Pulse|Pulse|※|

※「イベントハンドラーの設定」で使用するパラメーターなので、実際の実装では必須ではない。

設定すると`userComp`のプロパティは次のようになります。

<img width="429" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/e9c91d49-efaa-5451-433c-98040e01612d.jpeg">

# CallBackの設定
ここでは、Custom Compで発火したイベントを受け取って処理するCallBack（イベントハンドラー）の設定の仕方を2種類説明します。引き続き、👆でつくった`userComp`に追加する形で説明してきます。

### 1. Callback DATを使う方法

`userComp`の中に入り、<img alt="5" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/370fdc44-d4f0-10f3-b638-5ec07e7d7e2e.jpeg">`Parameter Execute DAT`を追加します。

![callbacks-ext_06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/88414801-63af-c547-baa3-2428c68e0b89.jpeg)

プロパティ→「Parameter Execute」の<img alt="6" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/2d2a289d-4fea-bb38-52e5-569f96c1123b.jpeg">パラメーターを次のように設定します。

|parameter|value|
|:-|:-|
|OPs|..|
|Parmeters|*|
|Built-in|Off|

`Parameter Execute DAT`を編集して、次のコードに書き換えます。
```
def valueChange(par, val, prev):
	infoDict = {'parameter': par, 'value': val, 'previousValue': prev}
	parent().DoCallback("onParChange", infoDict)
 ```
`useComp`に設定したCustom Parameterが更新されると、`valueChange`というメソッドが実行(call)されます。

`infoDict`は、CallBack（イベント受信）側に、変更後・前のパラメーター値などを渡すためのペイロードです。

`DoCallback()`が、イベント発火のためのメソッドになります。ここでは、イベント名を`onParChange`としています。

最後に、CallBack関数をつくります。上の階層に戻り、<img alt="7" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/bc1023ca-1cf4-6e33-5925-1216ca124cb1.jpeg">`Text DAT`を追加します。

![callbacks-ext_07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/1b3f439c-9197-1623-2c68-355799ca0a55.jpeg)

このDATに、以下のコードを入力します。

```
def onParChange(info):
	debug("PAR CHANGE:", info['parameter'].name, info['value'])
```
`useComp`の<img alt="8" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/120bdd93-99f6-3ac1-32ef-0dfc16d4955a.jpeg">`Callback DAT`で、このDATを指定します。

`useComp`のToggleパラメーターのON/OFFを切り替えると、Textportに次のようなログが出力されます
```
PAR CHANGE: Toggle on (Debug - DAT:/project1/example1 fn:onParChange line:2)
onParChange - callbackInfo:
 {   'parameter': type:Par name:Toggle owner:/project1/userComp value:True,
    'value': 'on',
    'previousValue': 'off',
    'ownerComp': type:baseCOMP path:/project1/userComp,
    'callbackName': 'onParChange',
    'returnValue': None} 
 
  (Debug - DAT:/project1/userComp/CallbacksExt fn:DoCallback line:206)
```
1行目は、`Text DAT`かれ出力され、2行目以降は<img alt="9" width="15" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/c28735da-5291-017b-96ed-c6d96fe0067e.jpeg">`PrintCallbacks`パラメーターが`ON`の場合に、`TDCallbacksExt`から出力されるログです。

### 2.コードだけでCallBackを追加する方法

`useComp`の中に入り、`parexec1 DAT`で、以下のコードを書き換えます。
```
def onPulse(par):
	infoDict = {'parameter': par}
	parent().DoCallback("onPulse", infoDict)
```
ここでは、`onPulse`イベントが発火されています。

1つ上の階層に戻り、`Text DAT`を追加します。`DAT`に以下のコードを追加します。
```
def printPulse(info):
	print("PAR PULSE:", info['parameter'].name)

op('userComp').SetAssignedCallback('onPulse', printPulse)
```
`printPulse()`は、イベントを受信後、実行されるメソッド（関数）です。最後の行の`SetAssignedCallback()`で、`onPulse`イベントに、`printPulse()`をコールバックとして割り当てています。
`onPulse`イベントが`DoCallback()`で発火されると、`printPulse()`が実行される流れです。

:::note warn
他の開発環境、コードに慣れ親しんでいる方には、わかりやすい方法ではありますが、`CallbacksExt`が定義されているCompが初期化されると、コールバックのアサインが解除されるという問題点があります。そのため、何らかの方法で再アサインする必要があります。この点は、ぜひ改良して欲しい。
:::