Minor changes

This commit is contained in:
Giorgio Calderone 2020-03-17 10:55:40 +01:00
parent 605ad0cfda
commit 5487e60222
2 changed files with 119 additions and 93 deletions

103
README.md
View File

@ -6,9 +6,9 @@
**Gnuplot.jl** allows easy and fast use of [Gnuplot](http://gnuplot.info/) as a data visualization tool in Julia. Its main features are: **Gnuplot.jl** allows easy and fast use of [Gnuplot](http://gnuplot.info/) as a data visualization tool in Julia. Its main features are:
- transparent interface between Julia and Gnuplot to exploit all functionalities of the latter, both present and future ones; - transparent interface between Julia and Gnuplot to exploit all functionalities of the latter, both present and future ones;
- fast data transmission to gnuplot through system pipes (no temporary files involved); - fast data transmission to gnuplot through system pipes (no temporary files involved);
- handles multiple Gnuplot process simultaneously; - handles multiple Gnuplot process simultaneously;
- support for multiplots; - support for multiplots;
@ -52,7 +52,7 @@ noise = randn(length(x))./2;
e = 0.5 * fill(1., length(x)); e = 0.5 * fill(1., length(x));
@gp("set key horizontal", "set grid", title="My title", @gp("set key horizontal", "set grid", title="My title",
xrange=(-7,7), ylabel="Y label", xlab="X label", xrange=(-7,7), ylabel="Y label", xlab="X label",
x, y, "w l t 'Real model' dt 2 lw 2 lc rgb 'red'", x, y, "w l t 'Real model' dt 2 lw 2 lc rgb 'red'",
x, y+noise, e, "w errorbars t 'Data'") x, y+noise, e, "w errorbars t 'Data'")
``` ```
@ -67,7 +67,7 @@ Note that this simple example already covers the vast majority of use cases, sin
If you set the verbose option (`Gnuplot.setverbose(true)`, which is `false` by default) you'll be able to see all the communication taking place between the **Gnuplot.jl** package and the underlyng Gnuplot process. Repeating the last command: If you set the verbose option (`Gnuplot.setverbose(true)`, which is `false` by default) you'll be able to see all the communication taking place between the **Gnuplot.jl** package and the underlyng Gnuplot process. Repeating the last command:
```Julia ```Julia
julia> @gp("set key horizontal", "set grid", title="My title", julia> @gp("set key horizontal", "set grid", title="My title",
xrange=(-7,7), ylabel="Y label", xlab="X label", xrange=(-7,7), ylabel="Y label", xlab="X label",
x, y, "w l t 'Real model' dt 2 lw 2 lc rgb 'red'", x, y, "w l t 'Real model' dt 2 lw 2 lc rgb 'red'",
x, y+noise, e, "w errorbars t 'Data'") x, y+noise, e, "w errorbars t 'Data'")
GNUPLOT (default) print GPVAL_TERM GNUPLOT (default) print GPVAL_TERM
@ -150,57 +150,84 @@ or show an interactive 3D plots using the `@gsp` macro in place of `@gp`, e.g.:
Further documentation for the `@gp` and `@gsp` macros is available in the REPL by means of the `@doc` macro or by typing `?` in the REPL followed by the macro name. Further documentation for the `@gp` and `@gsp` macros is available in the REPL by means of the `@doc` macro or by typing `?` in the REPL followed by the macro name.
### Export to image
TODO
### Save a script file
TODO
### Multiple gnuplot istances ### Multiple gnuplot istances
The **Gnuplot.jl** package can handle multiple Gnuplot istances simultaneously, each idenitified by a unique session name (actually a Julia symbol). To use a specific session simply name it in a `@gp` or `@gsp` call. If the session is not yet created it will be automatically started: The **Gnuplot.jl** package can handle multiple Gnuplot istances simultaneously, each idenitified by a unique session name (actually a Julia symbol). To use a specific session simply name it in a `@gp` or `@gsp` call. If the session is not yet created it will be automatically started:
``` Julia ``` Julia
# Plot using session GP1 # Plot using a session named GP1
x = 1:10 x = 1:10
@gp :GP1 x x.^2 @gp :GP1 x x.^2
# Plot using session GP2 # Plot using a session named GP2
@gp x x.^2 :GP2 @gp x x.^2 :GP2
# Plot using default session # Plot using default session (i.e. do not specify any session name)
@gp x x.^2 @gp x x.^2
``` ```
### Customization
A custom command to start a Gnuplot process can be specified as follows
``` Julia
Gnuplot.state.cmd = "/path/to/gnuplot/executable"
```
Also, the package may work in *dry* mode, i.e. without any underlying Gnuplot process:
``` Julia
Gnuplot.state.dry = true
```
The prupose is to create gnuplot scripts without running them, e.g:
```Julia
@gp x x.^2 "w l"
save("test.gp")
```
The `test.gp` can then be loaded directly in gnuplot with:
```
gnuplot> load 'test.gp'
```
### Direct execution of gnuplot commands
Both the `@gp` and `@gsp` macros store data and commands in the package state to allow using multiple statements for a single plot, or to save all data and commands on a script file. However the user may directly execute command on the underlying Gnuplot process using the `Gnuplot.exec` function. E.g., we can retrieve the values of the fitting parameters of the previous example:
```Julia
# Retrieve values fr a, b and c
a = Meta.parse(Gnuplot.exec("print a"))
b = Meta.parse(Gnuplot.exec("print b"))
c = Meta.parse(Gnuplot.exec("print c"))
```
### Terminating a session ### Terminating a session
A session and the associated gnuplot process can be terminated by a call to `quit`, specifying the session name, e.g.: A session and the associated gnuplot process can be terminated by a call to `quit`, specifying the session name, e.g.:
``` Julia ``` Julia
julia> Gnuplot.quit(:GP1) julia> Gnuplot.quit(:GP1)
``` ```
A call to `Gnuplot.quitall()` will terminate all active sessions. A call to `Gnuplot.quitall()` will terminate all active sessions.
## Direct execution of gnuplot commands
Both the `@gp` and `@gsp` macros store data and commands in the package state to allow using multiple statements for a single plot, or to save all data and commands on a script file. However the user may directly execute command on the underlying Gnuplot process using the `Gnuplot.exec` function. E.g., we can retrieve the values of the fitting parameters of the previous example:
```Julia
# Retrieve values fr a, b and c
a = Meta.parse(Gnuplot.exec("print a"))
b = Meta.parse(Gnuplot.exec("print b"))
c = Meta.parse(Gnuplot.exec("print c"))
```
## Customization
A custom command to start a Gnuplot process can be specified as follows
``` Julia
Gnuplot.options.cmd = "/path/to/gnuplot/executable"
```
Also, the package may work in *dry* mode, i.e. without any underlying Gnuplot process:
``` Julia
Gnuplot.options.dry = true
```
The prupose is to create gnuplot scripts without running them, e.g:
```Julia
@gp x x.^2 "w l"
save("test.gp")
```
The `test.gp` can then be loaded directly in gnuplot with:
```
gnuplot> load 'test.gp'
```
Finally, you can specify initialising commands to be executed when the Gnuplot process starts, in the same way as you use `.gnuplotrc`. For instance, to set up a default terminal:
```julia
push!(Gnuplot.options.init, "set term sixelgd")
```
The above command should be executed *BEFORE* starting a new session. (use `Gnuplot.quitall()` will terminate all active sessions).
## Plot in a terminal (no X11)
Gnuplot supports displaying plot in a terminal application, with no need for X11 or other window frameworks. This is very useful when you run Julia on a remote shell through `ssh`, through a slow network link.
The Gnuplot terminals able to operate on "terminal" applications are `dumb` and `sixelgd`. You can use them as default with:
```julia
push!(Gnuplot.options.init, "set term dumb")
```
or
```julia
push!(Gnuplot.options.init, "set term sixelgd")
```
Note that the latter requires Sixel graphics to be enabled (e.g. `xterm -ti vt340`).

View File

@ -49,18 +49,17 @@ end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
mutable struct State Base.@kwdef mutable struct Options
sessions::Dict{Symbol, DrySession}; dry::Bool = false # Use "dry" sessions (i.e. without an underlying Gnuplot process)
dry::Bool cmd::String = "gnuplot" # Customizable command to start the Gnuplot process
cmd::String default::Symbol = :default # Default session name
default::Symbol; # default session name init::Vector{String} = 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 = false # verbosity flag (true/false)
verbose::Bool; # verbosity level (true/false) datalines::Int = 4; # How many lines of a dataset 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, 4)
end end
const state = State() const sessions = Dict{Symbol, DrySession}()
state.dry = false const options = Options()
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
@ -281,17 +280,17 @@ end
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
function DrySession(sid::Symbol) function DrySession(sid::Symbol)
global state global options
(sid in keys(state.sessions)) && error("Gnuplot session $sid is already active") (sid in keys(sessions)) && error("Gnuplot session $sid is already active")
out = DrySession(sid, Vector{DataSet}(), [SinglePlot()], 1) out = DrySession(sid, Vector{DataSet}(), [SinglePlot()], 1)
state.sessions[sid] = out sessions[sid] = out
return out return out
end end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
function GPSession(sid::Symbol) function GPSession(sid::Symbol)
function readTask(sid, stream, channel) function readTask(sid, stream, channel)
global state global options
saveOutput = false saveOutput = false
while isopen(stream) while isopen(stream)
@ -313,26 +312,26 @@ function GPSession(sid::Symbol)
if line == "GNUPLOT_CAPTURE_BEGIN" if line == "GNUPLOT_CAPTURE_BEGIN"
saveOutput = true saveOutput = true
else else
if (line != "") && (line != "GNUPLOT_CAPTURE_END") && (state.verbose) if (line != "") && (line != "GNUPLOT_CAPTURE_END") && (options.verbose)
printstyled(color=:cyan, "GNUPLOT ($sid) -> $line\n") printstyled(color=:cyan, "GNUPLOT ($sid) -> $line\n")
end end
(saveOutput) && (put!(channel, line)) (saveOutput) && (put!(channel, line))
(line == "GNUPLOT_CAPTURE_END") && (saveOutput = false) (line == "GNUPLOT_CAPTURE_END") && (saveOutput = false)
end end
end end
delete!(state.sessions, sid) delete!(sessions, sid)
return nothing return nothing
end end
global state global options
CheckGnuplotVersion(state.cmd) CheckGnuplotVersion(options.cmd)
session = DrySession(sid) session = DrySession(sid)
pin = Base.Pipe() pin = Base.Pipe()
pout = Base.Pipe() pout = Base.Pipe()
perr = Base.Pipe() perr = Base.Pipe()
proc = run(pipeline(`$(state.cmd)`, stdin=pin, stdout=pout, stderr=perr), wait=false) proc = run(pipeline(`$(options.cmd)`, stdin=pin, stdout=pout, stderr=perr), wait=false)
chan = Channel{String}(32) chan = Channel{String}(32)
# Close unused sides of the pipes # Close unused sides of the pipes
@ -348,7 +347,7 @@ function GPSession(sid::Symbol)
out = GPSession(getfield.(Ref(session), fieldnames(concretetype(DrySession)))..., out = GPSession(getfield.(Ref(session), fieldnames(concretetype(DrySession)))...,
pin, pout, perr, proc, chan) pin, pout, perr, proc, chan)
state.sessions[sid] = out sessions[sid] = out
# Set window title # Set window title
term = writeread(out, "print GPVAL_TERM")[1] term = writeread(out, "print GPVAL_TERM")[1]
@ -358,7 +357,7 @@ function GPSession(sid::Symbol)
writeread(out, "set term $term $opts title 'Gnuplot.jl: $(out.sid)'") writeread(out, "set term $term $opts title 'Gnuplot.jl: $(out.sid)'")
end end
end end
for l in state.init for l in options.init
writeread(out, l) writeread(out, l)
end end
@ -367,16 +366,16 @@ end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
function getsession(sid::Symbol=state.default) function getsession(sid::Symbol=options.default)
global state global options
if !(sid in keys(state.sessions)) if !(sid in keys(sessions))
if state.dry if options.dry
DrySession(sid) DrySession(sid)
else else
GPSession(sid) GPSession(sid)
end end
end end
return state.sessions[sid] return sessions[sid]
end end
@ -398,8 +397,8 @@ end
""" """
println(gp::DrySession, str::AbstractString) = nothing println(gp::DrySession, str::AbstractString) = nothing
function println(gp::GPSession, str::AbstractString) function println(gp::GPSession, str::AbstractString)
global state global options
if state.verbose if options.verbose
printstyled(color=:light_yellow, "GNUPLOT ($(gp.sid)) $str\n") printstyled(color=:light_yellow, "GNUPLOT ($(gp.sid)) $str\n")
end end
w = write(gp.pin, strip(str) * "\n") w = write(gp.pin, strip(str) * "\n")
@ -411,10 +410,10 @@ end
println(gp::DrySession, d::DataSet) = nothing println(gp::DrySession, d::DataSet) = nothing
function println(gp::GPSession, d::DataSet) function println(gp::GPSession, d::DataSet)
if state.verbose if options.verbose
v = "" v = ""
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.name) << EOD\n") printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.name) << EOD\n")
n = min(state.printlines, length(d.lines)) n = min(options.printlines, length(d.lines))
for i in 1:n for i in 1:n
printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.lines[i])\n") printstyled(color=:light_black, "GNUPLOT ($(gp.sid)) $(d.lines[i])\n")
end end
@ -434,18 +433,18 @@ end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
writeread(gp::DrySession, str::AbstractString) = [""] writeread(gp::DrySession, str::AbstractString) = [""]
function writeread(gp::GPSession, str::AbstractString) function writeread(gp::GPSession, str::AbstractString)
global state global options
verbose = state.verbose verbose = options.verbose
state.verbose = false options.verbose = false
println(gp, "print 'GNUPLOT_CAPTURE_BEGIN'") println(gp, "print 'GNUPLOT_CAPTURE_BEGIN'")
state.verbose = verbose options.verbose = verbose
println(gp, str) println(gp, str)
state.verbose = false options.verbose = false
println(gp, "print 'GNUPLOT_CAPTURE_END'") println(gp, "print 'GNUPLOT_CAPTURE_END'")
state.verbose = verbose options.verbose = verbose
out = Vector{String}() out = Vector{String}()
while true while true
@ -517,8 +516,8 @@ end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
function quit(gp::DrySession) function quit(gp::DrySession)
global state global options
delete!(state.sessions, gp.sid) delete!(sessions, gp.sid)
return 0 return 0
end end
@ -882,8 +881,8 @@ end
# Read/evaluate/print/loop # Read/evaluate/print/loop
# """ # """
# function repl(sid::Symbol) # function repl(sid::Symbol)
# verb = state.verbose # verb = options.verbose
# state.verbose = 0 # options.verbose = 0
# gp = getsession(sid) # gp = getsession(sid)
# while true # while true
# line = readline(stdin) # line = readline(stdin)
@ -893,12 +892,12 @@ end
# println(line) # println(line)
# end # end
# end # end
# state.verbose = verb # options.verbose = verb
# return nothing # return nothing
# end # end
# function repl() # function repl()
# global state # global options
# return repl(state.default) # return repl(options.default)
# end # end
@ -913,11 +912,11 @@ end
Quit the session and the associated gnuplot process (if any). Quit the session and the associated gnuplot process (if any).
""" """
function quit(sid::Symbol) function quit(sid::Symbol)
global state global options
if !(sid in keys(state.sessions)) if !(sid in keys(sessions))
error("Gnuplot session $sid do not exists") error("Gnuplot session $sid do not exists")
end end
return quit(state.sessions[sid]) return quit(sessions[sid])
end end
""" """
@ -926,8 +925,8 @@ end
Quit all the sessions and the associated gnuplot processes. Quit all the sessions and the associated gnuplot processes.
""" """
function quitall() function quitall()
global state global options
for sid in keys(state.sessions) for sid in keys(sessions)
quit(sid) quit(sid)
end end
return nothing return nothing
@ -947,7 +946,7 @@ exec("plot sin(x)")
``` ```
""" """
function exec(sid::Symbol, s::Vector{String}) function exec(sid::Symbol, s::Vector{String})
global state global options
gp = getsession(sid) gp = getsession(sid)
answer = Vector{String}() answer = Vector{String}()
for v in s for v in s
@ -956,8 +955,8 @@ function exec(sid::Symbol, s::Vector{String})
return join(answer, "\n") return join(answer, "\n")
end end
function exec(s::String) function exec(s::String)
global state global options
exec(state.default, [s]) exec(options.default, [s])
end end
exec(sid::Symbol, s::String) = exec(sid, [s]) exec(sid::Symbol, s::String) = exec(sid, [s])
@ -969,8 +968,8 @@ exec(sid::Symbol, s::String) = exec(sid, [s])
Set verbose flag to `true` or `false` (default: `false`). Set verbose flag to `true` or `false` (default: `false`).
""" """
function setverbose(b::Bool) function setverbose(b::Bool)
global state global options
state.verbose = b options.verbose = b
end end
@ -1076,7 +1075,7 @@ end
function contourlines(args...; cntrparam="level auto 10") function contourlines(args...; cntrparam="level auto 10")
tmpfile = Base.Filesystem.tempname() tmpfile = Base.Filesystem.tempname()
sid = Symbol("j", Base.Libc.getpid()) sid = Symbol("j", Base.Libc.getpid())
if !haskey(Gnuplot.state.sessions, sid) if !haskey(Gnuplot.sessions, sid)
gp = getsession(sid) gp = getsession(sid)
end end