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

Feature request: add keyword to make julia wait until plot window closes

Open rvanvenetie opened this issue 8 years ago • 18 comments

How do I prevent the script from terminating when the plot window is still open? I cannot find it anywhere in the documentation.

using Plots pyplot() plot(1:50) I see the window opening, but I cannot prevent the script from shutting down directly after the plot command.. I'm simply running 'julia test.jl'.

By directly using pyplot I can get this to work with the show command, but this appears to be missing in Plots? using PyPlot plot(1:50) show()

rvanvenetie avatar Oct 27 '17 08:10 rvanvenetie

Any particular reason for running julia like that from bash, rather than just running julia and working in an interactive session? You can include("test.jl") from the interactive prompt to run a script. I think for most script-based uses of Plots I'd assume the script to save the plot as an image file (savefig()).

mkborregaard avatar Oct 27 '17 10:10 mkborregaard

I wasn't aware that this is the normal workflow for running scripts.

I guess that include() clutters the workspace right? Suppose all of my students hand in some exercise which is supposed to generate a plot(s). It seems natural to execute each of their scripts in a clean workspace, check the graph and proceed.

rvanvenetie avatar Oct 27 '17 11:10 rvanvenetie

It very much is the normal workflow to just keep a Julia REPL open and work interactively. Most of the time you wouldn't want to write a script out of your session and run it, but instead work in Jupyter notebooks (ideal for students IMO since it keeps code, comments/reflections and plots together) or to have your script open in something like Juno and evaluate from there. You can also play with the plot, changing settings etc.

If you don't want to clutter the workspace, the technique is to use modules, they create independent global scopes. If you do want to work by include()'ing script files you can e.g. make each script a Module, wrap the script in a function in that module (good approach anyway to avoid globals) and export that function from the module.

mkborregaard avatar Oct 27 '17 12:10 mkborregaard

Just add:

show()

At the end of the script. Example:

using PyPlot

plot(rand(10))
show()

samuel-gf avatar Nov 26 '17 14:11 samuel-gf

@profesors This is the Plots repo, not the PyPlot repo. It doesn't make any sense to comment a solution using a different plotting package - AND the code snippet there was listed in the OP.

mkborregaard avatar Nov 19 '18 07:11 mkborregaard

Closing as this is not a Plots issue, but a workflow problem.

mkborregaard avatar Nov 19 '18 07:11 mkborregaard

Sad.

jampekka avatar Dec 05 '18 17:12 jampekka

Thanks for your contributions here @jampekka

mkborregaard avatar Dec 05 '18 17:12 mkborregaard

I don't think it is very sensible for this to be closed as "a workflow issue".

I guess the thing about workflows is, different people have different ones. For example, when I'm developing code I like it to run repeatably, exactly the same every time. Having to use a persistent REPL or notebook session brings the potential for subtle errors which would cause my code to work now, but not to work the next time I try it (potentially years later), because the state of my runtime environment has changed.

So to my mind the issue is "Plots fails to support a very common and extremely sensible workflow", and not "the user is in error for having the wrong workflow."

nathanielvirgo avatar May 22 '19 02:05 nathanielvirgo

What exactly are you requesting though? You cannot have a plotting window open after your julia session closes - at least I don't see how that should be implemented from Plots. If you've got specific workable suggestions other than just complaints I'm happy to reopen the issue.

mkborregaard avatar May 22 '19 09:05 mkborregaard

The minimal feature that would enable this workflow is simply an option for the display method to block until the window is closed.

Potentially, for some users, it might be useful to have an event loop system, allowing user interaction to continue while dynamically updating the plot.

Improvements to the import time would go a long way towards making this kind of workflow feasible in practice, since you have to import the package every time you run the script.

nathanielvirgo avatar May 22 '19 09:05 nathanielvirgo

OK, I have reopened the issue with a more suitable name. Anyone who wishes to implement this and add a PR to Plots are free to do so. I feel I should note here, though:

when I'm developing code I like it to run repeatably, exactly the same every time. Having to use a persistent REPL or notebook session brings the potential for subtle errors

The ideomatic way of running code repeatedly, exactly the same every time is to wrap the code in a function, then run that. More generally, you control environment hygiene in Julia with modules. Reloading a module should clear the module namespace. But in this case it's easier to just use a function.

brings the potential for subtle errors which would cause my code to work now, but not to work the next time I try it (potentially years later), because the state of my runtime environment has changed.

This should not happen. Your code should not depend on the state of all kinds of bindings floating around in global scope. If it does, that is considered poor coding style, and will also make your julia code very slow. Just wrapping your code in a function and paying attention to scope will solve this.

(potentially years later), because the state of my runtime environment has changed.

What is it that you imagine have changed years later? Your global scope should not affect the running of your code, but over a time scale of years your julia version and dependencies may have changed. You use environments to deal with this - essentially you don't have to update the environment associated with a certain project, no matter how many years pass. If you do want to use a newer version of Julia and environments, you may suffer breakage - script or not.

Plots fails to support a very common and extremely sensible workflow

No.

Improvements to the import time would go a long way

I believe that is priority # 3 for the compiler core team: https://discourse.julialang.org/t/compiler-work-priorities/17623 Note that this is a general Julia issue - Plots is most likely loading as fast as possible under the circumstances, or at least a lot of work has gone into assuring this.

mkborregaard avatar May 22 '19 13:05 mkborregaard

for some users, it might be useful to have an event loop system, allowing user interaction to continue while dynamically updating the plot

I may be mistaken here, but isn't this an exact description of the current system? Or do you mean click-and-drag?

mkborregaard avatar May 22 '19 19:05 mkborregaard

Thanks for reopening, that's appreciated.

Regarding global variables, I am new to Julia and might be overcautious because of bad experience with Python, where it's quite easy to accidentally refer to global state without realising it, especially when reloading modules.

What is it that you imagine have changed years later?

Quite simply, I will not have the same REPL session open any more. Since years may have passed, I'm unlikely to remember anything special I did before running the script.

I may be mistaken here, but isn't this an exact description of the current system?

Maybe. In that case, how do I currently register a callback, e.g. for closing the window, and how does one actually enter the event loop?

nathanielvirgo avatar May 23 '19 02:05 nathanielvirgo

Since years may have passed, I'm unlikely to remember anything special I did before running the script.

What would that be, and how would that be the responsibility of Plots? It does really seem like a workflow issue more than anything else.

pkofod avatar May 26 '19 19:05 pkofod

I'm not sure how useful it is to have this conversation, since it's just an argument about my workflow versus your workflow, and it's not actually giving you any information that you would need in order to implement this. But here is an example to show what I mean.

First, put the following in Test.jl:

module Test
	using Plots
	unicodeplots()
	function test()
		plot([1,5,2])
	end
end

Then, in a REPL, run

include("Test.jl");Test.test()

Everything is in a module, so it should run the same every time, right?

Now comment out the line unicodeplots() and run it again. The outcome is the same, a unicode plot. But now if you re-start the REPL and run the exact same code again, it will instead produce a graphical plot using the gr backend.

This happens because although includeing the Test module clears the namespace of the Test module, it does not clear any other namespaces, and global state can be stored in those. So even if my code is in a module it is quite possible that, unknown to me, it is actually relying on global state left over from running previous versions of the same code. I have been bitten by this kind of issue in Python, and I want to work in a way that it can be guaranteed not to occur.

The obvious way to do that is to run all my code in a script and not in a REPL, so a new process is started every time and there is no runtime state. The issue is that Plots doesn't readily support that workflow.

So yes, it is a "workflow issue", but it is one that seems to need changes to Plots in order to be convenient. (And also more major changes to julia itself, to avoid the long load times.) This is, of course, the only sense in which any of this is Plots' responsibility.

If that doesn't satisfy you, let's just agree to differ. It seems off-topic and counterproductive to continue arguing about whether one workflow is better than another.

nathanielvirgo avatar May 27 '19 04:05 nathanielvirgo

If that doesn't satisfy you, let's just agree to differ. It seems off-topic and counterproductive to continue arguing about whether one workflow is better than another.

I think you're assuming my position far too soon. You wrote

I'm unlikely to remember anything special I did before running the script.

I took that to be something "outside of julia", since you said "before running my script".

I don't like the "global"-ness of pgfplots(), gr() at all. Unfortunately, I don't have time to contribute to Plots, but I'd much rather it was like a show(io, ...) kind of thing where you'd pass in the plotting backend as the first argument... Every time, or with a sensible default (but I'd prefer every time because it makes it easier to maintain, and is very explicit).

using Plots
# using PlotsBackends/GRBackend if we don't want to depend on the backend
pb = GRBackend(theme=..., size=..., ...)
x = ...
y = ...
p = plot(pb, x, y)

You could then `plot!(p, x2, y2) as the backend would be part of the plot object.

but.. yeah.. I don't have the time, and Plots doesn't exactly work like that either, so it would be quite some work I think.

pkofod avatar May 27 '19 09:05 pkofod

To keep the script running (and thus the display window open), just put readline() at the end of your script. Or, if you prefer, put it after every display() call to make it block until you press Enter. This seems to me like an easy enough solution to OP's problem, so I'm not sure if it warrants special support from Plots.jl.

sandorzm avatar Aug 19 '22 14:08 sandorzm

To keep the script running (and thus the display window open), just put readline() at the end of your script. Or, if you prefer, put it after every display() call to make it block until you press Enter. This seems to me like an easy enough solution to OP's problem, so I'm not sure if it warrants special support from Plots.jl.

But the question is why do I need this extra step of pressing enter after I closed all the plot windows? Why closing all the plot windows isn’t enough to tell the script that I finished what I wanted? I am new to Julia, and I have done all my plotting in Python with matplotlib until now. I think it’s very intuitive that I just put plt.show() at the end of the script, and then the plots show while the script is running and when I close all the windows, plt.show() returns and the script terminates. No further code or user interaction is required. Personally, I like to work in scripts. Notebooks are nice, but I don’t like the idea of running cells out of order, so I only use notebooks for small and quick tasks. The REPL, in my opinion, should be used for interactive testing and experimentation. If I have an important code I need to run, I would save it in a script and run the script. I am not going to include the script in an interactive session. I think scripts are reliable and consistent, and any serious work I do will be done in a script and ran by a script. I also think that writing and running scripts is a common enough practice (especially for people coming from Python) that having a smooth workflow in a script is very important.

ShaiAvr avatar Apr 06 '24 23:04 ShaiAvr

@ShaiAvr you described the issue perfectly

mbUSC avatar Apr 17 '24 05:04 mbUSC