DaVinci FusionのOn Changeと実行の挙動についてあれこれ
マクロ化すると2回以上実行される
print(tool.Name)
と書いて保存します。

toolは変更が行われたノードを指してくれる変数で、.Nameはその名前です。
マクロにするとき、このコントロールを操作できるように指定して、操作できるマクロを作ったとします。

そしてコントロールを操作すると、マクロからでも、もとのノードからでも、どちらから操作してもprintが2回実行されます
オリジナルのノード名とマクロのノード名が表示されると思います。

このようにtoolが変わって2度実行される挙動になっていることを知っておくとよいでしょう。
なお、ButtonのExecuteからは2回実行されないようです。
対策
これはスクリプトの書かれた大本のコントロールからの実行である、といった情報の取得方法はたぶんないと思います。これができない以上はどの対策も限定的なものになります。
なんらかの特徴を紐付けて判定するしかないでしょう。
もしノード名で判定するならtool.Name:gsub("[_%d]+$", "")とすれば末尾のアンダーバーと数字を除いた文字列を取得できます。
一番の対策は可能な限り、2回実行されても影響がないようにすることです。
※追記:対策としてActiveToolも使えます。親子関係を辿る方法も良さそうです。コメントで教えてもらいました!
自動リネームされない
エクスプレッションはリネームされます。

コピー後↓↓

このとき、5_1のボタンの実行にprint(Transform5:GetInput("Size"))と書いていました。コピー先の5_1_1を見ても変更されていませんでした。

対策
追跡用コントロールを作りエクスプレッションから取得するのが確実だと思います。
上記の例でもエクスプレッションはリネームされていたので、その性質を使おうということですね。
※追記、下記のスクリプトの冒頭2行は、読み取り専用のTextEditControlを使う方法に変更したほうが良さそうです。コメント欄にて教えてもらいました。
local expression_str = tool.Track:GetExpression() -- なんでもよいので追跡用のパラメーターを用意しておく
local nodeName = string.match(expression_str, "^([^.]+)") -- ピリオドより左側を取得
local node = FindTool(nodeName)
print(node.Name)

コピー前に実行し、コピー後に新しくコピーしたノードで実行しました。コピー先を追跡できていますね。
ほかにもGetChildrenList()で子の一覧を取得してフィルタリングしたり、GetConnectedOutput()で前後を辿るなどの、あれやこれやといった方法もありますが、確実性に欠けます。
Pythonも書ける
スクリプトの先頭に!Py3: (←半角スペースも必須)と入力することでスクリプトをPythonを書くことができます。参考記事:【DaVinci Resolve】AIを使用してButtonControlのExecuteに設定するスクリプトを作る話
1000文字の制限
1000文字までしか入力させてくれません。ちょっと長めのものを書こうとするとすぐに制限が来てしまいます。
対策
文字数を抑える対策です。
- タブをスペースからインデントにする
- AIにコードを圧縮してもらう
これに対して、根本的に記述場所を変えるという方法があります。
- コントロールに記述して読み込ませる。参考記事:DaVinci Resolve20の新機能
On Changeを試してみる - 外部ファイルに書いたファイルからインポートする(下記インポートのサンプルです)
!Py3: import sys
import os
import importlib
# ホームディレクトリを動的に取得(オプション)
home_dir = os.path.expanduser('~')
script_dir = os.path.join(home_dir, r'davinci_python') # 自分のpythonを置いてある場所
if script_dir not in sys.path:
sys.path.append(script_dir)
# インポートして使用できる
import python_filename
python_filename.func_name(self, tool, comp)
配布する予定などがある場合は、コントロールに記述して読み込ませる方法がよさそうです。
Discussion
2回実行される問題は、操作してる側のツールのときは~ってするのが良さそうかなって思いました
これでいつもうまくいくかどうかは未確認です💦
ありがとうございます!
そういえばセカイルさんの記事を見た記憶がなんとなくあって
それで記事にしたほうがいいという気がして書いたのでした。
改めて確認もしてきました。
いろいろと思い出せてよかったです🙌
※追記
マクロの内部にマクロがある、という場合、3回実行されるのですが、その際は
アクティブマクロ、非アクティブマクロ、非アクティブノードという並びになります。
1回だけ実行、という条件なら問題なかったのですが、
例えば非アクティブマクロにおいてパススルーをONにしたい、という処理をしたい場合には
また対応が難しいかもしれません。
toolがマクロではない場合のみ実行できれば良いということであれば、tool.IDプロパティが"MacroOperator"かどうかで判別すればマクロの多重構造にも対応できます。(グループの場合は
"GroupOperator")あとは
tool.ParentToolプロパティは自身が所属するマクロもしくはグループを返却するのでtool.ParentTool == nilという条件なら多重構造マクロの一番外側(最上位)だけ処理する、あるいはtool.ParentToolで親マクロを辿って自身が上から何層目のノード・マクロか判別するなどもできそうです。追記における非アクティブマクロのみパススルーONにする処理であれば、ノード本体を直接含むマクロだけを対象にすれば可能かと思います。
また、自動リネームされない仕様への対策案ですが、追跡用コントロールを

TextEditControlにして追跡対象ノード.Nameというエクスプレッションを追記すればノード名を直接取得できるようになり、正規表現が不要な分だけ少しシンプルにできそうです。火注ゆかなさん、コメントいただきありがとうございます。
個人的に改めてDaVinci Resolveに挑戦するにあたり、Mugさんとゆかなさんのお二人の発信内容がとても参考になりました。カスタマイズの過程でソフトに慣れていくタイプの人間のため 助かりました。
改めて感謝申し上げます。
実践で使用をしていたため コメント返信が遅れました。
ーーー
追跡対象を追うために読み取り専用の
TextEditControlを使うのは最適と言えそうですね!ありがとうございます。親子関係を辿るのは良い妥協点になりそうですね。特に下から数えれば、状態は安定しそうです。上から数えると、親子関係の深さによって参照が変わってしまいますからね。下から数えれば、うっかり状態によって参照できなくなるケースも起きづらいように思います。
発火したコントロールが大本の記述対象だったかどうかを明示的に得られるのが、その他の状態に依存しなくていいんですが、やはり難しそうでした。
INPS_ExecuteOnChangeがあるかどうかを追うというのもやってみましたがコピーされていますね…出力: