diff --git a/src/backends.jl b/src/backends.jl index e7ca5ec3..f1077905 100644 --- a/src/backends.jl +++ b/src/backends.jl @@ -44,21 +44,19 @@ include("backends/web.jl") # --------------------------------------------------------- - -plot(pkg::AbstractBackend; kw...) = error("plot($pkg; kw...) is not implemented") -plot!(pkg::AbstractBackend, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented") -_update_plot(pkg::AbstractBackend, plt::Plot, d::KW) = error("_update_plot($pkg, plt, d) is not implemented") - # don't do anything as a default _create_backend_figure(plt::Plot) = nothing +_prepare_plot_object(plt::Plot) = nothing _initialize_subplot(plt::Plot, sp::Subplot) = nothing -_update_min_padding!(sp::Subplot) = nothing -_update_position!(sp::Subplot) = nothing -_before_update(plt::Plot) = nothing + _series_added(plt::Plot, series::Series) = nothing -_update_plot(plt::Plot, d::KW) = nothing _series_updated(plt::Plot, series::Series) = nothing +_before_layout_calcs(plt::Plot) = nothing +_update_min_padding!(sp::Subplot) = nothing + +_update_plot_object(plt::Plot) = nothing + # --------------------------------------------------------- diff --git a/src/backends/bokeh.jl b/src/backends/bokeh.jl index 5d3c46db..5317016b 100644 --- a/src/backends/bokeh.jl +++ b/src/backends/bokeh.jl @@ -170,7 +170,7 @@ end # ---------------------------------------------------------------- # TODO: override this to update plot items (title, xlabel, etc) after creation -function _update_plot(plt::Plot{BokehBackend}, d::KW) +function _update_plot_object(plt::Plot{BokehBackend}, d::KW) end # ---------------------------------------------------------------- diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index 359a724c..28e00d72 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -595,7 +595,7 @@ end -function _update_plot(plt::Plot{GadflyBackend}, d::KW) +function _update_plot_object(plt::Plot{GadflyBackend}, d::KW) updateGadflyGuides(plt, d) updateGadflyPlotTheme(plt, d) end diff --git a/src/backends/glvisualize.jl b/src/backends/glvisualize.jl index 965201d0..0f5752d7 100644 --- a/src/backends/glvisualize.jl +++ b/src/backends/glvisualize.jl @@ -95,80 +95,42 @@ function _create_backend_figure(plt::Plot{GLVisualizeBackend}) end -# function _series_added(::GLVisualizeBackend, plt::Plot, d::KW) +# ---------------------------------------------------------------- + function _series_added(plt::Plot{GLVisualizeBackend}, series::Series) # TODO: add one series to the underlying package - # push!(plt.seriesargs, d) # TODO: this should be moved to the display method? x, y, z = map(Float32, series.d[:x]), map(Float32, series.d[:y]), map(Float32, series.d[:z].surf) GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface), plt.o.window) # plt end -function _add_annotations{X,Y,V}(plt::Plot{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})}) - for ann in anns - # TODO: add the annotation to the plot - end + +# When series data is added/changed, this callback can do dynamic updates to the backend object. +# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here. +function _series_updated(plt::Plot{GLVisualizeBackend}, series::Series) + # TODO end # ---------------------------------------------------------------- -function _before_update_plot(plt::Plot{GLVisualizeBackend}) + +# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations]) +function _update_plot_object(plt::Plot{GLVisualizeBackend}) + # TODO end -# TODO: override this to update plot items (title, xlabel, etc) after creation -function _update_plot(plt::Plot{GLVisualizeBackend}, d::KW) -end - -function _update_plot_pos_size(plt::AbstractPlot{GLVisualizeBackend}, d::KW) -end # ---------------------------------------------------------------- -# accessors for x/y data - -function getxy(plt::Plot{GLVisualizeBackend}, i::Int) - # TODO: - # series = plt.o.lines[i] - # series.x, series.y - nothing, nothing -end - -function setxy!{X,Y}(plt::Plot{GLVisualizeBackend}, xy::Tuple{X,Y}, i::Integer) - # TODO: - # series = plt.o.lines[i] - # series.x, series.y = xy - plt -end - -# ---------------------------------------------------------------- - -# function _create_subplot(subplt::Subplot{GLVisualizeBackend}) -# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example -# end - -function _expand_limits(lims, plt::Plot{GLVisualizeBackend}, isx::Bool) - # TODO: call expand limits for each plot data -end - -function _remove_axis(plt::Plot{GLVisualizeBackend}, isx::Bool) - # TODO: if plot is inner subplot, might need to remove ticks or axis labels -end - -# ---------------------------------------------------------------- - -function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend}) +function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend}) # TODO: write a png to io end -function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend}) +function _display(plt::Plot{GLVisualizeBackend}) # TODO: display/show the plot # NOTE: I think maybe this should be empty? We can start with the assumption that creating # and adding to a plot will automatically open a window and draw to it, then the display # wouldn't actually need to do anything end - -# function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend}) -# # TODO: display/show the subplot -# end diff --git a/src/backends/immerse.jl b/src/backends/immerse.jl index 42576158..267af35e 100644 --- a/src/backends/immerse.jl +++ b/src/backends/immerse.jl @@ -53,7 +53,7 @@ function _series_added(plt::Plot{ImmerseBackend}, series::Series) end -function _update_plot(plt::Plot{ImmerseBackend}, d::KW) +function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW) updateGadflyGuides(plt, d) updateGadflyPlotTheme(plt, d) end diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl index fb351306..c29e3e35 100644 --- a/src/backends/pgfplots.jl +++ b/src/backends/pgfplots.jl @@ -259,7 +259,7 @@ end # end # TODO: override this to update plot items (title, xlabel, etc) after creation -function _update_plot(plt::Plot{PGFPlotsBackend}, d::KW) +function _update_plot_object(plt::Plot{PGFPlotsBackend}, d::KW) end # function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW) diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index 151468f4..253b16f7 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -112,10 +112,10 @@ end # end # # # TODO: override this to update plot items (title, xlabel, etc) after creation -# function _update_plot(plt::Plot{PlotlyBackend}, d::KW) +# function _update_plot_object(plt::Plot{PlotlyBackend}, d::KW) # end -# function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW) +# function _update_plot_pos_size(plt::Plot{PlotlyBackend}, d::KW) # end # ---------------------------------------------------------------- @@ -573,7 +573,7 @@ end # ---------------------------------------------------------------- -function html_head(plt::AbstractPlot{PlotlyBackend}) +function html_head(plt::Plot{PlotlyBackend}) "" end @@ -603,24 +603,25 @@ end # ---------------------------------------------------------------- -# compute layout bboxes -function plotly_finalize(plt::Plot) - w, h = plt.attr[:size] - plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px) - update_child_bboxes!(plt.layout) -end +# # compute layout bboxes +# function plotly_finalize(plt::Plot) +# w, h = plt.attr[:size] +# plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px) +# update_child_bboxes!(plt.layout) +# end -function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend}) - plotly_finalize(plt) +function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend}) + # plotly_finalize(plt) writemime_png_from_html(io, plt) end -function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend}) - plotly_finalize(plt) +function _writemime(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend}) + # plotly_finalize(plt) write(io, html_head(plt) * html_body(plt)) end -function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend}) - plotly_finalize(plt) +# function Base.display(::PlotsDisplay, plt::Plot{PlotlyBackend}) +function _display(plt::Plot{PlotlyBackend}) + # plotly_finalize(plt) standalone_html_window(plt) end diff --git a/src/backends/plotlyjs.jl b/src/backends/plotlyjs.jl index 8a729df9..33d4f549 100644 --- a/src/backends/plotlyjs.jl +++ b/src/backends/plotlyjs.jl @@ -114,48 +114,35 @@ end function _series_added(plt::Plot{PlotlyJSBackend}, series::Series) - # d = series.d syncplot = plt.o - - # add to the data array pdict = plotly_series(plt, series) typ = pop!(pdict, :type) gt = PlotlyJS.GenericTrace(typ; pdict...) PlotlyJS.addtraces!(syncplot, gt) - - # push!(plt.seriesargs, d) - # plt end +function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series) + xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y)) + PlotlyJS.restyle!( + plt.o, + findfirst(plt.series_list, series), + KW(xsym => (series.d[:x],), ysym => (series.d[:y],)) + ) +end -# --------------------------------------------------------------------------- - - -# function _add_annotations{X,Y,V}(plt::Plot{PlotlyJSBackend}, anns::AVec{@compat(Tuple{X,Y,V})}) -# # set or add to the annotation_list -# if !haskey(plt.attr, :annotation_list) -# plt.attr[:annotation_list] = Any[] -# end -# append!(plt.attr[:annotation_list], anns) -# end # ---------------------------------------------------------------- -# function _before_update_plot(plt::Plot{PlotlyJSBackend}) -# end - # TODO: override this to update plot items (title, xlabel, etc) after creation -function _update_plot(plt::Plot{PlotlyJSBackend}, d::KW) +function _update_plot_object(plt::Plot{PlotlyJSBackend}) pdict = plotly_layout(plt) syncplot = plt.o w,h = plt.attr[:size] + DD(pdict) PlotlyJS.relayout!(syncplot, pdict, width = w, height = h) end -# function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW) -# end - # ---------------------------------------------------------------- # accessors for x/y data @@ -165,15 +152,6 @@ end # d[:x], d[:y] # end -function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series) - xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y)) - PlotlyJS.restyle!( - plt.o, - findfirst(plt.series_list, series), - KW(xsym => series.d[:x], ysym => series.d[:y]) - ) -end - # function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer) # d = plt.seriesargs[i] # ispolar = get(plt.attr, :polar, false) @@ -185,47 +163,25 @@ end # plt # end -# ---------------------------------------------------------------- - -# function _create_subplot(subplt::Subplot{PlotlyJSBackend}, isbefore::Bool) -# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example -# true -# end - -# function _expand_limits(lims, plt::Plot{PlotlyJSBackend}, isx::Bool) -# # TODO: call expand limits for each plot data -# end -# -# function _remove_axis(plt::Plot{PlotlyJSBackend}, isx::Bool) -# # TODO: if plot is inner subplot, might need to remove ticks or axis labels -# end # ---------------------------------------------------------------- -# function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{PlotlyJSBackend}) -# Base.writemime(io, m, plt.o) -# end - -# function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyJSBackend}) -# println("here!") -# writemime_png_from_html(io, plt) -# end - - function _update_min_padding!(sp::Subplot{PlotlyBackend}) sp.minpad = plotly_minpad(sp) end -function plotlyjs_finalize(plt::Plot) - plotly_finalize(plt) - PlotlyJS.relayout!(plt.o, plotly_layout(plt)) +# function plotlyjs_finalize(plt::Plot) +# plotly_finalize(plt) +# PlotlyJS.relayout!(plt.o, plotly_layout(plt)) +# end + +function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) + tmpfn = tempname() * "png" + PlotlyJS.savefig(plt.o, tmpfn) + write(io, read(open(tmpfn))) end -function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend}) - plotlyjs_finalize(plt) +function _display(plt::Plot{PlotlyJSBackend}) + # plotlyjs_finalize(plt) display(plt.o) end - -# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend}) -# error() -# end diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 65f6a1b8..b3337c11 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -300,12 +300,12 @@ drawax(ax) = ax[:draw](renderer(ax[:get_figure]())) # get a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!) get_extents(obj) = obj[:get_window_extent]()[:get_points]() -# bounding box of the figure -function py_bbox_fig(fig) - fl, fr, fb, ft = get_extents(fig) - BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px) -end -py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o) +# # bounding box of the figure +# function py_bbox_fig(fig) +# fl, fr, fb, ft = get_extents(fig) +# BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px) +# end +# py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o) # compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left function py_bbox(obj) @@ -399,58 +399,27 @@ function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend ) sp.o = ax end - -# Set the (left, top, right, bottom) minimum padding around the plot area -# to fit ticks, tick labels, guides, colorbars, etc. -function _update_min_padding!(sp::Subplot{PyPlotBackend}) - ax = sp.o - ax == nothing && return sp.minpad - plotbb = py_bbox(ax) - - # TODO: this should initialize to the margin from sp.attr - # figure out how much the axis components and title "stick out" from the plot area - # leftpad = toppad = rightpad = bottompad = 1mm - leftpad = sp.attr[:left_margin] - toppad = sp.attr[:top_margin] - rightpad = sp.attr[:right_margin] - bottompad = sp.attr[:bottom_margin] - for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax)) - if ispositive(width(bb)) && ispositive(height(bb)) - leftpad = max(leftpad, left(plotbb) - left(bb)) - toppad = max(toppad, top(plotbb) - top(bb)) - rightpad = max(rightpad, right(bb) - right(plotbb)) - bottompad = max(bottompad, bottom(bb) - bottom(plotbb)) - end - end - - # optionally add the width of colorbar labels and colorbar to rightpad - if haskey(sp.attr, :cbar_ax) - bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]()) - sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm - rightpad = rightpad + sp.attr[:cbar_width] - end - - sp.minpad = (leftpad, toppad, rightpad, bottompad) -end - -# Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to -# position the subplot in the backend. -function _update_position!(sp::Subplot{PyPlotBackend}) - ax = sp.o - ax == nothing && return - figw, figh = size(py_bbox_fig(sp.plt)) - pcts = bbox_to_pcts(sp.plotarea, figw, figh) - ax[:set_position](pcts) - - # set the cbar position if there is one - if haskey(sp.attr, :cbar_ax) - cbw = sp.attr[:cbar_width] - # this is the bounding box of just the colors of the colorbar (not labels) - cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox)+2mm, _cbar_width-1mm, height(sp.bbox)-4mm) - pcts = bbox_to_pcts(cb_bbox, figw, figh) - sp.attr[:cbar_ax][:set_position](pcts) - end -end +# +# # Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to +# # position the subplot in the backend. +# function _update_position!(sp::Subplot{PyPlotBackend}) +# ax = sp.o +# ax == nothing && return +# # figw, figh = size(py_bbox_fig(sp.plt)) +# figw, figh = sp.plt.attr[:size] +# figw, figh = figw*px, figh*px +# pcts = bbox_to_pcts(sp.plotarea, figw, figh) +# ax[:set_position](pcts) +# +# # set the cbar position if there is one +# if haskey(sp.attr, :cbar_ax) +# cbw = sp.attr[:cbar_width] +# # this is the bounding box of just the colors of the colorbar (not labels) +# cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox)+2mm, _cbar_width-1mm, height(sp.bbox)-4mm) +# pcts = bbox_to_pcts(cb_bbox, figw, figh) +# sp.attr[:cbar_ax][:set_position](pcts) +# end +# end # --------------------------------------------------------------------------- @@ -1003,7 +972,7 @@ end # -------------------------------------------------------------------------- -function _update_plot(plt::Plot{PyPlotBackend}, d::KW) +function _before_layout_calcs(plt::Plot{PyPlotBackend}) for sp in plt.subplots attr = sp.attr ax = getAxis(sp) @@ -1054,6 +1023,7 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW) ax[axissym][:grid](true, color = fgcolor) ax[:set_axisbelow](true) end + updateAxisColors(ax, axis) end # aspect ratio @@ -1061,7 +1031,44 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW) if aratio != :none ax[:set_aspect](isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C") end + + addPyPlotLegend(plt, sp, ax) end + drawfig(plt.o) +end + + +# Set the (left, top, right, bottom) minimum padding around the plot area +# to fit ticks, tick labels, guides, colorbars, etc. +function _update_min_padding!(sp::Subplot{PyPlotBackend}) + ax = sp.o + ax == nothing && return sp.minpad + plotbb = py_bbox(ax) + + # TODO: this should initialize to the margin from sp.attr + # figure out how much the axis components and title "stick out" from the plot area + # leftpad = toppad = rightpad = bottompad = 1mm + leftpad = sp.attr[:left_margin] + toppad = sp.attr[:top_margin] + rightpad = sp.attr[:right_margin] + bottompad = sp.attr[:bottom_margin] + for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax)) + if ispositive(width(bb)) && ispositive(height(bb)) + leftpad = max(leftpad, left(plotbb) - left(bb)) + toppad = max(toppad, top(plotbb) - top(bb)) + rightpad = max(rightpad, right(bb) - right(plotbb)) + bottompad = max(bottompad, bottom(bb) - bottom(plotbb)) + end + end + + # optionally add the width of colorbar labels and colorbar to rightpad + if haskey(sp.attr, :cbar_ax) + bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]()) + sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm + rightpad = rightpad + sp.attr[:cbar_width] + end + + sp.minpad = (leftpad, toppad, rightpad, bottompad) end @@ -1160,33 +1167,68 @@ end # ----------------------------------------------------------------- -# add legend, update colors and positions, then draw -function finalizePlot(plt::Plot{PyPlotBackend}) +# # add legend, update colors and positions, then draw +# function finalizePlot(plt::Plot{PyPlotBackend}) +# # for sp in plt.subplots +# # # ax = getLeftAxis(plt) +# # ax = getAxis(sp) +# # ax == nothing && continue +# # addPyPlotLegend(plt, sp, ax) +# # for asym in (:xaxis, :yaxis, :zaxis) +# # updateAxisColors(ax, sp.attr[asym]) +# # end +# # end +# drawfig(plt.o) +# # plt.layout.bbox = py_bbox_fig(plt) +# +# # TODO: these should be called outside of pyplot... how? +# update_child_bboxes!(plt.layout) +# _update_position!(plt.layout) +# +# PyPlot.draw() +# end + +# function _before_layout_calcs(plt::Plot{PyPlotBackend}) +# drawfig(plt.o) +# end + +# Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to +# position the subplot in the backend. +function _update_plot_object(plt::Plot{PyPlotBackend}) for sp in plt.subplots - # ax = getLeftAxis(plt) - ax = getAxis(sp) - ax == nothing && continue - addPyPlotLegend(plt, sp, ax) - for asym in (:xaxis, :yaxis, :zaxis) - updateAxisColors(ax, sp.attr[asym]) + ax = sp.o + ax == nothing && return + # figw, figh = size(py_bbox_fig(sp.plt)) + figw, figh = sp.plt.attr[:size] + figw, figh = figw*px, figh*px + pcts = bbox_to_pcts(sp.plotarea, figw, figh) + ax[:set_position](pcts) + + # set the cbar position if there is one + if haskey(sp.attr, :cbar_ax) + cbw = sp.attr[:cbar_width] + # this is the bounding box of just the colors of the colorbar (not labels) + cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox)+2mm, _cbar_width-1mm, height(sp.bbox)-4mm) + pcts = bbox_to_pcts(cb_bbox, figw, figh) + sp.attr[:cbar_ax][:set_position](pcts) end end - drawfig(plt.o) - plt.layout.bbox = py_bbox_fig(plt) - - # TODO: these should be called outside of pyplot... how? - update_child_bboxes!(plt.layout) - _update_position!(plt.layout) - PyPlot.draw() end - # ----------------------------------------------------------------- # display/output -function Base.display(::PlotsDisplay, plt::AbstractPlot{PyPlotBackend}) - finalizePlot(plt) +# function Base.display(::PlotsDisplay, plt::Plot{PyPlotBackend}) +# finalizePlot(plt) +# if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) +# display(getfig(plt.o)) +# end +# getfig(plt.o)[:show]() +# end + +function _display(plt::Plot{PyPlotBackend}) + # finalizePlot(plt) if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay) display(getfig(plt.o)) end @@ -1194,6 +1236,7 @@ function Base.display(::PlotsDisplay, plt::AbstractPlot{PyPlotBackend}) end + const _pyplot_mimeformats = Dict( "application/eps" => "eps", "image/eps" => "eps", @@ -1205,8 +1248,9 @@ const _pyplot_mimeformats = Dict( for (mime, fmt) in _pyplot_mimeformats - @eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::AbstractPlot{PyPlotBackend}) - finalizePlot(plt) + # @eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::Plot{PyPlotBackend}) + @eval function _writemime(io::IO, ::MIME{symbol($mime)}, plt::Plot{PyPlotBackend}) + # finalizePlot(plt) fig = getfig(plt.o) fig.o["canvas"][:print_figure]( io, diff --git a/src/backends/qwt.jl b/src/backends/qwt.jl index e7651341..588dfe3e 100644 --- a/src/backends/qwt.jl +++ b/src/backends/qwt.jl @@ -190,7 +190,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool) end -function _update_plot(plt::Plot{QwtBackend}, d::KW) +function _update_plot_object(plt::Plot{QwtBackend}, d::KW) haskey(d, :title) && Qwt.title(plt.o, d[:title]) haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide]) haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide]) diff --git a/src/backends/template.jl b/src/backends/template.jl index 10580287..6d989518 100644 --- a/src/backends/template.jl +++ b/src/backends/template.jl @@ -3,7 +3,7 @@ # [ADD BACKEND WEBSITE] -function _initialize_backend(::[PkgName]AbstractBackend; kw...) +function _initialize_backend(::[PkgName]Backend; kw...) @eval begin import [PkgName] export [PkgName] @@ -19,42 +19,43 @@ function _create_backend_figure(plt::Plot{[PkgName]Backend}) nothing end +# this is called early in the pipeline, use it to make the plot current or something +function _prepare_plot_object(plt::Plot{[PkgName]}) +end + # Set up the subplot within the backend object. function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend}) end +# --------------------------------------------------------------------------- + +# Add one series to the underlying backend object. +function _series_added(plt::Plot{[PkgName]Backend}, series::Series) +end + +# When series data is added/changed, this callback can do dynamic updates to the backend object. +# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here. +function _series_updated(plt::Plot{[PkgName]Backend}, series::Series) +end + +# --------------------------------------------------------------------------- + +# called just before updating layout bounding boxes... in case you need to prep +# for the calcs +function _before_layout_calcs(plt::Plot) +end + # Set the (left, top, right, bottom) minimum padding around the plot area # to fit ticks, tick labels, guides, colorbars, etc. function _update_min_padding!(sp::Subplot{[PkgName]Backend}) sp.minpad = (20mm, 5mm, 2mm, 10mm) end -# Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to -# position the subplot in the backend. -function _update_position!(sp::Subplot{[PkgName]Backend}) -end # ---------------------------------------------------------------- -# This is called before series processing... use it if you need to make the backend object current or something. -function _before_update(plt::Plot{[PkgName]AbstractBackend}) -end - - -# Add one series to the underlying backend object. -function _series_added(plt::Plot{[PkgName]Backend}, series::Series) -end - - # Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations]) -function _update_plot(plt::Plot{[PkgName]AbstractBackend}, d::KW) -end - -# ---------------------------------------------------------------- - -# When series data is added/changed, this callback can do dynamic updates to the backend object. -# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here. -function _series_updated(plt::Plot{[PkgName]AbstractBackend}, series::Series) +function _update_plot_object(plt::Plot{[PkgName]Backend}) end # ---------------------------------------------------------------- @@ -66,9 +67,9 @@ end # "image/png" => "png", # "application/postscript" => "ps", # "image/svg+xml" => "svg" -function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{[PkgName]AbstractBackend}) +function _writemime(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend}) end # Display/show the plot (open a GUI window, or browser page, for example). -function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend}) +function _display(plt::Plot{[PkgName]Backend}) end diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl index fb8ae0a6..a09b255a 100644 --- a/src/backends/unicodeplots.jl +++ b/src/backends/unicodeplots.jl @@ -208,7 +208,7 @@ function _series_added(plt::Plot{UnicodePlotsBackend}, series::Series) end -function _update_plot(plt::Plot{UnicodePlotsBackend}, d::KW) +function _update_plot_object(plt::Plot{UnicodePlotsBackend}, d::KW) for k in (:title, :xguide, :yguide, :xlims, :ylims) if haskey(d, k) plt.attr[k] = d[k] diff --git a/src/backends/winston.jl b/src/backends/winston.jl index 93f8cf2c..a6ed1504 100644 --- a/src/backends/winston.jl +++ b/src/backends/winston.jl @@ -227,7 +227,7 @@ end :yscale => :ylog, ) -function _update_plot(plt::Plot{WinstonBackend}, d::KW) +function _update_plot_object(plt::Plot{WinstonBackend}, d::KW) window, canvas, wplt = getWinstonItems(plt) for k in (:xguide, :yguide, :title, :xlims, :ylims) if haskey(d, k) diff --git a/src/components.jl b/src/components.jl index 981a4955..50eae130 100644 --- a/src/components.jl +++ b/src/components.jl @@ -256,6 +256,13 @@ text(t::PlotText) = t function text(str, args...) PlotText(string(str), font(args...)) end + + +annotations(::Void) = [] +annotations(anns::AVec) = anns +annotations(anns) = Any[anns] + + # ----------------------------------------------------------------------- # ----------------------------------------------------------------------- diff --git a/src/output.jl b/src/output.jl index ce16f582..6e0d0654 100644 --- a/src/output.jl +++ b/src/output.jl @@ -103,15 +103,37 @@ savefig(fn::@compat(AbstractString)) = savefig(current(), fn) gui(plt::AbstractPlot = current()) = display(PlotsDisplay(), plt) +function Base.display(::PlotsDisplay, plt::Plot) + prepare_output(plt) + _display(plt) +end # override the REPL display to open a gui window Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = gui(plt) +# --------------------------------------------------------- + +const _mimeformats = Dict( + "application/eps" => "eps", + "image/eps" => "eps", + "application/pdf" => "pdf", + "image/png" => "png", + "application/postscript" => "ps", + "image/svg+xml" => "svg" +) + # a backup for html... passes to svg function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot) writemime(io, MIME("image/svg+xml"), plt) end +for mime in keys(_mimeformats) + @eval function writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot) + prepare_output(plt) + _writemime(io, m, plt) + end +end + # --------------------------------------------------------- # IJulia # --------------------------------------------------------- diff --git a/src/plot.jl b/src/plot.jl index 1b17e218..bb2bca6f 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -163,7 +163,7 @@ end # note: at entry, we only have those preprocessed args which were passed in... no default values yet function _plot!(plt::Plot, d::KW, args...) # just in case the backend needs to set up the plot (make it current or something) - _before_update(plt) + _prepare_plot_object(plt) # first apply any args for the subplots for (idx,sp) in enumerate(plt.subplots) @@ -289,8 +289,14 @@ function _plot!(plt::Plot, d::KW, args...) _apply_series_recipe(plt, kw) end - # TODO just need to pass plt... and we should do all non-series updates here - _update_plot(plt, plt.attr) + # # everything is processed, time to compute the layout bounding boxes + # _before_layout_calcs(plt) + # w, h = plt.attr[:size] + # plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px) + # update_child_bboxes!(plt.layout) + # + # # TODO just need to pass plt... and we should do all non-series updates here + # _update_plot_object(plt) current(plt) @@ -311,6 +317,17 @@ function _replace_linewidth(d::KW) end end +# we're getting ready to display/output. prep for layout calcs, then update +# the plot object after +function prepare_output(plt::Plot) + _before_layout_calcs(plt) + + w, h = plt.attr[:size] + plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px) + update_child_bboxes!(plt.layout) + + _update_plot_object(plt) +end # -------------------------------------------------------------------- @@ -370,9 +387,6 @@ end # -------------------------------------------------------------------- -annotations(::Void) = [] -annotations(anns::AVec) = anns -annotations(anns) = Any[anns] # --------------------------------------------------------------------