PlotlyJS.jl icon indicating copy to clipboard operation
PlotlyJS.jl copied to clipboard

Options provided to plot(..., options=Dict) arent recognized when calling savefig

Open jwintersinger opened this issue 4 years ago • 8 comments
trafficstars

Hi! The options specified in P = plot(rand(10, 4), options=Dict(:staticPlot => true)) aren't recognized when I call savefig(P, "out.html") and view the resulting HTML file in my browser, as the plot isn't static. (This is the minimal example given in PlotlyJS.jl/docs/src/syncplots.md.)

Here's a more full example taken from #326:

using PlotlyJS

trace1 = scatter(
    x=[0, 1, 2, 3, 4, 5, 6],
    y= [1, 9, 4, 7, 5, 2, 4],
    mode= "markers",
    marker_size = [20, 40, 25, 10, 60, 90, 30]
)

layout = Layout(;
    title= "Download Chart as SVG instead of PNG",
    showlegend= false
);

config = Dict(
  :toImageButtonOptions => Dict(
    :format => "svg",
    :filename => "custom_image",
    :height => 500,
    :width => 700,
    :scale => 1 # Multiply title/legend/axis/canvas sizes by this factor
  )
);
P = plot(trace1,layout;options=config)
savefig(P, "out.html")

When I view out.html in my browser, the "download image" button still gives a PNG with the default dimensions, not an SVG. The options I specified are not present in the out.html source. The problem occurs for me on Julia 1.5.3 with PlotlyJS.jl v0.14.0 or the master HEAD.

Thanks for any help you can provide!

jwintersinger avatar Feb 23 '21 13:02 jwintersinger

Plotlyjs.jl, savefig is almost the equivalent of the Python function plotly.io.write_image(), that doesn't save a html file. I say almost, because savefig saves all image formats, as well as html. Instead, Python version has a special function for saving the plot in html:

plotly.io.write_html(fig, "filename.html",  config=config)

It saves the plot with the config settings (I checked it).

PlotlyJS.jl savefig has the same paramters as plotly.io.write_image():

?savefig
savefig(
    io::IO,
    p::Plot;
    width::Union{Nothing,Int}=nothing,
    height::Union{Nothing,Int}=nothing,
    scale::Union{Nothing,Real}=nothing,
    format::String="png"
)

But

help(plotly.io.write_html) 

lists:

Help on function write_html in module plotly.io._html:

write_html(fig, file, config=None, auto_play=True, include_plotlyjs=True, include_mathjax=False, post_script=None, full_html=True, animation_opts=None, validate=True, default_width='100%', default_height='100%', auto_open=False)
    Write a figure to an HTML file representation
    
    Parameters
    ----------
    fig:
        Figure object or dict representing a figure
    file: str or writeable
        A string representing a local file path or a writeable object
        (e.g. an open file descriptor)
    config: dict or None (default None)
        Plotly.js figure config options
    auto_play: bool (default=True)
        Whether to automatically start the animation sequence on page load
        if the figure contains frames. Has no effect if the figure does not
        contain frames.
    include_plotlyjs: bool or string (default True)
        Specifies how the plotly.js library is included/loaded in the output
        div string.

I think that saving a plot with config settings is not implemented in PlotlyJS.jl. @sglyon can confirm this?

empet avatar Feb 23 '21 18:02 empet

A closely related problem, so I didn't open a new issue for it: the size keyword argument is also not recognised when calling savefig.

Minimal example:

plt = plot( 1:5, size=(1600,900) )
savefig( plt, "test.svg" )

The resulting SVG file has width="700" and height="500" instead of 1600 and 900.

This occurs in Julia v1.6.2, with Plots v1.21.3 and PlotlyJS v0.18.7.

jvkerckh avatar Sep 07 '21 13:09 jvkerckh

@jvkerckh There is no size keyword in the trace definition or Layout definition. It's width and/or height that must be set.

Just run these lines, and see the difference:

plt = plot( scatter(y=1:5, Layout(width=750, height=600)) 
savefig( plt, "test1.svg", width=600, height=300, scale=1 )#although the width/height is set in layout, 
                                                                                            #    the image is saved with these new   sizes

plt = plot( scatter(y=1:5) )#  width and height  for displaying plot are  the default values here
savefig( plt, "test2.svg", width=1000, height=800, scale=1 ) # the image is saved with 1000 x 800 

empet avatar Sep 07 '21 15:09 empet

Thanks @empet good help!

There is a pretty recent function called to_html that implements more of the options from Python's write_html:

help?> PlotlyBase.to_html
  to_html(
      io::IO,
      p::Plot;
      autoplay::Bool=true,
      include_plotlyjs::Union{String,Missing}="cdn",
      include_mathjax::Union{String,Missing}="cdn",
      post_script::Union{String,Missing}=missing,
      full_html::Bool=true,
      animation_opts::Union{Dict,Missing}=missing,
      default_width::String="100%",
      default_height::String="100%"
  )

    •  io: IO stream to write to

    •  p: Plot to save

    •  autoplay: Should animations start automatically

    •  include_plotlyjs: How to include plotly.js. Options are
       • cdn: include a <script> tag to load plotly.js from cdn. Output will be standalone
       • require: load using requirejs. Useful in Jupyter notebooks
       • require-loaded: assume a plotly statement has already loaded via requirejs (don't load it in context
       of this plot)
       • directory: hardcode <script src="plotly.min.js> – will only work when the plotly.min.js file is in
       the same directory as the output file
       • anything ending in js: we assume you give us the path to the plotly.js file. We will read it in and
       include it inline in the output. Works best when points to a minified file (plotly.min.js)

    •  include_mathjax: How mathjax should be included. Options are
       • string ending in .js: we load via <script src="$(include_mathjax)">. You are responsible for making
       sure it resolves
       • anything else: we load via cdn for you

    •  post_script: arbitrary javascript to run after plotly.js finishes drawing the plot

    •  full_html: include all parts necessary for standalone html file

    •  animation_opts: extra options used to control animation. included in addFrames call after the actual frames.
       See plotly.js docs for more info on addFrames

    •  default_width: valid css specifier for width

    •  default_height: valid css specifier for height


sglyon avatar Sep 08 '21 01:09 sglyon

I am not sure, if I use it correctly. Here my snippet:

using PlotlyJS
hdl_plt = PlotlyJS.Plot([1,2,3])
PlotlyJS.savefig(hdl_plt, raw"c:\tmp\plt\online_html.html")
# --- include a script tag containing the plotly.js source code (~3MB) ---
open(raw"c:\tmp\plt\offline_html.html", "w") do io
    PlotlyJS.PlotlyBase.to_html(io, hdl_plt, include_plotlyjs = "cdn")
end

The strange thing is, both html-files have the identical size.

StefanPofahl avatar Dec 22 '22 09:12 StefanPofahl

They are identical because you did not set width and height. For the first version it saves the file with the default settlngs (width=600, height=450, for example, has no effect). But this one works:

open(raw"c:\tmp\plt\offline_html.html", "w") do io
    PlotlyJS.PlotlyBase.to_html(io, hdl_plt, include_plotlyjs = "cdn", default_width="75%", default_height="60%")
end

empet avatar Dec 22 '22 10:12 empet

Thanks for your reply! Sorry, I mean the file-size in kB. I saw here that the file-size will grow approx 3MB

StefanPofahl avatar Dec 22 '22 16:12 StefanPofahl

via PyCall I can generate an offline ready HTML:

    using PyCall
    go = PyCall.pyimport("plotly.graph_objects")
    # ---
    fig = go.Figure()
    fig.add_trace(go.Scatter(y=[1,2,3]))
    # fig.show()
    fig.write_html(raw"c:\tmp\plt\test_offline.html")
    fig.write_html(raw"c:\tmp\plt\test_online.html", include_plotlyjs = false)

StefanPofahl avatar Dec 23 '22 16:12 StefanPofahl