working on subplots

This commit is contained in:
Thomas Breloff 2016-05-16 01:07:33 -04:00
parent e9ab6e7301
commit c5bcae1e34
22 changed files with 483 additions and 412 deletions

View File

@ -14,10 +14,11 @@ export
AbstractPlot,
Plot,
Subplot,
SubplotLayout,
AbstractLayout,
GridLayout,
RowsLayout,
FlexLayout,
EmptyLayout,
# RowsLayout,
# FlexLayout,
AVec,
AMat,
KW,

View File

@ -156,6 +156,7 @@ _seriesDefaults[:weights] = nothing # optional weights for histograms
_seriesDefaults[:contours] = false # add contours to 3d surface and wireframe plots
_seriesDefaults[:match_dimensions] = false # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
# this ONLY effects whether or not the z-matrix is transposed for a heatmap display!
_seriesDefaults[:subplot_index] = :auto
const _plotDefaults = KW()

View File

@ -48,8 +48,8 @@ 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")
subplot(pkg::AbstractBackend; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::AbstractBackend, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# subplot(pkg::AbstractBackend; kw...) = error("subplot($pkg; kw...) is not implemented")
# subplot!(pkg::AbstractBackend, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# don't do anything as a default
_create_backend_figure(plt::Plot) = nothing

View File

@ -209,10 +209,10 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{BokehBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# function _create_subplot(subplt::Subplot{BokehBackend}, isbefore::Bool)
# # 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{BokehBackend}, isx::Bool)
@ -234,6 +234,6 @@ function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
Bokeh.showplot(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# TODO: display/show the subplot
end
# function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
# # TODO: display/show the subplot
# end

View File

@ -626,12 +626,12 @@ end
# ----------------------------------------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
isbefore && return false # wait until after plotting to create the subplots
subplt.o = nothing
true
end
# # create the underlying object (each backend will do this differently)
# function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
# isbefore && return false # wait until after plotting to create the subplots
# subplt.o = nothing
# true
# end
function _remove_axis(plt::Plot{GadflyBackend}, isx::Bool)
@ -651,31 +651,31 @@ end
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
# getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
function buildGadflySubplotContext(subplt::Subplot)
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
# add the Plot object to the row
push!(row, getGadflyContext(subplt.plts[i]))
# add the row
if c == ncols(subplt.layout, r)
push!(rows, Gadfly.hstack(row...))
row = Any[]
end
end
# stack the rows
Gadfly.vstack(rows...)
end
# # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
# function buildGadflySubplotContext(subplt::Subplot)
# rows = Any[]
# row = Any[]
# for (i,(r,c)) in enumerate(subplt.layout)
#
# # add the Plot object to the row
# push!(row, getGadflyContext(subplt.plts[i]))
#
# # add the row
# if c == ncols(subplt.layout, r)
# push!(rows, Gadfly.hstack(row...))
# row = Any[]
# end
# end
#
# # stack the rows
# Gadfly.vstack(rows...)
# end
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.plotargs[:size]...)
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, 1)[:size]...)
# setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, 1)[:size]...)
# -------------------------------------------------------------------------
@ -708,39 +708,39 @@ function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
end
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
setGadflyDisplaySize(getplotargs(subplt,1)[:size]...)
ctx = buildGadflySubplotContext(subplt)
# taken from Gadfly since I couldn't figure out how to do it directly
filename = string(Gadfly.tempname(), ".html")
output = open(filename, "w")
plot_output = IOBuffer()
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
Compose.default_graphic_height, false), ctx)
plotsvg = takebuf_string(plot_output)
write(output,
"""
<!DOCTYPE html>
<html>
<head>
<title>Gadfly Plot</title>
<meta charset="utf-8">
</head>
<body>
<script charset="utf-8">
$(readall(Compose.snapsvgjs))
</script>
<script charset="utf-8">
$(readall(Gadfly.gadflyjs))
</script>
$(plotsvg)
</body>
</html>
""")
close(output)
Gadfly.open_file(filename)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
# setGadflyDisplaySize(getplotargs(subplt,1)[:size]...)
# ctx = buildGadflySubplotContext(subplt)
#
# # taken from Gadfly since I couldn't figure out how to do it directly
#
# filename = string(Gadfly.tempname(), ".html")
# output = open(filename, "w")
#
# plot_output = IOBuffer()
# Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
# Compose.default_graphic_height, false), ctx)
# plotsvg = takebuf_string(plot_output)
#
# write(output,
# """
# <!DOCTYPE html>
# <html>
# <head>
# <title>Gadfly Plot</title>
# <meta charset="utf-8">
# </head>
# <body>
# <script charset="utf-8">
# $(readall(Compose.snapsvgjs))
# </script>
# <script charset="utf-8">
# $(readall(Gadfly.gadflyjs))
# </script>
# $(plotsvg)
# </body>
# </html>
# """)
# close(output)
# Gadfly.open_file(filename)
# end

View File

@ -143,9 +143,9 @@ 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 _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
@ -169,6 +169,6 @@ function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend})
# wouldn't actually need to do anything
end
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
# TODO: display/show the subplot
end
# function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
# # TODO: display/show the subplot
# end

View File

@ -832,21 +832,21 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
update && GR.updatews()
end
function gr_display(subplt::Subplot{GRBackend})
clear = true
update = false
l = enumerate(subplt.layout)
nr = nrows(subplt.layout)
for (i, (r, c)) in l
nc = ncols(subplt.layout, r)
if i == length(l)
update = true
end
subplot = [(c-1)/nc, c/nc, 1-r/nr, 1-(r-1)/nr]
gr_display(subplt.plts[i], clear, update, subplot)
clear = false
end
end
# function gr_display(subplt::Subplot{GRBackend})
# clear = true
# update = false
# l = enumerate(subplt.layout)
# nr = nrows(subplt.layout)
# for (i, (r, c)) in l
# nc = ncols(subplt.layout, r)
# if i == length(l)
# update = true
# end
# subplot = [(c-1)/nc, c/nc, 1-r/nr, 1-(r-1)/nr]
# gr_display(subplt.plts[i], clear, update, subplot)
# clear = false
# end
# end
# function _create_plot(pkg::GRBackend, d::KW)
# Plot(nothing, pkg, 0, d, KW[])
@ -948,7 +948,7 @@ function Base.display(::PlotsDisplay, plt::Plot{GRBackend})
gr_display(plt)
end
function Base.display(::PlotsDisplay, plt::Subplot{GRBackend})
gr_display(plt)
true
end
# function Base.display(::PlotsDisplay, plt::Subplot{GRBackend})
# gr_display(plt)
# true
# end

View File

@ -88,51 +88,51 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
return false
# isbefore && return false
end
function showSubplotObject(subplt::Subplot{ImmerseBackend})
# create the Gtk window with vertical box vsep
d = getplotargs(subplt,1)
w,h = d[:size]
vsep = Gtk.GtkBoxLeaf(:v)
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
figindices = []
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
for (i,(r,c)) in enumerate(subplt.layout)
plt = subplt.plts[i]
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
box, toolbar, canvas = Immerse.createPlotGuiComponents()
# add the plot's box to the row
push!(row, box)
# create the figure and store the index returned for destruction later
figidx = Immerse.figure(canvas)
push!(figindices, figidx)
fig = Immerse.figure(figidx)
plt.o = (fig, plt.o[2])
# add the row
if c == ncols(subplt.layout, r)
row = Gtk.GtkBoxLeaf(:h)
push!(vsep, row)
end
end
# destructor... clean up plots
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
subplt.o = win
true
end
# function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
# return false
# # isbefore && return false
# end
#
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
# # create the Gtk window with vertical box vsep
# d = getplotargs(subplt,1)
# w,h = d[:size]
# vsep = Gtk.GtkBoxLeaf(:v)
# win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
#
# figindices = []
# row = Gtk.GtkBoxLeaf(:h)
# push!(vsep, row)
# for (i,(r,c)) in enumerate(subplt.layout)
# plt = subplt.plts[i]
#
# # get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
# box, toolbar, canvas = Immerse.createPlotGuiComponents()
#
# # add the plot's box to the row
# push!(row, box)
#
# # create the figure and store the index returned for destruction later
# figidx = Immerse.figure(canvas)
# push!(figindices, figidx)
#
# fig = Immerse.figure(figidx)
# plt.o = (fig, plt.o[2])
#
# # add the row
# if c == ncols(subplt.layout, r)
# row = Gtk.GtkBoxLeaf(:h)
# push!(vsep, row)
# end
#
# end
#
# # destructor... clean up plots
# Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
#
# subplt.o = win
# true
# end
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
@ -151,7 +151,7 @@ end
# ----------------------------------------------------------------
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
# getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
@ -168,20 +168,20 @@ function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
end
function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
# if we haven't created the window yet, do it
if subplt.o == nothing
showSubplotObject(subplt)
end
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
for plt in subplt.plts
fig, gplt = plt.o
Immerse.figure(fig.figno; displayfig = false)
display(gplt)
end
# o is the window... show it
showall(subplt.o)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
#
# # if we haven't created the window yet, do it
# if subplt.o == nothing
# showSubplotObject(subplt)
# end
#
# # display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
# for plt in subplt.plts
# fig, gplt = plt.o
# Immerse.figure(fig.figno; displayfig = false)
# display(gplt)
# end
#
# # o is the window... show it
# showall(subplt.o)
# end

View File

@ -135,10 +135,10 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyBackend}, 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 _create_subplot(subplt::Subplot{PlotlyBackend}, 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{PlotlyBackend}, isx::Bool)
# TODO: call expand limits for each plot data
@ -516,15 +516,15 @@ function get_series_json(plt::Plot{PlotlyBackend})
JSON.json(map(d -> plotly_series(d, plt.plotargs), plt.seriesargs))
end
function get_series_json(subplt::Subplot{PlotlyBackend})
ds = KW[]
for (i,plt) in enumerate(subplt.plts)
for d in plt.seriesargs
push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
end
end
JSON.json(ds)
end
# function get_series_json(subplt::Subplot{PlotlyBackend})
# ds = KW[]
# for (i,plt) in enumerate(subplt.plts)
# for d in plt.seriesargs
# push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
# end
# end
# JSON.json(ds)
# end
# ----------------------------------------------------------------
@ -557,29 +557,29 @@ function js_body(plt::Plot{PlotlyBackend}, uuid)
end
function html_body(subplt::Subplot{PlotlyBackend})
w, h = subplt.plts[1].plotargs[:size]
html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
nr = nrows(subplt.layout)
ph = h / nr
for r in 1:nr
push!(html, "<div style=\"clear:both;\">")
nc = ncols(subplt.layout, r)
pw = w / nc
for c in 1:nc
plt = subplt[r,c]
push!(html, html_body(plt, "float:left; width:$(pw)px; height:$(ph)px;"))
end
push!(html, "</div>")
end
push!(html, "</div>")
join(html)
end
# function html_body(subplt::Subplot{PlotlyBackend})
# w, h = subplt.plts[1].plotargs[:size]
# html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
# nr = nrows(subplt.layout)
# ph = h / nr
#
# for r in 1:nr
# push!(html, "<div style=\"clear:both;\">")
#
# nc = ncols(subplt.layout, r)
# pw = w / nc
#
# for c in 1:nc
# plt = subplt[r,c]
# push!(html, html_body(plt, "float:left; width:$(pw)px; height:$(ph)px;"))
# end
#
# push!(html, "</div>")
# end
# push!(html, "</div>")
#
# join(html)
# end
# ----------------------------------------------------------------

View File

@ -178,10 +178,10 @@ 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 _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
@ -206,6 +206,6 @@ function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
display(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend})
error()
end
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend})
# error()
# end

View File

@ -222,40 +222,62 @@ end
# ---------------------------------------------------------------------------
type PyPlotAxisWrapper
ax
rightax
fig
kwargs # for add_subplot
end
getfig(wrap::PyPlotAxisWrapper) = wrap.fig
# get a reference to the correct axis
function getLeftAxis(wrap::PyPlotAxisWrapper)
if wrap.ax == nothing
axes = wrap.fig.o[:axes]
if isempty(axes)
return wrap.fig.o[:add_subplot](111; wrap.kwargs...)
end
axes[1]
else
wrap.ax
function getAxis(plt::Plot{PyPlotBackend}, subplot::Subplot = plt.subplots[1])
if subplot.o == nothing
@show subplot
fig = plt.o
@show plt
# if fig == nothing
# fig =
# TODO: actual coords?
# NOTE: might want to use ax[:get_tightbbox](ax[:get_renderer_cache]()) to calc size of guides?
# NOTE: can set subplot location later with ax[:set_position]([left, bottom, width, height])
left, bottom, width, height = 0.3, 0.3, 0.5, 0.5
ax = fig[:add_axes]([left, bottom, width, height])
subplot.o = ax
end
subplot.o
end
function getRightAxis(wrap::PyPlotAxisWrapper)
if wrap.rightax == nothing
wrap.rightax = getLeftAxis(wrap)[:twinx]()
end
wrap.rightax
end
getLeftAxis(plt::Plot{PyPlotBackend}, subplot::Subplot = plt.subplots[1]) = getAxis(plt, subplot)
getfig(o) = o
getLeftAxis(plt::Plot{PyPlotBackend}) = getLeftAxis(plt.o)
getRightAxis(plt::Plot{PyPlotBackend}) = getRightAxis(plt.o)
getAxis(plt::Plot{PyPlotBackend}, axis::Symbol) = (axis == :right ? getRightAxis : getLeftAxis)(plt)
# ---------------------------------------------------------------------------
# type PyPlotAxisWrapper
# ax
# rightax
# fig
# kwargs # for add_subplot
# end
#
# getfig(wrap::PyPlotAxisWrapper) = wrap.fig
#
#
#
# # get a reference to the correct axis
# function getLeftAxis(wrap::PyPlotAxisWrapper)
# if wrap.ax == nothing
# axes = wrap.fig.o[:axes]
# if isempty(axes)
# return wrap.fig.o[:add_subplot](111; wrap.kwargs...)
# end
# axes[1]
# else
# wrap.ax
# end
# end
#
# function getRightAxis(wrap::PyPlotAxisWrapper)
# if wrap.rightax == nothing
# wrap.rightax = getLeftAxis(wrap)[:twinx]()
# end
# wrap.rightax
# end
#
# getLeftAxis(plt::Plot{PyPlotBackend}) = getLeftAxis(plt.o)
# getRightAxis(plt::Plot{PyPlotBackend}) = getRightAxis(plt.o)
# # getAxis(plt::Plot{PyPlotBackend}, axis::Symbol) = (axis == :right ? getRightAxis : getLeftAxis)(plt)
function handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Bool)
@ -272,13 +294,13 @@ handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(p
# ---------------------------------------------------------------------------
makePyPlotCurrent(wrap::PyPlotAxisWrapper) = wrap.ax == nothing ? PyPlot.figure(wrap.fig.o[:number]) : nothing
makePyPlotCurrent(plt::Plot{PyPlotBackend}) = plt.o == nothing ? nothing : makePyPlotCurrent(plt.o)
function _before_add_series(plt::Plot{PyPlotBackend})
makePyPlotCurrent(plt)
end
# makePyPlotCurrent(wrap::PyPlotAxisWrapper) = wrap.ax == nothing ? PyPlot.figure(wrap.fig.o[:number]) : nothing
# makePyPlotCurrent(plt::Plot{PyPlotBackend}) = plt.o == nothing ? nothing : makePyPlotCurrent(plt.o)
#
#
# function _before_add_series(plt::Plot{PyPlotBackend})
# makePyPlotCurrent(plt)
# end
# ------------------------------------------------------------------
@ -297,7 +319,7 @@ function pyplot_figure(plotargs::KW)
fig[:set_size_inches](w, h, forward = true)
fig[:set_facecolor](getPyPlotColor(plotargs[:background_color_outside]))
fig[:set_dpi](DPI)
fig[:set_tight_layout](true)
# fig[:set_tight_layout](true)
# clear the figure
PyPlot.clf()
@ -315,21 +337,27 @@ end
# ---------------------------------------------------------------------------
function _create_backend_figure(plt::Plot)
if haskey(plt.plotargs, :subplot)
wrap = nothing
else
wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(plt.plotargs), [])
pyplot_3d_setup!(wrap, plt.plotargs)
if get(plt.plotargs, :polar, false)
push!(wrap.kwargs, (:polar, true))
end
end
wrap
# plt = Plot(wrap, pkg, 0, plt.plotargs, KW[])
# plt
function _create_backend_figure(plt::Plot{PyPlotBackend})
fig = pyplot_figure(plt.plotargs)
# TODO: handle 3d and polar
fig
end
# function _create_backend_figure(plt::Plot)
# if haskey(plt.plotargs, :subplot)
# wrap = nothing
# else
# wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(plt.plotargs), [])
# pyplot_3d_setup!(wrap, plt.plotargs)
# if get(plt.plotargs, :polar, false)
# push!(wrap.kwargs, (:polar, true))
# end
# end
# wrap
# # plt = Plot(wrap, pkg, 0, plt.plotargs, KW[])
# # plt
# end
# ---------------------------------------------------------------------------
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, d::KW)
@ -368,11 +396,12 @@ pyfillcolormap(d::KW) = getPyPlotColorMap(d[:fillcolor], d[:fillalpha])
function _add_series(plt::Plot{PyPlotBackend}, series::Series)
d = series.d
st = d[:seriestype]
if !(st in supportedTypes(pkg))
error("seriestype $(st) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))")
if !(st in supportedTypes(plt.backend))
error("seriestype $(st) is unsupported in PyPlot. Choose from: $(supportedTypes(plt.backend))")
end
# 3D plots have a different underlying Axes object in PyPlot
# TODO: BUG: this adds to kwargs but never changes anything... source of subplot(wireframe(...)) bug?
if st in _3dTypes && isempty(plt.o.kwargs)
push!(plt.o.kwargs, (:projection, "3d"))
end
@ -380,7 +409,8 @@ function _add_series(plt::Plot{PyPlotBackend}, series::Series)
# PyPlot doesn't handle mismatched x/y
fix_xy_lengths!(plt, d)
ax = getAxis(plt, d[:axis])
# ax = getAxis(plt, d[:axis])
ax = getAxis(plt, get(d, :subplot, plt.subplots[1]))
x, y, z = d[:x], d[:y], d[:z]
@show typeof((x,y,z))
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
@ -769,11 +799,11 @@ end
# given a dimension (:x, :y, or :z), loop over the seriesargs KWs to find the min/max of the underlying data
function minmaxseries(ds, dimension, axis)
function minmaxseries(series_list, dimension, axis)
lo, hi = Inf, -Inf
for d in ds
d[:axis] == axis || continue
v = d[dimension]
for series in series_list
series.d[:axis] == axis || continue
v = series.d[dimension]
if length(v) > 0
vlo, vhi = extrema(v)
lo = min(lo, vlo)
@ -795,13 +825,13 @@ function set_lims!(plt::Plot{PyPlotBackend}, axis::Symbol)
ax = getAxis(plt, axis)
pargs = plt.plotargs
if pargs[:xlims] == :auto
ax[pargs[:polar] ? :set_tlim : :set_xlim](minmaxseries(plt.seriesargs, :x, axis)...)
ax[pargs[:polar] ? :set_tlim : :set_xlim](minmaxseries(plt.series_list, :x, axis)...)
end
if pargs[:ylims] == :auto
ax[pargs[:polar] ? :set_rlim : :set_ylim](minmaxseries(plt.seriesargs, :y, axis)...)
ax[pargs[:polar] ? :set_rlim : :set_ylim](minmaxseries(plt.series_list, :y, axis)...)
end
if pargs[:zlims] == :auto && haskey(ax, :set_zlim)
ax[:set_zlim](minmaxseries(plt.seriesargs, :z, axis)...)
ax[:set_zlim](minmaxseries(plt.series_list, :z, axis)...)
end
end
@ -811,7 +841,7 @@ end
# the x/y data for each handle (for example, plot and scatter)
function setxy!{X,Y}(plt::Plot{PyPlotBackend}, xy::Tuple{X,Y}, i::Integer)
d = plt.seriesargs[i]
d = plt.series_list[i].d
d[:x], d[:y] = xy
for handle in d[:serieshandle]
try
@ -826,7 +856,7 @@ end
function setxyz!{X,Y,Z}(plt::Plot{PyPlotBackend}, xyz::Tuple{X,Y,Z}, i::Integer)
d = plt.seriesargs[i]
d = plt.series_list[i].d
d[:x], d[:y], d[:z] = xyz
for handle in d[:serieshandle]
handle[:set_data](d[:x], d[:y])
@ -901,9 +931,9 @@ function updateAxisColors(ax, d::KW)
ax[:title][:set_color](guidecolor)
end
function usingRightAxis(plt::Plot{PyPlotBackend})
any(args -> args[:axis] in (:right,:auto), plt.seriesargs)
end
# function usingRightAxis(plt::Plot{PyPlotBackend})
# any(args -> args[:axis] in (:right,:auto), plt.seriesargs)
# end
# --------------------------------------------------------------------------
@ -912,7 +942,8 @@ end
function _update_plot(plt::Plot{PyPlotBackend}, d::KW)
# @show d
figorax = plt.o
ax = getLeftAxis(figorax)
# ax = getLeftAxis(figorax)
ax = getAxis(plt, plt.subplots[1])
# ticksz = get(d, :tickfont, plt.plotargs[:tickfont]).pointsize
guidesz = get(d, :guidefont, plt.plotargs[:guidefont]).pointsize
@ -920,15 +951,16 @@ function _update_plot(plt::Plot{PyPlotBackend}, d::KW)
haskey(d, :title) && ax[:set_title](d[:title])
ax[:title][:set_fontsize](guidesz)
# handle right y axis
axes = [getLeftAxis(figorax)]
if usingRightAxis(plt)
push!(axes, getRightAxis(figorax))
if get(d, :yrightlabel, "") != ""
rightax = getRightAxis(figorax)
rightax[:set_ylabel](d[:yrightlabel])
end
end
axes = [ax]
# # handle right y axis
# axes = [getLeftAxis(figorax)]
# if usingRightAxis(plt)
# push!(axes, getRightAxis(figorax))
# if get(d, :yrightlabel, "") != ""
# rightax = getRightAxis(figorax)
# rightax[:set_ylabel](d[:yrightlabel])
# end
# end
for letter in ("x", "y", "z")
axissym = symbol(letter*"axis")
@ -1044,7 +1076,7 @@ end
#
# # this will be called internally, when creating a subplot from existing plots
# # NOTE: if I ever need to "Rebuild a "ubplot from individual Plot's"... this is what I should use!
# function subplot(plts::AVec{Plot{PyPlotBackend}}, layout::SubplotLayout, d::KW)
# function subplot(plts::AVec{Plot{PyPlotBackend}}, layout::AbstractLayout, d::KW)
# validateSubplotSupported()
#
# p = length(layout)
@ -1104,15 +1136,27 @@ function addPyPlotLegend(plt::Plot, ax)
leg = plt.plotargs[:legend]
if leg != :none
# gotta do this to ensure both axes are included
args = filter(x -> !(x[:seriestype] in (
:hist,:density,:hexbin,:hist2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:heatmap,:path3d,:scatter3d, :pie, :image
)), plt.seriesargs)
args = filter(x -> x[:label] != "", args)
if length(args) > 0
leg = ax[:legend]([d[:serieshandle][1] for d in args],
[d[:label] for d in args],
labels = []
handles = []
for series in plt.series_list
if series.d[:label] != "" && !(series.d[:seriestype] in (
:hist,:density,:hexbin,:hist2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:heatmap,:path3d,:scatter3d, :pie, :image))
push!(handles, series.d[:serieshandle][1])
push!(labels, series.d[:label])
end
end
# args = filter(x -> !(x.d[:seriestype] in (
# :hist,:density,:hexbin,:hist2d,:hline,:vline,
# :contour,:contour3d,:surface,:wireframe,
# :heatmap,:path3d,:scatter3d, :pie, :image
# )), plt.series_list)
# args = filter(x -> x[:label] != "", args)
# if length(args) > 0
if !isempty(handles)
leg = ax[:legend](handles, #[d[:serieshandle][1] for d in args],
labels, #[d[:label] for d in args],
loc = get(_pyplot_legend_pos, leg, "best"),
scatterpoints = 1,
fontsize = plt.plotargs[:legendfont].pointsize

View File

@ -269,28 +269,28 @@ end
# -------------------------------
# create the underlying object (each backend will do this differently)
function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
isbefore && return false
i = 0
rows = Any[]
row = Any[]
for (i,(r,c)) in enumerate(subplt.layout)
push!(row, subplt.plts[i].o)
if c == ncols(subplt.layout, r)
push!(rows, Qwt.hsplitter(row...))
row = Any[]
end
end
# for rowcnt in subplt.layout.rowcounts
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# i += rowcnt
# end
subplt.o = Qwt.vsplitter(rows...)
# Qwt.resizewidget(subplt.o, getplotargs(subplt,1)[:size]...)
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
true
end
# # create the underlying object (each backend will do this differently)
# function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
# isbefore && return false
# i = 0
# rows = Any[]
# row = Any[]
# for (i,(r,c)) in enumerate(subplt.layout)
# push!(row, subplt.plts[i].o)
# if c == ncols(subplt.layout, r)
# push!(rows, Qwt.hsplitter(row...))
# row = Any[]
# end
# end
# # for rowcnt in subplt.layout.rowcounts
# # push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
# # i += rowcnt
# # end
# subplt.o = Qwt.vsplitter(rows...)
# # Qwt.resizewidget(subplt.o, getplotargs(subplt,1)[:size]...)
# # Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
# true
# end
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
for series in plt.o.lines
@ -311,13 +311,13 @@ function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
write(io, readall("/tmp/dfskjdhfkh.png"))
end
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
write(io, readall("/tmp/dfskjdhfkh.png"))
end
# function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
# Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
# write(io, readall("/tmp/dfskjdhfkh.png"))
# end
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
@ -325,9 +325,9 @@ function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
Qwt.showwidget(plt.o)
end
function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
for plt in subplt.plts
Qwt.refresh(plt.o)
end
Qwt.showwidget(subplt.o)
end
# function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
# for plt in subplt.plts
# Qwt.refresh(plt.o)
# end
# Qwt.showwidget(subplt.o)
# end

View File

@ -57,9 +57,9 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{[PkgName]AbstractBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# function _create_subplot(subplt::Subplot{[PkgName]AbstractBackend}, isbefore::Bool)
# # 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{[PkgName]AbstractBackend}, isx::Bool)
# TODO: call expand limits for each plot data
@ -79,6 +79,6 @@ function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend})
# TODO: display/show the plot
end
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
# TODO: display/show the subplot
end
# function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
# # TODO: display/show the subplot
# end

View File

@ -247,10 +247,10 @@ end
# we don't do very much for subplots... just stack them vertically
function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
isbefore && return false
true
end
# function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
# isbefore && return false
# true
# end
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsBackend})
@ -260,8 +260,8 @@ end
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
for plt in subplt.plts
gui(plt)
end
end
# function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
# for plt in subplt.plts
# gui(plt)
# end
# end

View File

@ -261,9 +261,9 @@ end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
end
# function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
# end
# ----------------------------------------------------------------
@ -302,6 +302,6 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
end
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# TODO: display/show the Subplot object
end
# function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
# # TODO: display/show the Subplot object
# end

View File

@ -257,10 +257,6 @@ function text(str, args...)
end
# -----------------------------------------------------------------------
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
type Axis #<: Associative{Symbol,Any}
d::KW
end
xaxis(args...) = Axis("x", args...)
yaxis(args...) = Axis("y", args...)
@ -299,7 +295,7 @@ function Axis(letter::AbstractString, args...; kw...)
end
# update an Axis object with magic args and keywords
function update!(a::Axis, args..., kw...)
function update!(a::Axis, args...; kw...)
# first process args
d = a.d
for arg in args

View File

@ -4,7 +4,7 @@
# -----------------------------------------------------------
"Simple grid, indices are row-major."
immutable GridLayout <: SubplotLayout
immutable GridLayout <: AbstractLayout
nr::Int
nc::Int
end
@ -30,7 +30,7 @@ Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
# -----------------------------------------------------------
"Number of plots per row"
immutable RowsLayout <: SubplotLayout
immutable RowsLayout <: AbstractLayout
numplts::Int
rowcounts::AbstractVector{Int}
end
@ -62,7 +62,7 @@ Base.getindex(layout::RowsLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1])
# -----------------------------------------------------------
"Flexible, nested layout with optional size percentages."
immutable FlexLayout <: SubplotLayout
immutable FlexLayout <: AbstractLayout
n::Int
grid::Matrix # Nested layouts. Each position
# can be a plot index or another FlexLayout

View File

@ -100,7 +100,7 @@ function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}
end
# this will be called internally
function subplot{P<:AbstractBackend}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::KW)
function subplot{P<:AbstractBackend}(plts::AVec{Plot{P}}, layout::AbstractLayout, d::KW)
validateSubplotSupported()
p = length(layout)
n = sum([plt.n for plt in plts])

View File

@ -47,10 +47,15 @@ function plot(args...; kw...)
d = KW(kw)
preprocessArgs!(d)
layout = pop!(d, :layout, Subplot())
subplots = Subplot[layout] # TODO: build full list
smap = SubplotMap(1 => layout) # TODO: actually build a map
# TODO: this seems wrong... I only call getPlotArgs when creating a new plot??
plotargs = merge(d, getPlotArgs(pkg, d, 1))
# plt = _create_plot(pkg, plotargs) # create a new, blank plot
plt = Plot(nothing, pkg, 0, plt.plotargs, KW[])
plt = Plot(nothing, pkg, 0, plotargs, Series[], subplots, smap, layout)
plt.o = _create_backend_figure(plt)
# now update the plot

View File

@ -2,11 +2,7 @@
# we are going to build recipes to do the processing and splitting of the args
# build the argument dictionary for a series
# function getSeriesArgs(pkg::AbstractBackend, plotargs::KW, d, commandIndex::Int, plotIndex::Int, globalIndex::Int) # TODO, pass in plotargs, not plt
function _add_defaults!(d::KW, plt::Plot, commandIndex::Int)
# kwdict = KW(d)
# d = KW()
pkg = plt.backend
n = plt.n
plotargs = getplotargs(plt, n)
@ -18,16 +14,10 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int)
setDictValue(d, d, k, commandIndex, _seriesDefaults)
end
# # groupby args?
# for k in (:idxfilter, :numUncounted, :dataframe)
# if haskey(kwdict, k)
# d[k] = kwdict[k]
# end
# end
# if haskey(_typeAliases, d[:seriestype])
# d[:seriestype] = _typeAliases[d[:seriestype]]
# end
if d[:subplot_index] == :auto
# TODO: something useful
d[:subplot_index] = 1
end
aliasesAndAutopick(d, :axis, _axesAliases, supportedAxes(pkg), plotIndex)
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
@ -76,9 +66,7 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int)
label = string(label, " (R)")
end
d[:label] = label
# warnOnUnsupported(pkg, d)
d
end

View File

@ -1,18 +1,24 @@
# -----------------------------------------------------------
# GridLayout
# -----------------------------------------------------------
Base.size(layout::EmptyLayout) = (0,0)
Base.length(layout::EmptyLayout) = 0
Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing
Base.size(layout::RootLayout) = (1,1)
Base.length(layout::RootLayout) = 1
# Base.getindex(layout::RootLayout, r::Int, c::Int) = layout.child
Base.size(subplot::Subplot) = (1,1)
Base.length(subplot::Subplot) = 1
Base.getindex(subplot::Subplot, r::Int, c::Int) = subplot
"nested, gridded layout with optional size percentages."
immutable GridLayout <: SubplotLayout
grid::Matrix # Nested layouts. Each position is an AbstractSubplot or another GridLayout
widths::Vector{Float64}
heights::Vector{Float64}
end
Base.size(layout::GridLayout) = size(layout.grid)
Base.length(layout::GridLayout) = length(layout.grid)
Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r,c]
# Base.start(layout::GridLayout) = 1
# Base.done(layout::GridLayout, state) = state > length(layout)
# function Base.next(layout::GridLayout, state)
@ -31,11 +37,10 @@ Base.length(layout::GridLayout) = length(layout.grid)
# (r,c), state + 1
# end
nrows(layout::GridLayout) = size(layout, 1)
ncols(layout::GridLayout) = size(layout, 2)
# nrows(layout::GridLayout) = size(layout, 1)
# ncols(layout::GridLayout) = size(layout, 2)
# get the plot index given row and column
Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r,c]
# -----------------------------------------------------------

View File

@ -16,39 +16,74 @@ end
wrap{T}(obj::T) = InputWrapper{T}(obj)
Base.isempty(wrapper::InputWrapper) = false
# -----------------------------------------------------------
# Axes
# -----------------------------------------------------------
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
type Axis #<: Associative{Symbol,Any}
d::KW
end
type AxisView
label::UTF8String
axis::Axis
end
abstract AbstractSubplot
immutable EmptySubplot <: AbstractSubplot end
type Subplot <: AbstractSubplot
axisviews::Vector{AxisView}
subplotargs::KW # args specific to this subplot
obj # can store backend-specific data... like a pyplot ax
# -----------------------------------------------------------
# Layouts
# -----------------------------------------------------------
abstract AbstractLayout
# -----------------------------------------------------------
# contains blank space
immutable EmptyLayout <: AbstractLayout end
# this is the parent of the top-level layout
immutable RootLayout <: AbstractLayout
# child::AbstractLayout
end
type Series
d::KW
# x
# y
# z
# subplots::Vector{Subplot}
# -----------------------------------------------------------
# a single subplot
type Subplot <: AbstractLayout
parent::AbstractLayout
attr::KW # args specific to this subplot
# axisviews::Vector{AxisView}
o # can store backend-specific data... like a pyplot ax
# Subplot(parent = RootLayout(); attr = KW())
end
# function Series(d::KW)
# x = pop!(d, :x)
# y = pop!(d, :y)
# z = pop!(d, :z)
# Series(d, x, y, z)
# end
Subplot() = Subplot(RootLayout(), KW(), nothing)
# -----------------------------------------------------------
# nested, gridded layout with optional size percentages
immutable GridLayout <: AbstractLayout
parent::AbstractLayout
grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
# widths::Vector{Float64}
# heights::Vector{Float64}
attr::KW
end
# -----------------------------------------------------------
typealias SubplotMap Dict{Any, Subplot}
# -----------------------------------------------------------
# Plot
# -----------------------------------------------------------
type Series
d::KW
end
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
o # the backend's plot object
backend::T # the backend type
@ -57,19 +92,15 @@ type Plot{T<:AbstractBackend} <: AbstractPlot{T}
# seriesargs::Vector{KW} # arguments for each series
series_list::Vector{Series} # arguments for each series
subplots::Vector{Subplot}
subplot_map::SubplotMap # provide any label as a map to a subplot
layout::AbstractLayout
end
# -----------------------------------------------------------
# Layout
# -----------------------------------------------------------
abstract SubplotLayout
# -----------------------------------------------------------
# Subplot
# -----------------------------------------------------------
# type Subplot{T<:AbstractBackend, L<:SubplotLayout} <: AbstractPlot{T}
# type Subplot{T<:AbstractBackend, L<:AbstractLayout} <: AbstractPlot{T}
# o # the underlying object
# plts::Vector{Plot{T}} # the individual plots
# backend::T