Merge pull request #1956 from daschw/pyplot-update

Pyplot API update (fix #1932)
This commit is contained in:
Daniel Schwabeneder 2019-03-25 09:35:03 +01:00 committed by GitHub
commit 968b8109dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 182 additions and 134 deletions

45
NEWS.md
View File

@ -11,6 +11,51 @@
--- ---
## (current master) ## (current master)
## 0.24.0
- Update to the new PyCall and PyPlot API
## 0.23.2
- pyplot fixes
- Add option :tex_output_standalone to set the 'include_preamble' argument in the PGFPlots backend.
- fix ticks
- support plotly json mime
- fix image axis limits
- default to radius 0 at center for polar plots
## 0.23.1
- slightly faster load time
- fixed errant MethodError
- fix bar plots with unicodeplots
- better colorbars for contour
- add volume seriestype for GR
- fix passing a tuple to custom ticks
- add vline to pgfplots
- add tex output for pyplot
- better 3d axis labels for GR
## 0.23.0
- compatible with StatPlots -> StatsPlots name shift
- fix histograms for vectors with NaN and Inf
- change gif behaviour (remove cache-busting)
- improved docstrings for shorthands functions
- fix font rotation for pyplot
- fix greyscale images for pyplot
- clamp greyscale images with values outside 0,1
- support keyword argument for font options
- allow vector of markers for pyplot scatter
## 0.22.5
- improve behaviour of plotlyjs backend
## 0.22.4
- Add support for discrete contourf plots with GR
## 0.22.3
- Fix the `showtheme` function
## 0.22.2
- Allow annotations to accept a Tuple instead of the result of a text call (making it possible to specify font characteristics in recipes). E.g. `annotations = (2, 4, ("test", :right, 8, :red))` is the same as `annotations = (2, 4, text("test", :right, 8, :red))`
## 0.22.1 ## 0.22.1
- push PlotsDisplay just after REPLDisplay - push PlotsDisplay just after REPLDisplay

View File

@ -1,6 +1,6 @@
module Plots module Plots
_current_plots_version = v"0.20.6" _current_plots_version = v"0.24.0"
using Reexport using Reexport
@ -187,7 +187,7 @@ include("backends/plotly.jl")
include("backends/gr.jl") include("backends/gr.jl")
include("backends/web.jl") include("backends/web.jl")
include("shorthands.jl") include("shorthands.jl")
let PlotOrSubplot = Union{Plot, Subplot} let PlotOrSubplot = Union{Plot, Subplot}
global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...) global title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)

View File

@ -21,7 +21,7 @@ pyfont = PyPlot.pyimport("matplotlib.font_manager")
pyticker = PyPlot.pyimport("matplotlib.ticker") pyticker = PyPlot.pyimport("matplotlib.ticker")
pycmap = PyPlot.pyimport("matplotlib.cm") pycmap = PyPlot.pyimport("matplotlib.cm")
pynp = PyPlot.pyimport("numpy") pynp = PyPlot.pyimport("numpy")
pynp["seterr"](invalid="ignore") pynp."seterr"(invalid="ignore")
pytransforms = PyPlot.pyimport("matplotlib.transforms") pytransforms = PyPlot.pyimport("matplotlib.transforms")
pycollections = PyPlot.pyimport("matplotlib.collections") pycollections = PyPlot.pyimport("matplotlib.collections")
pyart3d = PyPlot.art3D pyart3d = PyPlot.art3D
@ -34,6 +34,11 @@ else
:set_facecolor :set_facecolor
end end
# PyCall API changes in v1.90.0
if !isdefined(PyCall, :_setproperty!)
@warn "Plots no longer supports PyCall < 1.90.0 and PyPlot < 2.8.0. Either update PyCall and PyPlot or pin Plots to a version <= 0.23.2."
end
# # convert colorant to 4-tuple RGBA # # convert colorant to 4-tuple RGBA
# py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha)) # py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
@ -64,8 +69,8 @@ py_color(c::Colorant, α) = py_color(plot_color(c, α))
function py_colormap(grad::ColorGradient) function py_colormap(grad::ColorGradient)
pyvals = [(z, py_color(grad[z])) for z in grad.values] pyvals = [(z, py_color(grad[z])) for z in grad.values]
cm = pycolors["LinearSegmentedColormap"][:from_list]("tmp", pyvals) cm = pycolors."LinearSegmentedColormap"."from_list"("tmp", pyvals)
cm[:set_bad](color=(0,0,0,0.0), alpha=0.0) cm."set_bad"(color=(0,0,0,0.0), alpha=0.0)
cm cm
end end
py_colormap(c) = py_colormap(cgrad()) py_colormap(c) = py_colormap(cgrad())
@ -73,8 +78,8 @@ py_colormap(c) = py_colormap(cgrad())
function py_shading(c, z) function py_shading(c, z)
cmap = py_colormap(c) cmap = py_colormap(c)
ls = pycolors["LightSource"](270,45) ls = pycolors."LightSource"(270,45)
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft") ls."shade"(z, cmap, vert_exag=0.1, blend_mode="soft")
end end
# get the style (solid, dashed, etc) # get the style (solid, dashed, etc)
@ -97,7 +102,7 @@ function py_marker(marker::Shape)
mat[i,2] = y[i] mat[i,2] = y[i]
end end
mat[n+1,:] = mat[1,:] mat[n+1,:] = mat[1,:]
pypath["Path"](mat) pypath."Path"(mat)
end end
const _path_MOVETO = UInt8(1) const _path_MOVETO = UInt8(1)
@ -182,7 +187,7 @@ end
# end # end
function get_locator_and_formatter(vals::AVec) function get_locator_and_formatter(vals::AVec)
pyticker["FixedLocator"](1:length(vals)), pyticker["FixedFormatter"](vals) pyticker."FixedLocator"(1:length(vals)), pyticker."FixedFormatter"(vals)
end end
function add_pyfixedformatter(cbar, vals::AVec) function add_pyfixedformatter(cbar, vals::AVec)
@ -204,7 +209,7 @@ end
function py_mask_nans(z) function py_mask_nans(z)
# pynp["ma"][:masked_invalid](z))) # pynp["ma"][:masked_invalid](z)))
PyCall.pycall(pynp["ma"][:masked_invalid], Any, z) PyCall.pycall(pynp."ma"."masked_invalid", Any, z)
# pynp["ma"][:masked_where](pynp["isnan"](z),z) # pynp["ma"][:masked_where](pynp["isnan"](z),z)
end end
@ -248,22 +253,22 @@ py_fillcolormap(series::Series) = py_colormap(series[:fillcolor])
# Figure utils -- F*** matplotlib for making me work so hard to figure this crap out # Figure utils -- F*** matplotlib for making me work so hard to figure this crap out
# the drawing surface # the drawing surface
py_canvas(fig) = fig[:canvas] py_canvas(fig) = fig."canvas"
# the object controlling draw commands # the object controlling draw commands
py_renderer(fig) = py_canvas(fig)[:get_renderer]() py_renderer(fig) = py_canvas(fig)."get_renderer"()
# draw commands... paint the screen (probably updating internals too) # draw commands... paint the screen (probably updating internals too)
py_drawfig(fig) = fig[:draw](py_renderer(fig)) py_drawfig(fig) = fig."draw"(py_renderer(fig))
# py_drawax(ax) = ax[:draw](py_renderer(ax[:get_figure]())) # py_drawax(ax) = ax[:draw](py_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!)
py_extents(obj) = obj[:get_window_extent]()[:get_points]() 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 # compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
function py_bbox(obj) function py_bbox(obj)
fl, fr, fb, ft = py_extents(obj[:get_figure]()) fl, fr, fb, ft = py_extents(obj."get_figure"())
l, r, b, t = py_extents(obj) l, r, b, t = py_extents(obj)
BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px) BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px)
end end
@ -279,13 +284,13 @@ end
# bounding box: union of axis tick labels # bounding box: union of axis tick labels
function py_bbox_ticks(ax, letter) function py_bbox_ticks(ax, letter)
labels = ax[Symbol("get_"*letter*"ticklabels")]() labels = getproperty(ax, Symbol("get_"*letter*"ticklabels"))()
py_bbox(labels) py_bbox(labels)
end end
# bounding box: axis guide # bounding box: axis guide
function py_bbox_axislabel(ax, letter) function py_bbox_axislabel(ax, letter)
pyaxis_label = ax[Symbol("get_"*letter*"axis")]()[:label] pyaxis_label = getproperty(ax, Symbol("get_"*letter*"axis"))().label
py_bbox(pyaxis_label) py_bbox(pyaxis_label)
end end
@ -301,7 +306,7 @@ end
function py_bbox_title(ax) function py_bbox_title(ax)
bb = defaultbox bb = defaultbox
for s in (:title, :_left_title, :_right_title) for s in (:title, :_left_title, :_right_title)
bb = bb + py_bbox(ax[s]) bb = bb + py_bbox(getproperty(ax, s))
end end
bb bb
end end
@ -339,7 +344,7 @@ function py_init_subplot(plt::Plot{PyPlotBackend}, sp::Subplot{PyPlotBackend})
proj = (proj in (nothing,:none) ? nothing : string(proj)) 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 # add a new axis, and force it to create a new one by setting a distinct label
ax = fig[:add_axes]( ax = fig."add_axes"(
[0,0,1,1], [0,0,1,1],
label = string(gensym()), label = string(gensym()),
projection = proj projection = proj
@ -440,7 +445,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
# push!(handles, handle) # push!(handles, handle)
# else # else
for (i, rng) in enumerate(iter_segments(series)) for (i, rng) in enumerate(iter_segments(series))
handle = ax[:plot]((arg[rng] for arg in xyargs)...; handle = ax."plot"((arg[rng] for arg in xyargs)...;
label = i == 1 ? series[:label] : "", label = i == 1 ? series[:label] : "",
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
color = py_color(get_linecolor(series, clims, i), get_linealpha(series, i)), color = py_color(get_linecolor(series, clims, i), get_linealpha(series, i)),
@ -468,7 +473,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
:linestyle => py_linestyle(st, get_linestyle(series)), :linestyle => py_linestyle(st, get_linestyle(series)),
) )
add_arrows(x, y) do xyprev, xy add_arrows(x, y) do xyprev, xy
ax[:annotate]("", ax."annotate"("",
xytext = (0.001xyprev[1] + 0.999xy[1], 0.001xyprev[2] + 0.999xy[2]), xytext = (0.001xyprev[1] + 0.999xy[1], 0.001xyprev[2] + 0.999xy[2]),
xy = xy, xy = xy,
arrowprops = arrowprops, arrowprops = arrowprops,
@ -514,7 +519,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
for i=1:length(y) for i=1:length(y)
extrakw[:c] = _cycle(markercolor, i) extrakw[:c] = _cycle(markercolor, i)
push!(handle, ax[:scatter](_cycle(x,i), _cycle(y,i); push!(handle, ax."scatter"(_cycle(x,i), _cycle(y,i);
label = series[:label], label = series[:label],
zorder = series[:series_plotindex] + 0.5, zorder = series[:series_plotindex] + 0.5,
marker = py_marker(_cycle(shapes,i)), marker = py_marker(_cycle(shapes,i)),
@ -553,7 +558,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
continue continue
end end
push!(handle, ax[:scatter](cur_x_list, cur_y_list; push!(handle, ax."scatter"(cur_x_list, cur_y_list;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex] + 0.5, zorder = series[:series_plotindex] + 0.5,
marker = prev_marker, marker = prev_marker,
@ -574,7 +579,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end end
if !isempty(cur_color_list) if !isempty(cur_color_list)
push!(handle, ax[:scatter](cur_x_list, cur_y_list; push!(handle, ax."scatter"(cur_x_list, cur_y_list;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex] + 0.5, zorder = series[:series_plotindex] + 0.5,
marker = prev_marker, marker = prev_marker,
@ -589,7 +594,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
push!(handles, handle) push!(handles, handle)
else else
# do a normal scatter plot # do a normal scatter plot
handle = ax[:scatter](xyargs...; handle = ax."scatter"(xyargs...;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex] + 0.5, zorder = series[:series_plotindex] + 0.5,
marker = py_marker(series[:markershape]), marker = py_marker(series[:markershape]),
@ -603,7 +608,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end end
if st == :hexbin if st == :hexbin
handle = ax[:hexbin](x, y; handle = ax."hexbin"(x, y;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
gridsize = series[:bins], gridsize = series[:bins],
@ -635,7 +640,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
end end
# contour lines # contour lines
handle = ax[:contour](x, y, z, levelargs...; handle = ax."contour"(x, y, z, levelargs...;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
linewidths = py_thickness_scale(plt, series[:linewidth]), linewidths = py_thickness_scale(plt, series[:linewidth]),
@ -643,13 +648,13 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
extrakw... extrakw...
) )
if series[:contour_labels] == true if series[:contour_labels] == true
PyPlot.clabel(handle, handle[:levels]) PyPlot."clabel"(handle, handle.levels)
end end
push!(handles, handle) push!(handles, handle)
# contour fills # contour fills
if series[:fillrange] != nothing if series[:fillrange] != nothing
handle = ax[:contourf](x, y, z, levelargs...; handle = ax."contourf"(x, y, z, levelargs...;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex] + 0.5, zorder = series[:series_plotindex] + 0.5,
extrakw... extrakw...
@ -675,7 +680,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
extrakw[:cmap] = py_fillcolormap(series) extrakw[:cmap] = py_fillcolormap(series)
end end
end end
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z; handle = getproperty(ax, st == :surface ? :plot_surface : :plot_wireframe)(x, y, z;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
rstride = series[:stride][1], rstride = series[:stride][1],
@ -690,7 +695,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
if series[:contours] if series[:contours]
for (zdir,mat) in (("x",x), ("y",y), ("z",z)) for (zdir,mat) in (("x",x), ("y",y), ("z",z))
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat)
handle = ax[:contourf](x, y, z, levelargs...; handle = ax."contourf"(x, y, z, levelargs...;
zdir = zdir, zdir = zdir,
cmap = py_fillcolormap(series), cmap = py_fillcolormap(series),
offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) # where to draw the contour plane offset = (zdir == "y" ? ignorenan_maximum : ignorenan_minimum)(mat) # where to draw the contour plane
@ -702,7 +707,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
elseif typeof(z) <: AbstractVector elseif typeof(z) <: AbstractVector
# tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots) # tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots)
handle = ax[:plot_trisurf](x, y, z; handle = ax."plot_trisurf"(x, y, z;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
cmap = py_fillcolormap(series), cmap = py_fillcolormap(series),
@ -727,7 +732,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
else else
z # hopefully it's in a data format that will "just work" with imshow z # hopefully it's in a data format that will "just work" with imshow
end end
handle = ax[:imshow](z; handle = ax."imshow"(z;
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
cmap = py_colormap(cgrad([:black, :white])), cmap = py_colormap(cgrad([:black, :white])),
vmin = 0.0, vmin = 0.0,
@ -737,7 +742,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
push!(handles, handle) push!(handles, handle)
# expand extrema... handle is AxesImage object # expand extrema... handle is AxesImage object
xmin, xmax, ymax, ymin = handle[:get_extent]() xmin, xmax, ymax, ymin = handle."get_extent"()
expand_extrema!(sp, xmin, xmax, ymin, ymax) expand_extrema!(sp, xmin, xmax, ymin, ymax)
# sp[:yaxis].series[:flip] = true # sp[:yaxis].series[:flip] = true
end end
@ -752,7 +757,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
discrete_colorbar_values = dvals discrete_colorbar_values = dvals
end end
handle = ax[:pcolormesh](x, y, py_mask_nans(z); handle = ax."pcolormesh"(x, y, py_mask_nans(z);
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
cmap = py_fillcolormap(series), cmap = py_fillcolormap(series),
@ -767,8 +772,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
handle = [] handle = []
for (i, rng) in enumerate(iter_segments(series)) for (i, rng) in enumerate(iter_segments(series))
if length(rng) > 1 if length(rng) > 1
path = pypath["Path"](hcat(x[rng], y[rng])) path = pypath."Path"(hcat(x[rng], y[rng]))
patches = pypatches["PathPatch"]( patches = pypatches."PathPatch"(
path; path;
label = series[:label], label = series[:label],
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
@ -778,14 +783,14 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
linestyle = py_linestyle(st, get_linestyle(series, i)), linestyle = py_linestyle(st, get_linestyle(series, i)),
fill = true fill = true
) )
push!(handle, ax[:add_patch](patches)) push!(handle, ax."add_patch"(patches))
end end
end end
push!(handles, handle) push!(handles, handle)
end end
if st == :pie if st == :pie
handle = ax[:pie](y; handle = ax."pie"(y;
# colors = # a vector of colors? # colors = # a vector of colors?
labels = pie_labels(sp, series) labels = pie_labels(sp, series)
)[1] )[1]
@ -820,7 +825,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng) dim1, _cycle(fillrange[1], rng), _cycle(fillrange[2], rng)
end end
handle = ax[f](args..., trues(n), false, py_fillstepstyle(st); handle = getproperty(ax, f)(args..., trues(n), false, py_fillstepstyle(st);
zorder = series[:series_plotindex], zorder = series[:series_plotindex],
facecolor = py_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)), facecolor = py_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
linewidths = 0 linewidths = 0
@ -841,27 +846,27 @@ end
function py_set_lims(ax, axis::Axis) function py_set_lims(ax, axis::Axis)
letter = axis[:letter] letter = axis[:letter]
lfrom, lto = axis_limits(axis) lfrom, lto = axis_limits(axis)
ax[Symbol("set_", letter, "lim")](lfrom, lto) getproperty(ax, Symbol("set_", letter, "lim"))(lfrom, lto)
end end
function py_set_ticks(ax, ticks, letter) function py_set_ticks(ax, ticks, letter)
ticks == :auto && return ticks == :auto && return
axis = ax[Symbol(letter,"axis")] axis = getproperty(ax, Symbol(letter,"axis"))
if ticks == :none || ticks == nothing || ticks == false if ticks == :none || ticks == nothing || ticks == false
kw = KW() kw = KW()
for dir in (:top,:bottom,:left,:right) for dir in (:top,:bottom,:left,:right)
kw[dir] = kw[Symbol(:label,dir)] = false kw[dir] = kw[Symbol(:label,dir)] = false
end end
axis[:set_tick_params](;which="both", kw...) axis."set_tick_params"(;which="both", kw...)
return return
end end
ttype = ticksType(ticks) ttype = ticksType(ticks)
if ttype == :ticks if ttype == :ticks
axis[:set_ticks](ticks) axis."set_ticks"(ticks)
elseif ttype == :ticks_and_labels elseif ttype == :ticks_and_labels
axis[:set_ticks](ticks[1]) axis."set_ticks"(ticks[1])
axis[:set_ticklabels](ticks[2]) axis."set_ticklabels"(ticks[2])
else else
error("Invalid input for $(letter)ticks: $ticks") error("Invalid input for $(letter)ticks: $ticks")
end end
@ -891,7 +896,7 @@ function py_set_scale(ax, axis::Axis)
scale = axis[:scale] scale = axis[:scale]
letter = axis[:letter] letter = axis[:letter]
scale in supported_scales() || return @warn("Unhandled scale value in pyplot: $scale") scale in supported_scales() || return @warn("Unhandled scale value in pyplot: $scale")
func = ax[Symbol("set_", letter, "scale")] func = getproperty(ax, Symbol("set_", letter, "scale"))
kw = KW() kw = KW()
arg = if scale == :identity arg = if scale == :identity
"linear" "linear"
@ -911,16 +916,16 @@ end
function py_set_axis_colors(sp, ax, a::Axis) function py_set_axis_colors(sp, ax, a::Axis)
for (loc, spine) in ax[:spines] for (loc, spine) in ax.spines
spine[:set_color](py_color(a[:foreground_color_border])) spine."set_color"(py_color(a[:foreground_color_border]))
end end
axissym = Symbol(a[:letter], :axis) axissym = Symbol(a[:letter], :axis)
if haskey(ax, axissym) if PyCall.hasproperty(ax, axissym)
tickcolor = sp[:framestyle] in (:zerolines, :grid) ? py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : py_color(a[:foreground_color_axis]) tickcolor = sp[:framestyle] in (:zerolines, :grid) ? py_color(plot_color(a[:foreground_color_grid], a[:gridalpha])) : py_color(a[:foreground_color_axis])
ax[:tick_params](axis=string(a[:letter]), which="both", ax."tick_params"(axis=string(a[:letter]), which="both",
colors=tickcolor, colors=tickcolor,
labelcolor=py_color(a[:tickfontcolor])) labelcolor=py_color(a[:tickfontcolor]))
ax[axissym][:label][:set_color](py_color(a[:guidefontcolor])) getproperty(ax, axissym).label.set_color(py_color(a[:guidefontcolor]))
end end
end end
@ -932,14 +937,14 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# update the fig # update the fig
w, h = plt[:size] w, h = plt[:size]
fig = plt.o fig = plt.o
fig[:clear]() fig."clear"()
dpi = plt[:dpi] dpi = plt[:dpi]
fig[:set_size_inches](w/DPI, h/DPI, forward = true) fig."set_size_inches"(w/DPI, h/DPI, forward = true)
fig[set_facecolor_sym](py_color(plt[:background_color_outside])) getproperty(fig, set_facecolor_sym)(py_color(plt[:background_color_outside]))
fig[:set_dpi](plt[:dpi]) fig."set_dpi"(plt[:dpi])
# resize the window # resize the window
PyPlot.plt[:get_current_fig_manager]()[:resize](w, h) PyPlot.plt."get_current_fig_manager"().resize(w, h)
# initialize subplots # initialize subplots
for sp in plt.subplots for sp in plt.subplots
@ -973,10 +978,10 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
else else
:title :title
end end
ax[func][:set_text](sp[:title]) getproperty(ax, func)."set_text"(sp[:title])
ax[func][:set_fontsize](py_thickness_scale(plt, sp[:titlefontsize])) getproperty(ax, func)."set_fontsize"(py_thickness_scale(plt, sp[:titlefontsize]))
ax[func][:set_family](sp[:titlefontfamily]) getproperty(ax, func)."set_family"(sp[:titlefontfamily])
ax[func][:set_color](py_color(sp[:titlefontcolor])) getproperty(ax, func)."set_color"(py_color(sp[:titlefontcolor]))
# ax[:set_title](sp[:title], loc = loc) # ax[:set_title](sp[:title], loc = loc)
end end
@ -996,7 +1001,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
kw[:boundaries] = vcat(0, kw[:values] + 0.5) kw[:boundaries] = vcat(0, kw[:values] + 0.5)
elseif any(colorbar_series[attr] != nothing for attr in (:line_z, :fill_z, :marker_z)) elseif any(colorbar_series[attr] != nothing for attr in (:line_z, :fill_z, :marker_z))
cmin, cmax = get_clims(sp) cmin, cmax = get_clims(sp)
norm = pycolors[:Normalize](vmin = cmin, vmax = cmax) norm = pycolors."Normalize"(vmin = cmin, vmax = cmax)
f = if colorbar_series[:line_z] != nothing f = if colorbar_series[:line_z] != nothing
py_linecolormap py_linecolormap
elseif colorbar_series[:fill_z] != nothing elseif colorbar_series[:fill_z] != nothing
@ -1004,21 +1009,21 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
else else
py_markercolormap py_markercolormap
end end
cmap = pycmap[:ScalarMappable](norm = norm, cmap = f(colorbar_series)) cmap = pycmap."ScalarMappable"(norm = norm, cmap = f(colorbar_series))
cmap[:set_array]([]) cmap."set_array"([])
handle = cmap handle = cmap
end end
# create and store the colorbar object (handle) and the axis that it is drawn on. # 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 # note: the colorbar axis is positioned independently from the subplot axis
fig = plt.o fig = plt.o
cbax = fig[:add_axes]([0.8,0.1,0.03,0.8], label = string(gensym())) cbax = fig."add_axes"([0.8,0.1,0.03,0.8], label = string(gensym()))
cb = fig[:colorbar](handle; cax = cbax, kw...) cb = fig."colorbar"(handle; cax = cbax, kw...)
cb[:set_label](sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor])) cb."set_label"(sp[:colorbar_title],size=py_thickness_scale(plt, sp[:yaxis][:guidefontsize]),family=sp[:yaxis][:guidefontfamily], color = py_color(sp[:yaxis][:guidefontcolor]))
for lab in cb[:ax][:yaxis][:get_ticklabels]() for lab in cb."ax"."yaxis"."get_ticklabels"()
lab[:set_fontsize](py_thickness_scale(plt, sp[:yaxis][:tickfontsize])) lab."set_fontsize"(py_thickness_scale(plt, sp[:yaxis][:tickfontsize]))
lab[:set_family](sp[:yaxis][:tickfontfamily]) lab."set_family"(sp[:yaxis][:tickfontfamily])
lab[:set_color](py_color(sp[:yaxis][:tickfontcolor])) lab."set_color"(py_color(sp[:yaxis][:tickfontcolor]))
end end
sp.attr[:cbar_handle] = cb sp.attr[:cbar_handle] = cb
sp.attr[:cbar_ax] = cbax sp.attr[:cbar_ax] = cbax
@ -1026,28 +1031,28 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# framestyle # framestyle
if !ispolar(sp) && !is3d(sp) if !ispolar(sp) && !is3d(sp)
ax[:spines]["left"][:set_linewidth](py_thickness_scale(plt, 1)) ax.spines["left"]."set_linewidth"(py_thickness_scale(plt, 1))
ax[:spines]["bottom"][:set_linewidth](py_thickness_scale(plt, 1)) ax.spines["bottom"]."set_linewidth"(py_thickness_scale(plt, 1))
if sp[:framestyle] == :semi if sp[:framestyle] == :semi
intensity = 0.5 intensity = 0.5
ax[:spines]["right"][:set_alpha](intensity) ax.spines["right"]."set_alpha"(intensity)
ax[:spines]["top"][:set_alpha](intensity) ax.spines["top"]."set_alpha"(intensity)
ax[:spines]["right"][:set_linewidth](py_thickness_scale(plt, intensity)) ax.spines["right"]."set_linewidth"(py_thickness_scale(plt, intensity))
ax[:spines]["top"][:set_linewidth](py_thickness_scale(plt, intensity)) ax.spines["top"]."set_linewidth"(py_thickness_scale(plt, intensity))
elseif sp[:framestyle] in (:axes, :origin) elseif sp[:framestyle] in (:axes, :origin)
ax[:spines]["right"][:set_visible](false) ax.spines["right"]."set_visible"(false)
ax[:spines]["top"][:set_visible](false) ax.spines["top"]."set_visible"(false)
if sp[:framestyle] == :origin if sp[:framestyle] == :origin
ax[:spines]["bottom"][:set_position]("zero") ax.spines["bottom"]."set_position"("zero")
ax[:spines]["left"][:set_position]("zero") ax.spines["left"]."set_position"("zero")
end end
elseif sp[:framestyle] in (:grid, :none, :zerolines) elseif sp[:framestyle] in (:grid, :none, :zerolines)
for (loc, spine) in ax[:spines] for (loc, spine) in ax.spines
spine[:set_visible](false) spine."set_visible"(false)
end end
if sp[:framestyle] == :zerolines if sp[:framestyle] == :zerolines
ax[:axhline](y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75)) ax."axhline"(y = 0, color = py_color(sp[:xaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
ax[:axvline](x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75)) ax."axvline"(x = 0, color = py_color(sp[:yaxis][:foreground_color_axis]), lw = py_thickness_scale(plt, 0.75))
end end
end end
end end
@ -1055,22 +1060,22 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# axis attributes # axis attributes
for letter in (:x, :y, :z) for letter in (:x, :y, :z)
axissym = Symbol(letter, :axis) axissym = Symbol(letter, :axis)
haskey(ax, axissym) || continue PyCall.hasproperty(ax, axissym) || continue
axis = sp[axissym] axis = sp[axissym]
pyaxis = ax[axissym] pyaxis = getproperty(ax, axissym)
if axis[:mirror] && letter != :z if axis[:mirror] && letter != :z
pos = letter == :x ? "top" : "right" pos = letter == :x ? "top" : "right"
pyaxis[:set_label_position](pos) # the guides pyaxis."set_label_position"(pos) # the guides
pyaxis[:set_ticks_position]("both") # the hash marks pyaxis."set_ticks_position"("both") # the hash marks
pyaxis[Symbol(:tick_, pos)]() # the tick labels getproperty(pyaxis, Symbol(:tick_, pos))() # the tick labels
end end
if axis[:guide_position] != :auto && letter != :z if axis[:guide_position] != :auto && letter != :z
pyaxis[:set_label_position](axis[:guide_position]) pyaxis."set_label_position"(axis[:guide_position])
end end
py_set_scale(ax, axis) py_set_scale(ax, axis)
axis[:ticks] != :native ? py_set_lims(ax, axis) : nothing axis[:ticks] != :native ? py_set_lims(ax, axis) : nothing
if ispolar(sp) && letter == :y if ispolar(sp) && letter == :y
ax[:set_rlabel_position](90) ax."set_rlabel_position"(90)
end end
ticks = sp[:framestyle] == :none ? nothing : get_ticks(axis) ticks = sp[:framestyle] == :none ? nothing : get_ticks(axis)
# don't show the 0 tick label for the origin framestyle # don't show the 0 tick label for the origin framestyle
@ -1078,28 +1083,28 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
ticks[2][ticks[1] .== 0] .= "" ticks[2][ticks[1] .== 0] .= ""
end end
axis[:ticks] != :native ? py_set_ticks(ax, ticks, letter) : nothing axis[:ticks] != :native ? py_set_ticks(ax, ticks, letter) : nothing
pyaxis[:set_tick_params](direction = axis[:tick_direction] == :out ? "out" : "in") pyaxis."set_tick_params"(direction = axis[:tick_direction] == :out ? "out" : "in")
ax[Symbol("set_", letter, "label")](axis[:guide]) getproperty(ax, Symbol("set_", letter, "label"))(axis[:guide])
if get(axis.plotattributes, :flip, false) if get(axis.plotattributes, :flip, false)
ax[Symbol("invert_", letter, "axis")]() getproperty(ax, Symbol("invert_", letter, "axis"))()
end end
pyaxis[:label][:set_fontsize](py_thickness_scale(plt, axis[:guidefontsize])) pyaxis."label"."set_fontsize"(py_thickness_scale(plt, axis[:guidefontsize]))
pyaxis[:label][:set_family](axis[:guidefontfamily]) pyaxis."label"."set_family"(axis[:guidefontfamily])
for lab in ax[Symbol("get_", letter, "ticklabels")]() for lab in getproperty(ax, Symbol("get_", letter, "ticklabels"))()
lab[:set_fontsize](py_thickness_scale(plt, axis[:tickfontsize])) lab."set_fontsize"(py_thickness_scale(plt, axis[:tickfontsize]))
lab[:set_family](axis[:tickfontfamily]) lab."set_family"(axis[:tickfontfamily])
lab[:set_rotation](axis[:rotation]) lab."set_rotation"(axis[:rotation])
end end
if axis[:grid] && !(ticks in (:none, nothing, false)) if axis[:grid] && !(ticks in (:none, nothing, false))
fgcolor = py_color(axis[:foreground_color_grid]) fgcolor = py_color(axis[:foreground_color_grid])
pyaxis[:grid](true, pyaxis."grid"(true,
color = fgcolor, color = fgcolor,
linestyle = py_linestyle(:line, axis[:gridstyle]), linestyle = py_linestyle(:line, axis[:gridstyle]),
linewidth = py_thickness_scale(plt, axis[:gridlinewidth]), linewidth = py_thickness_scale(plt, axis[:gridlinewidth]),
alpha = axis[:gridalpha]) alpha = axis[:gridalpha])
ax[:set_axisbelow](true) ax."set_axisbelow"(true)
else else
pyaxis[:grid](false) pyaxis."grid"(false)
end end
py_set_axis_colors(sp, ax, axis) py_set_axis_colors(sp, ax, axis)
end end
@ -1109,48 +1114,48 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
kw = KW() kw = KW()
for dir in (:top, :bottom) for dir in (:top, :bottom)
if ispolar(sp) if ispolar(sp)
ax[:spines]["polar"][:set_visible](false) ax."spines"."polar"."set_visible"(false)
else else
ax[:spines][string(dir)][:set_visible](false) getproperty(ax.spines, dir).set_visible(false)
end end
kw[dir] = kw[Symbol(:label,dir)] = false kw[dir] = kw[Symbol(:label,dir)] = false
end end
ax[:xaxis][:set_tick_params](; which="both", kw...) ax."xaxis"."set_tick_params"(; which="both", kw...)
end end
if !sp[:yaxis][:showaxis] if !sp[:yaxis][:showaxis]
kw = KW() kw = KW()
for dir in (:left, :right) for dir in (:left, :right)
if !ispolar(sp) if !ispolar(sp)
ax[:spines][string(dir)][:set_visible](false) getproperty(ax.spines, dir).set_visible(false)
end end
kw[dir] = kw[Symbol(:label,dir)] = false kw[dir] = kw[Symbol(:label,dir)] = false
end end
ax[:yaxis][:set_tick_params](; which="both", kw...) ax."yaxis"."set_tick_params"(; which="both", kw...)
end end
# aspect ratio # aspect ratio
aratio = sp[:aspect_ratio] aratio = sp[:aspect_ratio]
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
#camera/view angle #camera/view angle
if is3d(sp) if is3d(sp)
#convert azimuthal to match GR behaviour #convert azimuthal to match GR behaviour
#view_init(elevation, azimuthal) so reverse :camera args #view_init(elevation, azimuthal) so reverse :camera args
ax[:view_init]((sp[:camera].-(90,0))[end:-1:1]...) ax."view_init"((sp[:camera].-(90,0))[end:-1:1]...)
end end
# legend # legend
py_add_legend(plt, sp, ax) py_add_legend(plt, sp, ax)
# this sets the bg color inside the grid # this sets the bg color inside the grid
ax[set_facecolor_sym](py_color(sp[:background_color_inside])) getproperty(ax, set_facecolor_sym)(py_color(sp[:background_color_inside]))
# link axes # link axes
x_ax_link, y_ax_link = sp[:xaxis].sps[1].o, sp[:yaxis].sps[1].o x_ax_link, y_ax_link = sp[:xaxis].sps[1].o, sp[:yaxis].sps[1].o
ax != x_ax_link && ax[:get_shared_x_axes]()[:join](ax, sp[:xaxis].sps[1].o) ax != x_ax_link && ax."get_shared_x_axes"()."join"(ax, sp[:xaxis].sps[1].o)
ax != y_ax_link && ax[:get_shared_y_axes]()[:join](ax, sp[:yaxis].sps[1].o) ax != y_ax_link && ax."get_shared_y_axes"()."join"(ax, sp[:yaxis].sps[1].o)
end end
py_drawfig(fig) py_drawfig(fig)
end end
@ -1181,7 +1186,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
# optionally add the width of colorbar labels and colorbar to rightpad # optionally add the width of colorbar labels and colorbar to rightpad
if haskey(sp.attr, :cbar_ax) if haskey(sp.attr, :cbar_ax)
bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]()) bb = py_bbox(sp.attr[:cbar_handle]."ax"."get_yticklabels"())
sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px) sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
rightpad = rightpad + sp.attr[:cbar_width] rightpad = rightpad + sp.attr[:cbar_width]
end end
@ -1202,13 +1207,13 @@ end
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val) function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
ax = sp.o ax = sp.o
ax[:annotate](val, xy = (x,y), zorder = 999) ax."annotate"(val, xy = (x,y), zorder = 999)
end end
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText) function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
ax = sp.o ax = sp.o
ax[:annotate](val.str, ax."annotate"(val.str,
xy = (x,y), xy = (x,y),
family = val.font.family, family = val.font.family,
color = py_color(val.font.color), color = py_color(val.font.color),
@ -1244,14 +1249,14 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
if should_add_to_legend(series) if should_add_to_legend(series)
# add a line/marker and a label # add a line/marker and a label
push!(handles, if series[:seriestype] == :shape || series[:fillrange] != nothing push!(handles, if series[:seriestype] == :shape || series[:fillrange] != nothing
pypatches[:Patch]( pypatches."Patch"(
edgecolor = py_color(get_linecolor(series, clims), get_linealpha(series)), edgecolor = py_color(get_linecolor(series, clims), get_linealpha(series)),
facecolor = py_color(get_fillcolor(series, clims), get_fillalpha(series)), facecolor = py_color(get_fillcolor(series, clims), get_fillalpha(series)),
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)), linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
linestyle = py_linestyle(series[:seriestype], get_linestyle(series)) linestyle = py_linestyle(series[:seriestype], get_linestyle(series))
) )
elseif series[:seriestype] in (:path, :straightline, :scatter) elseif series[:seriestype] in (:path, :straightline, :scatter)
PyPlot.plt[:Line2D]((0,1),(0,0), PyPlot.plt."Line2D"((0,1),(0,0),
color = py_color(get_linecolor(series, clims), get_linealpha(series)), color = py_color(get_linecolor(series, clims), get_linealpha(series)),
linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)), linewidth = py_thickness_scale(plt, clamp(get_linewidth(series), 0, 5)),
linestyle = py_linestyle(:path, get_linestyle(series)), linestyle = py_linestyle(:path, get_linestyle(series)),
@ -1268,7 +1273,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
# if anything was added, call ax.legend and set the colors # if anything was added, call ax.legend and set the colors
if !isempty(handles) if !isempty(handles)
leg = ax[:legend](handles, leg = ax."legend"(handles,
labels, labels,
loc = get(_pyplot_legend_pos, leg, "best"), loc = get(_pyplot_legend_pos, leg, "best"),
scatterpoints = 1, scatterpoints = 1,
@ -1277,13 +1282,13 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
edgecolor = py_color(sp[:foreground_color_legend]), edgecolor = py_color(sp[:foreground_color_legend]),
framealpha = alpha(plot_color(sp[:background_color_legend])), framealpha = alpha(plot_color(sp[:background_color_legend])),
) )
frame = leg[:get_frame]() frame = leg."get_frame"()
frame[:set_linewidth](py_thickness_scale(plt, 1)) frame."set_linewidth"(py_thickness_scale(plt, 1))
leg[:set_zorder](1000) leg."set_zorder"(1000)
sp[:legendtitle] != nothing && leg[:set_title](sp[:legendtitle]) sp[:legendtitle] != nothing && leg."set_title"(sp[:legendtitle])
for txt in leg[:get_texts]() for txt in leg."get_texts"()
PyPlot.plt[:setp](txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily]) PyPlot.plt."setp"(txt, color = py_color(sp[:legendfontcolor]), family = sp[:legendfontfamily])
end end
end end
end end
@ -1301,7 +1306,7 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
figw, figh = sp.plt[:size] figw, figh = sp.plt[:size]
figw, figh = figw*px, figh*px figw, figh = figw*px, figh*px
pcts = bbox_to_pcts(sp.plotarea, figw, figh) pcts = bbox_to_pcts(sp.plotarea, figw, figh)
ax[:set_position](pcts) ax."set_position"(pcts)
# set the cbar position if there is one # set the cbar position if there is one
if haskey(sp.attr, :cbar_ax) if haskey(sp.attr, :cbar_ax)
@ -1311,7 +1316,7 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
has_toplabel = !(1e-7 < max(abs(ex.emax), abs(ex.emin)) < 1e7) has_toplabel = !(1e-7 < max(abs(ex.emax), abs(ex.emin)) < 1e7)
cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox) + (has_toplabel ? 4mm : 2mm), _cbar_width-1mm, height(sp.bbox) - (has_toplabel ? 6mm : 4mm)) cb_bbox = BoundingBox(right(sp.bbox)-cbw+1mm, top(sp.bbox) + (has_toplabel ? 4mm : 2mm), _cbar_width-1mm, height(sp.bbox) - (has_toplabel ? 6mm : 4mm))
pcts = bbox_to_pcts(cb_bbox, figw, figh) pcts = bbox_to_pcts(cb_bbox, figw, figh)
sp.attr[:cbar_ax][:set_position](pcts) sp.attr[:cbar_ax]."set_position"(pcts)
end end
end end
PyPlot.draw() PyPlot.draw()
@ -1320,9 +1325,7 @@ end
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# display/output # display/output
function _display(plt::Plot{PyPlotBackend}) _display(plt::Plot{PyPlotBackend}) = plt.o."show"()
isdefined(PyCall, :_setproperty!) ? plt.o.show() : plot.o[:show]()
end
@ -1340,16 +1343,16 @@ const _pyplot_mimeformats = Dict(
for (mime, fmt) in _pyplot_mimeformats for (mime, fmt) in _pyplot_mimeformats
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend}) @eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
fig = plt.o fig = plt.o
fig[:canvas][:print_figure]( fig."canvas"."print_figure"(
io, io,
format=$fmt, format=$fmt,
# bbox_inches = "tight", # bbox_inches = "tight",
# figsize = map(px2inch, plt[:size]), # figsize = map(px2inch, plt[:size]),
facecolor = fig[:get_facecolor](), facecolor = fig."get_facecolor"(),
edgecolor = "none", edgecolor = "none",
dpi = plt[:dpi] dpi = plt[:dpi]
) )
end end
end end
closeall(::PyPlotBackend) = PyPlot.plt[:close]("all") closeall(::PyPlotBackend) = PyPlot.plt."close"("all")