Giter Club home page Giter Club logo

Comments (10)

karlwessel avatar karlwessel commented on June 22, 2024 2

If I'm understanding what's happening in https://github.com/JuliaPlots/PlotlyJS.jl/blob/562082f9a584a22bd9bda42dc0402640b5e8f611/src/kaleido.jl#L86-L97, it should be possible to define a generic savefig function (in a new package) such that we wouldn't need to depend on it. At its core, Kaleido just needs a JSON-formatted string?

Seems like it.

The following code works to save figures from PlotlyLight.

import PlotlyJS
import JSON
using Base64
function savefig(
		p;
		width::Union{Nothing,Int}=nothing,
		height::Union{Nothing,Int}=nothing,
		scale::Union{Nothing,Real}=nothing,
		format::String="png"
	)::Vector{UInt8}

	# construct payload
	_get(x, def) = x === nothing ? def : x
	payload = Dict(
		:width => _get(width, 700),
		:height => _get(height, 500),
		:scale => _get(scale, 1),
		:format => format,
		:data => p
	)

	PlotlyJS._ensure_kaleido_running()

	# convert payload to vector of bytes
	bytes = transcode(UInt8, JSON.json(payload))
	write(PlotlyJS.P.stdin, bytes)
	write(PlotlyJS.P.stdin, transcode(UInt8, "\n"))
	flush(PlotlyJS.P.stdin)

	# read stdout and parse to json
	res = readline(PlotlyJS.P.stdout)
	js = JSON.parse(res)

	# check error code
	code = get(js, "code", 0)
	if code != 0
		msg = get(js, "message", nothing)
		error("Transform failed with error code $code: $msg")
	end

	# get raw image
	img = String(js["result"])

	# base64 decode if needed, otherwise transcode to vector of byte
	return base64decode(img)
end

function savefig(
		p, fn::AbstractString;
		format::Union{Nothing,String}=nothing,
		width::Union{Nothing,Int}=nothing,
		height::Union{Nothing,Int}=nothing,
		scale::Union{Nothing,Real}=nothing,
	)
	ext = split(fn, ".")[end]
	if format === nothing
		format = String(ext)
	end

	open(fn, "w") do f
		savefig(f, p; format=format, scale=scale, width=width, height=height)
	end
	return fn
end

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

This is basically just a copy&pase of PlotlyJSs savefig method and uses their kaleido pipeline.

If their savefig method wouldn't restrict itself to plots of type PlotlyBase.Plot one could directly use their method to save plots from PlottlyLight.Plot.

Maybe one could just ask PlotlyJS if they could change their methods to accept any plot object, or whether one could extract their kaleido pipeline into an extra package.

from plotlylight.jl.

joshday avatar joshday commented on June 22, 2024 2

This will be solved by a package in the near future! JuliaRegistries/General#67704

from plotlylight.jl.

joshday avatar joshday commented on June 22, 2024 1

Given the "light" aspect of PlotlyLight, I'd like to avoid a binary dependency like Kaleido if I can. I'll need to think through how to make this possible, and I'm happy to take a co-author credit in exchange for implementing it 😄

from plotlylight.jl.

KwatMDPhD avatar KwatMDPhD commented on June 22, 2024

We are trying to publish a bioinformatics software called GSEA. We binarize the final product. PlotlyLight has been a great alternative for PlotlyJS.jl. However, we need the ability to save a figure to publications. Our users should be able to run this software, save high-quality figures, and use them for their papers. I was wondering if it is possible to add the ability to export static images from PlotlyLight.jl. It would be amazing. Also, if you'd like, I can talk to my team about including you as a co-author :)

from plotlylight.jl.

KwatMDPhD avatar KwatMDPhD commented on June 22, 2024

There must be a JavaScript lib that saves static image. Hopefully.

I'll talk to my boss 👍

from plotlylight.jl.

disberd avatar disberd commented on June 22, 2024

If you are actually displaying the Plot inside on a browser (like in Pluto for example) I believe you can directly used the plotly downloadImage function: https://plotly.com/javascript/plotlyjs-function-reference/#plotlydownloadimage

You can create png, jpg webp and svg, which I think would suffice most outputs requirements (you could have conversion from svg to pdf outside of the plotlylight package).

from plotlylight.jl.

KwatMDPhD avatar KwatMDPhD commented on June 22, 2024

Thank you @disberd . This is great.

from plotlylight.jl.

karlwessel avatar karlwessel commented on June 22, 2024

Thanks to disberds comment I use this snipped as a workaround:

using HypertextLiteral

function saveplot(p, path, format="png")
	htl"<script>Plotly.downloadImage($(p.id), {format: $, width: 800, height: 600, filename: $path});</script>"
end

The drawbacks:

  • the result of the function has to be "displayed" by Pluto for the download to be executed
  • you can only save to you download folder

So, this is far from great, but still better than using the snapshot functionality.

from plotlylight.jl.

disberd avatar disberd commented on June 22, 2024

Both issues are a result of saving the function from the javascript side. I think the easiest to use solution would be Kaleido but as @joshday pointed out that is quite a dependency for the lightness of the package (Kaleido was also removed from PlotlyBase and moved to PlotlyJS partly also because of the burden on the loading time of the package).

For the second drawback of only being able to save within the download folder, I did use some workaround in Pluto before where I had an async julia function waiting for the file to appear in downloads before moving it to a target directory.

You can find an example within this code https://github.com/disberd/PlutoUtils.jl/blob/273e080b03074d181371a2066636571bfb06f08a/src/html2canvas_savediv.jl#L29-L71.
The code here was using html2canvas to save a generic div element rather than a Plotly plot, but the underlying concept is the same, and the julia function try_moveimage would basically be the same.

from plotlylight.jl.

joshday avatar joshday commented on June 22, 2024

If I'm understanding what's happening in https://github.com/JuliaPlots/PlotlyJS.jl/blob/562082f9a584a22bd9bda42dc0402640b5e8f611/src/kaleido.jl#L86-L97, it should be possible to define a generic savefig function (in a new package) such that we wouldn't need to depend on it. At its core, Kaleido just needs a JSON-formatted string?

from plotlylight.jl.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.