Palette Communication Failure: Python->JS (sendInfoToHTML) Messages Not Received by JS Handler
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I am developing an Add-In that uses a standard HTML Palette (adsk.core.Palettes.add) for user interaction. The Add-In requires bi-directional communication:
JavaScript -> Python: Sending user actions/data from the palette JS to the Python backend using window.adsk.fusionSendData(action, data).
Python -> JavaScript: Sending status updates, results, or commands from the Python backend to the palette JS using palette.sendInfoToHTML(action, data).
While developing, I've encountered a persistent issue where communication from Python to JavaScript appears to be broken or unreliable. Messages sent using palette.sendInfoToHTML from Python are logged as completing successfully on the Python side, but the corresponding window.fusionJavaScriptHandler.handle function in JavaScript is never invoked for these messages.
Interestingly, communication from JavaScript to Python seems partially functional, although I sometimes observe duplicate events or spurious 'response' events being received by my Python HTMLEventHandler.notify method immediately after a valid event from JavaScript.
The primary symptom, however, is the failure of Python-to-JavaScript messaging, preventing UI updates, status reporting, and result display within the palette.
To isolate the issue, I created a minimal test Add-In:
Observed Behavior with Minimal Test:
Python receives the html_ready signal successfully.
Python attempts to send py_ack_ready back to JS - Python logs show sendInfoToHTML completed, but JS logs/UI never show receipt of py_ack_ready.
Clicking "Send PING" successfully sends ping from JS to Python. Python logs receipt.
Python attempts to send pong back to JS - Python logs show sendInfoToHTML completed, but JS logs/UI never show receipt of pong.
Could this indicate a potential issue with the palette.sendInfoToHTML -> window.fusionJavaScriptHandler.handle communication bridge in this version of Fusion 360 ( installed March 25 2025 ) on Mac?
Thank you for any assistance.
<!DOCTYPE html>
<html>
<head><title>Minimal Test</title></head>
<body>
<h3>Minimal Test</h3>
<button id="pingButton">Send PING</button>
<p>Status: <span id="status">Loading...</span></p>
<p>Py Response: <span id="response">N/A</span></p>
<script>
const statusElem = document.getElementById('status');
const responseElem = document.getElementById('response');
function logJs(msg) { console.log(`JS: ${msg}`); statusElem.textContent = msg; }
if (window.fusionJavaScriptHandler) {
if (!window.fusionJavaScriptHandler.isMinimalHandlerAssigned_XYZ) {
logJs("Assigning JS handler...");
window.fusionJavaScriptHandler.handle = function (action, dataStr) {
logJs(`Handler called! Action: ${action}`); // *** THIS LOG IS NOT APPEARING for 'pong' or 'py_ack_ready' ***
responseElem.textContent = `Action=${action}, Data=${dataStr}`;
};
window.fusionJavaScriptHandler.isMinimalHandlerAssigned_XYZ = true;
logJs("JS Handler assigned.");
try {
logJs("Sending 'html_ready'...");
window.adsk.fusionSendData('html_ready', '{}'); // Signal Python
logJs("'html_ready' sent.");
} catch (e) { logJs("Error sending 'html_ready'"); console.error(e); }
}
} else { logJs("CRITICAL: fusionJavaScriptHandler not found!"); }
document.getElementById('pingButton').addEventListener('click', () => {
logJs("Ping clicked. Sending 'ping'...");
try {
window.adsk.fusionSendData('ping', '{}');
logJs("'ping' sent.");
} catch (e) { logJs("Error sending 'ping'"); console.error(e); }
});
logJs("Script loaded.");
</script>
</body>
</html>
##################### PYTHON STARTS HERE #############
import adsk.core
import adsk.fusion
import traceback
import json
_app = None
_ui = None
_handlers = []
_palette = None
_palette_id = 'minimal'
_html_is_ready = False
def log_py(message): print(f"PY LOG: {message}")
def send_to_js_minimal(action, data={}):
global _palette, _html_is_ready
if not _html_is_ready:
log_py(f"PY: HTML not ready, discarding {action}")
return
if _palette:
try:
payload_str = json.dumps(data)
log_py(f"PY: Attempting sendInfoToHTML: Action='{action}'")
_palette.sendInfoToHTML(action, payload_str)
log_py(f"PY: sendInfoToHTML completed for '{action}'.")
except Exception as e:
log_py(f"PY ERROR sending '{action}': {e}")
log_py(traceback.format_exc())
else: log_py("PY ERROR: Palette object lost.")
class MinimalHTMLEventHandler(adsk.core.HTMLEventHandler):
def __init__(self): super().__init__()
def notify(self, args: adsk.core.HTMLEventArgs):
global _html_is_ready
action = "ERR"; data = "ERR"
try:
action = args.action; data = args.data
log_py(f"PY Received: Action='{action}', Data='{data[:50]}...'")
if action == 'html_ready':
log_py("PY: HTML is ready!")
_html_is_ready = True
# Attempt to send an immediate response after ready
send_to_js_minimal('py_ack_ready', {'status': 'Python Acknowledged Ready'})
return
if action == 'ping':
log_py("PY: Received PING, sending PONG...")
send_to_js_minimal('pong', {'reply': 'PONG from Python'})
# Ignore spurious response if it occurs
if action == 'response':
log_py("PY: Ignoring spurious 'response'.")
return
except Exception as e:
log_py(f"PY ERROR in notify for action '{action}': {e}")
log_py(traceback.format_exc())
def run(context):
global _ui, _app, _palette, _handlers, _html_is_ready
try:
_html_is_ready = False # Reset flag
_app = adsk.core.Application.get(); _ui = _app.userInterface; _handlers = []
log_py("Minimal Add-In Starting...")
palettes = _ui.palettes; _palette = palettes.itemById(_palette_id)
if _palette: _palette.deleteMe(); _palette = None # Force recreation
_palette = palettes.add(_palette_id, 'Minimal Test', 'resources/test_palette.html', True, True, True, 300, 200)
if not _palette: log_py("PY ERROR: Failed to create palette."); return
onHTMLEvent = MinimalHTMLEventHandler(); _palette.incomingFromHTML.add(onHTMLEvent); _handlers.append(onHTMLEvent)
_palette.isVisible = True
log_py("Minimal Add-In Running. Waiting for html_ready...")
except Exception as e: log_py(f"PY ERROR during run: {e}"); log_py(traceback.format_exc())
def stop(context):
global _ui, _handlers, _palette, _palette_id, _html_is_ready
log_py("Minimal Add-In Stopping...")
_html_is_ready = False
if _ui:
palette = _ui.palettes.itemById(_palette_id)
if palette: palette.deleteMe()
_handlers = []; _palette = None; log_py("Minimal Add-In Stopped.")