From af0dabec991ef8dab275a91f46db3ad37ce7faf4 Mon Sep 17 00:00:00 2001 From: Daniel Schwabeneder Date: Thu, 19 Jul 2018 22:33:36 +0200 Subject: [PATCH 1/2] use Requires.jl for backend dependencies --- src/Plots.jl | 1 + src/backends.jl | 189 +++++++++++++++++++++++++++-------- src/backends/glvisualize.jl | 35 +------ src/backends/gr.jl | 8 +- src/backends/hdf5.jl | 73 ++++++-------- src/backends/inspectdr.jl | 42 +++----- src/backends/pgfplots.jl | 17 ---- src/backends/plotly.jl | 57 +++++------ src/backends/plotlyjs.jl | 29 ------ src/backends/pyplot.jl | 70 ++++--------- src/backends/template.jl | 11 +- src/backends/unicodeplots.jl | 16 --- src/output.jl | 110 -------------------- src/utils.jl | 4 +- test/REQUIRE | 1 + 15 files changed, 251 insertions(+), 412 deletions(-) diff --git a/src/Plots.jl b/src/Plots.jl index d67a0381..a97d8635 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -171,6 +171,7 @@ include("arg_desc.jl") include("plotattr.jl") include("backends.jl") include("output.jl") +include("init.jl") # --------------------------------------------------------- diff --git a/src/backends.jl b/src/backends.jl index 3e1affce..aa0b5189 100644 --- a/src/backends.jl +++ b/src/backends.jl @@ -6,6 +6,8 @@ const _backendType = Dict{Symbol, DataType}(:none => NoBackend) const _backendSymbol = Dict{DataType, Symbol}(NoBackend => :none) const _backends = Symbol[] const _initialized_backends = Set{Symbol}() +const _default_backends = (:none, :gr, :plotly) +const _backendPackage = Dict{Symbol, Symbol}() "Returns a list of supported backends" backends() = _backends @@ -13,9 +15,12 @@ backends() = _backends "Returns the name of the current backend" backend_name() = CURRENT_BACKEND.sym _backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym") +backend_package(pkg::Symbol) = pkg in _default_backends ? :Plots : Symbol("Plots", _backendPackage[pkg]) +backend_package_name(sym::Symbol) = sym in _default_backends ? :Plots : _backendPackage[sym] macro init_backend(s) - str = lowercase(string(s)) + package_str = string(s) + str = lowercase(package_str) sym = Symbol(str) T = Symbol(string(s) * "Backend") esc(quote @@ -23,14 +28,16 @@ macro init_backend(s) export $sym $sym(; kw...) = (default(; kw...); backend(Symbol($str))) backend_name(::$T) = Symbol($str) + backend_package_name(pkg::$T) = backend_package_name(Symbol($str)) push!(_backends, Symbol($str)) _backendType[Symbol($str)] = $T _backendSymbol[$T] = Symbol($str) - include("backends/" * $str * ".jl") + _backendPackage[Symbol($str)] = Symbol($package_str) + # include("backends/" * $str * ".jl") end) end -include("backends/web.jl") +# include("backends/web.jl") # include("backends/supported.jl") # --------------------------------------------------------- @@ -40,7 +47,6 @@ function add_backend(pkg::Symbol) println(add_backend_string(_backend_instance(pkg))) println() end -add_backend_string(b::AbstractBackend) = warn("No custom install defined for $(backend_name(b))") # don't do anything as a default _create_backend_figure(plt::Plot) = nothing @@ -135,30 +141,30 @@ CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym)) function pickDefaultBackend() env_default = get(ENV, "PLOTS_DEFAULT_BACKEND", "") if env_default != "" - if env_default in keys(Pkg.installed()) - sym = Symbol(lowercase(env_default)) - if haskey(_backendType, sym) + sym = Symbol(lowercase(env_default)) + if sym in _backends + if sym in _initialized_backends return backend(sym) else - warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t", - join(sort(_backends), "\n\t")) + @warn("You have set `PLOTS_DEFAULT_BACKEND=$env_default` but `$(backend_package_name(sym))` is not loaded.") end else - warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not installed.") + @warn("You have set PLOTS_DEFAULT_BACKEND=$env_default but it is not a valid backend package. Choose from:\n\t", + join(sort(_backends), "\n\t")) end end # the ordering/inclusion of this package list is my semi-arbitrary guess at # which one someone will want to use if they have the package installed...accounting for # features, speed, and robustness - for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize") - if pkgstr in keys(Pkg.installed()) - return backend(Symbol(lowercase(pkgstr))) - end - end + # for pkgstr in ("GR", "PyPlot", "PlotlyJS", "PGFPlots", "UnicodePlots", "InspectDR", "GLVisualize") + # if pkgstr in keys(Pkg.installed()) + # return backend(Symbol(lowercase(pkgstr))) + # end + # end # the default if nothing else is installed - backend(:plotly) + backend(:gr) end @@ -174,24 +180,6 @@ function backend() pickDefaultBackend() end - sym = CURRENT_BACKEND.sym - if !(sym in _initialized_backends) - - # # initialize - # println("[Plots.jl] Initializing backend: ", sym) - - inst = _backend_instance(sym) - try - _initialize_backend(inst) - catch err - warn("Couldn't initialize $sym. (might need to install it?)") - add_backend(sym) - rethrow(err) - end - - push!(_initialized_backends, sym) - - end CURRENT_BACKEND.pkg end @@ -199,16 +187,29 @@ end Set the plot backend. """ function backend(pkg::AbstractBackend) - CURRENT_BACKEND.sym = backend_name(pkg) - warn_on_deprecated_backend(CURRENT_BACKEND.sym) - CURRENT_BACKEND.pkg = pkg + sym = backend_name(pkg) + if sym in _initialized_backends + CURRENT_BACKEND.sym = backend_name(pkg) + CURRENT_BACKEND.pkg = pkg + else + # try + _initialize_backend(pkg) + push!(_initialized_backends, sym) + CURRENT_BACKEND.sym = backend_name(pkg) + CURRENT_BACKEND.pkg = pkg + # catch + # add_backend(sym) + # end + end backend() end -function backend(modname::Symbol) - warn_on_deprecated_backend(modname) - CURRENT_BACKEND.sym = modname - CURRENT_BACKEND.pkg = _backend_instance(modname) +function backend(sym::Symbol) + if sym in _backends + backend(_backend_instance(sym)) + else + @warn("`:$sym` is not a supported backend.") + end backend() end @@ -308,3 +309,109 @@ end # is_subplot_supported(::AbstractBackend) = false # is_subplot_supported() = is_subplot_supported(backend()) + + +################################################################################ +# initialize the backends + +function _initialize_backend(pkg::AbstractBackend) + sym = backend_package_name(pkg) + @eval Main begin + import $sym + export $sym + end +end + +function add_backend_string(pkg::AbstractBackend) + sym = backend_package_name(pkg) + """ + using Pkg + Pkg.add("$sym") + """ +end + +# ------------------------------------------------------------------------------ +# glvisualize + +function _initialize_backend(::GLVisualizeBackend; kw...) + @eval Main begin + import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour + import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec + import FileIO, Images + export GLVisualize + import Reactive: Signal + import GLAbstraction: Style + import GLVisualize: visualize + import Plots.GL + import UnicodeFun + end +end + +# ------------------------------------------------------------------------------ +# hdf5 + +function _initialize_backend(::HDF5Backend) + @eval Main begin + import HDF5 + export HDF5 + end +end + +# ------------------------------------------------------------------------------ +# PGFPLOTS + +function add_backend_string(::PGFPlotsBackend) + """ + using Pkg + Pkg.add("PGFPlots") + Pkg.build("PGFPlots") + """ +end + +# ------------------------------------------------------------------------------ +# plotlyjs + +function add_backend_string(::PlotlyJSBackend) + """ + using Pkg + Pkg.add("PlotlyJS") + Pkg.add("Rsvg") + import Blink + Blink.AtomShell.install() + """ +end + +# ------------------------------------------------------------------------------ +# pyplot + +function _initialize_backend(::PyPlotBackend) + @eval Main begin + import PyPlot, PyCall + import LaTeXStrings: latexstring + + export PyPlot + + # we don't want every command to update the figure + PyPlot.ioff() + end +end + +function add_backend_string(::PyPlotBackend) + """ + using Pkg + Pkg.add("PyPlot") + withenv("PYTHON" => "") do + Pkg.build("PyPlot") + end + """ +end + +# ------------------------------------------------------------------------------ +# unicodeplots +function add_backend_string(::UnicodePlotsBackend) + """ + using Pkg + Pkg.add("UnicodePlots") + Pkg.build("UnicodePlots") + """ +end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 2ec0b928..e2fe9b99 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -61,36 +61,11 @@ const _glvisualize_style = [:auto, :solid, :dash, :dot, :dashdot] const _glvisualize_marker = _allMarkers const _glvisualize_scale = [:identity, :ln, :log2, :log10] - - -# -------------------------------------------------------------------------------------- - -function _initialize_backend(::GLVisualizeBackend; kw...) - @eval begin - import GLVisualize, GeometryTypes, Reactive, GLAbstraction, GLWindow, Contour - import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0, GLNormalMesh, SimpleRectangle, Point, Vec - import FileIO, Images - export GLVisualize - import Reactive: Signal - import GLAbstraction: Style - import GLVisualize: visualize - import Plots.GL - import UnicodeFun - Plots.slice_arg(img::Matrix{C}, idx::Int) where {C<:Colorant} = img - is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true - is_marker_supported(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) where {C<:Colorant} = true - is_marker_supported(::GLVisualizeBackend, shape::Shape) = true - GL = Plots - end -end - -function add_backend_string(b::GLVisualizeBackend) - """ - if !Plots.is_installed("GLVisualize") - Pkg.add("GLVisualize") - end - """ -end +slice_arg(img::Matrix{C}, idx::Int) where {C<:Colorant} = img +is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true +is_marker_supported(::GLVisualizeBackend, shape::Union{Vector{Matrix{C}}, Matrix{C}}) where {C<:Colorant} = true +is_marker_supported(::GLVisualizeBackend, shape::Shape) = true +GL = Plots # --------------------------------------------------------------------------- diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 4ef4e7c6..a3569afd 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -65,12 +65,8 @@ function add_backend_string(::GRBackend) """ end -function _initialize_backend(::GRBackend; kw...) - @eval begin - import GR - export GR - end -end +import GR +export GR # -------------------------------------------------------------------------------------- diff --git a/src/backends/hdf5.jl b/src/backends/hdf5.jl index 0d8c1901..2f582259 100644 --- a/src/backends/hdf5.jl +++ b/src/backends/hdf5.jl @@ -108,12 +108,35 @@ const _hdf5_marker = vcat(_allMarkers, :pixel) const _hdf5_scale = [:identity, :ln, :log2, :log10] is_marker_supported(::HDF5Backend, shape::Shape) = true -function add_backend_string(::HDF5Backend) - """ - if !Plots.is_installed("HDF5") - Pkg.add("HDF5") - end - """ +if length(HDF5PLOT_MAP_TELEM2STR) < 1 + #Possible element types of high-level data types: + telem2str = Dict{String, Type}( + "NATIVE" => HDF5PlotNative, + "VOID" => Nothing, + "BOOL" => Bool, + "SYMBOL" => Symbol, + "TUPLE" => Tuple, + "CTUPLE" => HDF5CTuple, #Tuple of complex structures + "RGBA" => ARGB{N0f8}, + "EXTREMA" => Extrema, + "LENGTH" => Length, + "ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR + + #Sub-structure types: + "FONT" => Font, + "BOUNDINGBOX" => BoundingBox, + "GRIDLAYOUT" => GridLayout, + "ROOTLAYOUT" => RootLayout, + "SERIESANNOTATIONS" => SeriesAnnotations, +# "PLOTTEXT" => PlotText, + "COLORGRADIENT" => ColorGradient, + "AXIS" => Axis, + "SURFACE" => Surface, + "SUBPLOT" => Subplot, + "NULLABLE" => Nullable, + ) + merge!(HDF5PLOT_MAP_STR2TELEM, telem2str) + merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM)) end @@ -140,44 +163,6 @@ end #== ===============================================================================# -function _initialize_backend(::HDF5Backend) - @eval begin - import HDF5 - export HDF5 - if length(HDF5PLOT_MAP_TELEM2STR) < 1 - #Possible element types of high-level data types: - telem2str = Dict{String, Type}( - "NATIVE" => HDF5PlotNative, - "VOID" => Nothing, - "BOOL" => Bool, - "SYMBOL" => Symbol, - "TUPLE" => Tuple, - "CTUPLE" => HDF5CTuple, #Tuple of complex structures - "RGBA" => ARGB{N0f8}, - "EXTREMA" => Extrema, - "LENGTH" => Length, - "ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR - - #Sub-structure types: - "FONT" => Font, - "BOUNDINGBOX" => BoundingBox, - "GRIDLAYOUT" => GridLayout, - "ROOTLAYOUT" => RootLayout, - "SERIESANNOTATIONS" => SeriesAnnotations, -# "PLOTTEXT" => PlotText, - "COLORGRADIENT" => ColorGradient, - "AXIS" => Axis, - "SURFACE" => Surface, - "SUBPLOT" => Subplot, - "NULLABLE" => Nullable, - ) - merge!(HDF5PLOT_MAP_STR2TELEM, telem2str) - merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM)) - end - end -end - -# --------------------------------------------------------------------------- # Create the window/figure for this backend. function _create_backend_figure(plt::Plot{HDF5Backend}) diff --git a/src/backends/inspectdr.jl b/src/backends/inspectdr.jl index 3eb8e5fc..980160e8 100644 --- a/src/backends/inspectdr.jl +++ b/src/backends/inspectdr.jl @@ -152,37 +152,23 @@ end # --------------------------------------------------------------------------- -function add_backend_string(::InspectDRBackend) - """ - if !Plots.is_installed("InspectDR") - Pkg.add("InspectDR") - end - """ +#Glyph used when plotting "Shape"s: +INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline( + 2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y +) + +mutable struct InspecDRPlotRef + mplot::Union{Nothing, InspectDR.Multiplot} + gui::Union{Nothing, InspectDR.GtkPlot} end -function _initialize_backend(::InspectDRBackend; kw...) - @eval begin - import InspectDR - export InspectDR +_inspectdr_getmplot(::Any) = nothing +_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot - #Glyph used when plotting "Shape"s: - INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline( - 2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y - ) - - mutable struct InspecDRPlotRef - mplot::Union{Nothing, InspectDR.Multiplot} - gui::Union{Nothing, InspectDR.GtkPlot} - end - - _inspectdr_getmplot(::Any) = nothing - _inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot - - _inspectdr_getgui(::Any) = nothing - _inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot) - _inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui) - end -end +_inspectdr_getgui(::Any) = nothing +_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed ? nothing : gplot) +_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui) +push!(_initialized_backends, :inspectdr) # --------------------------------------------------------------------------- diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl index ab5f8bee..895afd7c 100644 --- a/src/backends/pgfplots.jl +++ b/src/backends/pgfplots.jl @@ -47,23 +47,6 @@ const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :d const _pgfplots_scale = [:identity, :ln, :log2, :log10] -# -------------------------------------------------------------------------------------- - -function add_backend_string(::PGFPlotsBackend) - """ - Pkg.add("PGFPlots") - Pkg.build("PGFPlots") - """ -end - -function _initialize_backend(::PGFPlotsBackend; kw...) - @eval begin - import PGFPlots - export PGFPlots - end -end - - # -------------------------------------------------------------------------------------- const _pgfplots_linestyles = KW( diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index 0b4c44d4..7b356140 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -76,46 +76,37 @@ end # -------------------------------------------------------------------------------------- -function add_backend_string(::PlotlyBackend) - """ - Pkg.build("Plots") - """ -end - const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js") const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js" -function _initialize_backend(::PlotlyBackend; kw...) - @eval begin - _js_code = open(readstring, _plotly_js_path, "r") +_js_code = open(readstring, _plotly_js_path, "r") - # borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2 - _js_script = """ - - """ +# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2 +_js_script = """ + +""" - # if we're in IJulia call setupnotebook to load js and css - if isijulia() - display("text/html", _js_script) - end - - # if isatom() - # import Atom - # Atom.@msg evaljs(_js_code) - # end - - end - # TODO: other initialization +# if we're in IJulia call setupnotebook to load js and css +if isijulia() + display("text/html", _js_script) end +# if isatom() +# import Atom +# Atom.@msg evaljs(_js_code) +# end +using UUIDs + +push!(_initialized_backends, :plotly) + # ---------------------------------------------------------------- @@ -906,7 +897,7 @@ function html_body(plt::Plot{PlotlyBackend}, style = nothing) w, h = plt[:size] style = "width:$(w)px;height:$(h)px;" end - uuid = Base.Random.uuid4() + uuid = UUIDs.uuid4() html = """