PlotlyJS.jl
PlotlyJS.jl copied to clipboard
The State of PlotlyJS + WebIO/JupyterLab
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.
- Easy: Define a global PlotlyJS object to define commands on.
- Hard(er): Figure out a good API for WebIO to forward "libraries" over to the client.
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.
Thank you for posting this here. I would be totally happy to define a global object and add my extra js routines there.
How about having a command registry that is global?
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.
- There is a possibility for naming conflicts. For example, two packages could define a
plotmethod; this is somewhat solved by having namespaces (à laCommandRegistry.Plotly) but still an issue (at least in principle). - There's a possibility for version conflicts. If I have a Julia 0.6 notebook in one pane (it just got EOL'd, I know, but not everything is updated yet!) and a Julia 1.1 notebook in a different pane, each using PlotlyJS, there's a decent chance that they'd have different commands defined but in the same namespace.
- It encourages global state which could lead to the kind of cross-talk we're trying to avoid.
- It requires code to make assumptions about the environment it will be running in (e.g. the code assumes that
window.CommandRegistry.PlotlyJS.scatteris defined instead of having an immediate handle to the correct method).
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).
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.
Or actually we can just have PlotlyJS tach on a bunch of commands, such as:
this.CommandSets.Plotly = {command1: ...}
inside onimport
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!
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.
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.
That might be easier than trying to inject/attach it. I could just have my few functions be another dependency like plotly.js
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?