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
Line 3 in 6ac8211
so that it clears p.config
before stringifying it, but perhaps there's a less-hacky way to achieve this.
This can be resolved by adjusting this method: https://github.com/sglyon/PlotlyBase.jl/blob/eca99a6f8b2fe84ab7a1413781453bf1229ec820/src/PlotlyBase.jl#L160
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 theJSON2.write(io, ::Plot)
method defined inPlotlyBase@v0.8.5
. In particular,dcc_graph
inDash@v0.1.6
is broken with all versions ofPlotlyBase
exceptv0.8.5
. Users do not needPlotlyBase
to createdcc_graph
components, but I suspect many users do usescatter
,Layout
and friends. Since peer dependencies aren't a thing in julia, this might lead to headaches. PlotlyBase@v0.8.5
assumes thatJSON2.write
is used only byDash
users. Stringified outputs ofJSON
vsJSON2
(andJSON2
vsJSON3
for that matter) are not the same. This might surprised users that e.g. want to write aPlot
struct to a json file usingJSON2
outside ofDash
.
So, what if instead:
PlotlyBase
definesJSON2.write
as before sglyon/PlotlyBase.jl#65 simply as:
@require JSON2="2535ab7d-5cd8-5a07-80ac-9b1792aadce3" JSON2.write(io::IO, p::Plot) = JSON.print(io, p)
- and then
Dash
would once again depend onPlotlyBase
to creates its own "to-json" method in whichPlotlyBase.Plot
structs would get their:config
field dropped.
Cheers and have a nice weekend 🍻