ドローイング空間

3D-CADを中心に、雑多なことをかいています。

Fusion360 APIを使用して、Pythonスクリプトで、ダイアログからデータを受け取る

広告

Fusion360のマクロの使い方についての情報は、あまり見つかりません。 ただ、サンプルコードは、見つかりやすいところにそれなりに公開されています。 そこで、サンプルコードから、基本操作のコードの記述を切り出して行きたいと考えました。

ここでは、Pythonスクリプトを使用して、入力ダイアログから、値を受け取り、 それを、ダイアログに表示するスクリプトを実現したいと考えました。 結果、値を受け取るコードは、見つけたものの、それをダイアログで表示する方法は、 現時点ではよく分かりませんでした。いずれ、方法に気がつくと考えています。

Fusion360では、C++PythonJavaScriptがマクロ言語として、選択できます。 その中で、Pythonを選択したのは、FreeCADがマクロで、Pythonを使用しているというそれだけの理由で選択しました。 どの言語も習得できていないので、どれがいいのか、さっぱり、理解できなかったのです。 情報量もC++については、少ないものの、PythonJavaScriptでは、どちらの情報が多いのかは、判断できませんでした。

 カスタム ダイアログの情報

Fusion 360 API:カスタム ダイアログ

JavaScriptを使った実装の情報です。

adndevblog.typepad.com

Command Inputs API Sample API Sample

オンラインヘルプのカスタムダイアログのサンプルコードです。 ダイアログから、値を取得する部分は、実装されていません。

SpurGear

スクリプトとアドインの中にあるサンプルコードの「SpurGear」を参考にしました。

スクリプトとアドインの中にあるサンプルコードの「SpurGear」を参考にしました。

実装の概要

クラスや関数を先に記述し、その後に、最初に実行するRun関数、 さらに、Add-insの場合は、終了時に実行されるClose関数を記述するようです。

Pythonのスタイルなのか、Fusion360APIのスタイルなのかは、区別ができませんが、 VBAC#とは、かなり、実装のスタイルが異なるようです。

カスタムダイアログは、クラスにまとめます。

xxxx DestroyHandler

デストラクタ。メモリの開放などの終了処理を行います。

xxxx CreatedHandler

カスタムダイアログを表示するクラスです

xxxx ValidateInputsHandler

カスタムダイアログの値を検証するクラスです。

xxxx ExecuteHandler

カスタムダイアログの値を使用して、処理を行うクラスです。

xxxx CreatedHandler以外のクラスは、xxxx CreatedHandlerのイベントとして呼び出します。

run関数

呼び出すとまず実行される関数です。

クラスをそのまま実行するのではなく、commandDefinitionsインスタンスに、クラスを追加したのち、commandDefinitionsを実行します。

このような構造で実装するので、かなりバグが入りやすく、見つけにくいコード構成になります。 どこかの段階でマクロ仕様が大きく変更される可能性が高いことを考えておく必要があるかと思います。

確認した問題点

サンプルコードでは、長さの単位に、cmが使われています。mmに変更しましたが、挙動がおかしいです。 回避方法はあるかもしれませんが、現在のVersion 2.0.1876では、対応していないと考えたほうが良いかと思います。

取得した値を、出力する方法が見つかりません。プリントディバグができないので、早めに方法が見つかることを望んでいます。

マクロを実行する

アドインから、アドインとスクリプトを選択します。

アドインから、アドインとスクリプトを選択します。

マクロを選択して、Runをクリックします。マクロ名の変更ができないので、最初につけた名前が、マクロの実態と異なっても変更できません。

※削除して、再作成する必要があります。

最初につけた名前が、マクロの実態と異なっても変更できません。

カスタムダイアログが表示されます。

カスタムダイアログが表示されます。

ドロップダウンには、今まで入力した値が表示されています。これをマクロで追加する方法は、わかりません。 一番下の計測は、計測ツールを使用して、モデルから長さを指定する機能です。Escキーを押すとキャンセルできます。

ドロップダウンには、今まで入力した値が表示されています。

値を入力して、OKをクリックすると終了します。値を0にすると、値の検証で否認されますので、OKボタンがグレーアウトします。

Spyderで実行すると、前回の実行結果が、コンソールに表示されます。

値を入力して、OKをクリックすると終了します。

Fusion360APIでは、寸法は、内部的にはcmで管理しているようです。カスタムダイアログは、Fusion360側に表示されます。 usion360側に表示されるカスタムダイアログのOKを押して終了します。

Fusion360APIでは、寸法は、内部的にはcmで管理しているようです。

Spyder側では、実行されたままなので、コンソールタブを閉じて終了します。

※おそらく、何らかの終了処理が不足しているのだと思います。

Spyder側では、実行されたままなので、コンソールタブを閉じて終了します。

コードを記述する

アドイン→スプリプトとアドインを選択します。

アドイン→スプリプトとアドインを選択します。

「Create」をクリックします。

「Create」をクリックします。

「Script」と「Add-Ins」のどちらかを選択し、Pythonを選択し、名前を指定します。 「Create」をクリックするとSpyderが表示され、スケルトンコードが表示されます。

「Script」と「Add-Ins」のどちらかを選択し、Pythonを選択し、名前を指定します。

コード

#Author-horio kazuhiko
#Description- Fusion360の入力ダイアログの動作コードの確認
# 参考
# http://help.autodesk.com/view/NINVFUS/ENU/?guid=GUID-e5c4dbe8-ee48-11e4-9823-f8b156d7cd97

import adsk.core, adsk.fusion, adsk.cam, traceback

# コマンドの識別データの設定
app = None
ui  = None
commandId = 'CommandInputGallery'
commandName = '入力ダイアログ'
commandDescription = 'Fusion360の入力ダイアログの動作コードの確認'

# *** 入力ダイアログは、4つのクラスとして独立させます ***
# クラスや関数を先に記述し、その後に、最初に実行するRun関数、その後、終了時に実行されるClose関数を記述する
# (Clos関数は、Add-insの時のみ必要)

# *** 以下、定型表記 ***

# コマンド間の参照を維持するための、イベントハンドラのグローバル設定
handlers = []

class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
# 終了処理
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            # When the command is done, terminate the script
            # コマンドが実行されると、スクリプトが終了します
            # This will release all globals which will remove all event handlers
            # これは、すべてのイベントハンドラを削除する、すべてのグローバルを解放します
            adsk.terminate()
        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):
        try:
            cmd = args.command
            # 値を取り出して使用するイベント
            onExecute = MyCommandExecuteHandler()
            cmd.execute.add(onExecute)
            # 終了イベント(デストラクタ)
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            # 妥当性検証イベント(バリデーション)
            onValidateInputs = MyCommandValidateInputsHandler()
            cmd.validateInputs.add(onValidateInputs)
            
            # keep the handler referenced beyond this function
            # この関数を越えてハンドラの参照を維持します
            handlers.append(onExecute)
            handlers.append(onDestroy)
            handlers.append(onValidateInputs)

            # inputsを定義する
            # 入力ダイアログの値は、commandInputsコレクションのinputsの中に入ります。
            inputs = cmd.commandInputs
            global commandId

# *** 定型表記終了 ***            
           # 入力ダイアログのデザイン

            # ValueInputに入力する
            # 引数の説明 コレクションの項目の名前、ラベル、単位、初期値
            inputs.addValueInput(commandId + '_value', 'Value', 'mm', adsk.core.ValueInput.createByReal(3))
        

# *** 以下、定型表記 ***
        except:
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
# *** 定型表記終了 ***  

class MyCommandValidateInputsHandler(adsk.core.ValidateInputsEventHandler):
# 入力ダイアログで入力された値が、妥当であるか検証するクラス
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            command = args.firingEvent.sender
            inputs = command.commandInputs
            
            # inputsコレクションから値を取り出す
            myValueInput = inputs.itemById(commandId + '_value')
            
            # 単位の処理
            unitsMgr = app.activeProduct.unitsManager
            myValue = unitsMgr.evaluateExpression(myValueInput.expression, "mm")

            # 値の妥当性の確認
            if myValue == 0 :
                args.areInputsValid = False
            else:
                args.areInputsValid = True           
        except:
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
# 入力ダイアログで入力された値を取り出すクラス
# 入力ダイアログ・クラスにonValidateInputsイベントを設定して使用する
    def __init__(self):
        super().__init__()
    def notify(self, args):
        ui = None
        try:
            app = adsk.core.Application.get()

            unitsMgr = app.activeProduct.unitsManager
            command = args.firingEvent.sender
            inputs = command.commandInputs
            
            command = args.firingEvent.sender
            inputs = command.commandInputs
            
            # inputsコレクションから値を取り出す
            myValueInput = inputs.itemById(commandId + '_value')

            # 単位の処理
            unitsMgr = app.activeProduct.unitsManager
            myValue = unitsMgr.evaluateExpression(myValueInput.expression, "mm")
            
            # 値を出力 
            # 取得した値をダイアログで、表示したいと思いましたが、以下のコードでは、エラーが発生します。
            # ui = app.userInterface
            # ui.messageBox(myValue)
            # そこで、コンソールに表示しようとしましたが、表示されません。
            # 再度、実行する際に、最初に前回の結果が表示されるので、バッファーフラッシュのためのコードが不足しているのだと思います。
            print (myValue)

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

                
def run(context):
    ui = None
    try:
        global app
        # アプリケーションを取得
        app = adsk.core.Application.get()
        global ui
        # ユーザーインターフェースを取得
        ui = app.userInterface

        global commandId
        global commandName
        global commandDescription

        # ***入力ダイアログの処理***
        # Create command definition
        # コマンド定義を作成します。
        cmdDef = ui.commandDefinitions.itemById(commandId)
        if not cmdDef:
            cmdDef = ui.commandDefinitions.addButtonDefinition(commandId, commandName, commandDescription)

        # Add command created event
        # 作成されたイベントのコマンドを追加します

        # 入力ダイアログをイベントに追加
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        # Keep the handler referenced beyond this function
        # この関数を越えてハンドラの参照を維持します
        handlers.append(onCommandCreated)

        # Execute command
        # コマンドを実行します
        cmdDef.execute()


        # Prevent this module from being terminate when the script returns, 
        # because we are waiting for event handlers to fire
        # 私たちが、発生するイベントハンドラの待機中のため、スクリプトが返るとき、このモジュールが終了するのを防ぎます。
        adsk.autoTerminate(False)        

# *** 以下、定型表記 ***
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))