Skip to content

Commit b617464

Browse files
committed
modernize API somewhat
1 parent 189987c commit b617464

File tree

5 files changed

+70
-58
lines changed

5 files changed

+70
-58
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,19 @@ see the [matplotlib.pyplot documentation for more
5252
information](http://matplotlib.org/api/pyplot_api.html). The Matplotlib
5353
version number is returned by `PythonPlot.version`.
5454

55+
### Differences from PyPlot
56+
57+
Compared to the PyPlot package, there are a few differences.
58+
59+
* To avoid type piracy, the functions `show`, `close`, `step`, and `fill` are renamed to `pltshow`, `pltclose`, `pltstep`, and `pltfill`, respectively. (You can also access them as `PythonPlot.show` etcetera.)
60+
* The PythonCall performs many fewer automatic conversions from Python types to Julia types (in comparison to PyCall). If you need to convert Matplotlib return values to native Julia objects, you'll need to do `using PythonCall` and call its `pyconvert(T, o)` or other conversion functions.
61+
5562
### Exported functions
5663

5764
Only the currently documented `matplotlib.pyplot` API is exported. To use
5865
other functions in the module, you can also call `matplotlib.pyplot.foo(...)`
59-
as `plt.foo(...)`. For example, `plt.plot(x, y)` also works. (And
60-
the raw `Py` object for the `matplotlib` modules is also accessible
66+
as `pyplot.foo(...)`. For example, `pyplot.plot(x, y)` also works. (And
67+
the raw `Py` object for the `matplotlib` module itself is also accessible
6168
as `PythonPlot.matplotlib`.)
6269

6370
Matplotlib is somewhat inconsistent about capitalization: it has
@@ -71,8 +78,8 @@ must be used to access `matplotlib.pyplot.xcorr`
7178
etcetera.
7279

7380
If you wish to access *all* of the PyPlot functions exclusively
74-
through `plt.somefunction(...)`, as is conventional in Python, you can
75-
do `import PythonPlot as plt` instead of `using PythonPlot`.
81+
through `pyplot.somefunction(...)`, as is conventional in Python, you can
82+
do `import PythonPlot as pyplot` instead of `using PythonPlot`.
7683

7784
### Figure objects
7885

@@ -96,7 +103,7 @@ function (`plot` etc.) is evaluated.
96103
However, if you use PythonPlot from a Julia script that is run non-interactively
97104
(e.g. `julia myscript.jl`), then Matplotlib is executed in
98105
[non-interactive mode](http://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode):
99-
a plot window is not opened until you run `show()` (equivalent to `plt.show()`
106+
a plot window is not opened until you run `show()` (equivalent to `pyplot.show()`
100107
in the Python examples).
101108

102109
## Interactive versus Julia graphics

src/PythonPlot.jl

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
"""
33
PythonPlot allows Julia to interface with the Matplotlib library in Python, specifically the matplotlib.pyplot module, so you can create beautiful plots in Julia with your favorite Python package.
44
5-
Only the currently documented matplotlib.pyplot API is exported. To use other functions in the module, you can also call matplotlib.pyplot.foo(...) as plt.foo(...).
6-
For example, plt.plot(x, y) also works. (And the raw Py object for the matplotlib modules is also accessible as PythonPlot.matplotlib.)
7-
85
In general, all the arguments are the same as in Python.
96
107
Here's a brief demo of a simple plot in Julia:
@@ -19,8 +16,7 @@ For more information on API, see the matplotlib.pyplot documentation and the Pyt
1916
module PythonPlot
2017

2118
using PythonCall
22-
import Base: convert, ==, isequal, hash, getindex, setindex!, haskey, keys, show
23-
export Figure, plt, matplotlib, pygui, withfig
19+
export Figure, matplotlib, pyplot, pygui, withfig, pltshow, pltstep, pltclose
2420

2521
###########################################################################
2622
# Define a documentation object
@@ -46,11 +42,11 @@ function Base.show(io::IO, ::MIME"text/plain", h::LazyHelp)
4642
print(io, "no Python docstring found for ", o)
4743
end
4844
end
49-
Base.show(io::IO, h::LazyHelp) = show(io, "text/plain", h)
45+
Base.show(io::IO, h::LazyHelp) = Base.show(io, "text/plain", h)
5046
function Base.Docs.catdoc(hs::LazyHelp...)
5147
Base.Docs.Text() do io
5248
for h in hs
53-
show(io, MIME"text/plain"(), h)
49+
Base.show(io, MIME"text/plain"(), h)
5450
end
5551
end
5652
end
@@ -68,10 +64,10 @@ mutable struct Figure
6864
end
6965
PythonCall.Py(f::Figure) = getfield(f, :o)
7066
PythonCall.pyconvert(::Type{Figure}, o::Py) = Figure(o)
71-
==(f::Figure, g::Figure) = Py(f) == Py(g)
72-
==(f::Figure, g::Py) = Py(f) == g
73-
==(f::Py, g::Figure) = f == Py(g)
74-
hash(f::Figure) = hash(Py(f))
67+
Base.:(==)(f::Figure, g::Figure) = pyconvert(Bool, Py(f) == Py(g))
68+
Base.:(==)(f::Figure, g::Py) = pyconvert(Bool, Py(f) == g)
69+
Base.:(==)(f::Py, g::Figure) = pyconvert(Bool, f == Py(g))
70+
Base.hash(f::Figure, h::UInt) = hash(Py(f), h)
7571
PythonCall.pycall(f::Figure, args...; kws...) = pycall(Py(f), args...; kws...)
7672
(f::Figure)(args...; kws...) = pycall(Py(f), PyAny, args...; kws...)
7773
Base.Docs.doc(f::Figure) = Base.Docs.Text(pyconvert(String, Py(f).__doc__))
@@ -88,7 +84,7 @@ for (mime,fmt) in aggformats
8884
@eval _showable(::MIME{Symbol($mime)}, f::Figure) = !isempty(f) && haskey(PyDict{Any,Any}(f.canvas.get_supported_filetypes()), $fmt)
8985
@eval function Base.show(io::IO, m::MIME{Symbol($mime)}, f::Figure)
9086
if !_showable(m, f)
91-
throw(MethodError(show, (io, m, f)))
87+
throw(MethodError(Base.show, (io, m, f)))
9288
end
9389
f.canvas.print_figure(io, format=$fmt, bbox_inches="tight")
9490
end
@@ -127,7 +123,7 @@ function display_figs() # called after IJulia cell executes
127123
if pyconvert(Int, f.number) withfig_fignums
128124
fig = Figure(f)
129125
isempty(fig) || display(fig)
130-
plt.close(f)
126+
pyplot.close(f)
131127
end
132128
end
133129
end
@@ -138,7 +134,7 @@ function close_figs() # called after error in IJulia cell
138134
for manager in Gcf.get_all_fig_managers()
139135
f = manager.canvas.figure
140136
if pyconvert(Int, f.number) withfig_fignums
141-
plt.close(f)
137+
pyplot.close(f)
142138
end
143139
end
144140
end
@@ -168,47 +164,56 @@ end
168164
###########################################################################
169165

170166
# export documented pyplot API (http://matplotlib.org/api/pyplot_api.html)
171-
export acorr,annotate,arrow,autoscale,autumn,axhline,axhspan,axis,axline,axvline,axvspan,bar,barbs,barh,bone,box,boxplot,broken_barh,cla,clabel,clf,clim,cohere,colorbar,colors,contour,contourf,cool,copper,csd,delaxes,disconnect,draw,errorbar,eventplot,figaspect,figimage,figlegend,figtext,figure,fill_between,fill_betweenx,findobj,flag,gca,gcf,gci,get_current_fig_manager,get_figlabels,get_fignums,get_plot_commands,ginput,gray,grid,hexbin,hist2D,hlines,hold,hot,hsv,imread,imsave,imshow,ioff,ion,ishold,jet,legend,locator_params,loglog,margins,matshow,minorticks_off,minorticks_on,over,pause,pcolor,pcolormesh,pie,pink,plot,plot_date,plotfile,polar,prism,psd,quiver,quiverkey,rc,rc_context,rcdefaults,rgrids,savefig,sca,scatter,sci,semilogx,semilogy,set_cmap,setp,show,specgram,spectral,spring,spy,stackplot,stem,step,streamplot,subplot,subplot2grid,subplot_tool,subplots,subplots_adjust,summer,suptitle,table,text,thetagrids,tick_params,ticklabel_format,tight_layout,title,tricontour,tricontourf,tripcolor,triplot,twinx,twiny,vlines,waitforbuttonpress,winter,xkcd,xlabel,xlim,xscale,xticks,ylabel,ylim,yscale,yticks,hist
167+
export acorr,annotate,arrow,autoscale,autumn,axhline,axhspan,axis,axline,axvline,axvspan,bar,barbs,barh,bone,box,boxplot,broken_barh,cla,clabel,clf,clim,cohere,colorbar,colors,contour,contourf,cool,copper,csd,delaxes,disconnect,draw,errorbar,eventplot,figaspect,figimage,figlegend,figtext,figure,fill_between,fill_betweenx,findobj,flag,gca,gcf,gci,get_current_fig_manager,get_figlabels,get_fignums,get_plot_commands,ginput,gray,grid,hexbin,hist2D,hlines,hold,hot,hsv,imread,imsave,imshow,ioff,ion,ishold,jet,legend,locator_params,loglog,margins,matshow,minorticks_off,minorticks_on,over,pause,pcolor,pcolormesh,pie,pink,plot,plot_date,plotfile,polar,prism,psd,quiver,quiverkey,rc,rc_context,rcdefaults,rgrids,savefig,sca,scatter,sci,semilogx,semilogy,set_cmap,setp,specgram,spectral,spring,spy,stackplot,stem,step,streamplot,subplot,subplot2grid,subplot_tool,subplots,subplots_adjust,summer,suptitle,table,text,thetagrids,tick_params,ticklabel_format,tight_layout,title,tricontour,tricontourf,tripcolor,triplot,twinx,twiny,vlines,waitforbuttonpress,winter,xkcd,xlabel,xlim,xscale,xticks,ylabel,ylim,yscale,yticks,hist
172168

173169
# The following pyplot functions must be handled specially since they
174170
# overlap with standard Julia functions:
175171
# close, fill, show, step
176-
#as in PyPlot.jl, we commit some type piracy here.
172+
#unlike PyPlot.jl, we'll avoid type piracy by renaming / not exporting.
177173

178174
const plt_funcs = (:acorr,:annotate,:arrow,:autoscale,:autumn,:axes,:axhline,:axhspan,:axis,:axline,:axvline,:axvspan,:bar,:barbs,:barh,:bone,:box,:boxplot,:broken_barh,:cla,:clabel,:clf,:clim,:cohere,:colorbar,:colors,:connect,:contour,:contourf,:cool,:copper,:csd,:delaxes,:disconnect,:draw,:errorbar,:eventplot,:figaspect,:figimage,:figlegend,:figtext,:fill_between,:fill_betweenx,:findobj,:flag,:gca,:gci,:get_current_fig_manager,:get_figlabels,:get_fignums,:get_plot_commands,:ginput,:gray,:grid,:hexbin,:hlines,:hold,:hot,:hsv,:imread,:imsave,:imshow,:ioff,:ion,:ishold,:jet,:legend,:locator_params,:loglog,:margins,:matshow,:minorticks_off,:minorticks_on,:over,:pause,:pcolor,:pcolormesh,:pie,:pink,:plot,:plot_date,:plotfile,:polar,:prism,:psd,:quiver,:quiverkey,:rc,:rc_context,:rcdefaults,:rgrids,:savefig,:sca,:scatter,:sci,:semilogx,:semilogy,:set_cmap,:setp,:specgram,:spectral,:spring,:spy,:stackplot,:stem,:streamplot,:subplot,:subplot2grid,:subplot_tool,:subplots,:subplots_adjust,:summer,:suptitle,:table,:text,:thetagrids,:tick_params,:ticklabel_format,:tight_layout,:title,:tricontour,:tricontourf,:tripcolor,:triplot,:twinx,:twiny,:vlines,:waitforbuttonpress,:winter,:xkcd,:xlabel,:xlim,:xscale,:xticks,:ylabel,:ylim,:yscale,:yticks,:hist,:xcorr,:isinteractive)
179175

180176
for f in plt_funcs
181177
sf = string(f)
182-
@eval @doc LazyHelp(plt,$sf) function $f(args...; kws...)
183-
if !hasproperty(plt, $(QuoteNode(f)))
178+
@eval @doc LazyHelp(pyplot,$sf) function $f(args...; kws...)
179+
if !hasproperty(pyplot, $(QuoteNode(f)))
184180
error("matplotlib ", version, " does not have pyplot.", $sf)
185181
end
186-
return pycall(plt.$f, args...; kws...)
182+
return pycall(pyplot.$f, args...; kws...)
187183
end
188184
end
189185

190-
# type piracy as in PyPlot.jl:
191-
@doc LazyHelp(plt,"step") Base.step(x, y; kws...) = pycall(plt.step, x, y; kws...)
186+
# rename to avoid type piracy:
187+
@doc LazyHelp(pyplot,"step") pltstep(x, y; kws...) = pycall(pyplot.step, x, y; kws...)
192188

193-
# type piracy as in PyPlot.jl:
194-
Base.show(; kws...) = begin pycall(plt.show; kws...); nothing; end
189+
# rename to avoid type piracy:
190+
pltshow(; kws...) = begin pycall(pyplot.show; kws...); nothing; end
195191

196-
Base.close(f::Figure) = close(f.number)
192+
Base.close(f::Figure) = pltclose(f)
197193

198-
# type piracy as in PyPlot.jl:
199-
function Base.close(f::Integer)
194+
# rename to avoid type piracy:
195+
@doc LazyHelp(pyplot,"close") pltclose() = pyplot.close()
196+
pltclose(f::Figure) = pyconvert(Int, pltclose(f.number))
197+
function pltclose(f::Integer)
200198
pop!(withfig_fignums, f, f)
201-
plt.close(f)
199+
pyplot.close(f)
202200
end
203-
Base.close(f::Union{AbstractString,Symbol}) = plt.close(f)
204-
@doc LazyHelp(plt,"close") Base.close() = plt.close()
201+
pltclose(f::AbstractString) = pyplot.close(f)
205202

206-
# type piracy as in PyPlot.jl:
207-
@doc LazyHelp(plt,"fill") Base.fill(x::AbstractArray,y::AbstractArray, args...; kws...) =
208-
pycall(plt."fill", PyAny, x, y, args...; kws...)
203+
# rename to avoid type piracy:
204+
@doc LazyHelp(pyplot,"fill") pltfill(x::AbstractArray,y::AbstractArray, args...; kws...) =
205+
pycall(pyplot.fill, PyAny, x, y, args...; kws...)
209206

210207
# consistent capitalization with mplot3d
211-
@doc LazyHelp(plt,"hist2d") hist2D(args...; kws...) = pycall(plt.hist2d, args...; kws...)
208+
@doc LazyHelp(pyplot,"hist2d") hist2D(args...; kws...) = pycall(pyplot.hist2d, args...; kws...)
209+
210+
# allow them to be accessed via their original names foo
211+
# as PythonPlot.foo … this also means that we must be careful
212+
# to use them as Base.foo in this module as needed!
213+
const close = pltclose
214+
const fill = pltfill
215+
const show = pltshow
216+
const step = pltstep
212217

213218
include("colormaps.jl")
214219

@@ -287,7 +292,7 @@ function withfig(actions::Function, f::Figure; clear=true)
287292
ax_save = gca()
288293
push!(withfig_fignums, f.number)
289294
figure(f.number)
290-
finalizer(close, f)
295+
finalizer(pltclose, f)
291296
try
292297
if clear && !isempty(f)
293298
clf()

src/colormaps.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ end
1313

1414
PythonCall.Py(c::ColorMap) = getfield(c, :o)
1515
PythonCall.pyconvert(::Type{ColorMap}, o::Py) = ColorMap(o)
16-
==(c::ColorMap, g::ColorMap) = Py(c) == Py(g)
17-
==(c::Py, g::ColorMap) = c == Py(g)
18-
==(c::ColorMap, g::Py) = Py(c) == g
19-
hash(c::ColorMap) = hash(Py(c))
16+
Base.:(==)(c::ColorMap, g::ColorMap) = Py(c) == Py(g)
17+
Base.:(==)(c::Py, g::ColorMap) = c == Py(g)
18+
Base.:(==)(c::ColorMap, g::Py) = Py(c) == g
19+
Base.hash(c::ColorMap, h::UInt) = hash(Py(c), h)
2020
PythonCall.pycall(c::ColorMap, args...; kws...) = pycall(Py(c), args...; kws...)
2121
(c::ColorMap)(args...; kws...) = pycall(Py(c), args...; kws...)
2222
Base.Docs.doc(c::ColorMap) = Base.Docs.Text(pyconvert(String, Py(c).__doc__))
@@ -29,7 +29,7 @@ Base.setproperty!(c::ColorMap, s::AbstractString, x) = setproperty!(Py(c), Symbo
2929
Base.propertynames(c::ColorMap) = propertynames(Py(c))
3030
Base.hasproperty(c::ColorMap, s::Union{Symbol,AbstractString}) = hasproperty(Py(c), s)
3131

32-
function show(io::IO, c::ColorMap)
32+
function Base.show(io::IO, c::ColorMap)
3333
print(io, "ColorMap \"$(pyconvert(String, c.name))\"")
3434
end
3535

@@ -187,7 +187,7 @@ function Base.show(io::IO, ::MIME"image/svg+xml", cs::AbstractVector{ColorMap})
187187
end
188188

189189
function Base.show(io::IO, m::MIME"image/svg+xml", c::ColorMap)
190-
show(io, m, [c])
190+
Base.show(io, m, [c])
191191
end
192192

193193
########################################################################

src/init.jl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using VersionParsing
88
# so that their type is known at compile-time.
99

1010
const matplotlib = PythonCall.pynew()
11-
const plt = PythonCall.pynew()
11+
const pyplot = PythonCall.pynew()
1212
const Gcf = PythonCall.pynew()
1313
const orig_draw = PythonCall.pynew()
1414
const orig_gcf = PythonCall.pynew()
@@ -158,12 +158,12 @@ function __init__()
158158
global backend = backend_gui[1]
159159
global gui = backend_gui[2]
160160

161-
PythonCall.pycopy!(plt, pyimport("matplotlib.pyplot")) # raw Python module
161+
PythonCall.pycopy!(pyplot, pyimport("matplotlib.pyplot")) # raw Python module
162162
PythonCall.pycopy!(Gcf, pyimport("matplotlib._pylab_helpers").Gcf)
163-
PythonCall.pycopy!(orig_gcf, plt.gcf)
164-
PythonCall.pycopy!(orig_figure, plt.figure)
165-
plt.gcf = gcf
166-
plt.figure = figure
163+
PythonCall.pycopy!(orig_gcf, pyplot.gcf)
164+
PythonCall.pycopy!(orig_figure, pyplot.figure)
165+
pyplot.gcf = gcf
166+
pyplot.figure = figure
167167

168168
if isdefined(Main, :IJulia) && Main.IJulia.inited
169169
Main.IJulia.push_preexecute_hook(force_new_fig)
@@ -172,8 +172,8 @@ function __init__()
172172
end
173173

174174
if isjulia_display[] && gui != :gr && backend != "Agg"
175-
plt.switch_backend("Agg")
176-
plt.ioff()
175+
pyplot.switch_backend("Agg")
176+
pyplot.ioff()
177177
end
178178

179179
init_colormaps()
@@ -182,12 +182,12 @@ end
182182
function pygui(b::Bool)
183183
if !b != isjulia_display[]
184184
if backend != "Agg"
185-
plt.switch_backend(b ? backend : "Agg")
185+
pyplot.switch_backend(b ? backend : "Agg")
186186
if b
187187
pygui_start(gui) # make sure event loop is started
188-
Base.isinteractive() && plt.ion()
188+
Base.isinteractive() && pyplot.ion()
189189
else
190-
plt.ioff()
190+
pyplot.ioff()
191191
end
192192
elseif b
193193
error("No working GUI backend found for matplotlib.")

src/plot3d.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ for f in mplot3d_funcs
4646
fs = string(f)
4747
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
4848
using3D() # make sure mplot3d is loaded
49-
ax = version <= v"3.4" ? gca(projection="3d") : plt.subplot(projection="3d")
49+
ax = version <= v"3.4" ? gca(projection="3d") : pyplot.subplot(projection="3d")
5050
pycall(ax.$fs, args...; kws...)
5151
end
5252
end
@@ -62,7 +62,7 @@ for f in zlabel_funcs
6262
fs = string("set_", f)
6363
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
6464
using3D() # make sure mplot3d is loaded
65-
ax = version <= v"3.4" ? gca(projection="3d") : plt.subplot(projection="3d")
65+
ax = version <= v"3.4" ? gca(projection="3d") : pyplot.subplot(projection="3d")
6666
pycall(ax.$fs, args...; kws...)
6767
end
6868
end

0 commit comments

Comments
 (0)