次の作戦の為にテストです。
表現力を豊かにするため、BrowserCommandInputを使用する事にします。
Fusion 360 Help
BrowserCommandInputは、僕がFusion360APIを取り組み始めて唯一後から追加された
CommandInputsです。
今まで使用した事が無かったのですが、ほぼPaletteと同じの為
"何とかなるだろう" とは感じています。
BrowserCommandInputで表示させるhtml側は、動的な表示が欲しいため
Reactを利用する事にします。・・・理解が不足しまくってますが、
お勉強がてらです。
# Fusion360API Python script import adsk.core import adsk.fusion import traceback _app: adsk.core.Application = None _ui: adsk.core.UserInterface = None _cmdId = 'browserInputReactTest' _selIpt: adsk.core.SelectionCommandInput = None _browserIpt: adsk.core.BrowserCommandInput = None _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 global _selIpt _selIpt = inputs.addSelectionInput( 'selIptId', 'Bodies', 'Select Body' ) _selIpt.addSelectionFilter(adsk.core.SelectionCommandInput.Bodies) _selIpt.setSelectionLimits(0) global _browserIpt _browserIpt = inputs.addBrowserCommandInput( 'browserIptId', '', 'index.html', 300, 300 ) onDestroy = MyDestroyHandler() cmd.destroy.add(onDestroy) _handlers.append(onDestroy) onInputChanged = MyCommandInputChangedHandler() cmd.inputChanged.add(onInputChanged) _handlers.append(onInputChanged) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler): def __init__(self): super().__init__() def notify(self, args: adsk.core.InputChangedEventArgs): try: global _selIpt if args.input != _selIpt: return names = [] for idx in range(_selIpt.selectionCount): ent = _selIpt.selection(idx).entity if ent: names.append(ent.name) global _browserIpt _browserIpt.sendInfoToHTML( 'test', '@'.join(names) ) except: _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) class MyDestroyHandler(adsk.core.CommandEventHandler): def __init__(self): super().__init__() def notify(self, args): eventArgs = adsk.core.CommandEventArgs.cast(args) adsk.terminate() 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 React Test', 'Browser Input React 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" として同じフォルダ内に設置します。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js" ></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" ></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <title>react_cdn</title> </head> <body> <div id="root"></div> </body> <script type="text/babel"> function ListItem(props) { return <li>{props.value}</li>; } function NamesList(props) { const names = props.names; return ( <ul> {names.map((name) => ( <ListItem key={name.toString()} value={name} /> ))} </ul> ); } const root = ReactDOM.createRoot(document.getElementById("root")); // 最初のレスポンスが悪すぎる root.render(<NamesList names={[" "]} />); root.render(<NamesList names={[" "]} />); root.render(<NamesList names={[]} />); window.fusionJavaScriptHandler = { handle: function (action, data) { try { switch (action) { case "test": const names = data.split("@"); root.render(<NamesList names={names} />); break; } } catch (e) { console.log(e); console.log("exception caught with command: " + action); } return "OK"; }, }; </script> </html>
こんな感じの動作です。
環境を作るのが面倒な為、React・Babel共にCDNにしています。
取り組むまでわからなかったのが、幾つか有りました。
今までPaletteを利用していたアドインでは、全てダイアログでアクションを
行い処理させるものばかりでした。
例えば、こちらはボタンを押すアクションで色々とPython側で処理させています。
Fusion360_Small_Tools_for_Developers/Developers_Small_ToolKit at master · kantoku-code/Fusion360_Small_Tools_for_Developers · GitHub
汚すぎるので、細かく見ない方が身のためです。
つまり、ダイアログ->Fusion360の処理、又はダイアログ->Fusion360->ダイアログ
の処理については、incomingFromHTMLイベントで処理出来ます。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-0921986D-D97E-4A96-931D-E2C74A63C358
細かなお話は、今回のテーマでは無いため割愛します。
わからなかったのがFusion360でアクションを起こした際、ダイアログの
表示を反映させる方法です。つまりFusion360->ダイアログです。
サンプル等を参考にしたところ、Palette.sendInfoToHTMLメソッドで処理が
可能だと分かりました。
Fusion 360 Help
今回の場合であれば、inputChangedイベントハンドラー内のこちらです。
_browserIpt.sendInfoToHTML( 'test', '@'.join(names) )
第2引数は文字列なのですが、選択されたボディ名のリストを改行で連結して
javascript側に投げたのですが、受け取り側で上手くsplit出来なかった為、
"@" の文字で連結しています。(原因は謎)
受け取り側のjavascriptではこの様にしています。
window.fusionJavaScriptHandler = { handle: function (action, data) { try { switch (action) { case "test": const names = data.split("@"); root.render(<NamesList names={names} />); break; } ・・・
fusionJavaScriptHandlerプロパティ(なのかな?)で受け取る様です。
受け取ったdataを再度リストに分割し、再レンダーさせてます。
React・Babel共にCDNとしている事が原因なのか? そもそもReact・Babel
を利用する事が原因なのか? Fusion360で行うとこんなものなのか?
原因がハッキリしないのですが、最初の標示のレスポンスが異常に悪く、
最初の時点で無駄に何度かレンダーさせています。
// 最初のレスポンスが悪すぎる root.render(<NamesList names={[" "]} />); root.render(<NamesList names={[" "]} />); root.render(<NamesList names={[]} />);
但し、完全な解決になっておらず、ボディを選択しても名前が表示されない
時があります。(特にダイアログが表示され直ぐに選択をした場合は失敗しやすい)
フロントサイドでゴリゴリやる予定も無く、単に動的な表示させたいだけの為に、
"React必要か?" とも感じますが、この路線で行きます。