Help with strange add_subplot behavior after change_geometry()...

I am using matplotlib in wxpython to dynamically add subplots to a figure and I am using the geometry_change() method to manage how they are shown. Basically I want all subplot to share the space evenly in one column. With the simple test code I have pasted in below
I have run into some weird behavior though.

Two cases, one good and one weird. (launch the wxpython app and do as follows to replicate)

Good behavior:

  1. Right-click in an empty frame.

  2. Click the ‘add subplot’ menu item to add a subplot.

  3. Repeat 1 & 2 to add a second subplot.

  4. Right-click on top of the LOWER subplot, and click the ‘delete’ menu item to delete that particular subplot.

  5. Right-click and click the ‘add subplot’ menu item to add a second subplot again.

You will have some debug stuff in the console, but this run does exactly what you would expect it to. You first end up with two subplots sharing the column 50-50, you delete the LOWER one, and you add a second one again to end up in the same situation, i.e., two subplots sharing the colum 50-50.

Weird behavior:

  1. Right-click in an empty frame.

  2. Click the ‘add subplot’ menu item to add a subplot.

  3. Repeat 1 & 2 to add a second subplot.

  4. Right-click on top of the UPPER subplot, and click the ‘delete’ menu item to delete that particular subplot.

  5. Right-click and click the ‘add subplot’ menu item to add a second subplot again.

This time you would of course expect the same behavior as above, but after 5 you won’t end up with two subplots sharing the column 50-50. Instead you have one subplot (which is the most recent one you added) in upper half, and the lower half is blank.

Looking at the console after 5 it says this:

add ----------------------------------------

changing geometry for subplot 0 to (211)

adding subplot(212)

count=2 c=3 n_before=1 n_after=1

···

So it has changed the geometry as you would expect. It has also added the new second subplot as you would expect too. But somehow the axes count before and after is the same (n_before and n_after). In other words it looks like it hasn’t added the subplot with 212, it looks like 211 has been used instead.

Am I doing something stupid, or is this a bug os some sort?

Comment regarding the code. The Frame class has two attributes outside of the usual figures etc, count & c. Both are simple counters. But while count is increased/decreased as subplots are added/subtracted, c is only increased as subplots are added it is never decreased. I then use c for making a simple plot in every subplot. I made it that way so I could tell the subplots apart.

Code starts here:

import wx

import matplotlib

matplotlib.use(‘wxagg’)

from matplotlib.figure import Figure

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas

class Frame(wx.Frame):

def __init__(self, title):

	wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(800,800))

	self.panel = wx.Panel(self)

	self.figure = Figure()

	self.canvas = FigureCanvas(self, -1, self.figure)

	self.figure.canvas.mpl_connect('button_press_event', self.OnEvent)

	self.figure.canvas.draw()

	self.Bind(wx.EVT_SIZE, self.OnSize)

	self.count = 0

	self.c = 0

def OnEvent(self, event):

	self.clicked_axes = event.inaxes

	if event.button == 3:

		id_add_subplot = wx.NewId()

		id_reset = wx.NewId()

		self.Bind(wx.EVT_MENU, self.OnAddSubplot, id=id_add_subplot)

		self.Bind(wx.EVT_MENU, self.OnReset, id=id_reset)

		self.menu = wx.Menu()

		self.menu.Append(id_add_subplot, "Add subplot")

		self.menu.Append(id_reset, "Reset")

		if self.clicked_axes is not None:

			id_delete_subplot = wx.NewId()

			self.Bind(wx.EVT_MENU, self.OnDeleteSubplot, id=id_delete_subplot)

			self.menu.Append(id_delete_subplot, "Delete")

		self.PopupMenu(self.menu)

		self.menu.Destroy()

def OnDeleteSubplot(self, event):

	self.count -= 1

	self.figure.delaxes(self.clicked_axes)

	n = len(self.figure.axes)

	print ''

	print 'delete -------------------------------------'

	for i in xrange(n):

		print 'changing geometry for subplot %s to (%s%s%s)' % (i, n, 1, i + 1)

		self.figure.axes[i].change_geometry(n, 1, i + 1)

		self.figure.axes[i].update_params()

	self.figure.subplots_adjust()

	self.figure.canvas.draw()

	print 'count=%s c=%s n=%s' % (self.count, self.c, n)

	print '--------------------------------------------'

def OnAddSubplot(self, event):

	self.count += 1

	self.c += 1

	n_before = len(self.figure.axes)

	print ''

	print 'add ----------------------------------------'

	for i in xrange(n_before):

		print 'changing geometry for subplot %s to (%s%s%s)' % (i, n_before + 1, 1, i + 1)

		self.figure.axes[i].change_geometry(n_before + 1, 1, i + 1)

		self.figure.axes[i].update_params()

	print 'adding subplot(%s%s%s)' % (n_before + 1, 1, n_before + 1)

	subplot = self.figure.add_subplot(n_before + 1, 1, n_before + 1)

	n_after = len(self.figure.axes)

	subplot.plot([self.c,self.c,self.c])

	self.figure.subplots_adjust()

	self.figure.canvas.draw()

	print 'count=%s c=%s n_before=%s n_after=%s' % (self.count, self.c, n_before, n_after)

	print '--------------------------------------------'

def OnReset(self, event):

	self.count = 0

	self.figure.clear()

	self.figure.canvas.draw()

def OnSize(self, event):

	pixels = tuple(event.GetSize())

	self.canvas.SetSize(pixels)

	self.figure.set_size_inches(float(pixels[0])/self.figure.get_dpi(),

								float(pixels[1])/self.figure.get_dpi())

if name == “main”:

app = wx.PySimpleApp()

frame = Frame('Hello')

frame.Show()

app.MainLoop()

I figured it out, I think. I read the docs (http://matplotlib.sourceforge.net/api/figure_api.html#matplotlib.figure.Figure.add_axes) on add_axes and found the part about adding a label to a new axes to force matplotlib to add a new axes.

So I changed this line from:

subplot = self.figure.add_subplot(n_before + 1, 1, n_before + 1)

to

subplot = self.figure.add_subplot(n_before + 1, 1, n_before + 1, label=‘%s’ % self.c)

and everything works as advertised.

···

On Thu, Dec 16, 2010 at 10:48 AM, Åke Kullenberg <ake.kullenberg@…287…> wrote:

I am using matplotlib in wxpython to dynamically add subplots to a figure and I am using the geometry_change() method to manage how they are shown. Basically I want all subplot to share the space evenly in one column. With the simple test code I have pasted in below I have run into some weird behavior though.

Two cases, one good and one weird. (launch the wxpython app and do as follows to replicate)

Good behavior:

  1. Right-click in an empty frame.
  1. Click the ‘add subplot’ menu item to add a subplot.
  1. Repeat 1 & 2 to add a second subplot.
  1. Right-click on top of the LOWER subplot, and click the ‘delete’ menu item to delete that particular subplot.
  1. Right-click and click the ‘add subplot’ menu item to add a second subplot again.

You will have some debug stuff in the console, but this run does exactly what you would expect it to. You first end up with two subplots sharing the column 50-50, you delete the LOWER one, and you add a second one again to end up in the same situation, i.e., two subplots sharing the colum 50-50.

Weird behavior:

  1. Right-click in an empty frame.
  1. Click the ‘add subplot’ menu item to add a subplot.
  1. Repeat 1 & 2 to add a second subplot.
  1. Right-click on top of the UPPER subplot, and click the ‘delete’ menu item to delete that particular subplot.
  1. Right-click and click the ‘add subplot’ menu item to add a second subplot again.

This time you would of course expect the same behavior as above, but after 5 you won’t end up with two subplots sharing the column 50-50. Instead you have one subplot (which is the most recent one you added) in upper half, and the lower half is blank.

Looking at the console after 5 it says this:

add ----------------------------------------

changing geometry for subplot 0 to (211)

adding subplot(212)

count=2 c=3 n_before=1 n_after=1


So it has changed the geometry as you would expect. It has also added the new second subplot as you would expect too. But somehow the axes count before and after is the same (n_before and n_after). In other words it looks like it hasn’t added the subplot with 212, it looks like 211 has been used instead.

Am I doing something stupid, or is this a bug os some sort?

Comment regarding the code. The Frame class has two attributes outside of the usual figures etc, count & c. Both are simple counters. But while count is increased/decreased as subplots are added/subtracted, c is only increased as subplots are added it is never decreased. I then use c for making a simple plot in every subplot. I made it that way so I could tell the subplots apart.

Code starts here:

import wx

import matplotlib

matplotlib.use(‘wxagg’)

from matplotlib.figure import Figure

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas

class Frame(wx.Frame):

def init(self, title):

  wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(800,800))
  self.panel = wx.Panel(self)
  self.figure = Figure()
  self.canvas = FigureCanvas(self, -1, self.figure)
  self.figure.canvas.mpl_connect('button_press_event', self.OnEvent)
  self.figure.canvas.draw()
  self.Bind(wx.EVT_SIZE, self.OnSize)
  self.count = 0
  self.c = 0

def OnEvent(self, event):

  self.clicked_axes = event.inaxes
  if event.button == 3:
  	id_add_subplot = wx.NewId()
  	id_reset = wx.NewId()
  	self.Bind(wx.EVT_MENU, self.OnAddSubplot, id=id_add_subplot)
  	self.Bind(wx.EVT_MENU, self.OnReset, id=id_reset)
  	self.menu = wx.Menu()
  	self.menu.Append(id_add_subplot, "Add subplot")
  	self.menu.Append(id_reset, "Reset")
  	if self.clicked_axes is not None:
  		id_delete_subplot = wx.NewId()
  		self.Bind(wx.EVT_MENU, self.OnDeleteSubplot, id=id_delete_subplot)
  		self.menu.Append(id_delete_subplot, "Delete")
  	self.PopupMenu(self.menu)
  	self.menu.Destroy()

def OnDeleteSubplot(self, event):

  self.count -= 1
  self.figure.delaxes(self.clicked_axes)
  n = len(self.figure.axes)
  print ''
  print 'delete -------------------------------------'
  for i in xrange(n):
  	print 'changing geometry for subplot %s to (%s%s%s)' % (i, n, 1, i + 1)
  	self.figure.axes[i].change_geometry(n, 1, i + 1)
  	self.figure.axes[i].update_params()
  self.figure.subplots_adjust()
  self.figure.canvas.draw()
  print 'count=%s c=%s n=%s' % (self.count, self.c, n)
  print '--------------------------------------------'

def OnAddSubplot(self, event):

  self.count += 1
  self.c += 1
  n_before = len(self.figure.axes)
  print ''
  print 'add ----------------------------------------'
  for i in xrange(n_before):
  	print 'changing geometry for subplot %s to (%s%s%s)' % (i, n_before + 1, 1, i + 1)
  	self.figure.axes[i].change_geometry(n_before + 1, 1, i + 1)
  	self.figure.axes[i].update_params()
  print 'adding subplot(%s%s%s)' % (n_before + 1, 1, n_before + 1)
  subplot = self.figure.add_subplot(n_before + 1, 1, n_before + 1)
  n_after = len(self.figure.axes)
  subplot.plot([self.c,self.c,self.c])
  self.figure.subplots_adjust()
  self.figure.canvas.draw()
  print 'count=%s c=%s n_before=%s n_after=%s' % (self.count, self.c, n_before, n_after)
  print '--------------------------------------------'

def OnReset(self, event):

  self.count = 0
  self.figure.clear()
  self.figure.canvas.draw()

def OnSize(self, event):

  pixels = tuple(event.GetSize())
  self.canvas.SetSize(pixels)
  self.figure.set_size_inches(float(pixels[0])/self.figure.get_dpi(),
  							float(pixels[1])/self.figure.get_dpi())

if name == “main”:

app = wx.PySimpleApp()

frame = Frame(‘Hello’)

frame.Show()

app.MainLoop()