title: Closing and deleteLater-ing PyQt tabs with Matplotlib figures on them doesn't free up memory
body: <p>I've created a simple PyQt gui that can open/close files containing time-series data and display graphs using matplotlib. Each new file is displayed on a new tab. When a tab is closed, all references to the figure etc. should be deleted. To tell PyQt to destroy the Qt items, I'm calling deleteLater() on the closing tab.</p>
  
  <p>However, someone's not letting go of the memory :(</p>
  
  <p>I've tried overriding deleteLater() and clearing the figure/axes before calling deleteLater() on the parent but that only frees up a fraction of the memory.</p>
  
  <p>Anyone?</p>
  
  <p>Update: Managed to make a debug example that reproduces some of the behaviour:</p>
  
  <pre><code>#!/usr/bin/env python
  # AbfView debug example
  # Copyright 2014 Michael Clerx (michael@myokit.org)
  import gc
  import sys
  # PyQt for python 2
  import sip
  sip.setapi('QString', 2)
  sip.setapi('QVariant', 2)
  from PyQt4 import QtGui, QtCore
  from PyQt4.QtCore import Qt
  QtCore.Signal = QtCore.pyqtSignal
  QtCore.Slot = QtCore.pyqtSlot
  # Matplotlib
  import matplotlib
  matplotlib.use('Qt4Agg') 
  import matplotlib.backends.backend_qt4agg as backend
  import matplotlib.figure
  class AbfView(QtGui.QMainWindow):
      """
      Main window
      """
      def __init__(self):
          super(AbfView, self).__init__()
          # Set size, center
          self.resize(800,600)
          qr = self.frameGeometry()
          cp = QtGui.QDesktopWidget().availableGeometry().center()
          qr.moveCenter(cp)
          self.move(qr.topLeft())
          self.create_toolbar()
          # Add widget for Abf file tabs
          self._tabs = QtGui.QTabWidget()
          self._tabs.setTabsClosable(True)
          self._tabs.tabCloseRequested.connect(self.action_close)
          self.setCentralWidget(self._tabs)
      def action_open(self, event):
          """
          Mock-up file opening
          """
          for i in xrange(1):
              filename = 'file_' + str(i) + '.txt'
              abf = AbfFile(filename)
              self._tabs.addTab(AbfTab(self, abf), filename)
      def action_close(self, index):
          """
          Called when a tab should be closed
          """
          tab = self._tabs.widget(index)
          self._tabs.removeTab(index)
          if tab is not None:
              tab.deleteLater()
          gc.collect()
          del(tab)
      def create_toolbar(self):
          """
          Creates this widget's toolbar
          """
          self._tool_open = QtGui.QAction('&amp;Open', self)
          self._tool_open.setShortcut('Ctrl+O')
          self._tool_open.setStatusTip('Open a file')
          self._tool_open.setIcon(QtGui.QIcon.fromTheme('document-open'))
          self._tool_open.triggered.connect(self.action_open)
          self._toolbar = self.addToolBar('tools')
          self._toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
          self._toolbar.addAction(self._tool_open)
  class AbfTab(QtGui.QTabWidget):
      """
      A Qt widget displaying an ABF file.
      """
      def __init__(self, parent, abf):
          super(AbfTab, self).__init__(parent)
          self.setTabsClosable(False)
          self.setTabPosition(self.East)
          self._abf = abf
          self._abf.fold_sweeps()
          self._abf.set_time_scale(1000)
          self._figures = []
          self._axes = []
          for i in xrange(self._abf.count_data_channels()):
              self.addTab(self.create_graph_tab(i), 'AD' + str(i))
          for i in xrange(self._abf.count_protocol_channels()):
              self.addTab(self.create_protocol_tab(i), 'DA' + str(i))
          self.addTab(self.create_info_tab(), 'Info')
      def create_graph_tab(self, channel):
          """
          Creates a widget displaying the main data.
          """
          widget = QtGui.QWidget(self)
          # Create figure
          figure = matplotlib.figure.Figure()
          figure.suptitle(self._abf.filename())
          canvas = backend.FigureCanvasQTAgg(figure)
          canvas.setParent(widget)
          axes = figure.add_subplot(1,1,1)        
          toolbar = backend.NavigationToolbar2QTAgg(canvas, widget)
          # Draw lines
          for i, sweep in enumerate(self._abf):
              c = sweep[channel]
              axes.plot(c.times(), c.values())
          # Create a layout
          vbox = QtGui.QVBoxLayout()
          vbox.addWidget(canvas)
          vbox.addWidget(toolbar)
          widget.setLayout(vbox)
          self._figures.append(figure)
          self._axes.append(axes)
          return widget
      def create_protocol_tab(self, channel):
          """
          Creates a widget displaying a stored D/A signal.
          """
          widget = QtGui.QWidget(self)
          # Create figure
          figure = matplotlib.figure.Figure()
          figure.suptitle(self._abf.filename())
          canvas = backend.FigureCanvasQTAgg(figure)
          canvas.setParent(widget)
          axes = figure.add_subplot(1,1,1)        
          toolbar = backend.NavigationToolbar2QTAgg(canvas, widget)
          # Draw lines
          for i, sweep in enumerate(self._abf.protocol()):
              c = sweep[channel]
              axes.plot(c.times(), c.values())
          # Create a layout
          vbox = QtGui.QVBoxLayout()
          vbox.addWidget(canvas)
          vbox.addWidget(toolbar)
          widget.setLayout(vbox)
          self._figures.append(figure)
          self._axes.append(axes)
          return widget
      def create_info_tab(self):
          """
          Creates a tab displaying information about the file.
          """
          widget = QtGui.QTextEdit(self)
          widget.setText(self._abf.info(show_header=True))
          widget.setReadOnly(True)
          return widget
      def deleteLater(self):
          """
          Deletes this tab (later).
          """
          for figure in self._figures:
              figure.clear()
          for axes in self._axes:
              axes.cla()
          del(self._abf, self._figures, self._axes)
          gc.collect()
          super(AbfTab, self).deleteLater()
  class AbfFile(object):
      """
      Mock-up for abf file class
      """
      def __init__(self, filename):
          import numpy as np
          self._filename = filename
          n = 500000
          s = 20
          self._time = np.linspace(0,6,n)
          self._data = []
          self._prot = []
          for i in xrange(s):
              self._data.append(AbfFile.Sweep(self._time,
                  np.sin(self._time + np.random.random() * 36)))
              self._prot.append(AbfFile.Sweep(self._time,
                  np.cos(self._time + np.random.random() * 36)))
      def count_data_channels(self):
          return 1
      def count_protocol_channels(self):
          return 4
      def info(self, show_header=False):
          return 'fake info'
      def fold_sweeps(self):
          pass
      def set_time_scale(self, scale):
          pass
      def __iter__(self):
          return iter(self._data)
      def protocol(self):
          return iter(self._prot)
      def filename(self):
          return self._filename
      class Sweep(object):
          def __init__(self, time, data):
              self._channel = AbfFile.Channel(time, data)
          def __getitem__(self, index):
              return self._channel
      class Channel(object):
          def __init__(self, time, data):
              self._time = time
              self._data = data
          def times(self):
              return self._time
          def values(self):
              return self._data
  def load():
      """
      Loads the Gui, and adds signal handling.
      """
      import sys
      import signal
      app = QtGui.QApplication(sys.argv)
      # Close with last window
      app.connect(app, QtCore.SIGNAL('lastWindowClosed()'),
                  app, QtCore.SLOT('quit()'))
      # Close on Ctrl-C
      def int_signal(signum, frame):
          app.closeAllWindows()
      signal.signal(signal.SIGINT, int_signal)
      # Create main window and show
      window = AbfView()
      window.show()
      # For some reason, PyQt needs the focus to handle the SIGINT catching...
      timer = QtCore.QTimer()
      timer.start(500) # Flags timeout every 500ms
      timer.timeout.connect(lambda: None)
      # Wait for app to exit
      sys.exit(app.exec_())
  if __name__ == '__main__':
      load()
  </code></pre>
  
  <p>To reproduce: Run the program, then click "Open" (or Ctrl-O), and then "Open" again. Now close all tabs. No memory is freed. I'm wondering if this is some kind of performance hack in matplotlib. If so, is there a way to tell it to let the memory go?</p>