Skip to content

Commit dd1433a

Browse files
committed
Switch to using display() to show the plots
This brings better compatibility with the Julia package ecosystem. Now, if Gnuplot.jl is used in an environment capable of showing multimedia content (IJulia, VS Code, Pluto), their internal viewer will take precedence over using gnuplot's built-in viewer. In the REPL, gnuplot viewer is still used by default. In VS Code, for example, when the *Use Plot Pane* settings is enabled, the plots show in that pane, but when it is disabled, gnuplot viewer is automatically used. For people who prefer to always use the gnuplot viewer, they can set Gnuplot.options.gpviewer to true. This should result in the same behaviour as before this commit.
1 parent 04484ad commit dd1433a

File tree

4 files changed

+64
-31
lines changed

4 files changed

+64
-31
lines changed

docs/src/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The **Gnuplot.jl** package allows easy and fast use of [gnuplot](http://gnuplot.
2525

2626
- export to a huge number of formats such as `pdf`, `png`, `gif`, ``\LaTeX``, `svg`, etc. (actually all those supported by gnuplot);
2727

28-
- compatibility with Jupyter and Juno;
28+
- compatibility with Jupyter, VS Code, Pluto, Juno, Weave.jl, etc.
2929

3030
- save sessions into gnuplot scripts, to enable easy plot customization and reproducibility.
3131

docs/src/options.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ saveas(file) = save(term="pngcairo size 550,350 fontscale 0.8", output="assets/$
1313

1414
The display behaviour of **Gnuplot.jl** depends on the value of the `Gnuplot.options.gpviewer` flag:
1515

16-
- if `true` the plot is displayed in a gnuplot window, using one of the interactive terminals such as `wxt`, `qt` or `aqua`. This is the default setting when running a Julia REPL session; The terminal options can be customized using `Gnuplot.options.term`;
17-
18-
- if `false` the plot is displayed through the Julia [multimedia interface](https://docs.julialang.org/en/v1/base/io-network/#Multimedia-I/O-1), i.e. it is exported as either a `png`, `svg` or `html` file, and displayed in an external viewer. This is the default setting when running a Jupyter, JupyterLab or Juno session. The terminal options can be customized using the `Gnuplot.options.mime` dictionary.
19-
20-
The `Gnuplot.options.gpviewer` flag is automatically set when the package is first loaded according to the runtime environment, however the user can change its value at any time to fit specific needs. Further informations and examples for both options are available in this Jupyter [notebook](https://github.com/gcalderone/Gnuplot.jl/blob/gh-pages/v1.3.0/options/display.ipynb).
16+
- if `false` (the default) the plot is displayed through the Julia [multimedia interface](https://docs.julialang.org/en/v1/base/io-network/#Multimedia-I/O-1), i.e. depending on the current environment, it is displayed either in a gnuplot window (REPL) or exported as either a `png`, `svg` or `html` file, and displayed in an external viewer (IDE, notebook). The terminal options for the gnuplot window can be customized using `Gnuplot.options.term`; options for exporting can be customized using the `Gnuplot.options.mime` dictionary.
2117

18+
- if `true` the plot is always displayed in a gnuplot window, using one of the interactive terminals such as `wxt`, `qt` or `aqua`. The terminal options can be customized using `Gnuplot.options.term`;
2219

20+
Further information and examples for both options are available in this Jupyter [notebook](https://github.com/gcalderone/Gnuplot.jl/blob/gh-pages/v1.3.0/options/display.ipynb).
2321

2422

2523
# Package options and initialization

src/Gnuplot.jl

+39-24
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ Structure containing the package global options, accessible through `Gnuplot.opt
202202
- `default::Symbol`: default session name (default: `:default`)
203203
- `term::String`: default terminal for interactive use (default: empty string, i.e. use gnuplot settings);
204204
- `mime::Dict{DataType, String}`: dictionary of MIME types and corresponding gnuplot terminals. Used to export images with either [`save()`](@ref) or `show()` (see [Display options](@ref));
205-
- `gpviewer::Bool`: use a gnuplot terminal as main plotting device (if `true`) or an external viewer (if `false`);
205+
- `gpviewer::Bool`: force using a gnuplot terminal as main plotting device (if `true`) or use Julia multimedia I/O, which automatically selects between the gnuplot terminal and external viewers (if `false`);
206206
- `init::Vector{String}`: commands to initialize the session when it is created or reset (e.g., to set default palette);
207207
- `verbose::Bool`: verbosity flag (default: `false`)
208208
- `preferred_format::Symbol`: preferred format to send data to gnuplot. Value must be one of:
@@ -236,14 +236,15 @@ const sessions = OrderedDict{Symbol, Session}()
236236
const options = Options()
237237

238238
function __init__()
239-
# Check whether we are running in an IJulia, Juno, VSCode or Pluto session.
240-
# (copied from Gaston.jl).
241-
options.gpviewer = !(
242-
((isdefined(Main, :IJulia) && Main.IJulia.inited) ||
243-
(isdefined(Main, :Juno) && Main.Juno.isactive()) ||
244-
(isdefined(Main, :VSCodeServer)) ||
245-
(isdefined(Main, :PlutoRunner)) )
246-
)
239+
# Copied from Plots - insert GnuplotDisplay before text displays,
240+
# but after graphic displays such as IJulia.
241+
insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, GnuplotDisplay())
242+
atreplinit(i -> begin
243+
while GnuplotDisplay() in Base.Multimedia.displays
244+
popdisplay(GnuplotDisplay())
245+
end
246+
insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, GnuplotDisplay())
247+
end)
247248
if isdefined(Main, :VSCodeServer)
248249
# VS Code shows "dynamic" plots with fixed and small size :-(
249250
options.mime[MIME"image/svg+xml"] = replace(options.mime[MIME"image/svg+xml"], "dynamic" => "")
@@ -585,6 +586,10 @@ function gp_write_table(args...; kw...)
585586
reset(gp)
586587
gpexec(sid, "set term unknown")
587588
driver(sid, "set table '$tmpfile'", args...; kw...)
589+
if !Gnuplot.options.gpviewer
590+
# driver() did not send plots to the gnuplot process - do it now
591+
execall(gp, term="unknown")
592+
end
588593
gpexec(sid, "unset table")
589594
quit(sid)
590595
out = readlines(tmpfile)
@@ -766,21 +771,20 @@ function reset(gp::Session)
766771
gpexec(gp, "set output")
767772
gpexec(gp, "reset session")
768773

769-
if options.gpviewer
770-
# Use gnuplot viewer
771-
(options.term != "") && gpexec(gp, "set term " * options.term)
772-
773-
# Set window title (if not already set)
774-
term = writeread(gp, "print GPVAL_TERM")[1]
775-
if term in ("aqua", "x11", "qt", "wxt")
776-
opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1]
777-
if findfirst("title", opts) == nothing
778-
writeread(gp, "set term $term $opts title 'Gnuplot.jl: $(gp.sid)'")
779-
end
774+
# Configure the gnuplot viewer. If options.gpviewer is false, it would be more
775+
# appropriate to do this in display(), but doing so leads to hang of the gnuplot
776+
# process in some situations (after manual close of the Qt window, gnuplot
777+
# receives the CAPTURE_END marker, but does not print it back). Therefore, we do
778+
# in here unconditionally.
779+
(options.term != "") && gpexec(gp, "set term " * options.term)
780+
781+
# Set window title (if not already set)
782+
term = writeread(gp, "print GPVAL_TERM")[1]
783+
if term in ("aqua", "x11", "qt", "wxt")
784+
opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1]
785+
if findfirst("title", opts) == nothing
786+
writeread(gp, "set term $term $opts title 'Gnuplot.jl: $(gp.sid)'")
780787
end
781-
else
782-
# Use external viewer
783-
gpexec(gp, "set term unknown")
784788
end
785789

786790
# Note: the reason to keep Options.term and .init separate are:
@@ -1547,10 +1551,21 @@ end
15471551

15481552

15491553
# ╭───────────────────────────────────────────────────────────────────╮
1550-
# Interfacing Julia's show
1554+
# │ Interfacing Julia's Multimedia I/O
15511555
# ╰───────────────────────────────────────────────────────────────────╯
15521556
# --------------------------------------------------------------------
15531557

1558+
struct GnuplotDisplay <: AbstractDisplay end
1559+
1560+
function Base.display(d::GnuplotDisplay, obj::SessionID)
1561+
if !options.gpviewer && obj.dump
1562+
gp = getsession(obj.sid)
1563+
execall(gp)
1564+
else
1565+
throw(MethodError(display, (d, obj)))
1566+
end
1567+
end
1568+
15541569
function show(obj::SessionID)
15551570
gp = getsession(obj.sid)
15561571
@info "Gnuplot session" sid=gp.sid datasets=length(gp.datas) plots=length(gp.plots)

test/runtests.jl

+21-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ try
55
catch
66
Gnuplot.options.dry = true
77
end
8-
Gnuplot.options.gpviewer = true
8+
9+
# Test the default, i.e. false
10+
#Gnuplot.options.gpviewer = true
911

1012
x = [1, 2, 3]
1113
y = [4, 5, 6]
@@ -110,6 +112,24 @@ dummy = terminals()
110112
# Force unknown on Travis CI
111113
Gnuplot.options.term = "unknown"
112114

115+
if Gnuplot.options.gpviewer == false
116+
# Wrap @gp and @gsp macrocalls in display() to ensure that the whole
117+
# plot is sent to the gnuplot process during testing. In interactive
118+
# sessions display() is called by the REPL.
119+
macro gp(args...)
120+
gpcall = :(Gnuplot.@gp)
121+
push!(gpcall.args, args...)
122+
out = :(display($gpcall))
123+
return esc(out)
124+
end
125+
macro gsp(args...)
126+
gpcall = :(Gnuplot.@gsp)
127+
push!(gpcall.args, args...)
128+
out = :(display($gpcall))
129+
return esc(out)
130+
end
131+
end
132+
113133
@gp 1:9
114134
@info "using terminal: " terminal()
115135
#test_terminal("unknown")

0 commit comments

Comments
 (0)