First attempt

This commit is contained in:
Giorgio Calderone 2020-03-16 19:26:14 +01:00
parent 622ef39837
commit 3283f54741
2 changed files with 341 additions and 451 deletions

View File

@ -3,26 +3,24 @@ module Gnuplot
using StructC14N, ColorTypes, Printf, StatsBase, ReusePatterns, DataFrames using StructC14N, ColorTypes, Printf, StatsBase, ReusePatterns, DataFrames
import Base.reset import Base.reset
import Base.println import Base.write
import Base.iterate import Base.iterate
import Base.convert import Base.convert
export gnuplot, quit, quitall, setverbose, export @gp, @gsp, save, contourlines, hist
@gp, @gsp, exec, save, contourlines, hist
# ╭───────────────────────────────────────────────────────────────────╮
# │ TYPE DEFINITIONS │
# ╰───────────────────────────────────────────────────────────────────╯
#_____________________________________________________________________ # ---------------------------------------------------------------------
# TYPE DEFINITIONS
#_____________________________________________________________________
# --------------------------------------------------------------------
mutable struct DataSet mutable struct DataSet
name::String name::String
lines::Vector{String} lines::Vector{String}
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
mutable struct SinglePlot mutable struct SinglePlot
cmds::Vector{String} cmds::Vector{String}
elems::Vector{String} elems::Vector{String}
@ -31,7 +29,7 @@ mutable struct SinglePlot
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
@quasiabstract mutable struct DrySession @quasiabstract mutable struct DrySession
sid::Symbol # session ID sid::Symbol # session ID
datas::Vector{DataSet} # data sets datas::Vector{DataSet} # data sets
@ -40,8 +38,8 @@ end
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
@quasiabstract mutable struct Session <: DrySession @quasiabstract mutable struct GPSession <: DrySession
pin::Base.Pipe; pin::Base.Pipe;
pout::Base.Pipe; pout::Base.Pipe;
perr::Base.Pipe; perr::Base.Pipe;
@ -50,27 +48,25 @@ end
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
mutable struct State mutable struct State
sessions::Dict{Symbol, DrySession}; sessions::Dict{Symbol, DrySession};
dry::Bool dry::Bool
cmd::String cmd::String
default::Symbol; # default session name default::Symbol; # default session name
initcmd::Vector{String} # Commands to initialize the gnuplot session (e.g., to set default terminal) init::Vector{String} # Commands to initialize the gnuplot session (e.g., to set default terminal)
verbose::Bool; # verbosity level (true/false) verbose::Bool; # verbosity level (true/false)
silent::Bool; # Silent flag
printlines::Int; # How many data lines are printed in log printlines::Int; # How many data lines are printed in log
State() = new(Dict{Symbol, DrySession}(), true, "gnuplot", :default, Vector{String}(), false, false, 4) State() = new(Dict{Symbol, DrySession}(), true, "gnuplot", :default, Vector{String}(), false, 4)
end end
const state = State() const state = State()
state.dry = false state.dry = false
#_____________________________________________________________________ # ╭───────────────────────────────────────────────────────────────────╮
# "PRIVATE" (NON-EXPORTED) FUNCTIONS # │ LOW LEVEL FUNCTIONS │
#_____________________________________________________________________ # ╰───────────────────────────────────────────────────────────────────╯
# ---------------------------------------------------------------------
# --------------------------------------------------------------------
""" """
# CheckGnuplotVersion # CheckGnuplotVersion
@ -105,7 +101,7 @@ function CheckGnuplotVersion(cmd::AbstractString)
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
function parseKeywords(; kwargs...) function parseKeywords(; kwargs...)
template = (xrange=NTuple{2, Real}, template = (xrange=NTuple{2, Real},
yrange=NTuple{2, Real}, yrange=NTuple{2, Real},
@ -136,176 +132,8 @@ function parseKeywords(; kwargs...)
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
function DrySession(sid::Symbol) function data2string(args...)
global state
(sid in keys(state.sessions)) &&
error("Gnuplot session $sid is already active")
out = DrySession(sid, Vector{DataSet}(), [SinglePlot()], 1)
return out
end
# --------------------------------------------------------------------
function getsession(sid::Symbol)
global state
if !(sid in keys(state.sessions))
#@info "Creating session $sid..."
if state.dry
gnuplot(sid)
else
gnuplot(sid, state.cmd)
end
end
return state.sessions[sid]
end
function getsession()
global state
return getsession(state.default)
end
# --------------------------------------------------------------------
"""
# println
Send a string to gnuplot's STDIN.
The commands sent through `send` are not stored in the current
session (use `newcmd` to save commands in the current session).
## Arguments:
- `gp`: a `Session` object;
- `str::String`: command to be sent;
- `capture=false`: set to `true` to capture and return the output.
"""
println(gp::DrySession, str::AbstractString) = nothing
function println(gp::Session, str::AbstractString)
global state
if !state.silent && state.verbose
printstyled(color=:yellow, "GNUPLOT ($(gp.sid)) $str\n")
end
w = write(gp.pin, strip(str) * "\n")
w <= 0 && error("Writing on gnuplot STDIN pipe returned $w")
#flush(gp.pin)
return w
end
println(gp::DrySession, d::DataSet) = nothing
function println(gp::Session, d::DataSet)
if typeof(gp) == concretetype(Session)
if !state.silent && state.verbose
for ii in 1:length(d.lines)
v = d.lines[ii]
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $v\n")
if ii == state.printlines
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) ...\n")
if ii < length(d.lines)
v = d.lines[end]
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $v\n")
end
break
end
end
end
w = write(gp.pin, "\n")
w = write(gp.pin, join(d.lines, "\n"))
w = write(gp.pin, "\n")
w = write(gp.pin, "\n")
flush(gp.pin)
end
return nothing
end
# --------------------------------------------------------------------
writeread(gp::DrySession, str::AbstractString) = [""]
function writeread(gp::Session, str::AbstractString)
global state
silent = state.silent
state.silent = true
write(gp.pin, "print 'GNUPLOT_CAPTURE_BEGIN'\n")
println(gp, strip(str))
write(gp.pin, "print 'GNUPLOT_CAPTURE_END'\n")
flush(gp.pin)
out = Vector{String}()
while true
l = take!(gp.channel)
l == "GNUPLOT_CAPTURE_END" && break
push!(out, l)
end
state.silent = silent
return out
end
# --------------------------------------------------------------------
setWindowTitle(session::DrySession) = nothing
function setWindowTitle(session::Session)
term = writeread(session, "print GPVAL_TERM")[1]
if term in ("aqua", "x11", "qt", "wxt")
opts = writeread(session, "print GPVAL_TERMOPTIONS")[1]
if findfirst("title", opts) == nothing
writeread(session, "set term $term $opts title 'Gnuplot.jl: $(session.sid)'")
end
end
end
# --------------------------------------------------------------------
function reset(gp::DrySession)
gp.datas = Vector{DataSet}()
gp.plots = [SinglePlot()]
gp.curmid = 1
println(gp, "reset session")
return nothing
end
# --------------------------------------------------------------------
function setmulti(gp::DrySession, mid::Int)
@assert mid >= 0 "Multiplot ID must be a >= 0"
for i in length(gp.plots)+1:mid
push!(gp.plots, SinglePlot())
end
(mid > 0) && (gp.curmid = mid)
end
# --------------------------------------------------------------------
function newdatasource(gp::DrySession, v::Vector{T}; name="") where T <: AbstractString
(name == "") && (name = string("data", length(gp.datas)))
name = "\$$name"
accum = Vector{String}()
push!(accum, "$name << EOD")
append!(accum, v)
push!(accum, "EOD")
d = DataSet(name, accum)
push!(gp.datas, d)
println(gp, d) # Send directly to gnuplot process
return name
end
# --------------------------------------------------------------------
function newdatasource(gp::DrySession, args...; name="")
(name == "") && (name = string("data", length(gp.datas)))
name = "\$$name"
accum = dataset(args...)
accum[1] = name * accum[1]
d = DataSet(name, accum)
push!(gp.datas, d)
println(gp, d) # Send directly to gnuplot process
return name
end
# --------------------------------------------------------------------
function dataset(args...)
@assert length(args) > 0 @assert length(args) > 0
# Check types of args # Check types of args
@ -339,7 +167,6 @@ function dataset(args...)
end end
accum = Vector{String}() accum = Vector{String}()
push!(accum, " << EOD")
# All scalars # All scalars
if minimum(dims) == 0 if minimum(dims) == 0
@ -351,7 +178,6 @@ function dataset(args...)
v *= " " * string(d) v *= " " * string(d)
end end
push!(accum, v) push!(accum, v)
push!(accum, "EOD")
return accum return accum
end end
@ -369,7 +195,6 @@ function dataset(args...)
end end
push!(accum, v) push!(accum, v)
end end
push!(accum, "EOD")
return accum return accum
end end
@ -389,7 +214,6 @@ function dataset(args...)
i += 1 i += 1
push!(accum, v) push!(accum, v)
end end
push!(accum, "EOD")
return accum return accum
end end
@ -421,7 +245,6 @@ function dataset(args...)
i += 1 i += 1
push!(accum, v) push!(accum, v)
end end
push!(accum, "EOD")
return accum return accum
else else
#@info "Case 4" #@info "Case 4"
@ -440,7 +263,6 @@ function dataset(args...)
i += 1 i += 1
push!(accum, v) push!(accum, v)
end end
push!(accum, "EOD")
return accum return accum
end end
end end
@ -449,16 +271,224 @@ function dataset(args...)
end end
# -------------------------------------------------------------------- # ╭───────────────────────────────────────────────────────────────────╮
""" # │ SESSION CONSTRUCTORS AND getsession() │
# newcmd # ╰───────────────────────────────────────────────────────────────────╯
# ---------------------------------------------------------------------
function DrySession(sid::Symbol)
global state
(sid in keys(state.sessions)) && error("Gnuplot session $sid is already active")
out = DrySession(sid, Vector{DataSet}(), [SinglePlot()], 1)
state.sessions[sid] = out
return out
end
Send a command to gnuplot process and store it in the current session. # ---------------------------------------------------------------------
function GPSession(sid::Symbol)
function readTask(sid, stream, channel)
global state
saveOutput = false
while isopen(stream)
line = readline(stream)
if (length(line) >= 1) && (line[1] == Char(0x1b)) # Escape (xterm -ti vt340)
buf = Vector{UInt8}()
append!(buf, convert(Vector{UInt8}, [line...]))
push!(buf, 0x0a)
c = 0x00
while c != 0x1b
c = read(stream, 1)[1]
push!(buf, c)
end
c = read(stream, 1)[1]
push!(buf, c)
write(stdout, buf)
continue
end
if line == "GNUPLOT_CAPTURE_BEGIN"
saveOutput = true
else
if (line != "") && (line != "GNUPLOT_CAPTURE_END") && (state.verbose)
printstyled(color=:cyan, "GNUPLOT ($sid) -> $line\n")
end
(saveOutput) && (put!(channel, line))
(line == "GNUPLOT_CAPTURE_END") && (saveOutput = false)
end
end
delete!(state.sessions, sid)
return nothing
end
global state
CheckGnuplotVersion(state.cmd)
session = DrySession(sid)
pin = Base.Pipe()
pout = Base.Pipe()
perr = Base.Pipe()
proc = run(pipeline(`$(state.cmd)`, stdin=pin, stdout=pout, stderr=perr), wait=false)
chan = Channel{String}(32)
# Close unused sides of the pipes
Base.close(pout.in)
Base.close(perr.in)
Base.close(pin.out)
Base.start_reading(pout.out)
Base.start_reading(perr.out)
# Start reading tasks
@async readTask(sid, pout, chan)
@async readTask(sid, perr, chan)
out = GPSession(getfield.(Ref(session), fieldnames(concretetype(DrySession)))...,
pin, pout, perr, proc, chan)
state.sessions[sid] = out
# Set window title
term = writeread(out, "print GPVAL_TERM")[1]
if term in ("aqua", "x11", "qt", "wxt")
opts = writeread(out, "print GPVAL_TERMOPTIONS")[1]
if findfirst("title", opts) == nothing
writeread(out, "set term $term $opts title 'Gnuplot.jl: $(out.sid)'")
end
end
for l in state.init
writeread(out, l)
end
return out
end
# ---------------------------------------------------------------------
function getsession(sid::Symbol=state.default)
global state
if !(sid in keys(state.sessions))
if state.dry
DrySession(sid)
else
GPSession(sid)
end
end
return state.sessions[sid]
end
# ╭───────────────────────────────────────────────────────────────────╮
# │ write() and writeread() │
# ╰───────────────────────────────────────────────────────────────────╯
# ---------------------------------------------------------------------
""" """
# write
Send a string to gnuplot's STDIN.
The commands sent through `write` are not stored in the current
session (use `newcmd` to save commands in the current session).
## Arguments:
- `gp`: a `DrySession` object;
- `str::String`: command to be sent;
"""
write(gp::DrySession, str::AbstractString) = nothing
function write(gp::GPSession, str::AbstractString)
global state
if state.verbose
printstyled(color=:light_yellow, "GNUPLOT ($(gp.sid)) $str\n")
end
w = write(gp.pin, strip(str) * "\n")
w <= 0 && error("Writing on gnuplot STDIN pipe returned $w")
flush(gp.pin)
return w
end
write(gp::DrySession, d::DataSet) = nothing
function write(gp::GPSession, d::DataSet)
if state.verbose
v = ""
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.name) << EOD\n")
n = min(state.printlines, length(d.lines))
for i in 1:n
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.lines[i])\n")
end
if n < length(d.lines)
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) ...\n")
end
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) EOD\n")
end
write(gp.pin, "\n")
write(gp.pin, "$(d.name) << EOD\n")
write(gp.pin, join(d.lines, "\n") * "\n")
write(gp.pin, "EOD\n")
write(gp.pin, "\n")
write(gp.pin, "\n")
flush(gp.pin)
return nothing
end
# ---------------------------------------------------------------------
writeread(gp::DrySession, str::AbstractString) = [""]
function writeread(gp::GPSession, str::AbstractString)
global state
verbose = state.verbose
state.verbose = false
write(gp, "print 'GNUPLOT_CAPTURE_BEGIN'")
write(gp, str)
write(gp, "print 'GNUPLOT_CAPTURE_END'")
out = Vector{String}()
while true
l = take!(gp.channel)
l == "GNUPLOT_CAPTURE_END" && break
push!(out, l)
end
state.verbose = verbose
return out
end
# ╭───────────────────────────────────────────────────────────────────╮
# │ PRIVATE FUNCTIONS TO MANIPULATE SESSIONS │
# ╰───────────────────────────────────────────────────────────────────╯
# ---------------------------------------------------------------------
function reset(gp::DrySession)
gp.datas = Vector{DataSet}()
gp.plots = [SinglePlot()]
gp.curmid = 1
write(gp, "reset session")
return nothing
end
# ---------------------------------------------------------------------
function setmulti(gp::DrySession, mid::Int)
@assert mid >= 0 "Multiplot ID must be a >= 0"
for i in length(gp.plots)+1:mid
push!(gp.plots, SinglePlot())
end
(mid > 0) && (gp.curmid = mid)
end
# ---------------------------------------------------------------------
function newdataset(gp::DrySession, accum::Vector{String}; name="")
(name == "") && (name = string("data", length(gp.datas)))
name = "\$$name"
d = DataSet(name, accum)
push!(gp.datas, d)
write(gp, d) # Send now to gnuplot process
return name
end
newdataset(gp::DrySession, args...; name="") = newdataset(gp, data2string(args...), name=name)
# ---------------------------------------------------------------------
function newcmd(gp::DrySession, v::String; mid::Int=0) function newcmd(gp::DrySession, v::String; mid::Int=0)
setmulti(gp, mid) setmulti(gp, mid)
(v != "") && (push!(gp.plots[gp.curmid].cmds, v)) (v != "") && (push!(gp.plots[gp.curmid].cmds, v))
(length(gp.plots) == 1) && (println(gp, v)) (length(gp.plots) == 1) && (write(gp, v))
return nothing return nothing
end end
@ -470,105 +500,80 @@ function newcmd(gp::DrySession; mid::Int=0, args...)
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
function newplotelem(gp::DrySession, name, opt=""; mid=0) function newplot(gp::DrySession, name, opt=""; mid=0)
setmulti(gp, mid) setmulti(gp, mid)
push!(gp.plots[gp.curmid].elems, "$name $opt") push!(gp.plots[gp.curmid].elems, "$name $opt")
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
function quitsession(gp::DrySession) function quit(gp::DrySession)
global state global state
delete!(state.sessions, gp.sid) delete!(state.sessions, gp.sid)
return 0 return 0
end end
function quitsession(gp::Session) function quit(gp::GPSession)
close(gp.pin) close(gp.pin)
close(gp.pout) close(gp.pout)
close(gp.perr) close(gp.perr)
wait( gp.proc) wait( gp.proc)
exitCode = gp.proc.exitcode exitCode = gp.proc.exitcode
invoke(quitsession, Tuple{DrySession}, gp) invoke(quit, Tuple{DrySession}, gp)
return exitCode return exitCode
end end
# -------------------------------------------------------------------- # ╭───────────────────────────────────────────────────────────────────╮
iterate(gp::DrySession) = ("#ID: $(gp.sid)", (true, 1, 1)) # │ dump() and driver() │
function iterate(gp::DrySession, state) # ╰───────────────────────────────────────────────────────────────────╯
(onData, mid, ii) = state # ---------------------------------------------------------------------
if onData dump(gp::DrySession; kw...) = dump(gp, gp; kw...)
if mid <= length(gp.datas) function dump(gp::DrySession, stream; term::AbstractString="", output::AbstractString="")
if ii <= length(gp.datas[mid].lines) write(stream, "reset\n")
return (gp.datas[mid].lines[ii], (true, mid, ii+1))
end
return iterate(gp, (true, mid+1, 1))
end
return ("", (false, 1, 1))
end
if mid <= length(gp.plots)
if ii <= length(gp.plots[mid].cmds)
return (gp.plots[mid].cmds[ii], (false, mid, ii+1))
end
if length(gp.plots[mid].elems) == 0
return ("", (false, mid+1, 1))
end
s = (gp.plots[mid].flag3d ? "splot " : "plot ") * " \\\n " *
join(gp.plots[mid].elems, ", \\\n ")
return (s, (false, mid+1, 1))
end
if mid == length(gp.plots)+1
s = ""
(length(gp.plots) > 1) && (s *= "unset multiplot;")
return (s, (false, mid+1, 1))
end
return nothing
end
# --------------------------------------------------------------------
# function convert(::Type{Vector{String}}, gp::DrySession)
# out = Vector{String}()
# for l in gp
# push!(out, l)
# end
# return out
# end
# --------------------------------------------------------------------
internal_save(gp::DrySession; kw...) = internal_save(gp, gp; kw...)
function internal_save(gp::DrySession, stream; term::AbstractString="", output::AbstractString="")
println(stream, "reset")
if term != "" if term != ""
former_term = writeread(gp, "print GPVAL_TERM")[1] former_term = writeread(gp, "print GPVAL_TERM")[1]
former_opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1] former_opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1]
println(stream, "set term $term") write(stream, "set term $term\n")
end end
(output != "") && println(stream, "set output '$output'") (output != "") && write(stream, "set output '$output'\n")
i = (!(typeof(stream) <: DrySession), 1, 1) # Skip data sources ?
while (next = iterate(gp, i)) != nothing if !(typeof(stream) <: DrySession)
(s, i) = next # Dump datasets
println(stream, s) for i in 1:length(gp.datas)
d = gp.datas[i]
write(stream, d.name * " << EOD\n")
for j in 1:length(d.lines)
write(stream, d.lines[j] * "\n")
end end
(output != "") && println(stream, "set output") write(stream, "EOD\n")
end
end
for i in 1:length(gp.plots)
d = gp.plots[i]
for j in 1:length(d.cmds)
write(stream, d.cmds[j] * "\n")
end
s = (d.flag3d ? "splot " : "plot ") * " \\\n " *
join(d.elems, ", \\\n ")
write(stream, s * "\n")
end
(length(gp.plots) > 1) && write(stream, "unset multiplot\n")
(output != "") && write(stream, "set output\n")
if term != "" if term != ""
println(stream, "set term $former_term $former_opts") write(stream, "set term $former_term $former_opts\n")
end end
output return output
end end
# -------------------------------------------------------------------- # ---------------------------------------------------------------------
function driver(args...; flag3d=false) function driver(args...; flag3d=false)
if length(args) == 0 if length(args) == 0
gp = getsession() gp = getsession()
internal_save(gp) dump(gp)
return nothing return nothing
end end
@ -587,8 +592,8 @@ function driver(args...; flag3d=false)
end end
end end
if AllArraysAreNotEmpty if AllArraysAreNotEmpty
last = newdatasource(gp, data...; name=dataname) last = newdataset(gp, data...; name=dataname)
(dataplot != nothing) && (newplotelem(gp, last, dataplot)) (dataplot != nothing) && (newplot(gp, last, dataplot))
end end
end end
data = Vector{Any}() data = Vector{Any}()
@ -660,7 +665,7 @@ function driver(args...; flag3d=false)
(isPlot, flag3d, cmd) = isPlotCmd(arg) (isPlot, flag3d, cmd) = isPlotCmd(arg)
if isPlot if isPlot
gp.plots[gp.curmid].flag3d = flag3d gp.plots[gp.curmid].flag3d = flag3d
newplotelem(gp, cmd) newplot(gp, cmd)
else else
newcmd(gp, arg) newcmd(gp, arg)
end end
@ -674,7 +679,7 @@ function driver(args...; flag3d=false)
dataplot = "" dataplot = ""
dataCompleted() dataCompleted()
(doDump) && (internal_save(gp)) (doDump) && (dump(gp))
return nothing return nothing
end end
@ -684,132 +689,6 @@ end
# EXPORTED FUNCTIONS # EXPORTED FUNCTIONS
#_____________________________________________________________________ #_____________________________________________________________________
# --------------------------------------------------------------------
"""
`gnuplot(sid::Symbol)`
`gnuplot(sid::Symbol, cmd::AbstractString)`
Initialize a new session and (optionally) the associated Gnuplot process
## Arguments:
- `sid`: the session name (a Julia symbol);
- `cmd`: a string specifying the complete file path to a gnuplot executable. If not given a *dry* session will be created, i.e. a session without underlying gnuplot process.
"""
function gnuplot(sid::Symbol)
out = DrySession(sid)
state.sessions[sid] = out
return out
end
function gnuplot(sid::Symbol, cmd::AbstractString)
function readTask(sid, stream, channel)
global state
saveOutput = false
while isopen(stream)
line = readline(stream)
if (length(line) >= 1) && (line[1] == Char(0x1b)) # Escape (xterm -ti vt340)
buf = Vector{UInt8}()
append!(buf, convert(Vector{UInt8}, [line...]))
push!(buf, 0x0a)
c = 0x00
while c != 0x1b
c = read(stream, 1)[1]
push!(buf, c)
end
c = read(stream, 1)[1]
push!(buf, c)
write(stdout, buf)
continue
end
if line == "GNUPLOT_CAPTURE_BEGIN"
saveOutput = true
else
if (line != "") && (line != "GNUPLOT_CAPTURE_END") && (!state.silent)
printstyled(color=:cyan, "GNUPLOT ($sid) -> $line\n")
end
(saveOutput) && (put!(channel, line))
(line == "GNUPLOT_CAPTURE_END") && (saveOutput = false)
end
end
delete!(state.sessions, sid)
return nothing
end
global state
CheckGnuplotVersion(cmd)
session = DrySession(sid)
pin = Base.Pipe()
pout = Base.Pipe()
perr = Base.Pipe()
proc = run(pipeline(`$cmd`, stdin=pin, stdout=pout, stderr=perr), wait=false)
chan = Channel{String}(32)
# Close unused sides of the pipes
Base.close(pout.in)
Base.close(perr.in)
Base.close(pin.out)
Base.start_reading(pout.out)
Base.start_reading(perr.out)
# Start reading tasks
@async readTask(sid, pout, chan)
@async readTask(sid, perr, chan)
out = Session(getfield.(Ref(session), fieldnames(concretetype(DrySession)))..., pin, pout, perr, proc, chan)
state.sessions[sid] = out
setWindowTitle(out)
for l in state.initcmd
writeread(out, l)
end
return out
end
function gnuplot()
global state
return gnuplot(state.default)
end
function gnuplot(cmd::AbstractString)
global state
return gnuplot(state.default, cmd)
end
# --------------------------------------------------------------------
"""
`quit()`
Quit the session and the associated gnuplot process (if any).
"""
function quit(sid::Symbol)
global state
if !(sid in keys(state.sessions))
error("Gnuplot session $sid do not exists")
end
return quitsession(state.sessions[sid])
end
"""
`quitall()`
Quit all the sessions and the associated gnuplot processes.
"""
function quitall()
global state
for sid in keys(state.sessions)
quit(sid)
end
return nothing
end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
""" """
`@gp args...` `@gp args...`
@ -1015,6 +894,38 @@ end
# end # end
# ╭───────────────────────────────────────────────────────────────────╮
# │ FUNCTIONS MEANT TO BE INVOKED BY USERS │
# ╰───────────────────────────────────────────────────────────────────╯
# ---------------------------------------------------------------------
"""
`quit()`
Quit the session and the associated gnuplot process (if any).
"""
function quit(sid::Symbol)
global state
if !(sid in keys(state.sessions))
error("Gnuplot session $sid do not exists")
end
return quit(state.sessions[sid])
end
"""
`quitall()`
Quit all the sessions and the associated gnuplot processes.
"""
function quitall()
global state
for sid in keys(state.sessions)
quit(sid)
end
return nothing
end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
""" """
`exec(sid::Symbol, s::Vector{String})` `exec(sid::Symbol, s::Vector{String})`
@ -1055,11 +966,6 @@ function setverbose(b::Bool)
end end
function initcmd()
global state
return state.initcmd
end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
""" """
@ -1077,12 +983,12 @@ To save the data and command from a specific session pass the ID as first argume
In all cases the `term` keyword allows to specify a gnuplot terminal, and the `output` keyword allows to specify an output file. In all cases the `term` keyword allows to specify a gnuplot terminal, and the `output` keyword allows to specify an output file.
""" """
save( ; kw...) = internal_save(getsession() ; kw...) save( ; kw...) = dump(getsession() ; kw...)
save(sid::Symbol ; kw...) = internal_save(getsession(sid) ; kw...) save(sid::Symbol ; kw...) = dump(getsession(sid) ; kw...)
save( stream::IO ; kw...) = internal_save(getsession() , stream; kw...) save( stream::IO ; kw...) = dump(getsession() , stream; kw...)
save(sid::Symbol, stream::IO ; kw...) = internal_save(getsession(sid), stream; kw...) save(sid::Symbol, stream::IO ; kw...) = dump(getsession(sid), stream; kw...)
save( file::AbstractString; kw...) = open(file, "w") do stream; internal_save(getsession() , stream; kw...); end save( file::AbstractString; kw...) = open(file, "w") do stream; dump(getsession() , stream; kw...); end
save(sid::Symbol, file::AbstractString; kw...) = open(file, "w") do stream; internal_save(getsession(sid), stream; kw...); end save(sid::Symbol, file::AbstractString; kw...) = open(file, "w") do stream; dump(getsession(sid), stream; kw...); end
# -------------------------------------------------------------------- # --------------------------------------------------------------------

View File

@ -3,41 +3,30 @@ using Test, Gnuplot
x = [1, 2, 3] x = [1, 2, 3]
y = [4, 5, 6] y = [4, 5, 6]
s = Gnuplot.dataset(1) s = Gnuplot.data2string(1)
@test all(s .== [" << EOD", @test all(s .== [" 1"])
" 1" ,
"EOD" ])
s = Gnuplot.dataset(1, 2) s = Gnuplot.data2string(1, 2)
@test all(s .== [" << EOD", @test all(s .== [" 1 2"])
" 1 2" ,
"EOD" ])
s = Gnuplot.dataset(x) s = Gnuplot.data2string(x)
@test all(s .== [" << EOD", @test all(s .== [" 1" ,
" 1" ,
" 2" , " 2" ,
" 3" , " 3" ])
"EOD" ])
s = Gnuplot.dataset(x, y) s = Gnuplot.data2string(x, y)
@test all(s .== [ " << EOD", @test all(s .== [" 1 4",
" 1 4",
" 2 5", " 2 5",
" 3 6", " 3 6"])
"EOD" ])
s = Gnuplot.dataset(x, y, x.+y) s = Gnuplot.data2string(x, y, x.+y)
@test all(s .== [ " << EOD", @test all(s .== [" 1 4 5",
" 1 4 5",
" 2 5 7", " 2 5 7",
" 3 6 9", " 3 6 9"])
"EOD" ])
z = [X+Y for X in x, Y in y]; z = [X+Y for X in x, Y in y];
s = Gnuplot.dataset(z) s = Gnuplot.data2string(z)
@test all(s .== [" << EOD", @test all(s .== [" 1 1 5" ,
" 1 1 5" ,
" 2 1 6" , " 2 1 6" ,
" 3 1 7" , " 3 1 7" ,
"" , "" ,
@ -47,13 +36,11 @@ s = Gnuplot.dataset(z)
"" , "" ,
" 1 3 7" , " 1 3 7" ,
" 2 3 8" , " 2 3 8" ,
" 3 3 9" , " 3 3 9" ])
"EOD" ])
s = Gnuplot.dataset(x, y, z) s = Gnuplot.data2string(x, y, z)
@test all(s .== [" << EOD", @test all(s .== [" 1 4 5" ,
" 1 4 5" ,
" 2 4 6" , " 2 4 6" ,
" 3 4 7" , " 3 4 7" ,
"" , "" ,
@ -63,8 +50,7 @@ s = Gnuplot.dataset(x, y, z)
"" , "" ,
" 1 6 7" , " 1 6 7" ,
" 2 6 8" , " 2 6 8" ,
" 3 6 9" , " 3 6 9" ])
"EOD" ])
@ -72,9 +58,8 @@ c = [[X, Y] for Y in y for X in x]; # First Y (i.e. rows) then X (i.e. columns)
u = getindex.(c, 1) u = getindex.(c, 1)
v = getindex.(c, 2) v = getindex.(c, 2)
s = Gnuplot.dataset(u, v, z) s = Gnuplot.data2string(u, v, z)
@test all(s .== [" << EOD", @test all(s .== [" 1 4 5" ,
" 1 4 5" ,
" 2 4 6" , " 2 4 6" ,
" 3 4 7" , " 3 4 7" ,
"" , "" ,
@ -84,8 +69,7 @@ s = Gnuplot.dataset(u, v, z)
"" , "" ,
" 1 6 7" , " 1 6 7" ,
" 2 6 8" , " 2 6 8" ,
" 3 6 9" , " 3 6 9" ])
"EOD" ])
#----------------------------------------------------------------- #-----------------------------------------------------------------