figimage over plot?

Hi,

Is it at all possible to have figimage draw the image OVER the top of
the plot area? Currently I can only get it to draw underneath — even
if I set the frame alpha to zero, it is obscured by the grid. This can
be seen from the test code below:

---- testplot.py ----
#!/usr/bin/python

import numpy as np
import matplotlib.pyplot as plt

Z = np.arange(10000.0)
Z.shape = 100,100
Z[:,50:] = 1.

dpi = 80.0
width = 600
height = 480

data = zip(*[(x, x**2) for x in range(0, 100)])

fig = plt.figure()

ax1 = fig.add_axes([0.12,0.1,0.83,0.85])

plt.plot(data[0], data[1])

# This doesn't help, the grid still gets in the way...
ax1.get_frame().set_alpha(0)

ax1.grid(True, color='0.78', linestyle='-', lw=((1.0/dpi)*72))

logo_fig = fig.figimage(Z, 100, 100)

fig.set_size_inches(width/dpi,height/dpi)
fig.set_dpi(dpi)

fig.savefig("test.png", dpi=dpi)

···

----

So far, my best workaround is to use transform_point(...) to work out
the position that I want, and run imagemagick in a subprocess when I'm
done. I'd appreciate any help.

Cheers,
Jason

I added support for zorder at the level of figure artists to svn HEAD.
It is not a complete solution, but should be better than what we had
before, and should handle your use case. The reason it is not
complete is that it treats the Axes at the Figure level as a *single*
artist, so draws every artist contained in the Axes at the same
relative figure order (relative orders among Axes artists are still
respected). But you can now specify the zorder of all artists
contained by the figure, as shown in the attached example which puts
the figimages above the axes.

Some of this code is particularly tricky and difficult to get right
across use cases (eg composite figimages) and this is exacerbated by
the fact that this part of the codebase is lightly used. So testing
will be helpful. I'm also posting an svn diff for other devs to
review.

Index: lib/matplotlib/pyplot.py

figimage_demo_zorder.py (466 Bytes)

···

On Mon, Nov 30, 2009 at 9:04 PM, Jason Heeris <jason.heeris@...287...> wrote:

Is it at all possible to have figimage draw the image OVER the top of
the plot area? Currently I can only get it to draw underneath — even
if I set the frame alpha to zero, it is obscured by the grid. This can
be seen from the test code below:

===================================================================
--- lib/matplotlib/pyplot.py (revision 7992)
+++ lib/matplotlib/pyplot.py (revision 7994)
@@ -401,7 +401,7 @@
     # allow callers to override the hold state by passing hold=True|False
     ret = gcf().figimage(*args, **kwargs)
     draw_if_interactive()
- sci(ret)
+ #sci(ret) # JDH figimage should not set current image -- it is
not mappable, etc
     return ret

def figlegend(handles, labels, loc, **kwargs):
Index: lib/matplotlib/figure.py

--- lib/matplotlib/figure.py (revision 7992)
+++ lib/matplotlib/figure.py (revision 7994)
@@ -343,7 +343,8 @@
                  cmap=None,
                  vmin=None,
                  vmax=None,
- origin=None):
+ origin=None,
+ **kwargs):
         """
         call signatures::

@@ -393,11 +394,14 @@

         .. plot:: mpl_examples/pylab_examples/figimage_demo.py

+
+ Additional kwargs are Artist kwargs passed on to
+ :class:`~matplotlib.image.FigureImage`
         """

         if not self._hold: self.clf()

- im = FigureImage(self, cmap, norm, xo, yo, origin)
+ im = FigureImage(self, cmap, norm, xo, yo, origin, **kwargs)
         im.set_array(X)
         im.set_alpha(alpha)
         if norm is None:
@@ -735,11 +739,20 @@

         if self.frameon: self.patch.draw(renderer)

+ # a list of (zorder, func_to_call, list_of_args)
+ dsu = []
+
+
         # todo: respect zorder
- for p in self.patches: p.draw(renderer)
- for l in self.lines: l.draw(renderer)
- for a in self.artists: a.draw(renderer)
+ for a in self.patches:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))

+ for a in self.lines:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))
+
+ for a in self.artists:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))
+
         # override the renderer default if self.suppressComposite
         # is not None
         composite = renderer.option_image_nocomposite()
@@ -747,8 +760,8 @@
             composite = self.suppressComposite

         if len(self.images)<=1 or composite or not
allequal([im.origin for im in self.images]):
- for im in self.images:
- im.draw(renderer)
+ for a in self.images:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))
         else:
             # make a composite image blending alpha
             # list of (_image.Image, ox, oy)
@@ -762,21 +775,33 @@

             im.is_grayscale = False
             l, b, w, h = self.bbox.bounds
- gc = renderer.new_gc()
- gc.set_clip_rectangle(self.bbox)
- gc.set_clip_path(self.get_clip_path())
- renderer.draw_image(gc, l, b, im)
- gc.restore()

+ def draw_composite():
+ gc = renderer.new_gc()
+ gc.set_clip_rectangle(self.bbox)
+ gc.set_clip_path(self.get_clip_path())
+ renderer.draw_image(gc, l, b, im)
+ gc.restore()
+
+ if len(ims):
+ dsu.append((ims[0].get_zorder(), draw_composite, []))
+
         # render the axes
- for a in self.axes: a.draw(renderer)
+ for a in self.axes:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))

         # render the figure text
- for t in self.texts: t.draw(renderer)
+ for a in self.texts:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))

- for legend in self.legends:
- legend.draw(renderer)
+ for a in self.legends:
+ dsu.append( (a.get_zorder(), a.draw, [renderer]))

+
+ dsu.sort()
+ for zorder, func, args in dsu:
+ func(*args)
+

2009/12/1 John Hunter <jdh2358@...287...>:

Some of this code is particularly tricky and difficult to get right
across use cases (eg composite figimages) and this is exacerbated by
the fact that this part of the codebase is lightly used. So testing
will be helpful. I'm also posting an svn diff for other devs to
review.

I've finally gotten around to installing from SVN HEAD in a virtual
python environment and it works perfectly for my needs. Thanks very
much for this :slight_smile:

Cheers,
Jason