我创建了一个简单的PyQt gui,可以打开/关闭包含时间序列数据的文件,并使用matplotlib显示图形。每个新文件都显示在新选项卡上。当选项卡关闭时,应删除对图形等的所有引用。要告诉PyQt销毁Qt项目,我在结束选项卡上调用deleteLater()。
然而,有人不放弃记忆:(
我已尝试覆盖deleteLater()并清除图形/轴,然后再调用父级的deleteLater(),但这只会释放一小部分内存。
任何?
更新:管理以创建一个再现某些行为的调试示例:
#!/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('&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()
要重现:运行程序,然后单击“打开”(或Ctrl-O),然后再次“打开”。现在关闭所有标签。没有内存被释放。我想知道这是否是matplotlib中的某种性能破解。如果是这样,有没有办法告诉它让内存消失?