C#ATIA

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

BrowserCommandInputへ初期値の受け渡し

久々にFusion360です。
こちらを答えてみました。
How to create a `BrowserCommandInput` and populate it with initial data from Fusion? - Autodesk Community

"ダイアログを表示する時にBrowserCommandInputに初期値を渡したい”
と言う質問だと受け止めて答えてみました。
BrowserCommandInputはダイアログ上にHTMLを表示させる
コマンドインプットです。
全く使った事が無いのですが、仕組み的には全くパレットと同じです。

コードを見るとCommandCreatedイベント時にやろうとしていますが、
タイミングが違うんですよ。
CommandCreatedイベント時はコマンド自体が出来ておらず、イベント
終了後にコマンドが出来上がります。

その為、初期値の受け渡しの python -> javascript の一連の動きは

pythonでコマンド作成 -> ダイアログ表示 -> javascriptのDOMContentLoadedイベント発火
-> javascript側で"HTML読み込んだよ"と送信 -> pythonのincomingFromHTMLイベント発火 
-> pythonで必要な情報を戻り値として送信 -> javascriptで情報を受け取り -> html側に反映

と結構な処理をさせる必要があります。
それともう一つ。BrowserCommandInputで表示させているブラウザは、javascript
非同期処理で書かなければなりません。


フォーラムではファイルを添付して中身が見れないので、こちらで
公開しておきます。(こちらはスクリプトです)
まずはエントリーポイントとなる、"BrowserCommandInputInitialDataTest.py"

# Fusion360API Python script
import traceback
import adsk
import adsk.core as core
import json

_app: core.Application = None
_ui: core.UserInterface = None
_handlers = []

CMD_INFO = {
    'id': 'kantoku_BrowserCommandInputInitialDataTest',
    'name': 'test',
    'tooltip': 'test'
}

_browserIpt: core.BrowserCommandInput = None

class MyCommandCreatedHandler(core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandCreatedEventArgs):
        try:
            global _handlers
            cmd: core.Command = core.Command.cast(args.command)
            inputs: core.CommandInputs = cmd.commandInputs

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

            onHTMLEvent = MyHTMLEventHandler()
            cmd.incomingFromHTML.add(onHTMLEvent)
            _handlers.append(onHTMLEvent)

            global _browserIpt
            _browserIpt = inputs.addBrowserCommandInput(
                "_browserIptId",
                "Active Document Name:",
                "index.html",
                50,
            )

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


class MyHTMLEventHandler(core.HTMLEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        try:
            htmlArgs = core.HTMLEventArgs.cast(args)

            app: core.Application = core.Application.get()
            if htmlArgs.action == 'htmlLoaded':
                args.returnData = json.dumps({
                    "name": app.activeDocument.name,
                })
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class MyCommandDestroyHandler(core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: core.CommandEventArgs):
        adsk.terminate()


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

        cmdDef: core.CommandDefinition = _ui.commandDefinitions.itemById(
            CMD_INFO['id']
        )

        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                CMD_INFO['id'],
                CMD_INFO['name'],
                CMD_INFO['tooltip']
            )

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

続いて、表示されるHTMLファイル(index.html)。
(javascriptもこれに書き込んでます)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div>
      <label id="activeDocName">Unknown</label>
    </div>
  </body>

  <script>
    document.addEventListener("DOMContentLoaded", () => {
      let adskWaiter = setInterval(() => {
        if (window.adsk) {
          clearInterval(adskWaiter);

          adsk.fusionSendData("htmlLoaded", "").then((ret) => {
            let data = JSON.parse(ret);

            let activeDocNameLabel = document.getElementById("activeDocName");
            activeDocNameLabel.textContent = data["name"];
          }, 100);
        }
      });
    });
  </script>
</html>