C#ATIA

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

ダイアログなスクリプト入門2

時間が経ってしまいましたが、こちらの続きです。
ダイアログなスクリプト入門1 - C#ATIA

前回は、ダイアログを表示させるまでで特に何も役立たないものでした。
実際にダイアログ付きのスクリプトを作成する場合は、幾つかのCommand Inputsを
配置する事になるはずです。
恐らく一番必要とされるのは、画面上の要素を選択するSelectionCommandInputに
なるだろうと思われます。押し出しコマンドのプロファイルがそうですね。
f:id:kandennti:20220117090348p:plain
今回はこちらをテーマにお話を進めましょう。

今回のゴール

見た目で、こんな感じのものを目標にします。
f:id:kandennti:20220117090444p:plain

ベースとなるコード

結局は前回のものなのですが、煩わしいのでほとんどのコメントは削除しました。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None

_handlers = []


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            'test_cmd_id'
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'test_cmd_id',
                'ダイアログです',
                'ツールチップです'
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


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)

            # event
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            # inputs
            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()))


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()

SelectionCommandInputを実装

大げさに書いていますが、実は簡単に追加できます。正しくは簡単な動作のものであれば
簡単に追加出来ます。
MyCommandCreatedHandlerに1行追記するだけです。
(見やすくするため5行ですが、実際は1行で書けます)

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)

            # event
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            # inputs
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            txtIpt: adsk.core.TextBoxCommandInput = inputs.addTextBoxCommandInput(
                'txtIpt',
                'テキストボックス',
                'Hello World',
                1,
                True
            )

            # SelectionCommandInputを追加する
            selIpt: adsk.core.SelectionCommandInput = inputs.addSelectionInput(
                'selIpt',
                '要素を選択してください',
                '要素を選択してください'
            )

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Fusion 360 Help

前回追加していたTextBoxCommandInputとパラメータが違うものの大差無いです。
これだけで選択する為の機能を追加できます。

実行してみる

実際に上記のように修正して実行してみて下さい。
試しに原点を選択すると、この様になります。
f:id:kandennti:20220117090642p:plain
複数の要素の選択は出来ませんが、何でも選択出来る状態です。
残念ながら選択した状態でOKボタンを押しても何も実行されません。

OKボタンの表示

実際に実行した際、OKボタンの状態が切り替わってい事にお気づきでしょうか?
未選択時はOKボタンがグレーアウトし押せない状態となります。
f:id:kandennti:20220117091345p:plain

通常、要素の選択が必要となるコマンドの場合は、未選択時ではOKボタンを
押されることが困る場合が殆どです。
その為、SelectionCommandInputが追加されている場合、OKボタンの制御も
一緒に追加されることになります。

但し、複数追加した場合は、望んでいる動作をしてくれません。
又、OKボタンの制御を意図的に行いたい場面も出てくると思います。
それらの話については、別の機会に記載することにします。(長いと思うので)

フィルターの実装

今の状態は何でも選択出来る状態です。
あなたが作るコマンドで選択して欲しいものがスケッチの点だった場合、
コンストラクションの点を選択されてOKボタンを押される事は、都合が悪いはずです。
OKボタンを押した後に "スケッチの点を選択してください" とメッセージを
表示させても、ユーザーに取っては不親切なコマンドと言う印象を受けるでしょう。

”選択時に警告する” と言う方法もありますが、一番望ましいのはボディの
面のみを選択出来る状態にするべきのはずです。その為の機能がフィルターです。
Fusion 360 Help

フィルターとなる文字はこちらです。
Fusion 360 Help

addSelectionInputメソッドの後に追加しましょう。

・・・
            # SelectionCommandInputを追加する
            selIpt: adsk.core.SelectionCommandInput = inputs.addSelectionInput(
                'selIpt',
                '要素を選択してください',
                '要素を選択してください'
            )
            # フィルターを設定する
            selIpt.addSelectionFilter('SketchPoints')
・・・

実際に実行してみましょう。スケッチの点以外は選択出来ない状態となります。

addSelectionFilterメソッドは複数使用が可能です。
スケッチ点とコンストラクション点を許可する場合は、この様にすれば可能です。

・・・
            # フィルターを設定する
            selIpt.addSelectionFilter('SketchPoints')
            selIpt.addSelectionFilter('ConstructionPoints')
・・・

これはこれで簡単で便利です。しかしもっと細かな条件でフィルタリングを
行いたい場面があるはずです。(一定面積以上のサイズの面等)
その様な事も可能ですが、それは別の機会にします。

リミットの実装

今作っているものと、押し出しのプロファイル部分とではフィルター以外にも動作が
異なる部分があります。
f:id:kandennti:20220117091546p:plain
複数の要素の選択が出来ません。スケッチの点を2個選択させたい場合もあるでしょう。
その様な動作をさせるのがリミットになります。
Fusion 360 Help

例えば2個以上を選択させたい場合は、この様になります。

・・・
            # フィルターを設定する
            selIpt.addSelectionFilter('SketchPoints')
            # リミットの設定
            selIpt.setSelectionLimits(2)
・・・

選択許可の最大値はオプション引数の為、省略可能です。(省略時は上限なし)
上記を追記し実行してみて下さい。複数の選択が可能になりつつ、2個以上選択しないと
OKボタンがグレーアウトした状態となります。

念の為ですが、2~4個までを許可したい場合であれば

・・・
            # リミットの設定
            selIpt.setSelectionLimits(2, 4)
・・・

2個だけ許可したい場合であれば

・・・
            # リミットの設定
            selIpt.setSelectionLimits(2, 2)
・・・

全く制限しない場合(選択されていなくてもOKボタンが押せる状態)

・・・
            # リミットの設定
            selIpt.setSelectionLimits(0)
・・・

又、最初の様にsetSelectionLimitsを利用せずにデフォルトの状態は、実質
この様な状態です。

・・・
            # リミットの設定
            selIpt.setSelectionLimits(1, 1)
・・・

まとめ

複数の例を提示しましたが、全体的にはこの様になりました。

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core

_app: adsk.core.Application = None
_ui: adsk.core.UserInterface = None

_handlers = []


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef: adsk.core.CommandDefinition = _ui.commandDefinitions.itemById(
            'test_cmd_id'
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'test_cmd_id',
                'ダイアログです',
                'ツールチップです'
            )

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


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)

            # event
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            onExecute = MyExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            # inputs
            inputs: adsk.core.CommandInputs = cmd.commandInputs

            txtIpt: adsk.core.TextBoxCommandInput = inputs.addTextBoxCommandInput(
                'txtIpt',
                'テキストボックス',
                'Hello World',
                1,
                True
            )

            # SelectionCommandInputを追加する
            selIpt: adsk.core.SelectionCommandInput = inputs.addSelectionInput(
                'selIpt',
                '要素を選択してください',
                '要素を選択してください'
            )
            # フィルターを設定する
            selIpt.addSelectionFilter('SketchPoints')
            selIpt.addSelectionFilter('ConstructionPoints')
            # リミットの設定
            selIpt.setSelectionLimits(2)
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


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()

フィルター・リミット共にご自身で少し変更し実行して頂くと、実感出来ると思います。



今回はSelectionCommandInputの実装だった為、CommandCreatedハンドラーでの
修正のみとなりました。次回は今回保留とした部分の説明を予定しており、他のイベントを
利用する事になります。