Gnuplot.jl icon indicating copy to clipboard operation
Gnuplot.jl copied to clipboard

Switch to using display() to show the plots

Open wentasah opened this issue 2 years ago • 5 comments

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.

Things done:

  • [x] Updated documentation
  • [x] Tested (with gpviewer = false)
    • [x] Gnuplot.jl functionality
      • [x] Plotting from REPL
      • [x] Test suite
      • [x] Documentation build
      • [x] DrySessions
      • [x] Plot in a terminal application (dumb, sixelgd)
    • [x] Integrations
      • [x] VS Code
        • [x] With Plot Pane enabled
        • [x] With Plot Pane disabled
      • [x] IJulia
      • [x] Pluto
      • [x] Juno
      • [x] Weave.jl

Summary

The table below is copied from display.ipynb and updated. Only the false/REPL column needed to be changed. The values are almost the same as in true/REPL, the difference is in Calls to display() / automatic display (trailing ;) and The top level code works also within a function?.

Gnuplot.options.gpviewer = true REPL, Jupyter or Juno
Plots are shown in dedicated window(s)
Updating a plot updates window content
Output of different session goes to separate windows
How many simultaneous plots can be shown? all windows fitting the screen
Calls to display() / automatic display (trailing ;) does nothing / not applicable
The top level code works also within a function? yes, explicit display() calls are not required
Terminal options are specified in Gnuplot.options.term
Gnuplot.options.gpviewer = false REPL Jupyter Juno
Plots are shown in dedicated window(s) as inline images in the notebook as image in the plot pane
Updating a plot updates window content creates a separate image overwrites the plot pane content
Output of different session goes to separate windows the same notebook the plot pane
How many simultaneous plots can be shown? all windows fitting the screen all those fitting in the visible part of the notebook only one
Calls to display() / automatic display (trailing ;) updates the plot/applicable updates the plot / applicable updates the plot / applicable
The top level code works also within a function? no, explicit display() calls are are required no, explicit display() calls are required no, explicit display() calls are are required
Terminal options are specified in Gnuplot.options.term Gnuplot.options.mime Gnuplot.options.mime

Related to #45

wentasah avatar Jul 30 '21 10:07 wentasah

@pfitzseb Do you know why Plots.jl has this code?

    # Copied from Plots - insert GnuplotDisplay before text displays, but after graphic displays such as IJulia, VSCodeDisplay, etc.
    insert!(Base.Multimedia.displays, findlast(x -> x isa Base.TextDisplay || x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, GnuplotDisplay())
    atreplinit(i -> begin
        while GnuplotDisplay() in Base.Multimedia.displays
            popdisplay(GnuplotDisplay())
        end
        insert!(Base.Multimedia.displays, findlast(x -> x isa REPL.REPLDisplay, Base.Multimedia.displays) + 1, GnuplotDisplay())
    end)

It seems it's not necessary. In my tests, plain pushdisplay(GnuplotDisplay()) seems to work in all environments where I test it. VS Code always reorders displays to make its display last, and in IJulia, the order seems not to be important. Pluto does not hook itself into the displays.

wentasah avatar Aug 01 '21 12:08 wentasah

Do you know why Plots.jl has this code?

I was able to see the difference only in IJulia. With pushdisplay, the order of IJulia and Gnuplot displays was sometimes swapped, which resulted in different behavior when explicitly calling display().

wentasah avatar Aug 01 '21 23:08 wentasah

@gcalderone Can you try/review this? I tried hard not to break any functionality of gpviewer = true. All the changes are in gpviewer = false, which is now the default.

wentasah avatar Aug 01 '21 23:08 wentasah

I see that @gcalderone is working on Gnuplot.jl now, so I'm posting a comment about my experience with using this change for several months.

Having gpviewer = false being the default sometimes produces surprises (when one refactors the code by moving top-level code to a function). This was expected and pointed out by @gcalderone in advance.

However, when I want to use packages like Weave.jl, having gpviewer = true is not an option, because then Weave.jl cannot capture the output. The problem with Weave.jl is that when one creates a report by calling weave() from the REPL, it needs gpviewer = false but when calling other functions from the REPL, one wants gpviewer = true.

So my conclusion is that this change allows me to use Weave.jl, but it's not perfect. Ideally, we'd need a better way than a global variable to distinguish when to send the plot to Julia's Multimedia I/O and when to Gnuplot.

One idea, that comes to my mind (inspired by #51), is to check whether stdout is a terminal and decide based on that. If we're lucky packages like Weave.jl will run the code with stdout redirected to a pipe and we could use this as a sign to use Multimedia I/O. On the other hand, it will likely cause other problems in non-standard use-cases. So I'm not really sure what's the best.

wentasah avatar Jan 03 '22 17:01 wentasah

Hi @wentasah , thank you very much for providing such valuable insights on the interplay between the display() machinery and Gnuplot.jl.

However, as already discussed in #45, the best usage of Gnuplot.jl is (IMHO) outside tools like Jupyter, Juno, Weave, and the like, and therefore I would like to avoid introducing special cases or workarounds which are not thoroughly tested and coherent with the aim of the package. As a specific example, I would really like to avoid having gpviewer = false as default, or having a gnuplot viewer window being shown even if gpviewer = false.

I'm afraid the desire to make Gnuplot.jl works seamlessly in such tools/IDEs may lead to inconsistencies with its original use case, namely the use through the gnuplot external viewer.

As anticipated in #45,I would really love to see those functionalities moved in an external package, and I encourage anyone willing to do so. Besides, I have definitely limited experience with Jupyter, Juno, etc., and zero experience with Weave, making it very difficult for me to foresee all the consequences of a choice.

For the moment I will leave this PR open, in the hope that it could be used as a starting point for a discussion leading to new packages being developed which allow to use the Gnuplot.jl syntax in all the available (and amazing!) environments. If I understand correctly it all amounts to define the proper show(::IO, MIME, ::SessionID) methods and insert the proper structure in Base.Multimedia.displays. We may even use Reexport to avoid the direct dependency on Gnuplot.jl itself, and leave the user with a very simple using GnuplotOnJupyter or similar statemens.

I will be more than happy to modify the Gnuplot.jl source code to facilitate this changes, and make it usable / compatible with the new package.

gcalderone avatar Jan 04 '22 11:01 gcalderone