1284 lines
41 KiB
Julia
1284 lines
41 KiB
Julia
|
||
# https://github.com/stevengj/PyPlot.jl
|
||
|
||
|
||
supported_args(::PyPlotBackend) = merge_with_base_supported([
|
||
:annotations,
|
||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
|
||
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
|
||
:label,
|
||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||
:markershape, :markercolor, :markersize, :markeralpha,
|
||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||
:fillrange, :fillcolor, :fillalpha,
|
||
:bins, :bar_width, :bar_edges, :bar_position,
|
||
:title, :title_location, :titlefont,
|
||
:window_title,
|
||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||
:tickfont, :guidefont, :legendfont,
|
||
:grid, :legend, :colorbar,
|
||
:marker_z,
|
||
:line_z,
|
||
:levels,
|
||
:ribbon, :quiver, :arrow,
|
||
:orientation,
|
||
:overwrite_figure,
|
||
:polar,
|
||
:normalize, :weights,
|
||
:contours, :aspect_ratio,
|
||
:match_dimensions,
|
||
:clims,
|
||
:inset_subplots,
|
||
])
|
||
supported_types(::PyPlotBackend) = [
|
||
:path, :steppre, :steppost, :shape,
|
||
:scatter, :histogram2d, :hexbin, :histogram,
|
||
:bar,
|
||
:heatmap, :pie, :image,
|
||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||
]
|
||
supported_styles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||
supported_markers(::PyPlotBackend) = vcat(_allMarkers, Shape)
|
||
supported_scales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
|
||
is_subplot_supported(::PyPlotBackend) = true
|
||
|
||
|
||
# --------------------------------------------------------------------------------------
|
||
|
||
|
||
function _initialize_backend(::PyPlotBackend)
|
||
@eval begin
|
||
# see: https://github.com/tbreloff/Plots.jl/issues/308
|
||
ENV["OVERRIDE_PYPLOT_DISPLAY"] = true
|
||
|
||
import PyPlot
|
||
export PyPlot
|
||
const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
|
||
const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
|
||
const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d"))
|
||
const pypatches = PyPlot.pywrap(PyPlot.pyimport("matplotlib.patches"))
|
||
const pyfont = PyPlot.pywrap(PyPlot.pyimport("matplotlib.font_manager"))
|
||
const pyticker = PyPlot.pywrap(PyPlot.pyimport("matplotlib.ticker"))
|
||
const pycmap = PyPlot.pywrap(PyPlot.pyimport("matplotlib.cm"))
|
||
const pynp = PyPlot.pywrap(PyPlot.pyimport("numpy"))
|
||
const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms"))
|
||
const pycollections = PyPlot.pywrap(PyPlot.pyimport("matplotlib.collections"))
|
||
const pyart3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d.art3d"))
|
||
end
|
||
|
||
# we don't want every command to update the figure
|
||
PyPlot.ioff()
|
||
end
|
||
|
||
# --------------------------------------------------------------------------------------
|
||
# --------------------------------------------------------------------------------------
|
||
|
||
# convert colorant to 4-tuple RGBA
|
||
py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
|
||
py_color(cvec::ColorVector, α=nothing) = map(py_color, convertColor(cvec, α).v)
|
||
py_color(grad::ColorGradient, α=nothing) = map(c -> py_color(c, α), grad.colors)
|
||
py_color(scheme::ColorScheme, α=nothing) = py_color(convertColor(getColor(scheme), α))
|
||
py_color(vec::AVec, α=nothing) = map(c->py_color(c,α), vec)
|
||
py_color(c, α=nothing) = py_color(convertColor(c, α))
|
||
|
||
function py_colormap(c::ColorGradient, α=nothing)
|
||
pyvals = [(v, py_color(getColorZ(c, v), α)) for v in c.values]
|
||
pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
|
||
end
|
||
|
||
# convert vectors and ColorVectors to standard ColorGradients
|
||
# TODO: move this logic to colors.jl and keep a barebones wrapper for pyplot
|
||
py_colormap(cv::ColorVector, α=nothing) = py_colormap(ColorGradient(cv.v), α)
|
||
py_colormap(v::AVec, α=nothing) = py_colormap(ColorGradient(v), α)
|
||
|
||
# anything else just gets a bluesred gradient
|
||
py_colormap(c, α=nothing) = py_colormap(default_gradient(), α)
|
||
|
||
function py_shading(c, z, α=nothing)
|
||
cmap = py_colormap(c, α)
|
||
ls = pycolors.pymember("LightSource")(270,45)
|
||
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
|
||
end
|
||
|
||
# get the style (solid, dashed, etc)
|
||
function py_linestyle(seriestype::Symbol, linestyle::Symbol)
|
||
seriestype == :none && return " "
|
||
linestyle == :solid && return "-"
|
||
linestyle == :dash && return "--"
|
||
linestyle == :dot && return ":"
|
||
linestyle == :dashdot && return "-."
|
||
warn("Unknown linestyle $linestyle")
|
||
return "-"
|
||
end
|
||
|
||
function py_marker(marker::Shape)
|
||
x, y = shape_coords(marker)
|
||
n = length(x)
|
||
mat = zeros(n+1,2)
|
||
for i=1:n
|
||
mat[i,1] = x[i]
|
||
mat[i,2] = y[i]
|
||
end
|
||
mat[n+1,:] = mat[1,:]
|
||
pypath.pymember("Path")(mat)
|
||
end
|
||
|
||
const _path_MOVETO = UInt8(1)
|
||
const _path_LINETO = UInt8(2)
|
||
const _path_CLOSEPOLY = UInt8(79)
|
||
|
||
# see http://matplotlib.org/users/path_tutorial.html
|
||
# and http://matplotlib.org/api/path_api.html#matplotlib.path.Path
|
||
function py_path(x, y)
|
||
n = length(x)
|
||
mat = zeros(n+1, 2)
|
||
codes = zeros(UInt8, n+1)
|
||
lastnan = true
|
||
for i=1:n
|
||
mat[i,1] = x[i]
|
||
mat[i,2] = y[i]
|
||
nan = !ok(x[i], y[i])
|
||
codes[i] = if nan
|
||
_path_CLOSEPOLY
|
||
else
|
||
lastnan ? _path_MOVETO : _path_LINETO
|
||
end
|
||
lastnan = nan
|
||
end
|
||
codes[n+1] = _path_CLOSEPOLY
|
||
pypath.pymember("Path")(mat, codes)
|
||
end
|
||
|
||
# get the marker shape
|
||
function py_marker(marker::Symbol)
|
||
marker == :none && return " "
|
||
marker == :circle && return "o"
|
||
marker == :rect && return "s"
|
||
marker == :diamond && return "D"
|
||
marker == :utriangle && return "^"
|
||
marker == :dtriangle && return "v"
|
||
marker == :cross && return "+"
|
||
marker == :xcross && return "x"
|
||
marker == :star5 && return "*"
|
||
marker == :pentagon && return "p"
|
||
marker == :hexagon && return "h"
|
||
marker == :octagon && return "8"
|
||
haskey(_shapes, marker) && return py_marker(_shapes[marker])
|
||
|
||
warn("Unknown marker $marker")
|
||
return "o"
|
||
end
|
||
|
||
# py_marker(markers::AVec) = map(py_marker, markers)
|
||
function py_marker(markers::AVec)
|
||
warn("Vectors of markers are currently unsupported in PyPlot: $markers")
|
||
py_marker(markers[1])
|
||
end
|
||
|
||
# pass through
|
||
function py_marker(marker::AbstractString)
|
||
@assert length(marker) == 1
|
||
marker
|
||
end
|
||
|
||
function py_stepstyle(seriestype::Symbol)
|
||
seriestype == :steppost && return "steps-post"
|
||
seriestype == :steppre && return "steps-pre"
|
||
return "default"
|
||
end
|
||
|
||
# untested... return a FontProperties object from a Plots.Font
|
||
function py_font(font::Font)
|
||
pyfont.pymember("FontProperties")(
|
||
family = font.family,
|
||
size = font.size
|
||
)
|
||
end
|
||
|
||
function get_locator_and_formatter(vals::AVec)
|
||
pyticker.pymember("FixedLocator")(1:length(vals)), pyticker.pymember("FixedFormatter")(vals)
|
||
end
|
||
|
||
function add_pyfixedformatter(cbar, vals::AVec)
|
||
cbar[:locator], cbar[:formatter] = get_locator_and_formatter(vals)
|
||
cbar[:update_ticks]()
|
||
end
|
||
|
||
# # TODO: smoothing should be moved into the SliceIt method, should not touch backends
|
||
# function handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Bool)
|
||
# if smooth
|
||
# xs, ys = regressionXY(d[:x], d[:y])
|
||
# ax[:plot](xs, ys,
|
||
# # linestyle = py_linestyle(:path, :dashdot),
|
||
# color = py_color(d[:linecolor]),
|
||
# linewidth = 2
|
||
# )
|
||
# end
|
||
# end
|
||
# handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(plt, ax, d, true)
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, d::KW)
|
||
x, y = d[:x], d[:y]
|
||
nx, ny = length(x), length(y)
|
||
if !isa(get(d, :z, nothing), Surface) && nx != ny
|
||
if nx < ny
|
||
d[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||
else
|
||
d[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||
end
|
||
end
|
||
end
|
||
|
||
# total hack due to PyPlot bug (see issue #145).
|
||
# hack: duplicate the color vector when the total rgba fields is the same as the series length
|
||
function py_color_fix(c, x)
|
||
if (typeof(c) <: AbstractArray && length(c)*4 == length(x)) ||
|
||
(typeof(c) <: Tuple && length(x) == 4)
|
||
vcat(c, c)
|
||
else
|
||
c
|
||
end
|
||
end
|
||
|
||
py_linecolor(d::KW) = py_color(d[:linecolor], d[:linealpha])
|
||
py_markercolor(d::KW) = py_color(d[:markercolor], d[:markeralpha])
|
||
py_markerstrokecolor(d::KW) = py_color(d[:markerstrokecolor], d[:markerstrokealpha])
|
||
py_fillcolor(d::KW) = py_color(d[:fillcolor], d[:fillalpha])
|
||
|
||
py_linecolormap(d::KW) = py_colormap(d[:linecolor], d[:linealpha])
|
||
py_markercolormap(d::KW) = py_colormap(d[:markercolor], d[:markeralpha])
|
||
py_fillcolormap(d::KW) = py_colormap(d[:fillcolor], d[:fillalpha])
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
# TODO: these can probably be removed eventually... right now they're just keeping things working before cleanup
|
||
|
||
# getAxis(sp::Subplot) = sp.o
|
||
|
||
# function getAxis(plt::Plot{PyPlotBackend}, series::Series)
|
||
# sp = get_subplot(plt, get(series.d, :subplot, 1))
|
||
# getAxis(sp)
|
||
# end
|
||
|
||
# getfig(o) = o
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Figure utils -- F*** matplotlib for making me work so hard to figure this crap out
|
||
|
||
# the drawing surface
|
||
py_canvas(fig) = fig[:canvas]
|
||
|
||
# the object controlling draw commands
|
||
py_renderer(fig) = py_canvas(fig)[:get_renderer]()
|
||
|
||
# draw commands... paint the screen (probably updating internals too)
|
||
py_drawfig(fig) = fig[:draw](py_renderer(fig))
|
||
# py_drawax(ax) = ax[:draw](py_renderer(ax[:get_figure]()))
|
||
|
||
# get a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!)
|
||
py_extents(obj) = obj[:get_window_extent]()[:get_points]()
|
||
|
||
|
||
# compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
|
||
function py_bbox(obj)
|
||
fl, fr, fb, ft = py_extents(obj[:get_figure]())
|
||
l, r, b, t = py_extents(obj)
|
||
BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px)
|
||
end
|
||
|
||
# get the bounding box of the union of the objects
|
||
function py_bbox(v::AVec)
|
||
bbox_union = defaultbox
|
||
for obj in v
|
||
bbox_union = bbox_union + py_bbox(obj)
|
||
end
|
||
bbox_union
|
||
end
|
||
|
||
# bounding box: union of axis tick labels
|
||
function py_bbox_ticks(ax, letter)
|
||
labels = ax[Symbol("get_"*letter*"ticklabels")]()
|
||
py_bbox(labels)
|
||
end
|
||
|
||
# bounding box: axis guide
|
||
function py_bbox_axislabel(ax, letter)
|
||
pyaxis_label = ax[Symbol("get_"*letter*"axis")]()[:label]
|
||
py_bbox(pyaxis_label)
|
||
end
|
||
|
||
# bounding box: union of axis ticks and guide
|
||
function py_bbox_axis(ax, letter)
|
||
ticks = py_bbox_ticks(ax, letter)
|
||
labels = py_bbox_axislabel(ax, letter)
|
||
# letter == "x" && @show ticks labels ticks+labels
|
||
ticks + labels
|
||
end
|
||
|
||
# bounding box: axis title
|
||
function py_bbox_title(ax)
|
||
bb = defaultbox
|
||
for s in (:title, :_left_title, :_right_title)
|
||
bb = bb + py_bbox(ax[s])
|
||
end
|
||
bb
|
||
end
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
# Create the window/figure for this backend.
|
||
function _create_backend_figure(plt::Plot{PyPlotBackend})
|
||
w,h = map(px2inch, plt[:size])
|
||
|
||
# # reuse the current figure?
|
||
fig = if plt[:overwrite_figure]
|
||
PyPlot.gcf()
|
||
else
|
||
fig = PyPlot.figure()
|
||
# finalizer(fig, close)
|
||
fig
|
||
end
|
||
|
||
# clear the figure
|
||
# PyPlot.clf()
|
||
fig
|
||
end
|
||
|
||
# Set up the subplot within the backend object.
|
||
# function _initialize_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||
|
||
function py_init_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
|
||
fig = plt.o
|
||
proj = sp[:projection]
|
||
proj = (proj in (nothing,:none) ? nothing : string(proj))
|
||
|
||
# add a new axis, and force it to create a new one by setting a distinct label
|
||
ax = fig[:add_axes](
|
||
[0,0,1,1],
|
||
label = string(gensym()),
|
||
projection = proj
|
||
)
|
||
sp.o = ax
|
||
end
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
# function _series_added(pkg::PyPlotBackend, plt::Plot, d::KW)
|
||
# TODO: change this to accept Subplot??
|
||
# function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||
|
||
function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||
d = series.d
|
||
st = d[:seriestype]
|
||
sp = d[:subplot]
|
||
ax = sp.o
|
||
|
||
if !(st in supported_types(plt.backend))
|
||
error("seriestype $(st) is unsupported in PyPlot. Choose from: $(supported_types(plt.backend))")
|
||
end
|
||
|
||
# PyPlot doesn't handle mismatched x/y
|
||
fix_xy_lengths!(plt, d)
|
||
|
||
# ax = getAxis(plt, series)
|
||
x, y, z = d[:x], d[:y], d[:z]
|
||
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
|
||
|
||
# handle zcolor and get c/cmap
|
||
extrakw = KW()
|
||
|
||
# holds references to any python object representing the matplotlib series
|
||
handles = []
|
||
needs_colorbar = false
|
||
discrete_colorbar_values = nothing
|
||
|
||
|
||
# pass in an integer value as an arg, but a levels list as a keyword arg
|
||
levels = d[:levels]
|
||
levelargs = if isscalar(levels)
|
||
(levels)
|
||
elseif isvector(levels)
|
||
extrakw[:levels] = levels
|
||
()
|
||
else
|
||
error("Only numbers and vectors are supported with levels keyword")
|
||
end
|
||
|
||
# for each plotting command, optionally build and add a series handle to the list
|
||
|
||
# line plot
|
||
if st in (:path, :path3d, :steppre, :steppost)
|
||
if d[:linewidth] > 0
|
||
if d[:line_z] == nothing
|
||
handle = ax[:plot](xyargs...;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
color = py_linecolor(d),
|
||
linewidth = d[:linewidth],
|
||
linestyle = py_linestyle(st, d[:linestyle]),
|
||
solid_capstyle = "round",
|
||
drawstyle = py_stepstyle(st)
|
||
)[1]
|
||
push!(handles, handle)
|
||
|
||
else
|
||
# multicolored line segments
|
||
n = length(x) - 1
|
||
segments = Array(Any,n)
|
||
kw = KW(
|
||
:label => d[:label],
|
||
:zorder => plt.n,
|
||
:cmap => py_linecolormap(d),
|
||
:linewidth => d[:linewidth],
|
||
:linestyle => py_linestyle(st, d[:linestyle])
|
||
)
|
||
handle = if is3d(st)
|
||
for i=1:n
|
||
segments[i] = [(cycle(x,i), cycle(y,i), cycle(z,i)), (cycle(x,i+1), cycle(y,i+1), cycle(z,i+1))]
|
||
end
|
||
lc = pyart3d.Line3DCollection(segments; kw...)
|
||
lc[:set_array](d[:line_z])
|
||
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||
lc
|
||
else
|
||
for i=1:n
|
||
segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
|
||
end
|
||
lc = pycollections.LineCollection(segments; kw...)
|
||
lc[:set_array](d[:line_z])
|
||
ax[:add_collection](lc)
|
||
lc
|
||
end
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
end
|
||
|
||
a = d[:arrow]
|
||
if a != nothing && !is3d(st) # TODO: handle 3d later
|
||
if typeof(a) != Arrow
|
||
warn("Unexpected type for arrow: $(typeof(a))")
|
||
else
|
||
arrowprops = KW(
|
||
:arrowstyle => "simple,head_length=$(a.headlength),head_width=$(a.headwidth)",
|
||
:shrinkA => 0,
|
||
:shrinkB => 0,
|
||
:edgecolor => py_linecolor(d),
|
||
:facecolor => py_linecolor(d),
|
||
:linewidth => d[:linewidth],
|
||
:linestyle => py_linestyle(st, d[:linestyle]),
|
||
)
|
||
add_arrows(x, y) do xyprev, xy
|
||
ax[:annotate]("",
|
||
xytext = (0.001xyprev[1] + 0.999xy[1], 0.001xyprev[2] + 0.999xy[2]),
|
||
xy = xy,
|
||
arrowprops = arrowprops,
|
||
zorder = 999
|
||
)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if st == :bar
|
||
bw = d[:bar_width]
|
||
if bw == nothing
|
||
bw = mean(diff(isvertical(d) ? x : y))
|
||
end
|
||
extrakw[isvertical(d) ? :width : :height] = bw
|
||
fr = get(d, :fillrange, nothing)
|
||
if fr != nothing
|
||
extrakw[:bottom] = fr
|
||
d[:fillrange] = nothing
|
||
end
|
||
handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
color = py_fillcolor(d),
|
||
edgecolor = py_linecolor(d),
|
||
linewidth = d[:linewidth],
|
||
align = d[:bar_edges] ? "edge" : "center",
|
||
extrakw...
|
||
)[1]
|
||
push!(handles, handle)
|
||
end
|
||
|
||
# if st == :sticks
|
||
# extrakw[isvertical(d) ? :width : :height] = 0.0
|
||
# handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||
# label = d[:label],
|
||
# zorder = plt.n,
|
||
# color = py_linecolor(d),
|
||
# edgecolor = py_linecolor(d),
|
||
# linewidth = d[:linewidth],
|
||
# align = "center",
|
||
# extrakw...
|
||
# )[1]
|
||
# push!(handles, handle)
|
||
# end
|
||
|
||
# add markers?
|
||
if d[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||
:scatter3d, :steppre, :steppost,
|
||
:bar)
|
||
extrakw = KW()
|
||
if d[:marker_z] == nothing
|
||
extrakw[:c] = py_color_fix(py_markercolor(d), x)
|
||
else
|
||
extrakw[:c] = convert(Vector{Float64}, d[:marker_z])
|
||
extrakw[:cmap] = py_markercolormap(d)
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
needs_colorbar = true
|
||
end
|
||
xyargs = if st == :bar && !isvertical(d)
|
||
(y, x)
|
||
else
|
||
xyargs
|
||
end
|
||
handle = ax[:scatter](xyargs...;
|
||
label = d[:label],
|
||
zorder = plt.n + 0.5,
|
||
marker = py_marker(d[:markershape]),
|
||
s = d[:markersize] .^ 2,
|
||
edgecolors = py_markerstrokecolor(d),
|
||
linewidths = d[:markerstrokewidth],
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
end
|
||
|
||
if st == :histogram
|
||
handle = ax[:hist](y;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
color = py_fillcolor(d),
|
||
edgecolor = py_linecolor(d),
|
||
linewidth = d[:linewidth],
|
||
bins = d[:bins],
|
||
normed = d[:normalize],
|
||
weights = d[:weights],
|
||
orientation = (isvertical(d) ? "vertical" : "horizontal"),
|
||
histtype = (d[:bar_position] == :stack ? "barstacked" : "bar")
|
||
)[3]
|
||
push!(handles, handle)
|
||
|
||
# expand the extrema... handle is a list of Rectangle objects
|
||
for rect in handle
|
||
xmin, ymin, xmax, ymax = rect[:get_bbox]()[:extents]
|
||
expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||
# expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||
# expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||
end
|
||
end
|
||
|
||
if st == :histogram2d
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
handle = ax[:hist2d](x, y;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
bins = d[:bins],
|
||
normed = d[:normalize],
|
||
weights = d[:weights],
|
||
cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||
extrakw...
|
||
)[4]
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
|
||
# expand the extrema... handle is a AxesImage object
|
||
expand_extrema!(sp, handle[:get_extent]()...)
|
||
# xmin, xmax, ymin, ymax = handle[:get_extent]()
|
||
# expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||
# expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||
end
|
||
|
||
if st == :hexbin
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
handle = ax[:hexbin](x, y;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
gridsize = d[:bins],
|
||
linewidths = d[:linewidth],
|
||
edgecolors = py_linecolor(d),
|
||
cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
end
|
||
|
||
# if st in (:hline,:vline)
|
||
# for yi in d[:y]
|
||
# func = ax[st == :hline ? :axhline : :axvline]
|
||
# handle = func(yi;
|
||
# linewidth=d[:linewidth],
|
||
# color=py_linecolor(d),
|
||
# linestyle=py_linestyle(st, d[:linestyle])
|
||
# )
|
||
# push!(handles, handle)
|
||
# end
|
||
# end
|
||
|
||
if st in (:contour, :contour3d)
|
||
# z = z.surf'
|
||
z = transpose_z(d, z.surf)
|
||
needs_colorbar = true
|
||
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
|
||
if st == :contour3d
|
||
extrakw[:extend3d] = true
|
||
end
|
||
|
||
# contour lines
|
||
handle = ax[:contour](x, y, z, levelargs...;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
linewidths = d[:linewidth],
|
||
linestyles = py_linestyle(st, d[:linestyle]),
|
||
cmap = py_linecolormap(d),
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
|
||
# contour fills
|
||
if d[:fillrange] != nothing
|
||
handle = ax[:contourf](x, y, z, levelargs...;
|
||
label = d[:label],
|
||
zorder = plt.n + 0.5,
|
||
cmap = py_fillcolormap(d),
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
end
|
||
end
|
||
|
||
if st in (:surface, :wireframe)
|
||
if typeof(z) <: AbstractMatrix || typeof(z) <: Surface
|
||
x, y, z = map(Array, (x,y,z))
|
||
if !ismatrix(x) || !ismatrix(y)
|
||
x = repmat(x', length(y), 1)
|
||
y = repmat(y, 1, length(d[:x]))
|
||
end
|
||
# z = z'
|
||
z = transpose_z(d, z)
|
||
if st == :surface
|
||
if d[:marker_z] != nothing
|
||
extrakw[:facecolors] = py_shading(d[:fillcolor], d[:marker_z], d[:fillalpha])
|
||
extrakw[:shade] = false
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
else
|
||
extrakw[:cmap] = py_fillcolormap(d)
|
||
needs_colorbar = true
|
||
end
|
||
end
|
||
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
rstride = 1,
|
||
cstride = 1,
|
||
linewidth = d[:linewidth],
|
||
edgecolor = py_linecolor(d),
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
|
||
# contours on the axis planes
|
||
if d[:contours]
|
||
for (zdir,mat) in (("x",x), ("y",y), ("z",z))
|
||
offset = (zdir == "y" ? maximum : minimum)(mat)
|
||
handle = ax[:contourf](x, y, z, levelargs...;
|
||
zdir = zdir,
|
||
cmap = py_fillcolormap(d),
|
||
offset = (zdir == "y" ? maximum : minimum)(mat) # where to draw the contour plane
|
||
)
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
end
|
||
end
|
||
|
||
# no colorbar if we are creating a surface LightSource
|
||
if haskey(extrakw, :facecolors)
|
||
needs_colorbar = false
|
||
end
|
||
|
||
elseif typeof(z) <: AbstractVector
|
||
# tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots)
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
handle = ax[:plot_trisurf](x, y, z;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
cmap = py_fillcolormap(d),
|
||
linewidth = d[:linewidth],
|
||
edgecolor = py_linecolor(d),
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
else
|
||
error("Unsupported z type $(typeof(z)) for seriestype=$st")
|
||
end
|
||
end
|
||
|
||
if st == :image
|
||
# @show typeof(z)
|
||
img = Array(transpose_z(d, z.surf))
|
||
z = if eltype(img) <: Colors.AbstractGray
|
||
float(img)
|
||
elseif eltype(img) <: Colorant
|
||
map(c -> Float64[red(c),green(c),blue(c)], img)
|
||
else
|
||
z # hopefully it's in a data format that will "just work" with imshow
|
||
end
|
||
handle = ax[:imshow](z;
|
||
zorder = plt.n,
|
||
cmap = py_colormap([:black, :white]),
|
||
vmin = 0.0,
|
||
vmax = 1.0
|
||
)
|
||
push!(handles, handle)
|
||
|
||
# expand extrema... handle is AxesImage object
|
||
xmin, xmax, ymax, ymin = handle[:get_extent]()
|
||
expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||
# sp[:yaxis].d[:flip] = true
|
||
end
|
||
|
||
if st == :heatmap
|
||
x, y, z = heatmap_edges(x), heatmap_edges(y), transpose_z(d, z.surf)
|
||
# if !(eltype(z) <: Number)
|
||
# z, discrete_colorbar_values = indices_and_unique_values(z)
|
||
# end
|
||
dvals = sp[:zaxis][:discrete_values]
|
||
if !isempty(dvals)
|
||
discrete_colorbar_values = dvals
|
||
end
|
||
|
||
clims = sp[:clims]
|
||
if is_2tuple(clims)
|
||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||
end
|
||
|
||
handle = ax[:pcolormesh](x, y, z;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
cmap = py_fillcolormap(d),
|
||
edgecolors = (d[:linewidth] > 0 ? py_linecolor(d) : "face"),
|
||
extrakw...
|
||
)
|
||
push!(handles, handle)
|
||
needs_colorbar = true
|
||
|
||
# TODO: this should probably be handled generically
|
||
# expand extrema... handle is a QuadMesh object
|
||
for path in handle[:properties]()["paths"]
|
||
verts = path[:vertices]
|
||
xmin, ymin = minimum(verts, 1)
|
||
xmax, ymax = maximum(verts, 1)
|
||
expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||
end
|
||
|
||
end
|
||
|
||
if st == :shape
|
||
path = py_path(x, y)
|
||
patches = pypatches.pymember("PathPatch")(path;
|
||
label = d[:label],
|
||
zorder = plt.n,
|
||
edgecolor = py_markerstrokecolor(d),
|
||
facecolor = py_markercolor(d),
|
||
linewidth = d[:markerstrokewidth],
|
||
fill = true
|
||
)
|
||
handle = ax[:add_patch](patches)
|
||
push!(handles, handle)
|
||
end
|
||
|
||
if st == :pie
|
||
handle = ax[:pie](y;
|
||
# colors = # a vector of colors?
|
||
labels = pie_labels(sp, series)
|
||
)[1]
|
||
push!(handles, handle)
|
||
|
||
# # expand extrema... get list of Wedge objects
|
||
# for wedge in handle
|
||
# path = wedge[:get_path]()
|
||
# for
|
||
lim = 1.1
|
||
expand_extrema!(sp, -lim, lim, -lim, lim)
|
||
end
|
||
|
||
d[:serieshandle] = handles
|
||
|
||
# # smoothing
|
||
# handleSmooth(plt, ax, d, d[:smooth])
|
||
|
||
# add the colorbar legend
|
||
if needs_colorbar && sp[:colorbar] != :none
|
||
# add keyword args for a discrete colorbar
|
||
handle = handles[end]
|
||
kw = KW()
|
||
if discrete_colorbar_values != nothing
|
||
locator, formatter = get_locator_and_formatter(discrete_colorbar_values)
|
||
# kw[:values] = 1:length(discrete_colorbar_values)
|
||
kw[:values] = sp[:zaxis][:continuous_values]
|
||
kw[:ticks] = locator
|
||
kw[:format] = formatter
|
||
kw[:boundaries] = vcat(0, kw[:values] + 0.5)
|
||
end
|
||
|
||
# create and store the colorbar object (handle) and the axis that it is drawn on.
|
||
# note: the colorbar axis is positioned independently from the subplot axis
|
||
fig = plt.o
|
||
cbax = fig[:add_axes]([0.8,0.1,0.03,0.8], label = string(gensym()))
|
||
sp.attr[:cbar_handle] = fig[:colorbar](handle; cax = cbax, kw...)
|
||
sp.attr[:cbar_ax] = cbax
|
||
end
|
||
|
||
# handle area filling
|
||
fillrange = d[:fillrange]
|
||
if fillrange != nothing && st != :contour
|
||
f, dim1, dim2 = if isvertical(d)
|
||
:fill_between, x, y
|
||
else
|
||
:fill_betweenx, y, x
|
||
end
|
||
args = if typeof(fillrange) <: Union{Real, AVec}
|
||
dim1, fillrange, dim2
|
||
else
|
||
dim1, fillrange...
|
||
end
|
||
|
||
handle = ax[f](args...;
|
||
zorder = plt.n,
|
||
facecolor = py_fillcolor(d),
|
||
linewidths = 0
|
||
)
|
||
push!(handles, handle)
|
||
end
|
||
end
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
|
||
# function update_limits!(sp::Subplot{PyPlotBackend}, series::Series, letters)
|
||
# for letter in letters
|
||
# py_set_lims(sp.o, sp[Symbol(letter, :axis)])
|
||
# end
|
||
# end
|
||
|
||
# function _series_updated(plt::Plot{PyPlotBackend}, series::Series)
|
||
# d = series.d
|
||
# for handle in get(d, :serieshandle, [])
|
||
# if is3d(series)
|
||
# handle[:set_data](d[:x], d[:y])
|
||
# handle[:set_3d_properties](d[:z])
|
||
# else
|
||
# try
|
||
# handle[:set_data](d[:x], d[:y])
|
||
# catch
|
||
# handle[:set_offsets](hcat(d[:x], d[:y]))
|
||
# end
|
||
# end
|
||
# end
|
||
# update_limits!(d[:subplot], series, is3d(series) ? (:x,:y,:z) : (:x,:y))
|
||
# end
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
|
||
function py_set_lims(ax, axis::Axis)
|
||
letter = axis[:letter]
|
||
lfrom, lto = axis_limits(axis)
|
||
ax[Symbol("set_", letter, "lim")](lfrom, lto)
|
||
end
|
||
|
||
function py_set_ticks(ax, ticks, letter)
|
||
ticks == :auto && return
|
||
axis = ax[Symbol(letter,"axis")]
|
||
if ticks == :none || ticks == nothing
|
||
kw = KW()
|
||
for dir in (:top,:bottom,:left,:right)
|
||
kw[dir] = kw[Symbol(:label,dir)] = "off"
|
||
end
|
||
axis[:set_tick_params](;which="both", kw...)
|
||
return
|
||
end
|
||
|
||
ttype = ticksType(ticks)
|
||
if ttype == :ticks
|
||
axis[:set_ticks](ticks)
|
||
elseif ttype == :ticks_and_labels
|
||
axis[:set_ticks](ticks[1])
|
||
axis[:set_ticklabels](ticks[2])
|
||
else
|
||
error("Invalid input for $(letter)ticks: $ticks")
|
||
end
|
||
end
|
||
|
||
function py_compute_axis_minval(axis::Axis)
|
||
# compute the smallest absolute value for the log scale's linear threshold
|
||
minval = 1.0
|
||
sp = axis.sp
|
||
for series in series_list(axis.sp)
|
||
v = series.d[axis[:letter]]
|
||
if !isempty(v)
|
||
minval = min(minval, minimum(abs(v)))
|
||
end
|
||
end
|
||
|
||
# now if the axis limits go to a smaller abs value, use that instead
|
||
vmin, vmax = axis_limits(axis)
|
||
minval = min(minval, abs(vmin), abs(vmax))
|
||
|
||
minval
|
||
end
|
||
|
||
function py_set_scale(ax, axis::Axis)
|
||
scale = axis[:scale]
|
||
letter = axis[:letter]
|
||
scale in supported_scales() || return warn("Unhandled scale value in pyplot: $scale")
|
||
func = ax[Symbol("set_", letter, "scale")]
|
||
kw = KW()
|
||
arg = if scale == :identity
|
||
"linear"
|
||
else
|
||
kw[Symbol(:base,letter)] = if scale == :ln
|
||
e
|
||
elseif scale == :log2
|
||
2
|
||
elseif scale == :log10
|
||
10
|
||
end
|
||
kw[Symbol(:linthresh,letter)] = max(1e-16, py_compute_axis_minval(axis))
|
||
"symlog"
|
||
end
|
||
func(arg; kw...)
|
||
end
|
||
|
||
|
||
function py_set_axis_colors(ax, a::Axis)
|
||
for (loc, spine) in ax[:spines]
|
||
spine[:set_color](py_color(a[:foreground_color_border]))
|
||
end
|
||
axissym = Symbol(a[:letter], :axis)
|
||
if haskey(ax, axissym)
|
||
ax[:tick_params](axis=string(a[:letter]), which="both",
|
||
colors=py_color(a[:foreground_color_axis]),
|
||
labelcolor=py_color(a[:foreground_color_text]))
|
||
ax[axissym][:label][:set_color](py_color(a[:foreground_color_guide]))
|
||
end
|
||
end
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
|
||
|
||
function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||
# update the fig
|
||
w, h = plt[:size]
|
||
fig = plt.o
|
||
fig[:clear]()
|
||
fig[:set_size_inches](px2inch(w), px2inch(h), forward = true)
|
||
fig[:set_facecolor](py_color(plt[:background_color_outside]))
|
||
fig[:set_dpi](DPI)
|
||
|
||
# resize the window
|
||
PyPlot.plt[:get_current_fig_manager]()[:resize](w, h)
|
||
|
||
# initialize subplots
|
||
for sp in plt.subplots
|
||
py_init_subplot(plt, sp)
|
||
end
|
||
|
||
# add the series
|
||
for series in plt.series_list
|
||
py_add_series(plt, series)
|
||
end
|
||
|
||
# update subplots
|
||
for sp in plt.subplots
|
||
ax = sp.o
|
||
if ax == nothing
|
||
continue
|
||
end
|
||
|
||
# add the annotations
|
||
for ann in sp[:annotations]
|
||
py_add_annotations(sp, ann...)
|
||
end
|
||
|
||
# title
|
||
if sp[:title] != ""
|
||
loc = lowercase(string(sp[:title_location]))
|
||
func = if loc == "left"
|
||
:_left_title
|
||
elseif loc == "right"
|
||
:_right_title
|
||
else
|
||
:title
|
||
end
|
||
ax[func][:set_text](sp[:title])
|
||
ax[func][:set_fontsize](sp[:titlefont].pointsize)
|
||
ax[func][:set_color](py_color(sp[:foreground_color_title]))
|
||
# ax[:set_title](sp[:title], loc = loc)
|
||
end
|
||
|
||
# axis attributes
|
||
for letter in (:x, :y, :z)
|
||
axissym = Symbol(letter, :axis)
|
||
axis = sp[axissym]
|
||
haskey(ax, axissym) || continue
|
||
py_set_scale(ax, axis)
|
||
py_set_lims(ax, axis)
|
||
py_set_ticks(ax, get_ticks(axis), letter)
|
||
ax[Symbol("set_", letter, "label")](axis[:guide])
|
||
if get(axis.d, :flip, false)
|
||
ax[Symbol("invert_", letter, "axis")]()
|
||
end
|
||
ax[axissym][:label][:set_fontsize](axis[:guidefont].pointsize)
|
||
for lab in ax[Symbol("get_", letter, "ticklabels")]()
|
||
lab[:set_fontsize](axis[:tickfont].pointsize)
|
||
lab[:set_rotation](axis[:rotation])
|
||
end
|
||
if sp[:grid]
|
||
fgcolor = py_color(sp[:foreground_color_grid])
|
||
ax[axissym][:grid](true, color = fgcolor)
|
||
ax[:set_axisbelow](true)
|
||
end
|
||
py_set_axis_colors(ax, axis)
|
||
end
|
||
|
||
# aspect ratio
|
||
aratio = sp[:aspect_ratio]
|
||
if aratio != :none
|
||
ax[:set_aspect](isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
|
||
end
|
||
|
||
# legend
|
||
py_add_legend(plt, sp, ax)
|
||
|
||
# this sets the bg color inside the grid
|
||
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside]))
|
||
end
|
||
py_drawfig(fig)
|
||
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[:left_margin]
|
||
toppad = sp[:top_margin]
|
||
rightpad = sp[:right_margin]
|
||
bottompad = sp[: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
|
||
|
||
|
||
# -----------------------------------------------------------------
|
||
|
||
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
|
||
ax = sp.o
|
||
ax[:annotate](val, xy = (x,y), zorder = 999)
|
||
end
|
||
|
||
|
||
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
|
||
ax = sp.o
|
||
ax[:annotate](val.str,
|
||
xy = (x,y),
|
||
family = val.font.family,
|
||
color = py_color(val.font.color),
|
||
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
|
||
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
|
||
rotation = val.font.rotation * 180 / π,
|
||
size = val.font.pointsize,
|
||
zorder = 999
|
||
)
|
||
end
|
||
|
||
# -----------------------------------------------------------------
|
||
|
||
# function _remove_axis(plt::Plot{PyPlotBackend}, isx::Bool)
|
||
# if isx
|
||
# plot!(plt, xticks=zeros(0), xlabel="")
|
||
# else
|
||
# plot!(plt, yticks=zeros(0), ylabel="")
|
||
# end
|
||
# end
|
||
#
|
||
# function _expand_limits(lims, plt::Plot{PyPlotBackend}, isx::Bool)
|
||
# pltlims = plt.o.ax[isx ? :get_xbound : :get_ybound]()
|
||
# _expand_limits(lims, pltlims)
|
||
# end
|
||
|
||
# -----------------------------------------------------------------
|
||
|
||
const _pyplot_legend_pos = KW(
|
||
:right => "right",
|
||
:left => "center left",
|
||
:top => "upper center",
|
||
:bottom => "lower center",
|
||
:bottomleft => "lower left",
|
||
:bottomright => "lower right",
|
||
:topright => "upper right",
|
||
:topleft => "upper left"
|
||
)
|
||
|
||
function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||
leg = sp[:legend]
|
||
if leg != :none
|
||
# gotta do this to ensure both axes are included
|
||
labels = []
|
||
handles = []
|
||
for series in series_list(sp)
|
||
if should_add_to_legend(series)
|
||
# add a line/marker and a label
|
||
push!(handles, if series.d[:seriestype] == :histogram
|
||
PyPlot.plt[:Line2D]((0,1),(0,0), color=py_fillcolor(series.d), linewidth=4)
|
||
else
|
||
series.d[:serieshandle][1]
|
||
end)
|
||
push!(labels, series.d[:label])
|
||
end
|
||
end
|
||
|
||
# if anything was added, call ax.legend and set the colors
|
||
if !isempty(handles)
|
||
leg = ax[:legend](handles,
|
||
labels,
|
||
loc = get(_pyplot_legend_pos, leg, "best"),
|
||
scatterpoints = 1,
|
||
fontsize = sp[:legendfont].pointsize
|
||
# framealpha = 0.6
|
||
)
|
||
leg[:set_zorder](1000)
|
||
|
||
fgcolor = py_color(sp[:foreground_color_legend])
|
||
for txt in leg[:get_texts]()
|
||
PyPlot.plt[:setp](txt, color = fgcolor)
|
||
end
|
||
|
||
# set some legend properties
|
||
frame = leg[:get_frame]()
|
||
frame[:set_facecolor](py_color(sp[:background_color_legend]))
|
||
frame[:set_edgecolor](fgcolor)
|
||
end
|
||
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_plot_object(plt::Plot{PyPlotBackend})
|
||
for sp in plt.subplots
|
||
ax = sp.o
|
||
ax == nothing && return
|
||
figw, figh = sp.plt[: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
|
||
PyPlot.draw()
|
||
end
|
||
|
||
# -----------------------------------------------------------------
|
||
# display/output
|
||
|
||
function _display(plt::Plot{PyPlotBackend})
|
||
plt.o[:show]()
|
||
end
|
||
|
||
|
||
|
||
const _pyplot_mimeformats = Dict(
|
||
"application/eps" => "eps",
|
||
"image/eps" => "eps",
|
||
"application/pdf" => "pdf",
|
||
"image/png" => "png",
|
||
"application/postscript" => "ps",
|
||
"image/svg+xml" => "svg"
|
||
)
|
||
|
||
|
||
for (mime, fmt) in _pyplot_mimeformats
|
||
@eval function _writemime(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
|
||
fig = plt.o
|
||
fig.o["canvas"][:print_figure](
|
||
io,
|
||
format=$fmt,
|
||
# bbox_inches = "tight",
|
||
# figsize = map(px2inch, plt[:size]),
|
||
facecolor = fig.o["get_facecolor"](),
|
||
edgecolor = "none",
|
||
dpi = DPI
|
||
)
|
||
end
|
||
end
|