PyPlot not producing eps figure when used inside a module


#1

Hello,

Context: I am trying to save a figure to and .eps extension using PyPlot.
Since I am facing some troubles, I prepared two procedures to save the figure that explain the troubles. Procedure 1 works OK. Procedure 2 doesn’t work. I expect someone to help me out in understanding why Procedure 2 doesn’t work and how to fix it. I give the codes and the outputs I get from these codes below. I am running this in Atom + Juno + Win10.

Procedure 1: it consists in just one .jl file, which I called it “plot8.jl” and contains the following code

plot8.jl

using Plots
pyplot()

function plotme()
  x = 1:10; y = rand(10,2) 
  plot(x, y, title = "Two Lines", label = ["Line 1" "Line 2"], lw = 3)
  xlabel!("My x label")
  ylabel!("My y label")
  savefig("plot8.eps")
end

plotme()

After running this code, the file “plot8.eps” is created. So, right.

Procedure 2: it consists in a file called “plot9.jl” and a file called “Plotme.jl”. Both files are in the same directory. “Plotme.jl” contains a module called “Plotme”. The contents of the files are as follows:

plot9.jl

push!(LOAD_PATH, pwd())  # add working directory to the path to be able to find Plotme module
using Plotme: plotme
plotme()

Plotme.jl

module Plotme

using Plots
pyplot()

function plotme()
  x = 1:10; y = rand(10,2) 
  plot(x, y, title = "Two Lines", label = ["Line 1" "Line 2"], lw = 3)
  xlabel!("My x label")
  ylabel!("My y label")
  savefig("plot9.eps")
end

end

For the first time I run “plot9.jl”, I get right at the beginning the following warning:

[ Info: Recompiling stale cache file C:\Users\aaort\.julia\compiled\v1.0\Plotme.ji for Plotme [top-level]
WARNING: eval from module Main to Plotme:
Expr(:block, #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):529 =#, Expr(:import, Expr(:., :PyPlot)), #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):531 =#, Expr(:export, :PyPlot), #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):534 =#, Expr(:call, Expr(:., :PyPlot, :(:ioff))))
  ** incremental compilation may be broken for this module **

And after that, I get the following error, which I presume, is in regards that pyplot() is not doing the work and instead gr() is used as the backend (I know that GR can’t plot to .eps):

ERROR: LoadError: MethodError: no method matching _show(::IOStream, ::MIME{Symbol("image/eps")}, ::Plots.Plot{Plots.GRBackend})
Closest candidates are:
  _show(::IO, ::MIME{Symbol("text/html")}, ::Plots.Plot) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:165
  _show(::IO, ::MIME{Symbol("text/plain")}, ::Plots.Plot) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:206
  _show(::IO, ::MIME{Symbol("application/postscript")}, ::Plots.Plot{Plots.GRBackend}) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends\gr.jl:1357
  ...
Stacktrace:
 [1] show(::IOStream, ::MIME{Symbol("image/eps")}, ::Plots.Plot{Plots.GRBackend}) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:200
 [2] eps(::Plots.Plot{Plots.GRBackend}, ::String) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:42
 [3] savefig(::Plots.Plot{Plots.GRBackend}, ::String) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:123
 [4] savefig(::String) at C:\Users\aaort\.julia\packages\Plots\y6yik\src\output.jl:125
 [5] plotme() at C:\Dropbox\Universidad\codes\Julia\plot_tests\Plotme.jl:11
 [6] top-level scope at none:0
in expression starting at C:\Dropbox\Universidad\codes\Julia\plot_tests\plot9.jl:3

How can this issue be fixed if I must go with Procedure 2 rather than Procedure 1?

Thank you!


#2

Why not use PyPlot directly instead of Plots?


#3

Because replacing “using Plots; pyplot()” by “using PyPlot” in Procedure 2 above, this error shows up:

[ Info: Recompiling stale cache file C:\Users\aaort\.julia\compiled\v1.0\Plotme.ji for Plotme [top-level]
ERROR: LoadError: PyCall.PyError("\$(Expr(:escape, :(ccall(#= C:\\Users\\aaort\\.julia\\packages\\PyCall\\0jMpb\\src\\pyfncall.jl:44 =# @pysym(:PyObject_Call), PyPtr, (PyPtr, PyPtr, PyPtr), o, pyargsptr, kw))))", PyCall.PyObject(Ptr{PyCall.PyObject_struct} @0x000000005a167100), PyCall.PyObject(Ptr{PyCall.PyObject_struct} @0x000000002d24a888), PyCall.PyObject(Ptr{PyCall.PyObject_struct} @0x000000002d71a708))
Stacktrace:
 [1] pyerr_check at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\exception.jl:60 [inlined]
 [2] pyerr_check at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\exception.jl:64 [inlined]
 [3] macro expansion at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\exception.jl:84 [inlined]
 [4] __pycall!(::PyCall.PyObject, ::Ptr{PyCall.PyObject_struct}, ::PyCall.PyObject, ::PyCall.PyObject) at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\pyfncall.jl:44
 [5] _pycall!(::PyCall.PyObject, ::PyCall.PyObject, ::Tuple{UnitRange{Int64},Array{Float64,2}}, ::Int64, ::PyCall.PyObject) at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\pyfncall.jl:29
 [6] _pycall!(::PyCall.PyObject, ::PyCall.PyObject, ::Tuple{UnitRange{Int64},Array{Float64,2}}, ::Base.Iterators.Pairs{Symbol,Any,Tuple{Symbol,Symbol,Symbol},NamedTuple{(:title, :label, :lw),Tuple{String,Array{String,2},Int64}}}) at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\pyfncall.jl:11
 [7] #pycall#88(::Base.Iterators.Pairs{Symbol,Any,Tuple{Symbol,Symbol,Symbol},NamedTuple{(:title, :label, :lw),Tuple{String,Array{String,2},Int64}}}, ::Function, ::PyCall.PyObject, ::Type{PyCall.PyAny}, ::UnitRange{Int64}, ::Vararg{Any,N} where N) at C:\Users\aaort\.julia\packages\PyCall\0jMpb\src\pyfncall.jl:86
 [8] (::getfield(PyCall, Symbol("#kw##pycall")))(::NamedTuple{(:title, :label, :lw),Tuple{String,Array{String,2},Int64}}, ::typeof(PyCall.pycall), ::PyCall.PyObject, ::Type{PyCall.PyAny}, ::UnitRange{Int64}, ::Vararg{Any,N} where N) at .\none:0
 [9] #plot#85(::Base.Iterators.Pairs{Symbol,Any,Tuple{Symbol,Symbol,Symbol},NamedTuple{(:title, :label, :lw),Tuple{String,Array{String,2},Int64}}}, ::Function, ::UnitRange{Int64}, ::Vararg{Any,N} where N) at C:\Users\aaort\.julia\packages\PyPlot\fZuOQ\src\PyPlot.jl:179
 [10] #plot at .\none:0 [inlined]
 [11] plotme() at C:\Dropbox\Universidad\codes\Julia\plot_tests\Plotme.jl:8
 [12] top-level scope at none:0
in expression starting at C:\Dropbox\Universidad\codes\Julia\plot_tests\plot9.jl:3

In fact, trying to figure out what this error meant, I arrived at the Plots help:
https://docs.juliaplots.org/latest/

from where I saw that, at least in the examples, the help favors the use of “using Plots; name_of_the_backend()”.

The same error also appears when using PyPlot directly in Procedure 1.


#4

Calling a function for side-effect at module level is not right in most cases. I think you can make it work by removing the top level pyplot() from the Plotme module and invoke plotme() as following.

using Plots
pyplot()
using Plotme: plotme
plotme()

If you really want to enable pyplot backend automatically when importing Plotme module, use __init__ function:

module Plotme

using Plots
# pyplot()

function plotme()
    ...
end

function __init__()
    pyplot()
end

end

See: https://docs.julialang.org/en/v1/manual/modules/index.html#Module-initialization-and-precompilation-1


#5

Thanks tkf for the advice. The first suggestion won’t work since the function plot() inside plotme() won’t see the Plots module. It says “plot not defined”.

The second one works as it effectively creates the .eps file. However, the warning is still there:

[ Info: Recompiling stale cache file C:\Users\aaort\.julia\compiled\v1.0\Plotme.ji for Plotme [top-level]
WARNING: eval from module Main to Plotme:
Expr(:block, #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):529 =#, Expr(:import, Expr(:., :PyPlot)), #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):531 =#, Expr(:export, :PyPlot), #= Symbol("C:\Users\aaort\.julia\packages\Plots\y6yik\src\backends.jl"):534 =#, Expr(:call, Expr(:., :PyPlot, :(:ioff))))
  ** incremental compilation may be broken for this module **

It is just a warning so I think I will be able to cope with it.


#6

I wasn’t suggesting to remove using Plots. More precisely, I was suggesting this:

module Plotme

using Plots
# pyplot()  # don't call it at top-level; call it in, e.g., plot9.jl

function plotme()
    ...
end

end

Oops, I forgot to remove the top-level pyplot() in my example code in the previous comment (now removed).


#7

Both of your suggestions work great now. Thank you, tkf !