以前からどうやって良いのかわからなかった処理が、やっと
昨夜分かりましたので、記載しておきます。
(上手く行った際、思わず声が出た・・・)
元ネタはこちらです。
パレットに苦しむ2 - C#ATIA
パレットの中身はブラウザなのですが、旧タイプはCEFコンポーネント
(Chromium Embedded Framework ...chromeとかedgeに使われている奴)
ですが、新タイプはQtWebブラウザーコンポーネントです。
そして再三、旧タイプは無くなってしまうので切り替えるように
アナウンスされています。
先程の元ネタでは旧タイプを使用しているのですが、あのまま新タイプに
してしまうと、初期値をパレットに反映することが出来ませんでした。
(python -> javascriptのデータの受け渡しが出来なかったです)
その為、旧タイプから新タイプに切り替えることが出来ずに放置して
いました。・・・当然、後々のことを考えれば良くないです。
BrowserCommandInputも中身はブラウザなのですが、かなり後から
実装されたため旧タイプが無く、新タイプのQtWebブラウザー
コンポーネントです。 つまり初期値の受け渡し方法が分からなく
ここ一週間ほど、悩んでいました。
何せFusion360APIの内容な為、検索してもそれらしきサンプルが
見つからず、自分で試すしか方法がありません。
又、起動時の処理はデバッグが非常に行いにくく、無い知恵絞って
試すのですが、何れも上手く行かず途方に暮れていました。
解決のきっかけは、公式ドキュメントに記載されていました。
(真ん中より少し下に記載あり。ちゃんと読むべき)
Fusion 360 Help
CEFコンポーネントは同期処理されるが、QtWebブラウザーコンポーネント
は非同期処理されるとの事。 最初は "フーン" 程度で完全には理解
出来ませんでした。と言うか、どの様に対処知れば良いのかが
分かりませんでした。
javasprictを理解しきっていないのですが、どうやらコールバック関数や
thenを使えって事の様です。
と言う事で、必要最低限の出来上がったサンプルです。
スクリプト起動時に、アクティブなドキュメント名をBrowserCommandInput
で表示させています。
こちらは、python側です。まぁこちらはこれと言って問題無かったです。
# Fusion360API Python script import adsk.core import adsk.fusion import traceback _app: adsk.core.Application = None _ui: adsk.core.UserInterface = None _cmdId = 'browserInputTest' _handlers = [] class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler): def __init__(self): super().__init__() def notify(self, args): try: cmd = adsk.core.Command.cast(args.command) # inputs inputs: adsk.core.CommandInputs = cmd.commandInputs # global _browserIpt inputs.addBrowserCommandInput( 'browserIptId', '', 'index.html', 300, 100 ) # event onDestroy = MyDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) onIncomingFromHTML = MyIncomingFromHTMLHandler() cmd.incomingFromHTML.add(onIncomingFromHTML) _handlers.append(onIncomingFromHTML) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyIncomingFromHTMLHandler(adsk.core.HTMLEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.HTMLEventArgs): if args.action == 'DOMContentLoaded': args.returnData = getActDocName() class MyDestroyHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args): eventArgs = adsk.core.CommandEventArgs.cast(args) adsk.terminate() def getActDocName() -> str: global _app return _app.activeDocument.name def run(context): try: global _app, _ui _app = adsk.core.Application.get() _ui = _app.userInterface global _cmdId cmdDef = _ui.commandDefinitions.itemById(_cmdId) if not cmdDef: cmdDef = _ui.commandDefinitions.addButtonDefinition( _cmdId, 'Browser Input Test', 'Browser Input Test' ) 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" と言うファイル名にしています。
上記のpython(.py)ファイルと同一フォルダに配置しておきます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Qt Web Browser Test</title> </head> <body> <p id="xxx">** BrowserInput Test **</p> </body> <script> document.addEventListener("DOMContentLoaded", () => { let adskWaiter = setInterval(() => { console.log("DOMContentLoaded"); if (window.adsk) { console.log("adsk ok"); clearInterval(adskWaiter); let element = document.getElementById("xxx"); adsk .fusionSendData("DOMContentLoaded", "{}") .then((data) => element.insertAdjacentHTML("afterend", "<p>" + data + "</p>") ); } }, 100); }); </script> </html>
実行するとこんな無意味なダイアログが表示されます。
赤矢印はhtmlにベタに記載していますが、青矢印はイベントで
後から追記させたアクティブなドキュメント名です。
細々とした(幼児レベルの)覚書です。
・元ネタJeromeBriotさんの物は、"window.onload" でイベントを
拾っていますが、あの方法はあまり良くないとの記述を見つけました。
window.onloadの利用はお勧めしない その理由
その為、addEventListenerを利用するようにしました。
・"addEventListener" する際、 "load" "DomContentLoaded" の
どちらかのイベントを利用するのですが、呼び出しタイミングの
早い "DomContentLoaded" にしました。
・元ネタでは "window.adsk" の記載でしたが、"window” は
省略可能だと知っていました。
今回の例では
if (adsk) {
でも上手く処理出来るのですが、上手く行かない時がある為
if (window.adsk) {
とする方が良い事が分かりました。
・元ネタでは
var adskWaiter = setInterval(function () {
になっていますが、アロー関数を利用すると
let adskWaiter = setInterval(() => {
と書けることを知っていました。
(varも良くない!)
・恐らく一発では "adsk" (オブジェクト?) を見つける事が
出来ないのだと思うので、setIntervalでadskを探し出し
発見次第にclearIntervalで止めていると理解しました。
・非同期のpromiseオブジェクトの場合、thenを利用した
メソッドチェーンで処理することがお作法だと教わりました。
【ES6】 JavaScript初心者でもわかるPromise講座 - Qiita
あぁやっと進める。 時間出来たら他のアドインも書き換えなきゃ。