Repository navigation
Plots don't show automatically when PyPlot compiled with PackageCompiler #476
Description
Activity
Looks like the error goes away after upgrading matplotlib 3.1.1 -> 3.2.1 (as per #459 (comment)).
The plot still is not showing automatically though. Any way to reset whatever has gone wrong to make them show?
Hey @marius311, this happens because
PyPlot.display_figs(), the function called at the end of an IJulia cell, thinks there is no available display. You can see this in the notebook, i.e.using PyPlot PyPlot.isjulia_display
1-element Array{Bool,1}: 0PyPlot sets this global when it runs its init, which I suppose has now been compiled into your sysimage.
Line 178 in c451315
global isjulia_display = Bool[isdisplayok()] This kind of thing should happen to any PackageCompiler-ed package which relies on logic during package loading. You can manually
showthe figure for now, i.e.fig = plt.figure() plt.plot() figI'll see if I can make a PR.
Probably all it takes is just to check if there are displays whenever a cell is requested. For example, this works in a sysimage,
using PyCall using PyPlot function display_figs() # called after IJulia cell executes if PyPlot.isdisplayok() # CHANGED FROM QUERYING GLOBAL for manager in PyPlot.Gcf."get_all_fig_managers"() f = manager."canvas"."figure" if f.number ∉ PyPlot.withfig_fignums fig = Figure(f) isempty(fig) || display(fig) pycall(plt."close", PyAny, f) end end end end plt.plot() display_figs()
One also needs to push the pre-executes (which don't seem to make it in the sysimage)
Main.IJulia.push_preexecute_hook(PyPlot.force_new_fig) Main.IJulia.push_postexecute_hook(PyPlot.display_figs) Main.IJulia.push_posterror_hook(PyPlot.close_figs)
Thanks, yea I think you're on the right track. The thing is that PackageCompiler still runs
PyPlot.__init__()when you open a new terminal rather than when its being compiled, but I think whenever its running it must be too early becausePyPlot.isdisplayok()must be returning false, as evidenced by as show thatPyPlot.isjulia_displayis false. Its only later thatPyPlot.isdisplayok()starts returning true. So you end up with this when you use the system image:I'm afraid that's going to mess up other aspects of the initialization, too.
In particular, if a Julia display is detected when PyPlot is initialized, then it starts up Matplotlib in non-interactive mode, so that figures are only displayed when the cell finishes executing and the post-execute hook is called. Whereas if
displayok() == falseat the__init__call, then it will start up the GUI event loop and display plots in the GUI by default (assuming you have a working Matplotlib GUI backend).Why is
__init__not being called when the user runsimport PyPlot?Why is init not being called when the user runsimport PyPlot?
As you described in the PR, the best way to fix this is probably to do things in a lazy fashion within PyPlot. Here's an example of the order of init calls changing when using sysimages. Consider the following test where I just stick a print statement in the PyPlot init.
normal order
julia -e 'println("Julia started"); flush(stdout); sleep(1); import PyPlot;'Julia started INIT IS CALLEDwith sysimage
julia --sysimage=mysys.dylib -e 'println("Julia started"); flush(stdout); sleep(1); import PyPlot;'INIT IS CALLED Julia startedWith the sysimage, PyPlot's init is called before the first line of Julia code is started. Presumably PyPlot wakes up and runs init, only to find a barren land without any IJulia loaded.
I'd be interested in reading how Julia starts up, maybe it's in the docs.
My current solution is calling a custom function
autodisplay_figs()(see below) at the start of the notebook.
You can call it manually; I call it in the__init__of a custom package that getsusing'd in the notebook, i.e. after IJulia has been initialized. I suppose you could call it instartup_ijulia.jlas well.This function simply sets
PyPlot.isjulia_display[]manually totrue, and adds the three IJulia hooks, as in xzackli's proposal above.This doesn't fix the fact that a GUI event loop is, presumably, unnecesarily spun up.
But that is not noticeable (I suspect because the startup cost of that is in the sysimg?).using PyPlotis instant, and plots display automatically under cells again.`autodisplay_figs()`:
""" When using a sysimg with PyPlot in it, PyPlot's `__init__` gets called before IJulia is initialized. As a result, figures do not get automatically displayed in the notebook. (https://github.com/JuliaPy/PyPlot.jl/issues/476). Calling `autodisplay_figs()` after IJulia is initialized fixes that. """ function autodisplay_figs() if (isdefined(Main, :PyPlot) && isdefined(Main, :IJulia) && Main.IJulia.inited && (Main.PyPlot.isjulia_display[] == false) ) Main.PyPlot.isjulia_display[] = true Main.IJulia.push_preexecute_hook(Main.PyPlot.force_new_fig) Main.IJulia.push_postexecute_hook(Main.PyPlot.display_figs) Main.IJulia.push_posterror_hook(Main.PyPlot.close_figs) end end
See also my #480 (comment) on how I think this should be fixed.
The following command works for me as a workaround when using the REPL:
PyPlot.show(block=false)

Not sure if this is supposed to work (in any case it seems like its really close to working). I compiled PyPlot v2.8.2 with PackageCompiler v1.1.0 on julia 1.4,
then load it with
Any ideas if this can work and if so how? Thanks.