plotly / Dash.jl

Dash for Julia - A Julia interface to the Dash ecosystem for creating analytic web applications in Julia. No JavaScript required.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Graphs generated by Plotly don't work

roland-KA opened this issue · comments

I've tried the Iris scatter plot example (section "More About Visualization") from the Dash for Julia User Guide, where a scatter plot is generated using Plot from PlotlyBase:

iris = dataset("datasets", "iris")

p1 = Plot(iris, x=:SepalLength, y=:SepalWidth, mode="markers", marker_size=8, group=:Species)

app = dash()

app.layout = html_div() do
    html_h4("Iris Sepal Length vs Sepal Width"),
    dcc_graph(
        id = "example-graph-3",
        figure = p1,
    )
end

But the scatter plot didn't show. Instead Dash reported a "Failed component prop type":

Failed component prop type: Invalid component prop `figure` key `config` supplied to Graph.

    Bad object: {
      "layout": {
        "xaxis": {
          "title": {
            "text": "SepalLength"
          }
        },
        "legend": {
          "tracegroupgap": 0
        },
    ...

Valid keys: [
  "data",
  "layout",
  "frames"
]

How can I get that running?

I'm hitting this problem as well.

The JSON2.write result now contains a config container:

using PlotlyBase, Dash
using JSON, JSON2

fig = Plot(GenericTrace[], Layout())

io = IOBuffer()
write(io, JSON.json(fig))
str = String(take!(io))

# or
io2 = IOBuffer()
JSON2.write(io2, fig)
str2 = String(take!(io2))

@assert str == str2
@assert contains(str, "\"config\":{\"showLink\":false,\"editable\":false,\"responsive\":true,\"staticPlot\":false,\"scrollZoom\":true}")

which wasn't the case in Dash@v0.1.4


I'm thinking of patching

JSON2.write(io::IO, p::PlotlyBase.Plot; kwargs...) = write(io, JSON.json(p))

so that it clears p.config before stringifying it, but perhaps there's a less-hacky way to achieve this.

Thanks for the quick PR @sglyon !!

I wonder if a similar patch in this repo in src/Front.jl

--- a/src/Front.jl
+++ b/src/Front.jl
@@ -1,3 +1,7 @@
 import JSON, JSON2, PlotlyBase

-JSON2.write(io::IO, p::PlotlyBase.Plot; kwargs...) = write(io, JSON.json(p))
+function JSON2.write(io::IO, p::PlotlyBase.Plot; kwargs...)
+       dict = JSON.lower(p)
+       pop!(dict, :config, nothing)
+       JSON.print(io, dict)
+end

would be better?

Generally, there's nothing wrong with stringifying config from Plot. In fact, plotly.js accepts plot(gd, {data:[], layout:{}, config:{}) as a valid signature.

To me, the problem is specific to Dash where it wants to standardize config as a dcc_graph kwarg side-by-side with figure = {data:[], layout:{}, frames:[]}.

Hmm, I put it in PlotlyBase to avoid "type piracy" where Dash.jl defines a method from JSON2 on a type from PlotlyBase. Things get weird if JSON2.write suddenly changes behavior before and after Dash.jl is loaded

I think I'm ok leaving it in PlotlyBase as the "standard" JSON library over there is JSON.jl not JSON2.jl. So, by default users will get the full object with config

@sglyon If you have added JSON2 overload to PlotlyBase, then this is great. This means that I can remove it from the Dash altogether. Because this line in Front is firstly a type piracy, and secondly it is the only Dash dependency on PlotlyBase, so when you release a new PlotlyBase release, this dependency can be removed from Dash altogether

FYI with

  [1b08a953] Dash v0.1.5
  [2535ab7d] JSON2 v0.3.2
  [a03496cd] PlotlyBase v0.8.5

we still have

using PlotlyBase, Dash
using JSON2

fig = Plot(GenericTrace[], Layout())
io2 = IOBuffer()
JSON2.write(io2, fig)
str2 = String(take!(io2))

@assert contains(str2, "\"config\":{\"showLink\":false,\"editable\":false,\"responsive\":true,\"staticPlot\":false,\"scrollZoom\":true}")

Is this because both Dash.jl (in src/Front.jl) and PlotlyBase overload JSON2.write?

Should we now remove src/Front.jl?

Some Friday-afternoon thoughts (feel free to disregard them 😏 )

With the now merged sglyon/PlotlyBase.jl#65 that lead to PlotlyBase@v0.8.5 and assuming #119 will get merged and released as Dash@v0.1.6, I see two potential problems:

  • we have that Dash@v0.1.6 relies on the JSON2.write(io, ::Plot) method defined in PlotlyBase@v0.8.5. In particular, dcc_graph in Dash@v0.1.6 is broken with all versions of PlotlyBase except v0.8.5. Users do not need PlotlyBase to create dcc_graph components, but I suspect many users do use scatter, Layout and friends. Since peer dependencies aren't a thing in julia, this might lead to headaches.
  • PlotlyBase@v0.8.5 assumes that JSON2.write is used only by Dash users. Stringified outputs of JSON vs JSON2 (and JSON2 vs JSON3 for that matter) are not the same. This might surprised users that e.g. want to write a Plot struct to a json file using JSON2 outside of Dash.

So, what if instead:

@require JSON2="2535ab7d-5cd8-5a07-80ac-9b1792aadce3" JSON2.write(io::IO, p::Plot) = JSON.print(io, p)
  • and then Dash would once again depend on PlotlyBase to creates its own "to-json" method in which PlotlyBase.Plot structs would get their :config field dropped.

Cheers and have a nice weekend 🍻