Literate.jl
Literate.jl copied to clipboard
How would I capture PlotlyJS.jl figures as HTML?
I'm currently setup to convert a .jl script to markdown using Literate.markdown
for inclusion as generated pages in Documenter. But the captured output gives the figures a random number name and thus Plots.jl defaults to saving the figure as a PNG instead of HTML. Is this avoidable?
My basic script is here https://github.com/JuliaApproximation/FastTransforms.jl/blob/master/examples/disk.jl
The docs build is here https://github.com/JuliaApproximation/FastTransforms.jl/blob/master/docs/make.jl
Does Documenter handle this? In that case, perhaps try with execute=false
here: https://github.com/JuliaApproximation/FastTransforms.jl/blob/04008f6e863c7879e03b2bf345f0b8ec4023c6c2/docs/make.jl#L20 instead?
Currently, markdown execution only handles image/png
and image/jpeg
, see https://github.com/fredrikekre/Literate.jl/blob/5db8c22fcfe74470d1ab836d566afbc8a9e86d9f/src/Literate.jl#L481.
Ok so this is a feature request (for the whole package chain)?
Probably, but I thought it worked in Documenter already, but maybe I misremember. I think https://github.com/JuliaDocs/Documenter.jl/issues/1247 is causing some trouble for including the necessary js library.
Thanks for the link! I thought PloylyJS's docs used HTML plots a while back, but it appears they're PNG now. Perhaps this is related.
Maybe Literate could produce a raw block for documenter?
https://github.com/JuliaDocs/Documenter.jl/issues/1149
So my hack is to add commented raw html and use your post processing https://github.com/JuliaApproximation/FastTransforms.jl/commit/ecc0e592beabcdf32074468887c472c1d6263bb3 results in https://juliaapproximation.github.io/FastTransforms.jl/dev/generated/disk/
Okay, using <object>
is a nice idea. I developed that idea a bit further, and if you put this in docs/make.jl
import Plots
struct HTMLPlot
p # :: Plots.Plot
end
const ROOT_DIR = joinpath(@__DIR__, "build")
const PLOT_DIR = joinpath(ROOT_DIR, "plots")
function Base.show(io::IO, ::MIME"text/html", p::HTMLPlot)
mkpath(PLOT_DIR)
path = joinpath(PLOT_DIR, string(hash(p) % UInt32, ".html"))
Plots.savefig(p.p, path)
print(io, "<object type=\"text/html\" data=\"../$(relpath(path, ROOT_DIR))\" style=\"width:100%;height:600px;\"></object>")
end
you can just return HTMLPlot(p)
from your blocks, like this:
```@example test
import Plots, PlotlyJS
Plots.plotlyjs()
x = range(0, 2pi, length=100)
y = sin.(x)
p = Plots.plot(x, y)
Main.HTMLPlot(p)
```
I thought the point of using Literate was that the .jl scripts for, e.g. a repository's examples, are still standalone. Wouldn't Main.HTMLPlot(p)
throw an error unless one is building the docs (or loaded docs/make.jl
)?
Perhaps a more seamless solution would be if Literate.markdown
supported another keyword, say plot_extension = :html / :png / :svg / ...
and then execute_markdown!
would depend more specifically on the extension, with the guts of the show
method above for :html
.
Also, in the object type, the height seems to make an appreciable difference (I switched back to default size with height 400px for better mobile support), since it's a fixed pixel size. Given a plot p
, the object height could be specified by taking the plot's height as size(p.o)[2]
or p.attr[:size][2]
.
Okay, using
<object>
is a nice idea. I developed that idea a bit further, and if you put this indocs/make.jl
import Plots struct HTMLPlot p # :: Plots.Plot end const ROOT_DIR = joinpath(@__DIR__, "build") const PLOT_DIR = joinpath(ROOT_DIR, "plots") function Base.show(io::IO, ::MIME"text/html", p::HTMLPlot) mkpath(PLOT_DIR) path = joinpath(PLOT_DIR, string(hash(p) % UInt32, ".html")) Plots.savefig(p.p, path) print(io, "<object type=\"text/html\" data=\"../$(relpath(path, ROOT_DIR))\" style=\"width:100%;height:600px;\"></object>") end
you can just return
HTMLPlot(p)
from your blocks, like this:```@example test import Plots, PlotlyJS Plots.plotlyjs() x = range(0, 2pi, length=100) y = sin.(x) p = Plots.plot(x, y) Main.HTMLPlot(p)
Thanks @fredrikekre! Works like a charm! Just FYI, when using prettyurl in the CI framework, I had to make the following modification:
function Base.show(io::IO, ::MIME"text/html", p::HTMLPlot)
mkpath(PLOT_DIR)
path = joinpath(PLOT_DIR, string(hash(p) % UInt32, ".html"))
Plots.savefig(p.p, path)
if get(ENV, "CI", "false") == "true" # for prettyurl
print(io, "<object type=\"text/html\" data=\"../../$(relpath(path, ROOT_DIR))\" style=\"width:100%;height:425px;\"></object>")
else
print(io, "<object type=\"text/html\" data=\"../$(relpath(path, ROOT_DIR))\" style=\"width:100%;height:425px;\"></object>")
end
end
I have another question regarding PlotlyJS though. Does anyone have an idea how to get them working in Jupyter notebooks?
On my own computer I got them to work with this extension, i.e. by executing this command
jupyter labextension install jupyterlab-plotly
But I am puzzled how to incorporate that in the make.jl or somewhere so that a) the precomputed notebooks (executed by the GitHub action) display the figures and b) binder knows how to handle these plots. Neither is working right now.
@fredrikekre, do you have yet another trick up your sleeve? Thank a ton for your help in advance!
No idea, sorry.