C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

2022版 Fusion360 Pythonアドインテンプレートの入り口3

こちらの続きです。
2022版 Fusion360 Pythonアドインテンプレートの入り口2 - C#ATIA

"entry.py"について

中身はこんな感じです。(コメント部は翻訳サイトで訳しました)

import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
app = adsk.core.Application.get()
ui = app.userInterface


# TODO *** コマンド識別情報を指定します。 ***
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_cmdDialog'
CMD_NAME = 'Command Dialog Sample'
CMD_Description = 'A Fusion 360 Add-in Command with a dialog'

# コマンドがパネルに昇格することを指定します。
IS_PROMOTED = True

# TODO *** コマンド ボタンを作成する場所を定義します。 ***
# これは、ワークスペース、タブ、パネル、および
# コマンドの横に挿入されます。 配置するコマンドを提供していない
# 最後に挿入します。
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'

# コマンドアイコンのリソースの場所。ここでは、このディレクトリに「resources」という名前の
# サブフォルダーがあると想定しています。
ICON_FOLDER = os.path.join(
    os.path.dirname(os.path.abspath(__file__)),
    'resources',
    ''
)

# 参照を維持するために使用されるイベント ハンドラーのローカル リスト
# それらは解放されず、ガベージ コレクションも行われません。
local_handlers = []


# アドイン実行時に実行されます。
def start():
    # コマンド定義を作成します。
    cmd_def = ui.commandDefinitions.addButtonDefinition(
        CMD_ID,
        CMD_NAME,
        CMD_Description,
        ICON_FOLDER
    )

    # コマンド作成イベントのイベント ハンドラーを定義します。
    # ボタンがクリックされたときに呼び出されます。
    futil.add_handler(
        cmd_def.commandCreated,
        command_created
    )

    # ******** UI にボタンを追加して、ユーザーがコマンドを実行できるようにします。 ********
     # ボタンが作成されるターゲット ワークスペースを取得します。
    workspace = ui.workspaces.itemById(WORKSPACE_ID)

    # ボタンが作成されるパネルを取得します。
    panel = workspace.toolbarPanels.itemById(PANEL_ID)

    # UI で、指定された既存のコマンドの後にボタン コマンド コントロールを作成します。
    control = panel.controls.addCommand(
        cmd_def,
        COMMAND_BESIDE_ID,
        False
    )

    # コマンドがメイン ツールバーに昇格するかどうかを指定します。
    control.isPromoted = IS_PROMOTED


# アドイン停止時に実行。
def stop():
    # このコマンドのさまざまな UI 要素を取得します
    workspace = ui.workspaces.itemById(WORKSPACE_ID)
    panel = workspace.toolbarPanels.itemById(PANEL_ID)
    command_control = panel.controls.itemById(CMD_ID)
    command_definition = ui.commandDefinitions.itemById(CMD_ID)

    # ボタンコマンドコントロールを削除
    if command_control:
        command_control.deleteMe()

    # コマンド定義を削除
    if command_definition:
        command_definition.deleteMe()


# ユーザーが UI の対応するボタンをクリックしたときに呼び出される関数。
# これは、コマンド ダイアログの内容を定義し、コマンド関連のイベントに接続します。
def command_created(args: adsk.core.CommandCreatedEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Command Created Event')

    # https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
    inputs = args.command.commandInputs

    # TODO コマンドにさまざまな入力を追加して、コマンドのダイアログを定義します。

    # 簡単なテキスト ボックス入力を作成します。
    inputs.addTextBoxCommandInput('text_box', 'Some Text', 'Enter some text.', 1, False)

    # 値入力フィールドを作成し、デフォルトの長さ単位の 1 単位を使用してデフォルトを設定します。
    defaultLengthUnits = app.activeProduct.unitsManager.defaultLengthUnits
    default_value = adsk.core.ValueInput.createByString('1')
    inputs.addValueInput(
        'value_input',
        'Some Value',
        defaultLengthUnits,
        default_value
    )

    # TODO このコマンドで必要なイベントに接続します。
    futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
    futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
    futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
    futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
    futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)


# このイベント ハンドラーは、ユーザーがコマンド ダイアログの [OK] ボタンをクリックしたとき、または
# コマンド入力ではなく、作成されたイベントがダイアログに対して作成された直後に呼び出されます。
def command_execute(args: adsk.core.CommandEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Command Execute Event')

    # TODO ******************************** ここにあなたのコード ************************************

    # コマンドの入力への参照を取得します。
    inputs = args.command.commandInputs
    text_box: adsk.core.TextBoxCommandInput = inputs.itemById('text_box')
    value_input: adsk.core.ValueCommandInput = inputs.itemById('value_input')

    # 面白いことをしよう
    text = text_box.text
    expression = value_input.expression
    msg = f'Your text: {text}<br>Your value: {expression}'
    ui.messageBox(msg)


# このイベント ハンドラは、コマンドがグラフィックス ウィンドウで新しいプレビューを
# 計算する必要があるときに呼び出されます。
def command_preview(args: adsk.core.CommandEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Command Preview Event')
    inputs = args.command.commandInputs


# このイベント ハンドラーは、ユーザーがコマンド ダイアログで何かを変更したときに呼び出されます
# その変更に基づいて他の入力の値を変更できるようにします。
def command_input_changed(args: adsk.core.InputChangedEventArgs):
    changed_input = args.input
    inputs = args.inputs

    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')


# このイベント ハンドラーは、ユーザーがダイアログ内のいずれかの入力を操作したときに呼び出されます
# すべての入力が有効であることを確認し、[OK] ボタンを有効にすることができます。
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Validate Input Event')

    inputs = args.inputs
    
    # 入力値の有効性を検証します。 これは、[OK] ボタンを有効にするかどうかを制御します。
    valueInput = inputs.itemById('value_input')
    if valueInput.value >= 0:
        inputs.areInputsValid = True
    else:
        inputs.areInputsValid = False
        

# このイベント ハンドラは、コマンドの終了時に呼び出されます。
def command_destroy(args: adsk.core.CommandEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Command Destroy Event')

    global local_handlers
    local_handlers = []

長いな・・・。

インポート部分

基本的には、変更する必要はない部分です。この様になってます。

import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config

"adsk.fusion" は読み込んでいないですね。恐らく後々の事を考えると
読み込むべきかな?とは思います。

”fusion360utils” は作業を楽にするためのユーティリティの様です。
メインの仕事としては、イベントハンドラーの登録・管理を行っている
ようです。

”config” は前回記載しましたが、アドイン全体で使用する定数的な
変数を管理させているファイルです。
2022版 Fusion360 Pythonアドインテンプレートの入り口2 - C#ATIA
特に "ADDIN_NAME" "COMPANY_NAME" はコマンドIDのバッティングを
避ける為、重要ですね。

グローバル部

"グローバル" と言う呼び名で良いものかどうか知りませんが、
便利上そうしました。
ここはガッツリ書き換える必要があります。

# TODO *** コマンド識別情報を指定します。 ***
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_cmdDialog'
CMD_NAME = 'Command Dialog Sample'
CMD_Description = 'A Fusion 360 Add-in Command with a dialog'

# コマンドがパネルに昇格することを指定します。
IS_PROMOTED = True

# TODO *** コマンド ボタンを作成する場所を定義します。 ***
# これは、ワークスペース、タブ、パネル、および
# コマンドの横に挿入されます。 配置するコマンドを提供していない
# 最後に挿入します。
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'

# コマンドアイコンのリソースの場所。ここでは、このディレクトリに「resources」という名前の
# サブフォルダーがあると想定しています。
ICON_FOLDER = os.path.join(
    os.path.dirname(os.path.abspath(__file__)),
    'resources',
    ''
)

# 参照を維持するために使用されるイベント ハンドラーのローカル リスト
# それらは解放されず、ガベージ コレクションも行われません。
local_handlers = []

主にツールバーにボタンを登録する際に必要なものを、定数的変数に
代入しています。”CMD_ID” が重要ですね。

又、”local_handlers” はこのコマンドの為に使用されるイベントハンドラ
格納しておくリストになっています。
確か、この辺がapperで問題になっていたと思います。
”local_handlers” に格納されるハンドラの寿命は、このコマンドが実行
されている間だけです。
アドインが起動している間、有効にしたいハンドラは、ここには代入しない
です。

start・stop関数

基本的にこちらの二つは修正する必要はありません。
グローバル部で設定した内容を反映しています。
"start" 関数でツールバーに表示されるように設定を行い、
"stop" 関数で不要になったものを削除する処理を行っています。

command_created関数

ボタンが押された際に実行される部分です。
ここもガッチリ書き換える事になります。

# ユーザーが UI の対応するボタンをクリックしたときに呼び出される関数。
# これは、コマンド ダイアログの内容を定義し、コマンド関連のイベントに接続します。
def command_created(args: adsk.core.CommandCreatedEventArgs):
    # デバッグ用の一般的なログ。
    futil.log(f'{CMD_NAME} Command Created Event')

    # https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
    inputs = args.command.commandInputs

    # TODO コマンドにさまざまな入力を追加して、コマンドのダイアログを定義します。

    # 簡単なテキスト ボックス入力を作成します。
    inputs.addTextBoxCommandInput('text_box', 'Some Text', 'Enter some text.', 1, False)

    # 値入力フィールドを作成し、デフォルトの長さ単位の 1 単位を使用してデフォルトを設定します。
    defaultLengthUnits = app.activeProduct.unitsManager.defaultLengthUnits
    default_value = adsk.core.ValueInput.createByString('1')
    inputs.addValueInput(
        'value_input',
        'Some Value',
        defaultLengthUnits,
        default_value
    )

    # TODO このコマンドで必要なイベントに接続します。
    futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
    futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
    futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
    futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
    futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)

ダイアログ上のinputsについては、以前と変わらないスタイルですね。

イベントに関しては大幅に変わっています。
例えばOKボタンを押した際に実行されるexecuteイベントに関して、以前はこの様な
記述になっていました。

onExecute = MyExecuteHandler()
command_var.execute.add(onExecute)
handlers.append(onExecute)

新しいテンプレートではfusion360utils(futil)のadd_handler関数を利用しています。

futil.add_handler(
    command_var.execute,
    command_execute,
    local_handlers=local_handlers
)

つまり

futil.add_handler(
    <コマンドのイベント>,
    <イベント発生時に呼び出される関数>,
    local_handlers=local_handlers # <-基本的に変更なし
)

のスタイルです。良いのかどうかは別として、この記述方法がデフォルトと
思っていた方が良いようです。
ドキュメントにも各イベント事に記述方法が記載されています。
Fusion 360 Help

他の関数

"command_destroy" に関しては、コマンド終了時の処理なので、変更の必要は
ありません。
それ以外については、各イベントに応じて書き換える必要がありますね。

まとめ

tapnairさんが作成されるテンプレートについては、一貫してイベントハンドラ
クラスレベルから関数レベルに引き落としています。
この部分に関しては、過去に記載した
俺コマンドを作る の検索結果 - C#ATIA

ダイアログなスクリプト入門 の検索結果 - C#ATIA
の二つとは大きくスタイルが異なります。

但し、手間ですが上記の2件の様に古いスタイルで少し作った方が、
スクリプト/アドインが動く仕組みが理解出来るので、テンプレート使用時の
トラブル回避も出来るような気がしています。(個人的に)