Gnuplot.jl/src/Gnuplot.jl
2017-08-27 15:41:42 +02:00

1107 lines
28 KiB
Julia

module Gnuplot
using AbbrvKW
export gp_getStartup, gp_getSpawnCmd, gp_getVerbose, gp_setOption,
gp_handles, gp_current, gp_setCurrent, gp_new, gp_exit, gp_exitAll,
gp_send, gp_reset, gp_cmd, gp_data, gp_plot, gp_multi, gp_next, gp_dump
######################################################################
# Structure definitions
######################################################################
"""
Structure containing the `Pipe` and `Process` objects associated to a
Gnuplot process.
"""
mutable struct GnuplotProc
pin::Base.Pipe
pout::Base.Pipe
perr::Base.Pipe
proc::Base.Process
channel::Channel{String}
"""
Start a new gnuplot process using the command given in the `cmd` argument.
"""
function GnuplotProc(cmd::String)
this = new()
this.pin = Base.Pipe()
this.pout = Base.Pipe()
this.perr = Base.Pipe()
this.channel = Channel{String}(32)
# Start gnuplot process
this.proc = spawn(`$cmd`, (this.pin, this.pout, this.perr))
# Close unused sides of the pipes
Base.close_pipe_sync(this.pout.in)
Base.close_pipe_sync(this.perr.in)
Base.close_pipe_sync(this.pin.out)
Base.start_reading(this.pout.out)
Base.start_reading(this.perr.out)
return this
end
end
#---------------------------------------------------------------------
"""
Structure containing a single command and the associated multiplot index
"""
mutable struct MultiCmd
cmd::String # command
id::Int # multiplot index
end
"""
Structure containing the state of a single gnuplot session.
"""
mutable struct GnuplotState
blockCnt::Int # data blocks counter
cmds::Vector{MultiCmd} # gnuplot commands
data::Vector{String} # data blocks
plot::Vector{MultiCmd} # plot specifications associated to each data block
splot::Bool # plot / splot session
lastDataName::String # name of the last data block
multiID::Int # current multiplot index (0 if no multiplot)
GnuplotState() = new(1, Vector{MultiCmd}(), Vector{String}(), Vector{MultiCmd}(), false, "", 0)
end
#---------------------------------------------------------------------
"""
Structure containing the global package state.
"""
mutable struct MainState
colorOut::Symbol # gnuplot STDOUT is printed with this color
colorIn::Symbol # gnuplot STDIN is printed with this color
verboseLev::Int # verbosity level (0 - 3), default: 3
gnuplotCmd::String # command used to start the gnuplot process
startup::String # commands automatically sent to each new gnuplot process
procs::Vector{GnuplotProc} # array of currently active gnuplot process and pipes
states::Vector{GnuplotState} # array of gnuplot sessions
handles::Vector{Int} # handles of gnuplot sessions
curPos::Int # index in the procs, states and handles array of current session
MainState() = new(:cyan, :yellow, 2,
"", "",
Vector{GnuplotProc}(), Vector{GnuplotState}(), Vector{Int}(),
0)
end
######################################################################
# Private functions
######################################################################
"""
Check gnuplot is runnable with the command given in `main.gnuplotCmd`.
Also check that gnuplot version is >= 4.7 (required to use data
blocks).
"""
function checkGnuplotVersion()
cmd = `$(main.gnuplotCmd) --version`
out, procs = open(`$cmd`, "r")
s = String(read(out))
if !success(procs)
error("An error occurred while running: " * string(cmd))
end
s = split(s, " ")
ver = ""
for token in s
try
ver = VersionNumber("$token")
break
catch
end
end
if ver < v"4.7"
error("gnuplot ver. >= 4.7 is required, but " * string(ver) * " was found.")
end
gp_log(1, "Found gnuplot version: " * string(ver))
end
#---------------------------------------------------------------------
"""
Logging facility (each line is prefixed with the session handle.)
Printing occur only if the logging level is >= current verbosity
level.
"""
function gp_log(level::Int, s::String; id=nothing, color=nothing)
if (main.verboseLev < level)
return
end
color == nothing && (color = main.colorOut)
prefix = ""
if (id == nothing) && (main.curPos > 0)
id = main.handles[main.curPos]
end
prefix = string("GP(", id, ")")
a = split(s, "\n")
for v in a
print_with_color(color, "$prefix $v\n")
end
end
#---------------------------------------------------------------------
"""
Read gnuplot outputs, and optionally redirect to a `Channel`.
This fuction is supposed to be run in a `Task`.
"""
function gp_readTask(sIN, channel; kw...)
saveOutput::Bool = false
while isopen(sIN)
line = convert(String, readline(sIN))
if line == "GNUPLOT_JL_SAVE_OUTPUT"
saveOutput = true
gp_log(4, "|start of captured data =========================")
else
if saveOutput
put!(channel, line)
end
if line == "GNUPLOT_JL_SAVE_OUTPUT_END"
saveOutput = false
gp_log(4, "|end of captured data ===========================")
elseif line != ""
if saveOutput
gp_log(3, "| " * line; kw...)
else
gp_log(2, " " * line; kw...)
end
end
end
end
gp_log(1, "pipe closed"; kw...)
end
#---------------------------------------------------------------------
"""
Return a unique data block name
"""
@AbbrvKW function gp_mkBlockName(;prefix::Union{Void,String}=nothing)
if prefix == nothing
prefix = string("d", gp_current())
end
cur = main.states[main.curPos]
name = string(prefix, "_", cur.blockCnt)
cur.blockCnt += 1
return name
end
#---------------------------------------------------------------------
"""
Return the GnuplotProc structure of current session, or start a new
gnuplot process if none is running.
"""
function gp_getProcOrStartIt()
if main.curPos == 0
gp_log(1, "Starting a new gnuplot process...")
id = gp_new()
end
p = main.procs[main.curPos]
if !Base.process_running(p.proc)
error("The current gnuplot process is no longer running.")
end
return p
end
######################################################################
# Get/set package options
######################################################################
"""
# Gnuplot.gp_getStartup
Return the gnuplot command to be executed at the beginning of each
session.
"""
gp_getStartup() = main.startup
"""
# Gnuplot.gp_getSpawnCmd
Return the command to spawn a gnuplot process.
"""
gp_getSpawnCmd() = main.gnuplotCmd
"""
# Gnuplot.gp_getVerbose
Return the verbosity level.
"""
gp_getVerbose() = main.verboseLev
#---------------------------------------------------------------------
"""
# Gnuplot.gp_setOption
Set package options.
## Example:
```
gp_setOption(cmd="/path/to/gnuplot", verb=2, startup="set term wxt")
```
## Keywords:
- `cmd::String`: command to spawn a gnuplot process;
- `startup::String`: gnuplot command to be executed at the beginning
of each session;
- `verbose::Int`: verbosity level (in the range 0 ÷ 4)
## See also: `gp_getStartup`, `gp_getSpawnCmd` and `gp_getVerbose`.
"""
@AbbrvKW function gp_setOption(;cmd::Union{Void,String}=nothing,
startup::Union{Void,String}=nothing,
verbose::Union{Void,Int}=nothing)
if startup != nothing
main.startup = startup
end
if cmd != nothing
main.gnuplotCmd = cmd
checkGnuplotVersion()
end
if verbose != nothing
@assert (0 <= verbose <= 4)
main.verboseLev = verbose
end
return nothing
end
######################################################################
# Handle multiple gnuplot instances
######################################################################
"""
# Gnuplot.gp_handles
Return a `Vector{Int}` of available session handles.
"""
function gp_handles()
return deepcopy(main.handles)
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_current
Return the handle of the current session.
"""
function gp_current()
return main.handles[main.curPos]
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_setCurrent
Change the current session handle.
## Arguments:
- `handle::Int`: the handle of the session to select as current.
## See also:
- `gp_current`: return the current session handle;
- `gp_handles`: return the list of available handles.
"""
function gp_setCurrent(handle)
i = find(main.handles .== handle)
@assert length(i) == 1
i = i[1]
@assert Base.process_running(main.procs[i].proc)
main.curPos = i
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_new
Create a new session (by starting a new gnuplot process), make it the
current one, and return the new handle.
E.g., to compare the look and feel of two terminals:
```
id1 = gp_new()
gp_send("set term qt")
gp_send("plot sin(x)")
id2 = gp_new()
gp_send("set term wxt")
gp_send("plot sin(x)")
gp_setCurrent(id1)
gp_send("set title 'My title'")
gp_send("replot")
gp_setCurrent(id2)
gp_send("set title 'My title'")
gp_send("replot")
gp_exitAll()
```
"""
function gp_new()
if length(main.handles) > 0
newhandle = max(main.handles...) + 1
else
newhandle = 1
end
if main.gnuplotCmd == ""
gp_setOption(cmd="gnuplot")
end
push!(main.procs, GnuplotProc(main.gnuplotCmd))
push!(main.states, GnuplotState())
push!(main.handles, newhandle)
main.curPos = length(main.handles)
# Start reading tasks for STDOUT and STDERR
@async gp_readTask(main.procs[end].pout, main.procs[end].channel, id=newhandle)
@async gp_readTask(main.procs[end].perr, main.procs[end].channel, id=newhandle)
if main.startup != ""
gp_cmd(main.startup)
end
gp_log(1, "New session started with handle $newhandle")
return newhandle
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_exit
Close current session and quit the corresponding gnuplot process.
"""
function gp_exit()
if main.curPos == 0
return
end
p = main.procs[main.curPos]
close(p.pin)
close(p.pout)
close(p.perr)
wait(p.proc)
@assert !Base.process_running(p.proc)
gp_log(1, string("Process exited with status ", p.proc.exitcode))
deleteat!(main.procs , main.curPos)
deleteat!(main.states, main.curPos)
deleteat!(main.handles , main.curPos)
if length(main.handles) > 0
gp_setCurrent(max(main.handles...))
else
main.curPos = 0
end
return p.proc.exitcode
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_exitAll
Repeatedly call `gp_exit` until all sessions are closed.
"""
function gp_exitAll()
while length(main.handles) > 0
gp_exit()
end
end
######################################################################
# Send data and commands to Gnuplot
######################################################################
"""
# Gnuplot.gp_send
Send a string to the current session's gnuplot STDIN.
The commands sent through `gp_send` are not stored in the current
session (use `gp_cmd` to save commands in the current session).
## Example:
```
println("Current terminal: ", gp_send("print GPVAL_TERM", capture=true))
```
## Arguments:
- `cmd::String`: command to be sent.
## Keywords:
- `capture::Bool`: if `true` waits until gnuplot provide a complete
reply, and return it as a `Vector{String}`. Otherwise return
`nothing` immediately.
"""
@AbbrvKW function gp_send(cmd::String; capture::Bool=false)
p = gp_getProcOrStartIt()
if capture
write(p.pin, "print 'GNUPLOT_JL_SAVE_OUTPUT'\n")
gp_log(4, "-> Start capture", color=main.colorIn)
end
for s in split(cmd, "\n")
w = write(p.pin, strip(s) * "\n")
gp_log(2, "-> $s" , color=main.colorIn)
w <= 0 && error("Writing on gnuplot STDIN pipe returned $w")
end
if capture
write(p.pin, "print 'GNUPLOT_JL_SAVE_OUTPUT_END'\n")
gp_log(4, "-> End capture", color=main.colorIn)
end
flush(p.pin)
if capture
out = Vector{String}()
while true
l = take!(p.channel)
l == "GNUPLOT_JL_SAVE_OUTPUT_END" && break
push!(out, l)
end
length(out) == 1 && (out = out[1])
return out
end
return nothing
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_reset
Send a 'reset session' command to gnuplot and delete all commands,
data, and plots in the current session.
"""
function gp_reset()
gp_send("reset session", capture=true)
main.states[main.curPos] = GnuplotState()
if main.startup != ""
gp_cmd(main.startup)
end
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_cmd
Send a command to gnuplot process and store it in the current session.
A few, commonly used, commands may be specified through keywords (see
below).
## Examples:
```
gp_cmd("set grid")
gp_cmd("set key left", xrange=(1,3))
gp_cmd(title="My title", xlab="X label", xla="Y label")
```
## Arguments:
- `cmd::String`: command to be sent.
## Keywords:
- `multiID::Int`: ID of the plot the commands belongs to (only useful
for multiplots);
- `splot::Bool`: set to `true` for a "splot" gnuplot session, `false`
for a "plot" one;
- `title::String`: plot title;
- `xlabel::String`: X axis label;
- `ylabel::String`: Y axis label;
- `zlabel::String`: Z axis label;
- `xlog::Bool`: logarithmic scale for X axis;
- `ylog::Bool`: logarithmic scale for Y axis;
- `zlog::Bool`: logarithmic scale for Z axis;
- `xrange::NTuple{2, Number}`: X axis range;
- `yrange::NTuple{2, Number}`: Y axis range;
- `zrange::NTuple{2, Number}`: Z axis range;
"""
@AbbrvKW function gp_cmd(cmd::String="";
splot::Union{Void,Bool}=nothing,
multiID::Union{Void,Int}=nothing,
xrange::Union{Void,NTuple{2, Number}}=nothing,
yrange::Union{Void,NTuple{2, Number}}=nothing,
zrange::Union{Void,NTuple{2, Number}}=nothing,
title::Union{Void,String}=nothing,
xlabel::Union{Void,String}=nothing,
ylabel::Union{Void,String}=nothing,
zlabel::Union{Void,String}=nothing,
xlog::Union{Void,Bool}=nothing,
ylog::Union{Void,Bool}=nothing,
zlog::Union{Void,Bool}=nothing)
gp_getProcOrStartIt()
cur = main.states[main.curPos]
splot == nothing || (cur.splot = splot)
mID = multiID == nothing ? cur.multiID : multiID
if cmd != ""
push!(cur.cmds, MultiCmd(cmd, mID))
if mID == 0
gp_send(cmd)
end
end
xrange == nothing || gp_cmd(multiID=mID, "set xrange [" * join(xrange, ":") * "]")
yrange == nothing || gp_cmd(multiID=mID, "set yrange [" * join(yrange, ":") * "]")
zrange == nothing || gp_cmd(multiID=mID, "set zrange [" * join(zrange, ":") * "]")
title == nothing || gp_cmd(multiID=mID, "set title '" * title * "'")
xlabel == nothing || gp_cmd(multiID=mID, "set xlabel '" * xlabel * "'")
ylabel == nothing || gp_cmd(multiID=mID, "set ylabel '" * ylabel * "'")
zlabel == nothing || gp_cmd(multiID=mID, "set zlabel '" * zlabel * "'")
xlog == nothing || gp_cmd(multiID=mID, (xlog ? "" : "un") * "set logscale x")
ylog == nothing || gp_cmd(multiID=mID, (ylog ? "" : "un") * "set logscale y")
zlog == nothing || gp_cmd(multiID=mID, (zlog ? "" : "un") * "set logscale z")
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_data
Send data to the gnuplot process, store it in the current session, and
return the name of the data block (to be later used with `gp_plot`).
## Example:
```
x = collect(1.:10)
# Automatically generated data block name
name1 = gp_data(x, x.^2)
# Specify a prefix for the data block name, a sequential counter will
# be appended to ensure the black names are unique
name2 = gp_data(x, x.^2.2, prefix="MyPrefix")
# Specify the whole data block name. NOTE: avoid using the same name
# multiple times!
name3 = gp_data(x, x.^1.8, name="MyChosenName")
gp_plot(name1)
gp_plot(name2)
gp_plot(name3)
gp_dump()
```
## Arguments:
- `data::Vararg{AbstractArray{T,1},N} where {T<:Number,N}`: the data
to be sent to gnuplot;
## Keywords:
- `name::String`: data block name. If not given an automatically
generated one will be used;
- `prefix::String`: prefix for data block name (an automatic counter
will be appended);
"""
function gp_data(data::Vararg{AbstractArray{T,1},N};
name::Union{Void,String}=nothing,
prefix::Union{Void,String}=nothing) where {T<:Number,N}
gp_getProcOrStartIt()
cur = main.states[main.curPos]
if name == nothing
name = gp_mkBlockName(pre=prefix)
end
name = "\$$name"
for i in 2:length(data)
@assert length(data[1]) == length(data[i])
end
v = "$name << EOD"
push!(cur.data, v)
gp_send(v)
for i in 1:length(data[1])
v = ""
for j in 1:length(data)
v *= " " * string(data[j][i])
end
push!(cur.data, v)
gp_send(v)
end
v = "EOD"
push!(cur.data, v)
gp_send(v)
cur.lastDataName = name
return name
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_plot
Add a new plot/splot comand to the current session
## Example:
```
x = collect(1.:10)
gp_data(x, x.^2)
gp_plot(last=true, "w l tit 'Pow 2'")
src = gp_data(x, x.^2.2)
gp_plot("\$src w l tit 'Pow 2.2'")
# Re use the same data block
gp_plot("\$src u 1:(\\\$2+10) w l tit 'Pow 2.2, offset=10'")
gp_dump() # Do the plot
```
## Arguments:
- `spec::String`: plot command (see Gnuplot manual) without the
leading "plot" string;
## Keywords:
- `file::String`: if given the plot command will be prefixed with
`'\$file'`;
- `lastData::Bool`: if true the plot command will be prefixed with the
last inserted data block name;
- `multiID::Int`: ID of the plot the command belongs to (only useful
for multiplots);
"""
@AbbrvKW function gp_plot(spec::String;
lastData::Bool=false,
file::Union{Void,String}=nothing,
multiID::Union{Void,Int}=nothing)
gp_getProcOrStartIt()
cur = main.states[main.curPos]
mID = multiID == nothing ? cur.multiID : multiID
src = ""
if lastData
src = cur.lastDataName
elseif file != nothing
src = "'" * file * "'"
end
push!(cur.plot, MultiCmd("$src $spec", mID))
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_multi
Initialize a multiplot (through the "set multiplot" Gnuplot command).
## Arguments:
- `multiCmd::String`: multiplot command (see Gnuplot manual) without
the leading "set multiplot" string;
## See also: `gp_next`.
"""
function gp_multi(multiCmd::String="")
gp_getProcOrStartIt()
cur = main.states[main.curPos]
if cur.multiID != 0
error("Current multiplot ID is $cur.multiID, while it should be 0")
end
gp_next()
gp_cmd("set multiplot $multiCmd")
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_next
Select next slot for multiplot sessions.
"""
function gp_next()
gp_getProcOrStartIt()
cur = main.states[main.curPos]
cur.multiID += 1
end
#---------------------------------------------------------------------
"""
# Gnuplot.gp_dump
Send all necessary commands to gnuplot to actually do the plot.
Optionally, the commands may be sent to a file. In any case the
commands are returned as `Vector{String}`.
## Keywords:
- `all::Bool`: if true all commands and data will be sent again to
gnuplot, if they were already sent (equivalent to `data=true,
cmd=true`);
- `cmd::Bool`: if true all commands will be sent again to gnuplot, if
they were already sent;
- `data::Bool`: if true all data will be sent again to gnuplot, if
they were already sent;
- `dry::Bool`: if true no command/data will be sent to gnuplot;
- `file::String`: filename to redirect all outputs. Implies
`all=true, dry=true`.
"""
@AbbrvKW function gp_dump(; all::Bool=false,
dry::Bool=false,
cmd::Bool=false,
data::Bool=false,
file::Union{Void,String}=nothing)
if main.curPos == 0
return ""
end
if file != nothing
all = true
dry = true
end
cur = main.states[main.curPos]
out = Vector{String}()
all && (push!(out, "reset session"))
if data || all
for s in cur.data
push!(out, s)
end
end
for id in 0:cur.multiID
for m in cur.cmds
if (m.id == id) && ((id > 0) || all)
push!(out, m.cmd)
end
end
tmp = Vector{String}()
for m in cur.plot
if m.id == id
push!(tmp, m.cmd)
end
end
if length(tmp) > 0
s = cur.splot ? "splot " : "plot "
s *= "\\\n "
s *= join(tmp, ", \\\n ")
push!(out, s)
end
end
if cur.multiID > 0
push!(out, "unset multiplot")
end
if file != nothing
sOut = open(file, "w")
for s in out; println(sOut, s); end
close(sOut)
end
if !dry
for s in out; gp_send(s); end
gp_send("", capture=true)
end
return join(out, "\n")
end
######################################################################
# Submodule: Utils
######################################################################
module Utils
import Gnuplot
export @gpi, @gp, @gp_str, @gp_cmd, gp_terminals, gp_terminal
#---------------------------------------------------------------------
"""
# Gnuplot.@gpi
Similar to `@gp`, but do not adds the `gp_reset()` and `gp_dump()`
calls. See documentation for `@gp`.
"""
macro gpi(args...)
if length(args) == 0
return :()
end
exprBlock = Expr(:block)
exprData = Expr(:call)
push!(exprData.args, :(Gnuplot.gp_data))
pendingPlot = false
pendingMulti = false
for arg in args
#println(typeof(arg), " ", arg)
if isa(arg, Expr) && (arg.head == :quote) && (arg.args[1] == :next)
push!(exprBlock.args, :(Gnuplot.gp_next()))
elseif isa(arg, Expr) && (arg.head == :quote) && (arg.args[1] == :plot)
pendingPlot = true
elseif isa(arg, Expr) && (arg.head == :quote) && (arg.args[1] == :multi)
pendingMulti = true
elseif (isa(arg, Expr) && (arg.head == :string)) || isa(arg, String)
# Either a plot or cmd string
if pendingPlot
if length(exprData.args) > 1
push!(exprBlock.args, exprData)
exprData = Expr(:call)
push!(exprData.args, :(Gnuplot.gp_data))
end
push!(exprBlock.args, :(Gnuplot.gp_plot(last=true, $arg)))
pendingPlot = false
elseif pendingMulti
push!(exprBlock.args, :(Gnuplot.gp_multi($arg)))
pendingMulti = false
else
push!(exprBlock.args, :(Gnuplot.gp_cmd($arg)))
end
elseif (isa(arg, Expr) && (arg.head == :(=)))
# A cmd keyword
sym = arg.args[1]
val = arg.args[2]
push!(exprBlock.args, :(Gnuplot.gp_cmd($sym=$val)))
else
# A data set
push!(exprData.args, arg)
pendingPlot = true
end
end
#dump(exprBlock)
if pendingPlot && length(exprData.args) >= 2
push!(exprBlock.args, exprData)
push!(exprBlock.args, :(Gnuplot.gp_plot(last=true, "")))
end
return esc(exprBlock)
end
#---------------------------------------------------------------------
"""
# Gnuplot.@gp
The `@gp` (and its companion `gpi`) allows to exploit almost all
**Gnuplot.jl** package functionalities using an extremely efficient
and concise syntax. In the vast majority of cases you can use a
single call to `@gpi` instead of many calls to `gp_cmd`, `gp_data`,
`gp_plot`, etc... to produce (even very complex) plots.
The syntax is as follows:
```
@gp( ["a command"], # passed to gp_cmd() as a command string
[Symbol=(Value | Expr)] # passed to gp_cmd() as a keyword
[(one or more Expression | Array) "plot spec"], # passed to gp_data() and
# gp_plot(last=true) respectively
[:plot "plot spec"], # passed to gp_plot()
[:multi "multi spec"], # passed to gp_multi()
[:next] # calls gp_next()
etc...
)
```
All entries are optional, and there is no mandatory order. The only
mandatory sequences are:
- the plot specification strings which must follow a data block or the `:plot` symbol;
- the multiplot specification string which must follow `:multi` symbol;
A simple example will clarify the usage:
```
@gp "set key left" title="My title" xr=(1,5) collect(1.:10) "with lines tit 'Data'"
```
This call epands as follows:
```
gp_reset()
begin
gp_cmd("set key left")
gp_cmd(title="My title")
gp_cmd(xr=(1, 5))
gp_data(collect(1.0:10))
gp_plot(last=true, "with lines tit 'Data'")
end
gp_dump()
```
A closely related macro is `@gpi` which do not adds the `gp_reset()`
and `gp_dump()` calls.
## Examples:
```
x = collect(1.:10)
@gp x
@gp x x
@gp x -x
@gp x x.^2
@gp x x.^2 "w l"
lw = 3
@gp x x.^2 "w l lw \$lw"
@gp("set grid", "set key left", xlog=true, ylog=true,
title="My title", xlab="X label", ylab="Y label",
x, x.^0.5, "w l tit 'Pow 0.5' dt 2 lw 2 lc rgb 'red'",
x, x , "w l tit 'Pow 1' dt 1 lw 3 lc rgb 'blue'",
x, x.^2 , "w l tit 'Pow 2' dt 3 lw 2 lc rgb 'purple'")
# Multiplot
@gp(xr=(-2pi,2pi), "unset key",
:multi, "layout 2,2 title 'Multiplot title'",
:plot, "sin(x)" , :next,
:plot, "sin(2*x)", :next,
:plot, "sin(3*x)", :next,
:plot, "sin(4*x)")
# or equivalently
gp_reset()
@gpi(xr=(-2pi,2pi), "unset key",
:multi, "layout 2,2 title 'Multiplot title'")
for i in 1:4
@gpi :plot "sin(\$i*x)" :next
end
gp_dump()
```
"""
macro gp(args...)
esc_args = Vector{Any}()
for arg in args
push!(esc_args, esc(arg))
end
e = :(@gpi($(esc_args...)))
f = Expr(:block)
push!(f.args, esc(:( Gnuplot.gp_reset())))
push!(f.args, e)
push!(f.args, esc(:( Gnuplot.gp_dump())))
return f
end
"""
# Gnuplot.@gp_str
Call `gp_send` with a non-standard string literal.
NOTE: this is supposed to be used interactively on the REPL, not in
functions.
## Examples:
```
println("Current terminal: ", gp"print GPVAL_TERM")
gp"plot sin(x)"
gp"
set title \\"3D surface from a grid (matrix) of Z values\\"
set xrange [-0.5:4.5]
set yrange [-0.5:4.5]
set grid
set hidden3d
\$grid << EOD
5 4 3 1 0
2 2 0 0 1
0 0 0 1 0
0 0 0 2 3
0 1 2 4 3
EOD
splot '\$grid' matrix with lines notitle
"
```
"""
macro gp_str(s::String)
return Gnuplot.gp_send(s, capture=true)
end
#---------------------------------------------------------------------
"""
# Gnuplot.@gp_cmd
Call the gnuplot "load" command passing the filename given as
non-standard string literal.
NOTE: this is supposed to be used interactively on the REPL, not in
functions.
Example:
```
@gp (1:10).^3 "w l notit lw 4"
gp_dump(file="test.gp")
gp_exitAll()
gp`test.gp`
```
"""
macro gp_cmd(file::String)
return Gnuplot.gp_send("load '$file'", capture=true)
end
#---------------------------------------------------------------------
gp_terminals() = Gnuplot.gp_send("print GPVAL_TERMINALS", capture=true)
gp_terminal() = Gnuplot.gp_send("print GPVAL_TERM", capture=true)
end #module
######################################################################
# Module initialization
######################################################################
const main = MainState()
end #module