JuliaPlots / PlotlyJS.jl

Julia library for plotting with plotly.js
Other
427 stars 78 forks source link

The State of PlotlyJS + WebIO/JupyterLab #268

Open twavv opened 5 years ago

twavv commented 5 years ago

PlotlyJS tries to access the global WebIO object - but it can't since that's not defined in JupyterLab (because JupyterLab opens multiple notebooks in one Javascript context and doing so leads to "cross-talk").

This works (everywhere, but notably in JupyterLab):

trace1 = scatter(x=1:4, y=[10, 15, 13, 17], mode="markers")
plot([trace1])

This does NOT work (in JupyterLab):

using Interact

@manipulate for m in [1, 2, 3]
    x = range(1, stop=5, length=5)
    trace1 = scatter(x=x, y=m*x, mode="markers")
    plot([trace1])
end

There are two ways to fix this.

We've discussed the latter API (in part of a discussion about making RPC's from JS into Julia) here (second comment). I'm not sure what the timeframe is on that though; we (well mostly @shashi at this point) are currently trying to implement request-response messaging which would be a pre-req.

sglyon commented 5 years ago

Thank you for posting this here. I would be totally happy to define a global object and add my extra js routines there.

shashi commented 5 years ago

How about having a command registry that is global?

twavv commented 5 years ago

How about having a command registry that is global?

As in like window.CommandRegistry? I don't think that's a great idea, personally, because global variables are bad™. I can think of a few cons.

Additionally, this kind of thing can be hacked without using globals provided by WebIO (e.g. if you really want to, define window.PlotlyJSCommands with all your commands on it; it still has most of the issues described above but at least it's not prescribed by library code).

shashi commented 5 years ago

In that case, how about allowing a mechanism to inject a bunch of functions as commands? We'll need some kind of way for PlotlyJS to expose a function that does that?

function PlotlyStuff(webioInstance) {
   this.WebIOCommandSet = true;
   ...

   webioInstance.command1 = function () {..} // etc
}

module.exports = PlotlyStuff

when we load a js asset, we check to see if the object it exports has this.WebIOCommandSet, if yes, we call it with the webIO instance... This will be per-scope however.

shashi commented 5 years ago

Or actually we can just have PlotlyJS tach on a bunch of commands, such as:

this.CommandSets.Plotly = {command1: ...}

inside onimport

sglyon commented 5 years ago

I don't know implications of all the different options and am happy to defer to @travigd and @shashi expertise here.

lol in other words I am happy to do whatever I'm told, as long as I can access my functions!

twavv commented 5 years ago

This conversation should probably be had at https://github.com/JuliaGizmos/WebIO.jl/issues/229.

But since I'm here, I think (once that commit is fully implemented) the idiomatic way to do things would simply be

Plotly = JSAsset("...")

w = Scope(...)
onjs(w["image"], @js function(value)
    $w["image"] = @await (@await Plotly).toImage(...)
end)

# equivalently
w = Scope(...)
onjs(w["image"], @js function(value)
    @var Plotly = @await $Plotly
    $w["image"] = @await Plotly.toImage(...)
end)

I'm still definitely not a fan of having global (either global across everything or just global with respect to a single WebIO instance) command sets.

shashi commented 5 years ago

In that case I guess we can just delete the CommandSets feature, and allow PlotlyJS.jl itself to simply load its own JS module and use it.

sglyon commented 5 years ago

That might be easier than trying to inject/attach it. I could just have my few functions be another dependency like plotly.js

pfarndt commented 5 years ago

Now that JupyterLab 1.0 is out more people will want to switch from Jupyter notebook to lab (me included). Is there something new on this issue?