From 1b946a18d202eda5b6fbd61bbcd73b6b1d9a0b25 Mon Sep 17 00:00:00 2001 From: Simon Christ Date: Wed, 29 Apr 2020 11:43:57 +0200 Subject: [PATCH] passing of extra keywords to pgfplotsx (#2200) * transfer commit * extra_kwargs gets populated * make extra_kwargs a series kw. * turn extra_kwargs into Dict of Dicts * pass to pgfplotsx-backend * add test * change to dict * it even works * cleanup * undo pgfplots changes * all of them * fix tests * add pgfplotsx tests and special handling of :add Co-authored-by: Simon Christ --- src/args.jl | 22 ++++++++++++++-------- src/backends/pgfplotsx.jl | 30 ++++++++++++++++++++++++++++-- src/pipeline.jl | 15 ++++++++++++++- test/test_pgfplotsx.jl | 20 ++++++++++++++++++++ tmpplotsave.hdf5 | Bin 0 -> 111432 bytes 5 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 tmpplotsave.hdf5 diff --git a/src/args.jl b/src/args.jl index 88b1638c..3b779f07 100644 --- a/src/args.jl +++ b/src/args.jl @@ -281,6 +281,7 @@ const _series_defaults = KW( # one logical series to be broken up (path and markers, for example) :hover => nothing, # text to display when hovering over the data points :stride => (1,1), # array stride for wireframe/surface, the first element is the row stride and the second is the column stride. + :extra_kwargs => Dict() ) @@ -292,7 +293,7 @@ const _plot_defaults = KW( :fontfamily => "sans-serif", :size => (600,400), :pos => (0,0), - :window_title => "Plots.jl", + :window_title => "Plots.jl", :show => false, :layout => 1, :link => :none, @@ -304,8 +305,9 @@ const _plot_defaults = KW( :dpi => DPI, # dots per inch for images, etc :thickness_scaling => 1, :display_type => :auto, - :extra_kwargs => KW(), :warn_on_unsupported => true, + :extra_plot_kwargs => Dict(), + :extra_kwargs => :series, # directs collection of extra_kwargs ) @@ -354,6 +356,7 @@ const _subplot_defaults = KW( :colorbar_title => "", :framestyle => :axes, :camera => (30,30), + :extra_kwargs => Dict() ) const _axis_defaults = KW( @@ -656,7 +659,7 @@ function default(k::Symbol) return _axis_defaults_byletter[letter][key] end k == :letter && return k # for type recipe processing - k in _suppress_warnings || error("Unknown key: ", k) + return missing end function default(k::Symbol, v) @@ -1121,26 +1124,29 @@ const _already_warned = Dict{Symbol,Set{Symbol}}() const _to_warn = Set{Symbol}() function warn_on_unsupported_args(pkg::AbstractBackend, plotattributes) - if !get(plotattributes, :warn_on_unsupported, _plot_defaults[:warn_on_unsupported]) - return - end empty!(_to_warn) bend = backend_name(pkg) already_warned = get!(_already_warned, bend, Set{Symbol}()) + extra_kwargs = Dict{Symbol,Any}() for k in keys(plotattributes) is_attr_supported(pkg, k) && continue k in _suppress_warnings && continue - if plotattributes[k] != default(k) + default_value = default(k) + if ismissing(default_value) + extra_kwargs[k] = pop_kw!(plotattributes, k) + elseif plotattributes[k] != default(k) k in already_warned || push!(_to_warn, k) end end - if !isempty(_to_warn) + if !isempty(_to_warn) && + !get(plotattributes, :warn_on_unsupported, _plot_defaults[:warn_on_unsupported]) for k in sort(collect(_to_warn)) push!(already_warned, k) @warn("Keyword argument $k not supported with $pkg. Choose from: $(supported_attrs(pkg))") end end + return extra_kwargs end # _markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supported_markers(pkg) diff --git a/src/backends/pgfplotsx.jl b/src/backends/pgfplotsx.jl index f66c517c..4486330c 100644 --- a/src/backends/pgfplotsx.jl +++ b/src/backends/pgfplotsx.jl @@ -86,7 +86,16 @@ end function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) if !pgfx_plot.is_created || pgfx_plot.was_shown pgfx_sanitize_plot!(plt) - the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options()) + # extract extra kwargs + extra_plot_opt = plt[:extra_plot_kwargs] + extra_plot = nothing + if haskey(extra_plot_opt, :add) + extra_plot = wraptuple(pop!(extra_plot_opt,:add)) + end + the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options(extra_plot_opt...)) + if extra_plot !== nothing + push!(the_plot, extra_plot...) + end bgc = plt.attr[:background_color_outside] == :match ? plt.attr[:background_color] : plt.attr[:background_color_outside] if bgc isa Colors.Colorant @@ -243,7 +252,15 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) else PGFPlotsX.Axis end - axis = axisf(axis_opt) + extra_sp_opt = sp[:extra_kwargs] + extra_sp = nothing + if haskey(extra_sp_opt, :add) + extra_sp = wraptuple(pop!(extra_sp_opt,:add)) + end + axis = axisf(merge(axis_opt, PGFPlotsX.Options(extra_sp_opt...))) + if extra_sp !== nothing + push!(axis, extra_sp...) + end if sp[:legendtitle] !== nothing push!(axis, PGFPlotsX.Options("\\addlegendimage{empty legend}" => nothing)) push!( @@ -265,6 +282,12 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) "color" => single_color(opt[:linecolor]), "name path" => string(series_id), ) + extra_series_opt = series[:extra_kwargs] + extra_series = nothing + if haskey(extra_series_opt, :add) + extra_series = wraptuple(pop!(extra_series_opt,:add)) + end + series_opt = merge(series_opt, PGFPlotsX.Options(extra_series_opt...)) if RecipesPipeline.is3d(series) || st == :heatmap series_func = PGFPlotsX.Plot3 else @@ -344,6 +367,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) pgfx_series_coordinates!(sp, series, segment_opt, opt, rng) segment_plot = series_func(merge(series_opt, segment_opt), coordinates) + if extra_series !== nothing + push!(segment_plot, extra_series...) + end push!(axis, segment_plot) # fill between functions if sf isa Tuple && series[:ribbon] === nothing diff --git a/src/pipeline.jl b/src/pipeline.jl index 4865b257..c595a80e 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -341,7 +341,20 @@ function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol) end function _add_the_series(plt, sp, plotattributes) - warn_on_unsupported_args(plt.backend, plotattributes) + extra_kwargs = warn_on_unsupported_args(plt.backend, plotattributes) + if (kw = plt[:extra_kwargs]) isa AbstractDict + plt[:extra_plot_kwargs] = get(kw,:plot,KW()) + sp[:extra_kwargs] = get(kw,:subplot, KW()) + plotattributes[:extra_kwargs] = get(kw,:series,KW()) + elseif plt[:extra_kwargs] == :plot + plt[:extra_plot_kwargs] = extra_kwargs + elseif plt[:extra_kwargs] == :subplot + sp[:extra_kwargs] = extra_kwargs + elseif plt[:extra_kwargs] == :series + plotattributes[:extra_kwargs] = extra_kwargs + else + ArgumentError("Unsupported type for extra keyword arguments") + end warn_on_unsupported(plt.backend, plotattributes) series = Series(plotattributes) push!(plt.series_list, series) diff --git a/test/test_pgfplotsx.jl b/test/test_pgfplotsx.jl index a21ed191..302d4fb2 100644 --- a/test/test_pgfplotsx.jl +++ b/test/test_pgfplotsx.jl @@ -326,3 +326,23 @@ end # end end # testset end # testset + +@testset "Extra kwargs" begin + pl = plot(1:5, test = "me") + @test pl[1][1].plotattributes[:extra_kwargs][:test] == "me" + pl = plot(1:5, test = "me", extra_kwargs = :subplot) + @test pl[1].attr[:extra_kwargs][:test] == "me" + pl = plot(1:5, test = "me", extra_kwargs = :plot) + @test pl.attr[:extra_plot_kwargs][:test] == "me" + pl = plot(1:5, extra_kwargs = Dict(:plot => Dict(:test => "me"), :series => Dict(:and => "me too"))) + @test pl.attr[:extra_plot_kwargs][:test] == "me" + @test pl[1][1].plotattributes[:extra_kwargs][:and] == "me too" + pl = plot( + plot(1:5, title="Line"), + scatter(1:5, title="Scatter", extra_kwargs=Dict(:subplot=>Dict("axis line shift" => "10pt"))) + ) + Plots._update_plot_object(pl) + axes = Plots.pgfx_axes(pl.o) + @test !haskey(axes[1].options.dict, "axis line shift") + @test haskey(axes[2].options.dict, "axis line shift") +end # testset diff --git a/tmpplotsave.hdf5 b/tmpplotsave.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..66f7bab52535a0fd3d37105f4f3d7b11f7e96dff GIT binary patch literal 111432 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 literal 0 HcmV?d00001