vandelay vandelay - 4 months ago 25
Python Question

Dynamicly add a new subplot2grid to a figure

I have an application that have 3 figures, and dynamicaly change them. For now it's been working fine by using

add_subplot()
to the figure. But now I have to make my graphs more complex, and need to use
subplot2grid()


self.figure1 = plt.figure()
self.canvas1 = FigureCanvas(self.figure1)
self.graphtoolbar1 = NavigationToolbar(self.canvas1, frameGraph1)

self.figure3 = plt.figure()
self.canvas3 = FigureCanvas(self.figure3)
self.graphtoolbar3 = NavigationToolbar(self.canvas3, frameGraph3)

self.figure4 = plt.figure()
self.canvas4 = FigureCanvas(self.figure4)
self.graphtoolbar4 = NavigationToolbar(self.canvas4, frameGraph4)


And here's the code that adds it, and what I've got so far.

#ax = self.figure1.add_subplot(2,1,1) <---- What I used to do

fig = self.figure1
ax = plt.subplot2grid((4,4), (0,0), rowspan=3, colspan=4) # <--- what I'm trying to do

ax.hold(False)
ax.plot(df['Close'], 'b-')
ax.legend(loc=0)
ax.set_xlabel("Date")
ax.set_ylabel("Price")
ax.grid(True)

for tick in ax.get_xticklabels():
tick.set_rotation(20)

self.canvas1.draw()


The above adds it to figure 4. Probably because that's the latest instantiated by plt. But I'd like the above to add a subplot2grid to
self.figure1
, while still having the dynamicly abilities as before.

Answer

Looking into the source code of matplotlib, the subplot2grid-function is defined in the following way:

def subplot2grid(shape, loc, rowspan=1, colspan=1, **kwargs):
    """
    Create a subplot in a grid.  The grid is specified by *shape*, at
    location of *loc*, spanning *rowspan*, *colspan* cells in each
    direction.  The index for loc is 0-based. ::

      subplot2grid(shape, loc, rowspan=1, colspan=1)

    is identical to ::

      gridspec=GridSpec(shape[0], shape[2])
      subplotspec=gridspec.new_subplotspec(loc, rowspan, colspan)
      subplot(subplotspec)
    """

    fig = gcf()  #  <-------- HERE
    s1, s2 = shape
    subplotspec = GridSpec(s1, s2).new_subplotspec(loc,
                                                   rowspan=rowspan,
                                                   colspan=colspan)
    a = fig.add_subplot(subplotspec, **kwargs)
    bbox = a.bbox
    byebye = []
    for other in fig.axes:
        if other==a: continue
        if bbox.fully_overlaps(other.bbox):
            byebye.append(other)
    for ax in byebye: delaxes(ax)

    draw_if_interactive()
    return a

As you can see from my comment "HERE" in the code snippet, it is only using the active figure, i.e. fig = gcf() (gcf is short for "get current figure").

Achieving your goal should be easily done by modifying the function slightly and put it into your script.

from matplotlib.gridspec import GridSpec
from matplotlib.backends import pylab_setup
_backend_mod, new_figure_manager, draw_if_interactive, _show = pylab_setup()

def my_subplot2grid(fig, shape, loc, rowspan=1, colspan=1, **kwargs):

    s1, s2 = shape
    subplotspec = GridSpec(s1, s2).new_subplotspec(loc,
                                                   rowspan=rowspan,
                                                   colspan=colspan)
    a = fig.add_subplot(subplotspec, **kwargs)
    bbox = a.bbox
    byebye = []
    for other in fig.axes:
        if other==a: continue
        if bbox.fully_overlaps(other.bbox):
            byebye.append(other)
    for ax in byebye: delaxes(ax)

    draw_if_interactive()
    return a

Now it should be possible to do your thing, with a modification to your function call as

ax = plt.my_subplot2grid(self.figure1, (4,4), (0,0), rowspan=3, colspan=4)

Hope it helps!