backend callback changes; output callbacks; prepare_output; fixes for pyplot, plotly, plotlyjs; and more

This commit is contained in:
Thomas Breloff 2016-05-24 23:19:51 -04:00
parent 71070da13e
commit 603dc30bb1
16 changed files with 262 additions and 257 deletions

View File

@ -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
# ---------------------------------------------------------

View File

@ -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
# ----------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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})
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
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

View File

@ -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

View File

@ -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,

View File

@ -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])

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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]
# -----------------------------------------------------------------------
# -----------------------------------------------------------------------

View File

@ -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
# ---------------------------------------------------------

View File

@ -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]
# --------------------------------------------------------------------