Skip to content

Commit 2c21a76

Browse files
committed
allow mplot3d to load lazily, so that 2d plotting still works on systems where mplot3d is not installed (closes #340)
1 parent 2476177 commit 2c21a76

File tree

3 files changed

+90
-73
lines changed

3 files changed

+90
-73
lines changed

src/PyPlot.jl

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,19 @@ export Figure, plt, matplotlib, pygui, withfig
1010
using Compat
1111
import Base.show
1212

13-
# Wrapper around matplotlib Figure, supporting graphics I/O and pretty display
14-
type Figure
15-
o::PyObject
16-
end
1713

1814
###########################################################################
1915
# Julia 0.4 help system: define a documentation object
2016
# that lazily looks up help from a PyObject via zero or more keys.
2117
# This saves us time when loading PyPlot, since we don't have
2218
# to load up all of the documentation strings right away.
2319
immutable LazyHelp
24-
o::PyObject
20+
o # a PyObject or similar object supporting getindex with a __doc__ key
2521
keys::Tuple{Vararg{String}}
26-
LazyHelp(o::PyObject) = new(o, ())
27-
LazyHelp(o::PyObject, k::AbstractString) = new(o, (k,))
28-
LazyHelp(o::PyObject, k1::AbstractString, k2::AbstractString) = new(o, (k1,k2))
29-
LazyHelp(o::PyObject, k::Tuple{Vararg{AbstractString}}) = new(o, k)
22+
LazyHelp(o) = new(o, ())
23+
LazyHelp(o, k::AbstractString) = new(o, (k,))
24+
LazyHelp(o, k1::AbstractString, k2::AbstractString) = new(o, (k1,k2))
25+
LazyHelp(o, k::Tuple{Vararg{AbstractString}}) = new(o, k)
3026
end
3127
function show(io::IO, ::MIME"text/plain", h::LazyHelp)
3228
o = h.o
@@ -53,8 +49,11 @@ end
5349
include("init.jl")
5450

5551
###########################################################################
56-
# Figure methods
52+
# Wrapper around matplotlib Figure, supporting graphics I/O and pretty display
5753

54+
type Figure
55+
o::PyObject
56+
end
5857
PyObject(f::Figure) = f.o
5958
convert(::Type{Figure}, o::PyObject) = Figure(o)
6059
==(f::Figure, g::Figure) = f.o == g.o
@@ -217,67 +216,14 @@ end
217216
bar{T<:Symbol}(x::AbstractVector{T}, y; kws...) =
218217
bar(map(string, x), y; kws...)
219218

220-
###########################################################################
221-
# Include mplot3d for 3d plotting.
222-
223-
export art3D, Axes3D, surf, mesh, bar3D, contour3D, contourf3D, plot3D, plot_surface, plot_trisurf, plot_wireframe, scatter3D, text2D, text3D, zlabel, zlim, zscale, zticks
224-
225-
const mplot3d_funcs = (:bar3d, :contour3D, :contourf3D, :plot3D, :plot_surface,
226-
:plot_trisurf, :plot_wireframe, :scatter3D,
227-
:text2D, :text3D)
228-
229-
for f in mplot3d_funcs
230-
fs = string(f)
231-
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
232-
ax = gca(projection="3d")
233-
pycall(ax[$fs], PyAny, args...; kws...)
234-
end
235-
end
236-
237-
# TODO: in Julia 0.4, change this to a callable object
238-
@doc LazyHelp(axes3D,"Axes3D") Axes3D(args...; kws...) = pycall(axes3D["Axes3D"], PyAny, args...; kws...)
239-
240-
# correct for annoying mplot3d inconsistency
241-
@doc LazyHelp(axes3D,"Axes3D", "bar3d") bar3D(args...) = bar3d(args...)
242-
243-
# it's annoying to have xlabel etc. but not zlabel
244-
const zlabel_funcs = (:zlabel, :zlim, :zscale, :zticks)
245-
for f in zlabel_funcs
246-
fs = string("set_", f)
247-
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
248-
ax = gca(projection="3d")
249-
pycall(ax[$fs], PyAny, args...; kws...)
250-
end
251-
end
252-
253-
# export Matlab-like names
254-
255-
function surf(Z::AbstractMatrix; kws...)
256-
plot_surface([1:size(Z,1);]*ones(1,size(Z,2)),
257-
ones(size(Z,1))*[1:size(Z,2);]', Z; kws...)
258-
end
259-
260-
@doc LazyHelp(axes3D,"Axes3D", "plot_surface") function surf(X, Y, Z::AbstractMatrix, args...; kws...)
261-
plot_surface(X, Y, Z, args...; kws...)
262-
end
263-
264-
function surf(X, Y, Z::AbstractVector, args...; kws...)
265-
plot_trisurf(X, Y, Z, args...; kws...)
266-
end
267-
268-
@doc LazyHelp(axes3D,"Axes3D", "plot_wireframe") mesh(args...; kws...) = plot_wireframe(args...; kws...)
269-
270-
function mesh(Z::AbstractMatrix; kws...)
271-
plot_wireframe([1:size(Z,1);]*ones(1,size(Z,2)),
272-
ones(size(Z,1))*[1:size(Z,2);]', Z; kws...)
273-
end
274-
275219
###########################################################################
276220
# Allow plots with 2 independent variables (contour, surf, ...)
277221
# to accept either 2 1d arrays or a row vector and a 1d array,
278222
# to simplify construction of such plots via broadcasting operations.
279223
# (Matplotlib is inconsistent about this.)
280224

225+
include("plot3d.jl")
226+
281227
for f in (:contour, :contourf)
282228
@eval function $f(X::AbstractMatrix, Y::AbstractVector, args...; kws...)
283229
if size(X,1) == 1 || size(X,2) == 1

src/init.jl

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ const orig_draw = PyNULL()
2020
const orig_gcf = PyNULL()
2121
const orig_figure = PyNULL()
2222
const orig_show = PyNULL()
23-
const mplot3d = PyNULL()
24-
const axes3D = PyNULL()
25-
const art3D = PyNULL()
2623

2724
###########################################################################
2825
# file formats supported by Agg backend, from MIME types
@@ -213,11 +210,6 @@ function __init__()
213210
plt["ioff"]()
214211
end
215212

216-
copy!(mplot3d, pyimport("mpl_toolkits.mplot3d"))
217-
copy!(axes3D, pyimport("mpl_toolkits.mplot3d.axes3d"))
218-
219-
copy!(art3D, pyimport("mpl_toolkits.mplot3d.art3d"))
220-
221213
init_colormaps()
222214
end
223215

src/plot3d.jl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
###########################################################################
2+
# Lazy wrapper around a PyObject to load a module on demand.
3+
4+
type LazyPyModule
5+
name::String
6+
o::PyObject
7+
LazyPyModule(n) = new(n, PyNULL())
8+
end
9+
PyObject(m::LazyPyModule) = m.o.o == C_NULL ? copy!(m.o, pyimport(m.name)) : m.o
10+
pycall(m::LazyPyModule, args...; kws...) = pycall(PyObject(m), args...; kws...)
11+
(m::LazyPyModule)(args...; kws...) = pycall(PyObject(m), PyAny, args...; kws...)
12+
Base.Docs.doc(m::LazyPyModule) = Base.Docs.doc(PyObject(m))
13+
getindex(m::LazyPyModule, x) = getindex(PyObject(m), x)
14+
setindex!(m::LazyPyModule, v, x) = setindex!(PyObject(m), v, x)
15+
haskey(m::LazyPyModule, x) = haskey(PyObject(m), x)
16+
keys(m::LazyPyModule) = keys(PyObject(m))
17+
18+
###########################################################################
19+
# Lazily load mplot3d modules. This (slightly) improves load time of PyPlot,
20+
# and it also allows PyPlot to load on systems where mplot3d is not installed.
21+
22+
const axes3D = LazyPyModule("mpl_toolkits.mplot3d.axes3d")
23+
const art3D = LazyPyModule("mpl_toolkits.mplot3d.art3d")
24+
25+
###########################################################################
26+
# 3d plotting functions from mplot3d
27+
28+
export art3D, Axes3D, surf, mesh, bar3D, contour3D, contourf3D, plot3D, plot_surface, plot_trisurf, plot_wireframe, scatter3D, text2D, text3D, zlabel, zlim, zscale, zticks
29+
30+
const mplot3d_funcs = (:bar3d, :contour3D, :contourf3D, :plot3D, :plot_surface,
31+
:plot_trisurf, :plot_wireframe, :scatter3D,
32+
:text2D, :text3D)
33+
34+
for f in mplot3d_funcs
35+
fs = string(f)
36+
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
37+
PyObject(axes3D) # make sure mplot3d is loaded
38+
ax = gca(projection="3d")
39+
pycall(ax[$fs], PyAny, args...; kws...)
40+
end
41+
end
42+
43+
@doc LazyHelp(axes3D,"Axes3D") Axes3D(args...; kws...) = pycall(axes3D["Axes3D"], PyAny, args...; kws...)
44+
45+
# correct for annoying mplot3d inconsistency
46+
@doc LazyHelp(axes3D,"Axes3D", "bar3d") bar3D(args...) = bar3d(args...)
47+
48+
# it's annoying to have xlabel etc. but not zlabel
49+
const zlabel_funcs = (:zlabel, :zlim, :zscale, :zticks)
50+
for f in zlabel_funcs
51+
fs = string("set_", f)
52+
@eval @doc LazyHelp(axes3D,"Axes3D", $fs) function $f(args...; kws...)
53+
PyObject(axes3D) # make sure mplot3d is loaded
54+
ax = gca(projection="3d")
55+
pycall(ax[$fs], PyAny, args...; kws...)
56+
end
57+
end
58+
59+
# export Matlab-like names
60+
61+
function surf(Z::AbstractMatrix; kws...)
62+
plot_surface([1:size(Z,1);]*ones(1,size(Z,2)),
63+
ones(size(Z,1))*[1:size(Z,2);]', Z; kws...)
64+
end
65+
66+
@doc LazyHelp(axes3D,"Axes3D", "plot_surface") function surf(X, Y, Z::AbstractMatrix, args...; kws...)
67+
plot_surface(X, Y, Z, args...; kws...)
68+
end
69+
70+
function surf(X, Y, Z::AbstractVector, args...; kws...)
71+
plot_trisurf(X, Y, Z, args...; kws...)
72+
end
73+
74+
@doc LazyHelp(axes3D,"Axes3D", "plot_wireframe") mesh(args...; kws...) = plot_wireframe(args...; kws...)
75+
76+
function mesh(Z::AbstractMatrix; kws...)
77+
plot_wireframe([1:size(Z,1);]*ones(1,size(Z,2)),
78+
ones(size(Z,1))*[1:size(Z,2);]', Z; kws...)
79+
end

0 commit comments

Comments
 (0)