モチベーションも奮い立たせる為、Fusion360API入門者向けコンテンツを
再開する事にしました。
ダイアログを利用したスクリプトを作成する際、ドキュメントにあまり良い
サンプルが無く、加えてイベント処理に関しては、それらしい説明が無さそうな為
それらをテーマにしたもので進めていこうかと思ってます。
(当方C++の知識が無い為Pythonです)
- 今回のゴール
- エントリーポイント(run関数)
- CommandCreatedイベントハンドラー(MyCommandCreatedHandler)
- executeイベントハンドラー(MyExecuteHandler)
- destroyイベントハンドラー(MyCommandDestroyHandler)
- イベントの呼び出し
- 結び
今回のゴール
いきなりですが、ゴールです。
取りあえず、こんな状態のダイアログが表示されるスクリプトを用意しました。
念の為、OKボタンを押しても何もしません。
全体的なコードはこちらです。
# Fusion360API Python script import traceback import adsk.fusion import adsk.core # 頻繁に使うため、2個をグローバル変数としています。 _app: adsk.core.Application = None _ui: adsk.core.UserInterface = None # スクリプトが終了するまで、各イベントハンドラを保持用のリスト。 # こちらにハンドラを代入する事でGCに回収されることを防いでいる。 # 逆に書けば、突っ込んでおしまい。 _handlers = [] # スクリプトのエントリーポイント。 def run(context): try: # グローバルな変数 global _app, _ui _app = adsk.core.Application.get() _ui = _app.userInterface # 既に同じIDのコマンドが定義されていないか調べる為、 # IDを利用してCommandDefinitionを取得 cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById( 'test_cmd_id' ) # 同じIDのコマンドが定義されていない場合、CommandDefinitionを新作する。 if not cmdDef: cmdDef = _ui.commandDefinitions.addButtonDefinition( 'test_cmd_id', 'ダイアログです', 'ツールチップです' ) # コマンドを作成する為、イベントハンドラーを作成し # commandCreatedイベントに登録する。 global _handlers onCommandCreated = MyCommandCreatedHandler() cmdDef.commandCreated.add(onCommandCreated) _handlers.append(onCommandCreated) # CommandDefinition実行する。 # 基本的に、これによりcommandCreatedイベントが発火する。 # と思っていたのですが、run関数終了後に発火します。 cmdDef.execute() # コマンドが自動的に終了しないようにする。 # ここをTrueとすると、commandCreatedイベントが発火せず # スクリプト自体が即終了する。 adsk.autoTerminate(False) except: if _ui: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) # コマンドを作る為、細かな定義を行う。 # このハンドラのnotifyメソッドが終了するまでは、コマンドの作成が完了していません。 # その為、他のイベントで利用出来る処理が出来ない反面、他のイベントでは出来ない # 処理を行う事が可能です。その一つはダイアログの設定です。 class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandCreatedEventArgs): adsk.core.Application.get().log(args.firingEvent.name) try: global _handlers # コマンドの取得 cmd: adsk.core.Command = adsk.core.Command.cast(args.command) # --- イベント --- # コマンド終了時のイベントの登録 onDestroy = MyCommandDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) # ダイアログのOKボタンを押した際のイベントの登録 onExecute = MyExecuteHandler() cmd.execute.add(onExecute) _handlers.append(onExecute) # --- ダイアログのデザイン(CommandInputs) --- # コマンドのダイアログの土台の様なもの inputs: adsk.core.CommandInputs = cmd.commandInputs # テキストボックスインプット txtIpt: adsk.core.TextBoxCommandInput = inputs.addTextBoxCommandInput( 'txtIpt', 'テキストボックス', 'Hello World', 1, True ) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) # ダイアログのOKボタンを押した際のイベントハンドラ class MyExecuteHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandEventArgs): adsk.core.Application.get().log(args.firingEvent.name) # 本来はここで目的の処理を行います。 # 今回は特に何も行いません。 # コマンド終了時のイベントハンドラ class MyCommandDestroyHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.CommandEventArgs): adsk.core.Application.get().log(args.firingEvent.name) # スクリプトを終了させる # こちらを行わないと、スクリプトが実行中の状態となります。 # 但し、それによる弊害は良く分かっていません。 adsk.terminate()
最低限+αぐらいのボリュームです。単に処理するだけのスクリプトとは
異なり、ソコソコ長くなります。
コメントをグダグダ付けていますが、これを元にご説明しましょう。
エントリーポイント(run関数)
単純なスクリプトと同様で、エントリーポイントはrun関数となります。
但し、大きく異なる点が一つあります。
CommandDefinitionのインスタンスを作成します。これは独自のコマンドを作成する事を
意味します。
cmdDef = _ui.commandDefinitions.addButtonDefinition( 'test_cmd_id', 'ダイアログです', 'ツールチップです' )
CommandDefinitionのインスタンスの作成は、一般的なpythonのオブジェクトの
作成方法とは異なり、該当するコレクションに用意されたメソッドを使用する事が
ほとんどです。
今回の場合であればaddButtonDefinitionメソッドが該当します。
Fusion 360 Help
今回は "resourceFolder" を省略しています。これはアイコンを利用したい場合に
指定する必要があります。アドインの場合は必要性を感じますが、スクリプトの場合は
必要無いと思います。
続いて、commandCreatedイベントにイベントハンドラーを登録する必要があります。
CommandDefinitionのインスタンスを作成だけでは、ダイアログを作成する事が
出来ません。
その為、CommandDefinitionオブジェクトの唯一のイベント "commandCreated" に
イベントハンドラーを登録します。
global _handlers
onCommandCreated = MyCommandCreatedHandler()
cmdDef.commandCreated.add(onCommandCreated)
_handlers.append(onCommandCreated)
イベントハンドラーとなる "MyCommandCreatedHandler" オブジェクトは一般的な
インスタンスの作り方になります。
単にcommandCreatedに追加するだけではNGで、グローバルな変数にインスタンスを
代入しておく必要が有ります。
これはスコープが外れてしまう事によりガベージコレクション(GC)に回収されることを
防ぐためです。(恐らく・・・)
この辺りは、ドキュメントの例文で記述されている通りで問題無いはずです。
Fusion 360 Help
残りは、executeメソッドを呼び出し、autoTerminate関数で即終了しないようにします。
cmdDef.execute()
adsk.autoTerminate(False)
よほど複雑な事を行う場合を除いて、run関数に関しては、ほぼ雛形だと思って頂いて
構わないと思います。唯一の注意点はaddButtonDefinitionメソッドのパラメータで
他のコマンドと重複しないコマンドIDを指定することぐらいです。
CommandCreatedイベントハンドラー(MyCommandCreatedHandler)
ここではコマンドのイベントやダイアログの設定等、コマンドの詳細を設定します。
コマンドのイベントですが、今回はdestroyとexecuteだけを利用しています。
onDestroy = MyCommandDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) onExecute = MyExecuteHandler() cmd.execute.add(onExecute) _handlers.append(onExecute)
executeイベントはOKボタンが押された場合に呼び出されるハンドラーを代入します。
Fusion 360 Help
destroyイベントはコマンドの終了直前に呼び出されるハンドラーを代入します。
Fusion 360 Help
こちらはOK・キャンセルどちらのボタンが押されても呼び出されます。
イベントに関しては、commandCreatedイベント同様でドキュメントの例文で
記述されている通りで問題無いはずです。
※各イベントは、リストとして保持されるため、複数のハンドラを代入する事が可能です。
続いてダイアログのUI設定です。APIとして提供されているCommand Inputsを
配置する事になります。
予めお伝えしておくと、Command Inputsが一個も無い(OK・キャンセルボタンのみ)
ダイアログは作成する事が出来ません。
今回は無難に読み込み専用のTextBoxCommandInputのみを配置しています。
txtIpt: adsk.core.TextBoxCommandInput = inputs.addTextBoxCommandInput( 'txtIpt', 'テキストボックス', 'Hello World', 1, True )
他にも複数のCommand Inputsが用意されております。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-8B9041D5-75CC-4515-B4BB-4CF2CD5BC359
まぁこれでも足りないのですが、大体は事足ります。
各Command InputsもインスタンスはCommandInputsオブジェクトのadd~メソッドを
利用します。
通常1個のCommand Inputsだけでは済まないはずですね。コンテナっぽいもの
(TabCommandInput等)を除いて、基本的にadd~メソッドを行った順にダイアログの
上から配置されます。・・・レイアウトの自由度は少ないです。
CommandCreatedイベントは特別です。CommandCreatedイベントが終了するまでは
コマンドが作成されていません。逆にCommandCreatedイベントでしか出来ない処理が
あります。
その一つはCommand Inputsのインスタンスの作成は他のイベントでは出来ません。
仮に何かのCommand Inputsを途中で表示させたい場合(BoolValueCommandInputを
ONにするとSelectionCommandInputが表示される等)は、予めCommandCreatedイベント時に
作成しておき非表示にしておく必要が有ります。 兎に角特別なイベントです。
executeイベントハンドラー(MyExecuteHandler)
こちらはOKボタンを押した場合に呼び出されます。
今回は何か処理を行うわけでは無いため特に記述していませんが、本来はこの位置に
実行したい処理を書いたり、呼び出したりをする部分です。
destroyイベントハンドラー(MyCommandDestroyHandler)
こちらはOK/キャンセルボタンのどちらを押しても、スクリプト終了直前に呼び出されます。
本来であれば、何らかを破棄したり(アドインであればボタン等)するのですが、
こちらも今回は特に処理する必要が無いため、一行のみ記載しています。
adsk.terminate()
これを実行しなければ、スクリプトが完全には終了しないようです。
イベントの呼び出し
今回は全てのイベントハンドラには
adsk.core.Application.get().log(args.firingEvent.name)
を記載しました。
これにより、実際に操作を行いながらイベントの呼び出しが確認できます。
出力先はテキストコマンドのパレット部分です。
もし、テキストコマンドのパレットが表示されていない場合は、こちらから表示
させてください。
OKボタンを押した場合とキャンセルボタンを押した場合で、出力される文字が
異なりますし、呼び出されるイベントの順番も確認出来ると思います。
結び
と、まぁ興味が湧くような内容が無い、薄い内容となりました。
個人的に "使いにくいな" と感じる部分が有ったり、有志の方が便利なフレーム
ワーク(apperやその他2個ぐらいあります)を公開されていますが、
Fusion360API的に標準的な記述方法は上記のようなスタイルです。
特にフレームワークを使ってしまうと、仕組みの様な部分が隠されてしまい
あまり知識が身に付かない印象を受けました。
取りあえずこれをベースに今後はお話を進めましょう。