Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Can't get Javascript add-in to communicate with palette

9 REPLIES 9
SOLVED
Reply
Message 1 of 10
Anonymous
946 Views, 9 Replies

Can't get Javascript add-in to communicate with palette

Hello!

 

I am developing a Javascript add-in with an accompanying HTML palette. I am currently trying to get the back and forth communication set up, but I can't get it to work in either direction.

 

When I try to send info to the add-in from the palette, Fusion freezes completely and I have to close it with the task manager. This only happens when the add-in has attached a listener to the receive event via "palette.incomingFromHTML.add(onHtmlReceiveEvent);" and the palette attempts to send via "adsk.fusionSendData();" (with a button click). At the moment, the callback does nothing but display a message box.

 

When I try to send info to the palette from the add-in, I get a "InternalValidationError: !response.empty()" error. I know that when add-ins send to the palette, it expects a string to be returned in response, but I still get this error whether I return something or not. The palette does load jQuery and a few other plugins (locally) so in order to eliminate the possibly that Fusion is sending before the palette has had a chance to attach the handler, I delayed "palette.sendInfoToHTML()" with setTimeout(). These stops the !response.empty() error, but if the timeout is more than 100-150 ms, Fusion freezes up.

 

have successfully duplicated the add-in/palette example given here, but that is a Python sample. The docs don't provide full samples for Javascript, and there are none elsewhere online. I've stripped everything down to the bare essentials to no successs so I'm at a total loss. I fear it may be an internal problem.

 

 

Only relevant parts of my code are below. When the add-in is run, it sets up a command and then the palette. The palette itself does load jQuery and a few other plugins.

 

Thanks in advance for any assistance!

- A. Babin

 

Javascript Add-in (Relevant code only)

 

var makePalette = function(args) {
    try {
	if (ui) {
	    var palette = ui.palettes.itemById(paletteId);

	    if (!palette) {
	        palette = ui.palettes.add(paletteId, 'Param Master', paletteHtmlLocation, true, true, true, 300, 200);
		palette.dockingState = 3;

		palette.closed.add(OnClosePalette);

	    } else {
		palette.isVisible = true;
    	    }
		palette.incomingFromHTML.add(onHtmlReceiveEvent);
	    }
    } catch (e) {
	ui.messageBox('Failed to execute command : ' + (e.description ? e.description : e));
    }
}

var OnCommandCreated = function(args) {
    try {

        //Set up and show palette.
        makePalette();

//Attempt to delay add-in to palette comm. //setTimeout(function() { // palette.sendInfoToHTML('send', "Hello?"); //}, 3000); } catch (e) { ui.messageBox('Failed to execute command : ' + (e.description ? e.description : e)); } } var onHtmlReceiveEvent = function(args) { eventArgs = adsk.core.HTMLEventArgs(args); ui.messageBox("RECEIVED"); }

Palette (Relevant code only)

 

 

function sendInfoToFusion(){
    adsk.fusionSendData('send', "Hello");
}

window.fusionJavaScriptHandler = {handle: function(action, data){
	return "OK";
}};

 

9 REPLIES 9
Message 2 of 10
Anonymous
in reply to: Anonymous

Strange... I left one of the Fusion windows that froze after calling sendInfoToHTML() open for about an hour or so, and after eventually coming back to it, I found that it had unfrozen and the communication had gone through as intended. I am now waiting to see if the same happens for the incomingFromHTML event. 

 

EDIT: I can confirm, the event eventually goes through after 20-30 minutes! 

Message 3 of 10
Anonymous
in reply to: Anonymous

I've done some testing with the official Python example I mentioned earlier. 

 

The only things I've changed is the add-in HTMLEventHandler and the palette sendInfoToFusion() function.

 

Add-in

 

class MyHTMLEventHandler(adsk.core.HTMLEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            palette = _ui.palettes.itemById('myPalette')
            if palette:
                htmlArgs = adsk.core.HTMLEventArgs.cast(args)            
                data = json.loads(htmlArgs.data)
                _ui.messageBox('Start')
                for i in range(len(data['expressions'])):
                    _ui.messageBox(data['expressions'][i])
                palette.sendInfoToHTML('send', 'Hello')
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc())) 

Palette:

 

function sendInfoToFusion(){
            /*var args = {
                arg1 : "Sample argument 1",
                arg2 : "Sample argument 2"
            };*/
            var args = {
            	expressions: ['(6mm + 3mm)', '(4mm + 3mmHg)']
            };
            adsk.fusionSendData('send', JSON.stringify(args));
        }

 

 

The expected behavior is that the button in the palette is clicked, it sends an array of strings to the add-in, the add-in iterates through, displaying each string in a message box, and then sends a string back to the palette to display in a <p> element.

 

Everything works fine until the add-in is supposed to send the string back to the palette, at which point I get the same locking-up behavior I get with the Javascript add-in (it's been an hour though and it hasn't un-locked, however). It is especially confusing that the command added by the example to send info to the palette works correctly, and I've copied it exactly. The only difference is that it's in the MyHTMLEventHandler.

 

 

Message 4 of 10
marshaltu
in reply to: Anonymous

Hello,

 

Unfortunately it is technical limitation of our HTML/JS engine that we don't support custom palette for Javascript language in Windows. I would like to recommend you use Python or C++ to create custom Palette.

 

As you may be aware, we didn't provide sample for Javascript language and also quite some events API were excluded from Javascript language with same reason. We mentioned that for event in help document and but not for Palette.

 

We will exclude the problematic APIs from Javascript language in future releases and add description for the issue.

 

Thanks,

Marshal

 

http://help.autodesk.com/view/fusion360/ENU/?guid=GUID-BC156A40-9E14-4C25-828D-764F087FD26C

 

"There are currently some limitations with events and JavaScript so there are some events that are not supported when working with JavaScript. These are identified in the documentation for that event."



Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Message 5 of 10
Anonymous
in reply to: marshaltu

Nowhere in the docs that I can find does it say that Javascript add-ins don't support the events necessary to actually make palettes worth using. 

 

It's not in the Javascript Specific Issues page.

 

It's not on the Palette.incomingFromHTML Event page (which, itself, has a Javascript example).

 

And I get a 404 error for the HTMLEvent and HTMLEventHandler pages.

 

I wasted at least 12 hours this weekend trying to get this work and on a Javascript add-in that I'll now have to completely rewrite in another language... so thanks for that. I still can't even get it to work reliably with Python (see my second reply). 

Message 6 of 10
marshaltu
in reply to: Anonymous

Hello,

 

Sorry for the trouble.

 

For Python issue you encountered, it was because of the line marked as "red". The root cause was the same with the one you encountered in Javascript language. I can explain a little bit about technical details of the issue.

 

When we call from HTML to Fusion by our JS engine, it waits for the return from Fusion. But if we call from Fusion to HTML at that moment in the html event handler, it would wait for the return from html side. That causes the deadlock between HTML and Fusion and sometimes freezes Fusion UI. The similar workflow for events in Javascript is that we fire event from Fusion to JS/HTML and the event will be handled in JS/HTML and then JS apis would be called from JS/HTML to Fusion.

 

If you wanted to send data back to the palette which fired the HTML event, you would be able to use the "returnData" property of HTMLEvents.  Or you can make use of CustomEvent API to break the circular calling loop "Fusion->HTML->Fusion", "HTML->Fusion->HTML". Please refer to the test sample(C++) I attached.

 

Thanks,

Marshal

 

class MyHTMLEventHandler(adsk.core.HTMLEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            palette = _ui.palettes.itemById('myPalette')
            if palette:
                htmlArgs = adsk.core.HTMLEventArgs.cast(args)            
                data = json.loads(htmlArgs.data)
                _ui.messageBox('Start')
                for i in range(len(data['expressions'])):
                    _ui.messageBox(data['expressions'][i])
                #palette.sendInfoToHTML('send', 'Hello')
                args.returnData = 'Hello'.
        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <thread>

using namespace adsk::core;
using namespace adsk::fusion;

Ptr<Application> _app;
Ptr<UserInterface> _ui;
size_t num;
Ptr<Palette> _palette;
Ptr<Palette> _palette1;
Ptr<CustomEvent> customEvent;
const std::string myCustomEvent = "MyCustomEventId1";

class ThreadEventHandler : public CustomEventHandler
{
public:
	void notify(const Ptr<CustomEventArgs>& eventArgs) override
	{
		if (eventArgs)
		{
			++num;
			_palette->sendInfoToHTML("send", "This is a message sent  to the palette from page1 . It has been sent " + std::to_string(num) + " times.");
		}
	}
} onCustomEvent_;

void myThreadRun()
{
	std::this_thread::sleep_for(std::chrono::seconds(1));
	std::string additionalInfo = std::to_string(rand());
	_app->fireCustomEvent(myCustomEvent, additionalInfo);
}

void onSendToHtml()
{
	std::thread myThread(myThreadRun);
	myThread.detach();
}


// Event handler for the palette close event.
class MyCloseEventHandler : public adsk::core::UserInterfaceGeneralEventHandler
{
public:
	void notify(const Ptr<UserInterfaceGeneralEventArgs>& eventArgs) override
	{
		_ui->messageBox("Close button is clicked.");
	}

} onClose_;


// Event handler for the palette HTML event. 
class MyHTMLEventHandler : public adsk::core::HTMLEventHandler
{
public:
	void notify(const Ptr<HTMLEventArgs>& eventArgs) override
	{
		std::string action = eventArgs->action();
		if (action == "send")
		{
			std::string msg = "An event has been fired from the html to Fusion with the following data:\n";
			msg += "Command: ";
			msg += eventArgs->action();
			msg += "\n";
			msg += eventArgs->data();
			_ui->messageBox(msg);
		}
		else if (action == "showPage")
		{
			if (_palette1)
				_palette1->isVisible(true);
		}
		else if (action == "sendInfoToHtml")
		{
			//++num;
			//_palette->sendInfoToHTML("send", "This is a message sent  to the palette from page1 . It has been sent " + std::to_string(num) + " times.");
			onSendToHtml();
		}

	}

} onHTMLEvent_;


// Event handler for the commandExecuted event to show the palette.
class ShowPaletteCommandExecuteHandler : public adsk::core::CommandEventHandler
{
public:
	void notify(const Ptr<CommandEventArgs>& eventArgs) override
	{
		// Create a palette
		Ptr<Palettes> palettes = _ui->palettes();
		if (!palettes)
			return;
		_palette = palettes->itemById("myPalette");
		if (!_palette)
		{
			_palette = palettes->add("myPalette", "My Palette", "palette.html", true, true, true, 300, 200);
			if (!_palette)
				return;

			// Dock the palette to the right side of Fusion window.
			_palette->dockingState(PaletteDockStateRight);

			// Add handler to HTMLEvent of the palette
			Ptr<HTMLEvent> htmlEvent = _palette->incomingFromHTML();
			if (!htmlEvent)
				return;

			htmlEvent->add(&onHTMLEvent_);

			// Add handler to CloseEvent of the palette
			Ptr<UserInterfaceGeneralEvent> closeEvent = _palette->closed();
			if (!closeEvent)
				return;
			closeEvent->add(&onClose_);
		}
		else
			_palette->isVisible(true);

		_palette1 = palettes->itemById("myPalette1");
		if (!_palette1)
		{
			_palette1 = palettes->add("myPalette1", "My Palette1", "palette1.html", true, true, true, 300, 200);
			if (!_palette1)
				return;

			// Dock the palette to the right side of Fusion window.
			_palette1->dockingState(PaletteDockStateRight);

			// Add handler to HTMLEvent of the palette
			Ptr<HTMLEvent> htmlEvent = _palette1->incomingFromHTML();
			if (!htmlEvent)
				return;

			htmlEvent->add(&onHTMLEvent_);

			// Add handler to CloseEvent of the palette
			Ptr<UserInterfaceGeneralEvent> closeEvent = _palette1->closed();
			if (!closeEvent)
				return;
			closeEvent->add(&onClose_);

			_palette1->isVisible(false);
		}
	}
} onShowPaletteCommandExecuted_;


// Event handler for the commandCreated event.
class ShowPaletteCommandCreatedHandler : public adsk::core::CommandCreatedEventHandler
{
public:
	void notify(const Ptr<CommandCreatedEventArgs>& eventArgs) override
	{
		Ptr<Command> command = eventArgs->command();
		if (!command)
			return;
		Ptr<CommandEvent> exec = command->execute();
		if (!exec)
			return;
		exec->add(&onShowPaletteCommandExecuted_);
	}
} onShowPaletteCommandCreated_;


// Event handler for the commandExecuted event to send info to the palette.
class SendInfoCommandExecuteHandler : public adsk::core::CommandEventHandler
{
public:
	void notify(const Ptr<CommandEventArgs>& eventArgs) override
	{
		// Send information to the palette.
		Ptr<Palettes> palettes = _ui->palettes();
		if (!palettes)
			return;

		Ptr<Palette> palette = palettes->itemById("myPalette");
		if (!palette)
			return;

		++num;
		_palette1->sendInfoToHTML("send", "This is a message sent to the palette from Fusion. It has been sent " + std::to_string(num) + " times.");
	}
} onSendInfoCommandExecuted_;


// Event handler for the commandCreated event.
class SendInfoCommandCreatedHandler : public adsk::core::CommandCreatedEventHandler
{
public:
	void notify(const Ptr<CommandCreatedEventArgs>& eventArgs) override
	{
		Ptr<Command> command = eventArgs->command();
		if (!command)
			return;
		Ptr<CommandEvent> exec = command->execute();
		if (!exec)
			return;
		exec->add(&onSendInfoCommandExecuted_);
	}
} onSendInfoCommandCreated_;


extern "C" XI_EXPORT bool run(const char* context)
{
	_app = Application::get();
	if (!_app)
		return false;

	_ui = _app->userInterface();
	if (!_ui)
		return false;

	Ptr<CommandDefinitions> commandDefinitions = _ui->commandDefinitions();
	if (!commandDefinitions)
		return false;

	// Add a command that displays the panel.
	Ptr<CommandDefinition> showPaletteCmdDef = commandDefinitions->itemById("showPalette");
	if (!showPaletteCmdDef) {
		showPaletteCmdDef = commandDefinitions->addButtonDefinition("showPalette", "Show custom palette", "Show the custom palette", "");
		if (!showPaletteCmdDef)
			return false;

		// Connect to Command Created event.
		Ptr<CommandCreatedEvent> created = showPaletteCmdDef->commandCreated();
		if (!created)
			return false;
		created->add(&onShowPaletteCommandCreated_);
	}
	// Add a command under ADD-INS panel which sends information from Fusion to the palette's HTML.
	Ptr<CommandDefinition> sendInfoCmdDef = commandDefinitions->itemById("sendInfoToHTML");
	if (!sendInfoCmdDef) 
	{
		sendInfoCmdDef = commandDefinitions->addButtonDefinition("sendInfoToHTML", "Send info to Palette", "Send Info to Palette HTML", "");
		if (!sendInfoCmdDef)
			return false;

		// Connect to Command Created event.
		Ptr<CommandCreatedEvent> created = sendInfoCmdDef->commandCreated();
		if (!created)
			return false;
		created->add(&onSendInfoCommandCreated_);
	}

	// Add the command to toolbar.
	Ptr<Workspaces> workspaces = _ui->workspaces();
	if (!workspaces)
		return false;


	Ptr<ToolbarPanelList> panels = _ui->allToolbarPanels();
	if (!panels)
		return false;

	Ptr<ToolbarPanel> panel = panels->itemById("SolidScriptsAddinsPanel");
	if (!panel)
		return false;

	Ptr<ToolbarControls> controls = panel->controls();
	if (!controls)
		return false;

	Ptr<ToolbarControl> ctrl = controls->itemById("showPalette");
	if (!ctrl)
		ctrl = controls->addCommand(showPaletteCmdDef);

	ctrl = controls->itemById("sendInfoToHTML");
	if (!ctrl)
		ctrl = controls->addCommand(sendInfoCmdDef);

	customEvent = _app->registerCustomEvent(myCustomEvent);
	if (!customEvent)
		return false;
	customEvent->add(&onCustomEvent_);

	return true;
}

extern "C" XI_EXPORT bool stop(const char* context)
{
	if (_ui)
	{
		// Delete the palette created by this add-in.
		Ptr<Palettes> palettes = _ui->palettes();
		if (!palettes)
			return false;

		Ptr<Palette> palette = palettes->itemById("myPalette");
		if (palette)
			palette->deleteMe();

		if (_palette1)
			_palette1->deleteMe();

		// Delete controls and associated command definitions
		Ptr<ToolbarPanelList> panels = _ui->allToolbarPanels();
		if (!panels)
			return false;

		Ptr<ToolbarPanel> panel = panels->itemById("SolidScriptsAddinsPanel");
		if (!panel)
			return false;

		Ptr<ToolbarControls> controls = panel->controls();
		if (!controls)
			return false;

		Ptr<ToolbarControl> ctrl = controls->itemById("sendInfoToHTML");
		if (ctrl)
			ctrl->deleteMe();

		ctrl = controls->itemById("showPalette");
		if (ctrl)
			ctrl->deleteMe();

		Ptr<CommandDefinitions> commandDefinitions = _ui->commandDefinitions();
		if (!commandDefinitions)
			return false;

		Ptr<CommandDefinition> cmdDef = commandDefinitions->itemById("sendInfoToHTML");
		if (cmdDef)
			cmdDef->deleteMe();

		cmdDef = commandDefinitions->itemById("showPalette");
		if (cmdDef)
			cmdDef->deleteMe();

		customEvent->remove(&onCustomEvent_);
		_app->unregisterCustomEvent(myCustomEvent);

		_ui->messageBox("stop add-in.");
		_ui = nullptr;
	}

	return true;
}


Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Message 7 of 10
Anonymous
in reply to: marshaltu

args.returnData does what I need it to do, and I appreciate that. 

 

But once again, I never would have been able to figure that out on my own because either the docs never mention it or the pages that do are inaccessible!

 

The Event Object page lists HTMLEvent in its derived classes, but the page it links to returns a 404 error. It's not listed in the Reference docs directory, and it doesn't come up in search results. What will it take to get this fixed?

Message 8 of 10
marshaltu
in reply to: Anonymous

Hello,

 

Thank you for pointing the issue of help document out. We will fix it in next update(late May or early June). 

 

Thanks,

Marshal



Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Message 9 of 10
Anonymous
in reply to: marshaltu

HTMLEvent pages are back online in the reference docs, thank you. 

 

However, there still doesn't seem to be any mention of the aforementioned lack of support for said events with Javascript add-ins, other than the vague...

 

 There are currently some limitations with events and JavaScript so there are some events that are not supported when workign(sic) with JavaScript. These are identified in the documentation for that event.

... line on the Events page.

 

Development on my add-in in Python is going well though! Man Happy

Message 10 of 10
marshaltu
in reply to: Anonymous

Hello,

 

Thank you for the remainder. We have made the changes to exclude HTML events from Javascript language. They would be available together with the release of Fusion June update. 

 

Thanks,

Marshal



Marshal Tu
Fusion 360 Developer
Autodesk, Inc.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Technology Administrators


Autodesk Design & Make Report