Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

package extensions - weak deps #4649

Merged
merged 14 commits into from
Feb 16, 2023
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
GKS_ENCODING: "utf8"
GKSwstype: "nul"
JULIA_CONDAPKG_BACKEND: "MicroMamba"
MPLBACKEND: "agg"
name: Julia ${{ matrix.version }} - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.version == 'nightly' }}
Expand Down
27 changes: 14 additions & 13 deletions .github/workflows/format_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Format Julia files
run: |
using JuliaFormatter
format(["RecipesBase", "RecipesPipeline", "src", "test"])
format(["RecipesBase", "RecipesPipeline", "src", "test", "ext"])
shell: julia --color=yes --compile=min -O0 {0}
- name: suggester / JuliaFormatter
uses: reviewdog/action-suggester@v1
Expand All @@ -43,15 +43,16 @@ jobs:
git clean --force
shell: bash

- name: Format Julia project files
if: success() || failure()
run: |
using JuliaProjectFormatter
format_projects()
shell: julia --color=yes --compile=min -O0 {0}
- name: suggester / JuliaProjectFormatter
if: success() || failure()
uses: reviewdog/action-suggester@v1
with:
tool_name: JuliaProjectFormatter
fail_on_error: true
# temporarily disable `JuliaProjectFormatter` until github.com/tkf/JuliaProjectFormatter.jl/pull/7 is merged
# - name: Format Julia project files
# if: success() || failure()
# run: |
# using JuliaProjectFormatter
# format_projects()
# shell: julia --color=yes --compile=min -O0 {0}
# - name: suggester / JuliaProjectFormatter
# if: success() || failure()
# uses: reviewdog/action-suggester@v1
# with:
# tool_name: JuliaProjectFormatter
# fail_on_error: true
14 changes: 14 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ UnicodePlots = "3.4"
Unzip = "0.1 - 0.2"
julia = "1.6"

[weakdeps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[extensions]
FileIOExt = "FileIO"
GeometryBasicsExt = "GeometryBasics"
IJuliaExt = "IJulia"
ImageInTerminalExt = "ImageInTerminal"
UnitfulExt = "Unitful"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Expand Down
19 changes: 14 additions & 5 deletions src/fileio.jl → ext/FileIOExt.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ---------------------------------------------------------
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
module FileIOExt

import Plots: Plots, Plot, @ext_imp_use
@ext_imp_use :import FileIO

_fileio_load(@nospecialize(filename::AbstractString)) =
FileIO.load(filename::AbstractString)
Expand All @@ -10,7 +12,7 @@ function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
fn = tempname()

# first save a pdf file
pdf(plt, fn)
Plots.pdf(plt, fn)

# load that pdf into a FileIO Stream
s = _fileio_load("$fn.pdf")
Expand All @@ -23,5 +25,12 @@ function _show_pdfbackends(io::IO, ::MIME"image/png", plt::Plot)
write(io, read(open(pngfn), String))
end

const PDFBackends =
Union{PGFPlotsBackend,PlotlyJSBackend,PyPlotBackend,InspectDRBackend,GRBackend}
for be in (
Plots.PGFPlotsBackend, # NOTE: I guess this can be removed in Plots@2.0
)
showable(MIME"image/png"(), Plot{be}) && continue
@eval Plots._show(io::IO, mime::MIME"image/png", plt::Plot{$be}) =
_show_pdfbackends(io, mime, plt)
end

end # module
20 changes: 20 additions & 0 deletions ext/GeometryBasicsExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module GeometryBasicsExt

import Plots: Plots, @ext_imp_use, @recipe
import RecipesPipeline
import Unzip

@ext_imp_use :import GeometryBasics

RecipesPipeline.unzip(points::AbstractVector{<:GeometryBasics.Point}) =
Unzip.unzip(Tuple.(points))
RecipesPipeline.unzip(points::AbstractVector{GeometryBasics.Point{N,T}}) where {N,T} =
isbitstype(T) && sizeof(T) > 0 ? Unzip.unzip(reinterpret(NTuple{N,T}, points)) :
Unzip.unzip(Tuple.(points))
# -----------------------------------------
# Lists of tuples and GeometryBasics.Points
# -----------------------------------------
@recipe f(v::Plots.AVec{<:GeometryBasics.Point}) = RecipesPipeline.unzip(v)
@recipe f(p::GeometryBasics.Point) = [p] # Special case for 4-tuples in :ohlc series

end # module
37 changes: 28 additions & 9 deletions src/ijulia.jl → ext/IJuliaExt.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
const _use_local_dependencies = Ref(false)
const _use_local_plotlyjs = Ref(false)
module IJuliaExt
BeastyBlacksmith marked this conversation as resolved.
Show resolved Hide resolved

import Plots: @ext_imp_use, Plots, Plot
using Base64

@ext_imp_use :import IJulia

function _init_ijulia_plotting()
# IJulia is more stable with local file
_use_local_plotlyjs[] =
_plotly_local_file_path[] === nothing ? false : isfile(_plotly_local_file_path[])
Plots._use_local_plotlyjs[] =
Plots._plotly_local_file_path[] === nothing ? false :
isfile(Plots._plotly_local_file_path[])

ENV["MPLBACKEND"] = "Agg"
end
Expand All @@ -18,22 +23,23 @@ frontends like jupyterlab and nteract.
"""
_ijulia__extra_mime_info!(plt::Plot, out::Dict) = out

function _ijulia__extra_mime_info!(plt::Plot{PlotlyJSBackend}, out::Dict)
function _ijulia__extra_mime_info!(plt::Plot{Plots.PlotlyJSBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] =
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
Dict(:data => Plots.plotly_series(plt), :layout => Plots.plotly_layout(plt))
out
end

function _ijulia__extra_mime_info!(plt::Plot{PlotlyBackend}, out::Dict)
function _ijulia__extra_mime_info!(plt::Plot{Plots.PlotlyBackend}, out::Dict)
out["application/vnd.plotly.v1+json"] =
Dict(:data => plotly_series(plt), :layout => plotly_layout(plt))
Dict(:data => Plots.plotly_series(plt), :layout => Plots.plotly_layout(plt))
out
end

function _ijulia_display_dict(plt::Plot)
output_type = Symbol(plt.attr[:html_output_format])
if output_type === :auto
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
output_type =
get(Plots._best_html_output_type, Plots.backend_name(plt.backend), :svg)
end
out = Dict()
if output_type === :txt
Expand All @@ -57,3 +63,16 @@ function _ijulia_display_dict(plt::Plot)
end
out
end

if IJulia.inited
_init_ijulia_plotting()
IJulia.display_dict(plt::Plot) = _ijulia_display_dict(plt)
end

# IJulia only... inline display
function Plots.inline(plt::Plot = Plots.current())
IJulia.clear_output(true)
display(IJulia.InlineDisplay(), plt)
end

end # module
32 changes: 32 additions & 0 deletions ext/ImageInTerminalExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module ImageInTerminalExt

import Plots
Plots.@ext_imp_use :import ImageInTerminal

if ImageInTerminal.ENCODER_BACKEND[] == :Sixel
get!(ENV, "GKSwstype", "nul") # disable `gr` output, we display in the terminal instead
for be in (
Plots.GRBackend,
Plots.PyPlotBackend,
Plots.PythonPlotBackend,
# Plots.UnicodePlotsBackend, # better and faster as MIME("text/plain") in terminal
Plots.PGFPlotsXBackend,
Plots.PlotlyJSBackend,
Plots.PlotlyBackend,
Plots.GastonBackend,
Plots.InspectDRBackend,
)
@eval function Base.display(::Plots.PlotsDisplay, plt::Plots.Plot{$be})
Plots.prepare_output(plt)
buf = PipeBuffer()
show(buf, MIME("image/png"), plt)
display(
ImageInTerminal.TerminalGraphicDisplay(stdout),
MIME("image/png"),
read(buf),
)
end
end
end

end # module
48 changes: 16 additions & 32 deletions src/unitful.jl → ext/UnitfulExt.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# previously https://github.com/jw3126/UnitfulRecipes.jl
# authors: Benoit Pasquier (@briochemc) - David Gustavsson (@gustaphe) - Jan Weidner (@jw3126)

module UnitfulRecipes
module UnitfulExt

using ..Unitful: Quantity, unit, ustrip, Unitful, dimension, Units, NoUnits
using ..RecipesBase
export @P_str

import ..locate_annotation,
..PlotText, ..Subplot, ..AVec, ..AMat, ..Axis, .._transform_ticks, ..process_limits
import Plots: Plots, @ext_imp_use, @recipe, PlotText, Subplot, AVec, AMat, Axis
import RecipesBase
@ext_imp_use :import Unitful Quantity unit ustrip Unitful dimension Units NoUnits

const MissingOrQuantity = Union{Missing,<:Quantity}

Expand Down Expand Up @@ -153,7 +150,7 @@ end
function fixmarkercolor!(attr)
u = ustripattribute!(attr, :marker_z)
ustripattribute!(attr, :clims, u)
u == Unitful.NoUnits || append_unit_if_needed!(attr, :colorbar_title, u)
u == NoUnits || append_unit_if_needed!(attr, :colorbar_title, u)
end
fixmarkersize!(attr) = ustripattribute!(attr, :markersize)
fixlinecolor!(attr) = ustripattribute!(attr, :line_z)
Expand All @@ -166,7 +163,7 @@ ustripattribute!(attr, key) =
attr[key] = ustrip.(u, v)
return u
else
return Unitful.NoUnits
return NoUnits
end
# If supplied, use the unit (optional 3rd argument)
function ustripattribute!(attr, key, u)
Expand Down Expand Up @@ -200,27 +197,14 @@ Base.ncodeunits(n::S) = ncodeunits(n.content)
Base.isvalid(n::S, i::Integer) = isvalid(n.content, i)
Base.pointer(n::S) = pointer(n.content)
Base.pointer(n::S, i::Integer) = pointer(n.content, i)
"""
P_str(s)

Creates a string that will be Protected from recipe passes.

Example:
```julia
julia> plot([0,1]u"m", [1,2]u"m/s^2", xlabel=P"This label will NOT display units")

julia> plot([0,1]u"m", [1,2]u"m/s^2", xlabel="This label will display units")
```
"""
macro P_str(s)
return ProtectedString(s)
end
Plots.protectedstring(s) = ProtectedString(s)
BeastyBlacksmith marked this conversation as resolved.
Show resolved Hide resolved

#=====================================
Append unit to labels when appropriate
=====================================#

append_unit_if_needed!(attr, key, u::Unitful.Units) =
append_unit_if_needed!(attr, key, u::Units) =
append_unit_if_needed!(attr, key, get(attr, key, nothing), u)
# dispatch on the type of `label`
append_unit_if_needed!(attr, key, label::ProtectedString, u) = nothing
Expand Down Expand Up @@ -268,30 +252,30 @@ getaxisunit(a::Axis) = getaxisunit(a[:guide])
#==============
Fix annotations
===============#
locate_annotation(
Plots.locate_annotation(
sp::Subplot,
x::MissingOrQuantity,
y::MissingOrQuantity,
label::PlotText,
) = (ustrip(x), ustrip(y), label)
locate_annotation(
Plots.locate_annotation(
sp::Subplot,
x::MissingOrQuantity,
y::MissingOrQuantity,
z::MissingOrQuantity,
label::PlotText,
) = (ustrip(x), ustrip(y), ustrip(z), label)
locate_annotation(sp::Subplot, rel::NTuple{N,<:MissingOrQuantity}, label) where {N} =
locate_annotation(sp, ustrip.(rel), label)
Plots.locate_annotation(sp::Subplot, rel::NTuple{N,<:MissingOrQuantity}, label) where {N} =
Plots.locate_annotation(sp, ustrip.(rel), label)

#==================#
# ticks and limits #
#==================#
_transform_ticks(ticks::AbstractArray{T}, axis) where {T<:Quantity} =
Plots._transform_ticks(ticks::AbstractArray{T}, axis) where {T<:Quantity} =
ustrip.(getaxisunit(axis), ticks)
process_limits(lims::AbstractArray{T}, axis) where {T<:Quantity} =
Plots.process_limits(lims::AbstractArray{T}, axis) where {T<:Quantity} =
ustrip.(getaxisunit(axis), lims)
process_limits(lims::Tuple{S,T}, axis) where {S<:Quantity,T<:Quantity} =
Plots.process_limits(lims::Tuple{S,T}, axis) where {S<:Quantity,T<:Quantity} =
ustrip.(getaxisunit(axis), lims)

end
end # module
8 changes: 4 additions & 4 deletions src/Plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@max_m
end

using Pkg, Dates, Printf, Statistics, Base64, LinearAlgebra, SparseArrays, Random
using SnoopPrecompile, Preferences, Requires, Reexport, RelocatableFolders
using SnoopPrecompile, Preferences, Reexport, RelocatableFolders
using Base.Meta
using Unzip
@reexport using RecipesBase
@reexport using PlotThemes
@reexport using PlotUtils

import RecipesBase: plot, plot!, animate, is_explicit, grid
import RecipesPipeline
import Requires: @require
import RecipesPipeline:
inverse_scale_func,
datetimeformatter,
Expand All @@ -40,6 +40,7 @@ import UnicodeFun
import StatsBase
import Downloads
import Showoff
import Unzip
import JLFzf
import JSON

Expand Down Expand Up @@ -107,6 +108,7 @@ export
animate,
@animate,
@gif,
@P_str,

test_examples,
iter_segments,
Expand Down Expand Up @@ -169,8 +171,6 @@ include("plotattr.jl")
include("backends.jl")
const CURRENT_BACKEND = CurrentBackend(:none)
include("output.jl")
include("ijulia.jl")
include("fileio.jl")
include("shorthands.jl")
include("backends/web.jl")
include("init.jl")
Expand Down
2 changes: 1 addition & 1 deletion src/components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ vertices(shape::Shape) = collect(zip(shape.x, shape.y))
"return the vertex points from a Shape or Segments object"
coords(shape::Shape) = shape.x, shape.y

coords(shapes::AVec{<:Shape}) = unzip(map(coords, shapes))
coords(shapes::AVec{<:Shape}) = RecipesPipeline.unzip(map(coords, shapes))

"get an array of tuples of points on a circle with radius `r`"
partialcircle(start_θ, end_θ, n = 20, r = 1) =
Expand Down
Loading