diff --git a/REQUIRE b/REQUIRE index 37795210..66cb8d87 100644 --- a/REQUIRE +++ b/REQUIRE @@ -4,5 +4,4 @@ RecipesBase Colors Reexport Compat -Requires FixedSizeArrays diff --git a/src/Plots.jl b/src/Plots.jl index 91fe70bb..665cb0d6 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -6,7 +6,7 @@ module Plots using Compat using Reexport @reexport using Colors -using Requires +# using Requires using FixedSizeArrays @reexport using RecipesBase @@ -171,7 +171,8 @@ include("backends.jl") include("args.jl") include("themes.jl") include("plot.jl") -include("series_args.jl") +# include("series_args.jl") +include("series_new.jl") include("subplot.jl") include("layouts.jl") include("recipes.jl") @@ -279,10 +280,11 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl const CURRENT_BACKEND = CurrentBackend(:none) +setup_dataframes() function __init__() setup_ijulia() - setup_dataframes() + # setup_dataframes() setup_atom() end diff --git a/src/plot.jl b/src/plot.jl index 47dfe353..50553e0d 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -45,16 +45,13 @@ When you pass in matrices, it splits by columns. See the documentation for more function plot(args...; kw...) pkg = backend() d = KW(kw) - userargs = preprocessArgs!(d) - dumpdict(d, "After plot preprocessing") + preprocessArgs!(d) plotargs = merge(d, getPlotArgs(pkg, d, 1)) - dumpdict(plotargs, "Plot args") plt = _create_plot(pkg, plotargs) # create a new, blank plot # now update the plot - _plot!(plt, d, userargs, args...) - # plot!(plt, args...; kw...) # add to it + _plot!(plt, d, args...) end @@ -73,15 +70,15 @@ end # this adds to a specific plot... most plot commands will flow through here function plot!(plt::Plot, args...; kw...) d = KW(kw) - userargs = preprocessArgs!(d) - _plot!(plt, d, userargs, args...) + preprocessArgs!(d) + _plot!(plt, d, args...) end # this is the core plotting function. recursively apply recipes to build # a list of series KW dicts. # note: at entry, we only have those preprocessed args which were passed in... no default values yet -function _plot!(plt::Plot, d::KW, userargs::KW, args...) +function _plot!(plt::Plot, d::KW, args...) # just in case the backend needs to set up the plot (make it current or something) _before_add_series(plt) @@ -93,7 +90,24 @@ function _plot!(plt::Plot, d::KW, userargs::KW, args...) end # for plotting recipes, swap out the args and update the parameter dictionary - args = RecipesBase.apply_recipe(d, userargs, args...) + # we are keeping a queue of series that still need to be processed. + # each pass through the loop, we pop one off and apply the recipe. + # the recipe will return a list a Series objects... the ones that are + # finished (no more args) get added to the kw_list, and the rest go into the queue + # for processing. + kw_list = KW[] + still_to_process = [RecipesBase.Series(copy(d), args)] + while !isempty(still_to_process) + next_series = pop!(still_to_process) + series_list = RecipesBase.apply_recipe(next_series.d, next_series.args...) + for series in series_list + if isempty(series.args) + push!(kw_list, series.d) + else + push!(still_to_process, series) + end + end + end # dumpdict(d, "After plot! preprocessing") @@ -110,32 +124,63 @@ function _plot!(plt::Plot, d::KW, userargs::KW, args...) # nothing # end + # TODO: why do i need to check for the subplot key? + # merge plot args if !haskey(d, :subplot) + # merge the plot args from the recipes, then update the plot colors for k in keys(_plotDefaults) - if haskey(d, k) - plt.plotargs[k] = d[k] + for kw in kw_list + if haskey(kw, k) + plt.plotargs[k] = kw[k] + end end end # merge!(plt.plotargs, d) handlePlotColors(plt.backend, plt.plotargs) end - _add_series(plt, d, args...) - _add_annotations(plt, d) + # _add_series(plt, d, args...) + # this is it folks! + for kw in kw_list + plt.n += 1 + + # TODO: can this be handled as a recipe?? + # if !stringsSupported() && di[:linetype] != :pie + # setTicksFromStringVector(plt, d, di, "x") + # setTicksFromStringVector(plt, d, di, "y") + # setTicksFromStringVector(plt, d, di, "z") + # end + + # TODO: unnecessary?? + # # remove plot args + # for k in keys(_plotDefaults) + # delete!(di, k) + # end + + # TODO: why?? + # # merge in plotarg_overrides + # plotarg_overrides = pop!(di, :plotarg_overrides, nothing) + # if plotarg_overrides != nothing + # merge!(plt.plotargs, plotarg_overrides) + # end + + _replace_linewidth(kw) + _add_series(plt.backend, plt, kw) + end + + + # _add_annotations(plt, d) # TODO # add title, axis labels, ticks, etc if !haskey(d, :subplot) - merge!(plt.plotargs, d) - # handlePlotColors(plt.backend, plt.plotargs) - dumpdict(plt.plotargs, "Updating plot items") + # merge!(plt.plotargs, d) # this shouldn't be needed since we merged the keys earlier _update_plot(plt, plt.plotargs) end - _update_plot_pos_size(plt, d) - + # _update_plot_pos_size(plt, d) # this is only used for Qwt... can we remove? current(plt) # note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement diff --git a/src/recipes.jl b/src/recipes.jl index 42d08bbb..cd290435 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -28,6 +28,53 @@ function RecipesBase.apply_recipe(d::KW, kw::KW, args...; issubplot=false) args end + +if is_installed("DataFrames") + @eval begin + import DataFrames + DFS = Union{Symbol, AbstractArray{Symbol}} + + function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS) + if isa(dfs, Symbol) + get!(d, symbol(letter * "label"), string(dfs)) + collect(df[dfs]) + else + get!(d, :label, reshape(dfs, 1, length(dfs))) + Any[collect(df[s]) for s in dfs] + end + end + + function handle_group(df::DataFrames.AbstractDataFrame, d::KW) + if haskey(d, :group) + g = d[:group] + if isa(g, Symbol) + d[:group] = collect(df[g]) + end + end + end + + @recipe function f(df::DataFrames.AbstractDataFrame, sy::DFS) + handle_group(df, d) + handle_dfs(df, d, "y", sy) + end + + @recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS) + handle_group(df, d) + x = handle_dfs(df, d, "x", sx) + y = handle_dfs(df, d, "y", sy) + x, y + end + + @recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS) + handle_group(df, d) + x = handle_dfs(df, d, "x", sx) + y = handle_dfs(df, d, "y", sy) + z = handle_dfs(df, d, "z", sz) + x, y, z + end + end +end + # macro kw(k, v) # esc(:(get!(d, $k, $v))) # end diff --git a/src/series_args.jl b/src/series_args.jl index 19c5015f..b96ffc53 100644 --- a/src/series_args.jl +++ b/src/series_args.jl @@ -463,78 +463,78 @@ end # For DataFrame support. Imports DataFrames and defines the necessary methods which support them. # -------------------------------------------------------------------- -function setup_dataframes() - @require DataFrames begin - # @eval begin - # import DataFrames - - DFS = Union{Symbol, AbstractArray{Symbol}} - - function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS) - if isa(dfs, Symbol) - get!(d, symbol(letter * "label"), string(dfs)) - collect(df[dfs]) - else - get!(d, :label, reshape(dfs, 1, length(dfs))) - Any[collect(df[s]) for s in dfs] - end - end - - function handle_group(df::DataFrames.AbstractDataFrame, d::KW) - if haskey(d, :group) - g = d[:group] - if isa(g, Symbol) - d[:group] = collect(df[g]) - end - end - end - - @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS) - handle_group(df, d) - handle_dfs(df, d, "y", sy) - end - - @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS) - handle_group(df, d) - x = handle_dfs(df, d, "x", sx) - y = handle_dfs(df, d, "y", sy) - x, y - end - - @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS) - handle_group(df, d) - x = handle_dfs(df, d, "x", sx) - y = handle_dfs(df, d, "y", sy) - z = handle_dfs(df, d, "z", sz) - x, y, z - end - - # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg] - # get_data(df::DataFrames.AbstractDataFrame, arg) = arg - # - # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...) - # # d[:dataframe] = df - # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...) - # end - # - # # expecting the column name of a dataframe that was passed in... anything else should error - # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...) - # if haskey(df, s) - # return extractGroupArgs(df[s]) - # else - # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d") - # end - # end - - # function getDataFrameFromKW(d::KW) - # get(d, :dataframe) do - # error("Missing dataframe argument!") - # end - # end - - # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes - # convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s - # convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v - - end -end +# function setup_dataframes() +# @require DataFrames begin +# # @eval begin +# # import DataFrames +# +# DFS = Union{Symbol, AbstractArray{Symbol}} +# +# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS) +# if isa(dfs, Symbol) +# get!(d, symbol(letter * "label"), string(dfs)) +# collect(df[dfs]) +# else +# get!(d, :label, reshape(dfs, 1, length(dfs))) +# Any[collect(df[s]) for s in dfs] +# end +# end +# +# function handle_group(df::DataFrames.AbstractDataFrame, d::KW) +# if haskey(d, :group) +# g = d[:group] +# if isa(g, Symbol) +# d[:group] = collect(df[g]) +# end +# end +# end +# +# @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS) +# handle_group(df, d) +# handle_dfs(df, d, "y", sy) +# end +# +# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS) +# handle_group(df, d) +# x = handle_dfs(df, d, "x", sx) +# y = handle_dfs(df, d, "y", sy) +# x, y +# end +# +# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS) +# handle_group(df, d) +# x = handle_dfs(df, d, "x", sx) +# y = handle_dfs(df, d, "y", sy) +# z = handle_dfs(df, d, "z", sz) +# x, y, z +# end +# +# # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg] +# # get_data(df::DataFrames.AbstractDataFrame, arg) = arg +# # +# # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...) +# # # d[:dataframe] = df +# # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...) +# # end +# # +# # # expecting the column name of a dataframe that was passed in... anything else should error +# # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...) +# # if haskey(df, s) +# # return extractGroupArgs(df[s]) +# # else +# # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d") +# # end +# # end +# +# # function getDataFrameFromKW(d::KW) +# # get(d, :dataframe) do +# # error("Missing dataframe argument!") +# # end +# # end +# +# # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes +# # convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s +# # convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v +# +# end +# end diff --git a/src/utils.jl b/src/utils.jl index 31644622..59fa6810 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -237,6 +237,14 @@ end isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected() +function is_installed(pkgstr::AbstractString) + try + Pkg.installed(pkgstr) === nothing ? false: true + catch + false + end +end + istuple(::Tuple) = true istuple(::Any) = false isvector(::AVec) = true diff --git a/test/REQUIRE b/test/REQUIRE index b8806bb9..5e5f499e 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -3,7 +3,6 @@ julia 0.4 RecipesBase Colors Reexport -Requires FactCheck Cairo Gadfly