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 # don't do anything as a default
_create_backend_figure(plt::Plot) = nothing _create_backend_figure(plt::Plot) = nothing
_prepare_plot_object(plt::Plot) = nothing
_initialize_subplot(plt::Plot, sp::Subplot) = 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 _series_added(plt::Plot, series::Series) = nothing
_update_plot(plt::Plot, d::KW) = nothing
_series_updated(plt::Plot, series::Series) = 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 # 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 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) updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d) updateGadflyPlotTheme(plt, d)
end end

View File

@ -95,80 +95,42 @@ function _create_backend_figure(plt::Plot{GLVisualizeBackend})
end end
# function _series_added(::GLVisualizeBackend, plt::Plot, d::KW) # ----------------------------------------------------------------
function _series_added(plt::Plot{GLVisualizeBackend}, series::Series) function _series_added(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO: add one series to the underlying package # TODO: add one series to the underlying package
# push!(plt.seriesargs, d)
# TODO: this should be moved to the display method? # 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) 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) GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface), plt.o.window)
# plt # plt
end end
function _add_annotations{X,Y,V}(plt::Plot{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
for ann in anns # When series data is added/changed, this callback can do dynamic updates to the backend object.
# TODO: add the annotation to the plot # note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
end function _series_updated(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO
end 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 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 _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
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})
# TODO: write a png to io # TODO: write a png to io
end end
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend}) function _display(plt::Plot{GLVisualizeBackend})
# TODO: display/show the plot # TODO: display/show the plot
# NOTE: I think maybe this should be empty? We can start with the assumption that creating # 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 # and adding to a plot will automatically open a window and draw to it, then the display
# wouldn't actually need to do anything # wouldn't actually need to do anything
end 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 end
function _update_plot(plt::Plot{ImmerseBackend}, d::KW) function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW)
updateGadflyGuides(plt, d) updateGadflyGuides(plt, d)
updateGadflyPlotTheme(plt, d) updateGadflyPlotTheme(plt, d)
end end

View File

@ -259,7 +259,7 @@ end
# end # end
# TODO: override this to update plot items (title, xlabel, etc) after creation # 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 end
# function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW) # function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)

View File

@ -112,10 +112,10 @@ end
# end # end
# #
# # TODO: override this to update plot items (title, xlabel, etc) after creation # # 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 # end
# function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW) # function _update_plot_pos_size(plt::Plot{PlotlyBackend}, d::KW)
# end # 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>" "<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
end end
@ -603,24 +603,25 @@ end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# compute layout bboxes # # compute layout bboxes
function plotly_finalize(plt::Plot) # function plotly_finalize(plt::Plot)
w, h = plt.attr[:size] # w, h = plt.attr[:size]
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px) # plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
update_child_bboxes!(plt.layout) # update_child_bboxes!(plt.layout)
end # end
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend}) function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
plotly_finalize(plt) # plotly_finalize(plt)
writemime_png_from_html(io, plt) writemime_png_from_html(io, plt)
end end
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend}) function _writemime(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
plotly_finalize(plt) # plotly_finalize(plt)
write(io, html_head(plt) * html_body(plt)) write(io, html_head(plt) * html_body(plt))
end end
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend}) # function Base.display(::PlotsDisplay, plt::Plot{PlotlyBackend})
plotly_finalize(plt) function _display(plt::Plot{PlotlyBackend})
# plotly_finalize(plt)
standalone_html_window(plt) standalone_html_window(plt)
end end

View File

@ -114,48 +114,35 @@ end
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series) function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
# d = series.d
syncplot = plt.o syncplot = plt.o
# add to the data array
pdict = plotly_series(plt, series) pdict = plotly_series(plt, series)
typ = pop!(pdict, :type) typ = pop!(pdict, :type)
gt = PlotlyJS.GenericTrace(typ; pdict...) gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt) PlotlyJS.addtraces!(syncplot, gt)
# push!(plt.seriesargs, d)
# plt
end 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 # 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) pdict = plotly_layout(plt)
syncplot = plt.o syncplot = plt.o
w,h = plt.attr[:size] w,h = plt.attr[:size]
DD(pdict)
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h) PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end end
# function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW)
# end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# accessors for x/y data # accessors for x/y data
@ -165,15 +152,6 @@ end
# d[:x], d[:y] # d[:x], d[:y]
# end # 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) # function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
# d = plt.seriesargs[i] # d = plt.seriesargs[i]
# ispolar = get(plt.attr, :polar, false) # ispolar = get(plt.attr, :polar, false)
@ -185,47 +163,25 @@ end
# plt # plt
# end # 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}) function _update_min_padding!(sp::Subplot{PlotlyBackend})
sp.minpad = plotly_minpad(sp) sp.minpad = plotly_minpad(sp)
end end
function plotlyjs_finalize(plt::Plot) # function plotlyjs_finalize(plt::Plot)
plotly_finalize(plt) # plotly_finalize(plt)
PlotlyJS.relayout!(plt.o, plotly_layout(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 end
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend}) function _display(plt::Plot{PlotlyJSBackend})
plotlyjs_finalize(plt) # plotlyjs_finalize(plt)
display(plt.o) display(plt.o)
end 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 a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!)
get_extents(obj) = obj[:get_window_extent]()[:get_points]() get_extents(obj) = obj[:get_window_extent]()[:get_points]()
# bounding box of the figure # # bounding box of the figure
function py_bbox_fig(fig) # function py_bbox_fig(fig)
fl, fr, fb, ft = get_extents(fig) # fl, fr, fb, ft = get_extents(fig)
BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px) # BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px)
end # end
py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o) # 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 # compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
function py_bbox(obj) function py_bbox(obj)
@ -399,58 +399,27 @@ function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend
) )
sp.o = ax sp.o = ax
end end
#
# Set the (left, top, right, bottom) minimum padding around the plot area # # Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to
# to fit ticks, tick labels, guides, colorbars, etc. # # position the subplot in the backend.
function _update_min_padding!(sp::Subplot{PyPlotBackend}) # function _update_position!(sp::Subplot{PyPlotBackend})
ax = sp.o # ax = sp.o
ax == nothing && return sp.minpad # ax == nothing && return
plotbb = py_bbox(ax) # # figw, figh = size(py_bbox_fig(sp.plt))
# figw, figh = sp.plt.attr[:size]
# TODO: this should initialize to the margin from sp.attr # figw, figh = figw*px, figh*px
# figure out how much the axis components and title "stick out" from the plot area # pcts = bbox_to_pcts(sp.plotarea, figw, figh)
# leftpad = toppad = rightpad = bottompad = 1mm # ax[:set_position](pcts)
leftpad = sp.attr[:left_margin] #
toppad = sp.attr[:top_margin] # # set the cbar position if there is one
rightpad = sp.attr[:right_margin] # if haskey(sp.attr, :cbar_ax)
bottompad = sp.attr[:bottom_margin] # cbw = sp.attr[:cbar_width]
for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax)) # # this is the bounding box of just the colors of the colorbar (not labels)
if ispositive(width(bb)) && ispositive(height(bb)) # cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox)+2mm, _cbar_width-1mm, height(sp.bbox)-4mm)
leftpad = max(leftpad, left(plotbb) - left(bb)) # pcts = bbox_to_pcts(cb_bbox, figw, figh)
toppad = max(toppad, top(plotbb) - top(bb)) # sp.attr[:cbar_ax][:set_position](pcts)
rightpad = max(rightpad, right(bb) - right(plotbb)) # end
bottompad = max(bottompad, bottom(bb) - bottom(plotbb)) # end
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
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -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 for sp in plt.subplots
attr = sp.attr attr = sp.attr
ax = getAxis(sp) ax = getAxis(sp)
@ -1054,6 +1023,7 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW)
ax[axissym][:grid](true, color = fgcolor) ax[axissym][:grid](true, color = fgcolor)
ax[:set_axisbelow](true) ax[:set_axisbelow](true)
end end
updateAxisColors(ax, axis)
end end
# aspect ratio # aspect ratio
@ -1061,7 +1031,44 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW)
if aratio != :none if aratio != :none
ax[:set_aspect](isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C") ax[:set_aspect](isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
end end
addPyPlotLegend(plt, sp, ax)
end 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 end
@ -1160,33 +1167,68 @@ end
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# add legend, update colors and positions, then draw # # add legend, update colors and positions, then draw
function finalizePlot(plt::Plot{PyPlotBackend}) # 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 for sp in plt.subplots
# ax = getLeftAxis(plt) ax = sp.o
ax = getAxis(sp) ax == nothing && return
ax == nothing && continue # figw, figh = size(py_bbox_fig(sp.plt))
addPyPlotLegend(plt, sp, ax) figw, figh = sp.plt.attr[:size]
for asym in (:xaxis, :yaxis, :zaxis) figw, figh = figw*px, figh*px
updateAxisColors(ax, sp.attr[asym]) 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
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() PyPlot.draw()
end end
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# display/output # display/output
function Base.display(::PlotsDisplay, plt::AbstractPlot{PyPlotBackend}) # function Base.display(::PlotsDisplay, plt::Plot{PyPlotBackend})
finalizePlot(plt) # 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) if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
display(getfig(plt.o)) display(getfig(plt.o))
end end
@ -1194,6 +1236,7 @@ function Base.display(::PlotsDisplay, plt::AbstractPlot{PyPlotBackend})
end end
const _pyplot_mimeformats = Dict( const _pyplot_mimeformats = Dict(
"application/eps" => "eps", "application/eps" => "eps",
"image/eps" => "eps", "image/eps" => "eps",
@ -1205,8 +1248,9 @@ const _pyplot_mimeformats = Dict(
for (mime, fmt) in _pyplot_mimeformats for (mime, fmt) in _pyplot_mimeformats
@eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::AbstractPlot{PyPlotBackend}) # @eval function Base.writemime(io::IO, ::MIME{symbol($mime)}, plt::Plot{PyPlotBackend})
finalizePlot(plt) @eval function _writemime(io::IO, ::MIME{symbol($mime)}, plt::Plot{PyPlotBackend})
# finalizePlot(plt)
fig = getfig(plt.o) fig = getfig(plt.o)
fig.o["canvas"][:print_figure]( fig.o["canvas"][:print_figure](
io, io,

View File

@ -190,7 +190,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
end 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, :title) && Qwt.title(plt.o, d[:title])
haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide]) haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide])
haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide]) haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide])

View File

@ -3,7 +3,7 @@
# [ADD BACKEND WEBSITE] # [ADD BACKEND WEBSITE]
function _initialize_backend(::[PkgName]AbstractBackend; kw...) function _initialize_backend(::[PkgName]Backend; kw...)
@eval begin @eval begin
import [PkgName] import [PkgName]
export [PkgName] export [PkgName]
@ -19,42 +19,43 @@ function _create_backend_figure(plt::Plot{[PkgName]Backend})
nothing nothing
end 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. # Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend}) function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
end 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 # Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc. # to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{[PkgName]Backend}) function _update_min_padding!(sp::Subplot{[PkgName]Backend})
sp.minpad = (20mm, 5mm, 2mm, 10mm) sp.minpad = (20mm, 5mm, 2mm, 10mm)
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{[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]) # Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot(plt::Plot{[PkgName]AbstractBackend}, d::KW) function _update_plot_object(plt::Plot{[PkgName]Backend})
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)
end end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
@ -66,9 +67,9 @@ end
# "image/png" => "png", # "image/png" => "png",
# "application/postscript" => "ps", # "application/postscript" => "ps",
# "image/svg+xml" => "svg" # "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 end
# Display/show the plot (open a GUI window, or browser page, for example). # 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 end

View File

@ -208,7 +208,7 @@ function _series_added(plt::Plot{UnicodePlotsBackend}, series::Series)
end 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) for k in (:title, :xguide, :yguide, :xlims, :ylims)
if haskey(d, k) if haskey(d, k)
plt.attr[k] = d[k] plt.attr[k] = d[k]

View File

@ -227,7 +227,7 @@ end
:yscale => :ylog, :yscale => :ylog,
) )
function _update_plot(plt::Plot{WinstonBackend}, d::KW) function _update_plot_object(plt::Plot{WinstonBackend}, d::KW)
window, canvas, wplt = getWinstonItems(plt) window, canvas, wplt = getWinstonItems(plt)
for k in (:xguide, :yguide, :title, :xlims, :ylims) for k in (:xguide, :yguide, :title, :xlims, :ylims)
if haskey(d, k) if haskey(d, k)

View File

@ -256,6 +256,13 @@ text(t::PlotText) = t
function text(str, args...) function text(str, args...)
PlotText(string(str), font(args...)) PlotText(string(str), font(args...))
end 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) 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 # override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = gui(plt) 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 # a backup for html... passes to svg
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot) function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot)
writemime(io, MIME("image/svg+xml"), plt) writemime(io, MIME("image/svg+xml"), plt)
end 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 # 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 # note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, d::KW, args...) function _plot!(plt::Plot, d::KW, args...)
# just in case the backend needs to set up the plot (make it current or something) # 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 # first apply any args for the subplots
for (idx,sp) in enumerate(plt.subplots) for (idx,sp) in enumerate(plt.subplots)
@ -289,8 +289,14 @@ function _plot!(plt::Plot, d::KW, args...)
_apply_series_recipe(plt, kw) _apply_series_recipe(plt, kw)
end end
# TODO just need to pass plt... and we should do all non-series updates here # # everything is processed, time to compute the layout bounding boxes
_update_plot(plt, plt.attr) # _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) current(plt)
@ -311,6 +317,17 @@ function _replace_linewidth(d::KW)
end end
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]
# -------------------------------------------------------------------- # --------------------------------------------------------------------