以前からどうやって良いのかわからなかった処理が、やっと
昨夜分かりましたので、記載しておきます。
(上手く行った際、思わず声が出た・・・)
元ネタはこちらです。
パレットに苦しむ2 - C#ATIA
パレットの中身はブラウザなのですが、旧タイプはCEFコンポーネント
(Chromium Embedded Framework ...chromeとかedgeに使われている奴)
ですが、新タイプはQtWebブラウザーコンポーネントです。
そして再三、旧タイプは無くなってしまうので切り替えるように
アナウンスされています。
先程の元ネタでは旧タイプを使用しているのですが、あのまま新タイプに
してしまうと、初期値をパレットに反映することが出来ませんでした。
(python -> javascriptのデータの受け渡しが出来なかったです)
その為、旧タイプから新タイプに切り替えることが出来ずに放置して
いました。・・・当然、後々のことを考えれば良くないです。
BrowserCommandInputも中身はブラウザなのですが、かなり後から
実装されたため旧タイプが無く、新タイプのQtWebブラウザー
コンポーネントです。 つまり初期値の受け渡し方法が分からなく
ここ一週間ほど、悩んでいました。
何せFusion360APIの内容な為、検索してもそれらしきサンプルが
見つからず、自分で試すしか方法がありません。
又、起動時の処理はデバッグが非常に行いにくく、無い知恵絞って
試すのですが、何れも上手く行かず途方に暮れていました。
解決のきっかけは、公式ドキュメントに記載されていました。
(真ん中より少し下に記載あり。ちゃんと読むべき)
Fusion 360 Help
CEFコンポーネントは同期処理されるが、QtWebブラウザーコンポーネント
は非同期処理されるとの事。 最初は "フーン" 程度で完全には理解
出来ませんでした。と言うか、どの様に対処知れば良いのかが
分かりませんでした。
javasprictを理解しきっていないのですが、どうやらコールバック関数や
thenを使えって事の様です。
と言う事で、必要最低限の出来上がったサンプルです。
スクリプト起動時に、アクティブなドキュメント名をBrowserCommandInput
で表示させています。
こちらは、python側です。まぁこちらはこれと言って問題無かったです。
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: adsk.core.CommandInputs = cmd.commandInputs
inputs.addBrowserCommandInput(
'browserIptId',
'',
'index.html',
300,
100
)
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)ファイルと同一フォルダに配置しておきます。
<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
あぁやっと進める。 時間出来たら他のアドインも書き換えなきゃ。