如何正确删除PySide中嵌入在子窗口中的matplotlib图形以释放内存

时间:2015-08-31 19:34:04

标签: python matplotlib qt4 pyside

问题:

我有一个应用程序,通过单击按钮从中打开一个无模式子窗口。这个子窗口包含一个嵌入的matplotlib图。在关闭之后,我希望这个子窗口与matplotlib图一起被销毁。

问题是,即使在Qt端似乎正确删除了子窗口,似乎没有从该进程中释放内存。问题似乎是累积的,即如果我打开多个子窗口然后用'X'手动关闭它们,我的应用程序占用的内存会增加。

我的系统: Ubuntu 15.04 + Matplotlib 1.4.2 + python 2.7.9或3.4.3 + PySide 1.2.2

注意:我目前通过不破坏子窗口并重复使用相同的艺术家用新数据更新图形来避免“内存泄漏”问题。但是,我希望能够在不需要时完全释放这个子窗口占用的内存。

到目前为止我尝试过:

我尝试了gc.collect()setParent(None)deleteLater()delset_attributes(QtCore.Qt.WA_DeleteOnClose)所能想到的所有组合,我试过要小心使用命名空间,并且只使用matplotlib艺术家的弱链接,我也试过使用和不使用pyplot,但没有真正成功...我已经能够释放一些内存由matplotlib子窗口通过重新实现子窗口类的closeEvent方法并手动清理东西来获取,但是,它仍然不能完美,并且它不漂亮。

下面是一个MCVE,它说明了迄今为止我所拥有的最佳“解决方案”的基本实现问题。我尝试了相同的代码,将FigureCanvasQTAgg窗口小部件替换为“纯”qt窗口小部件(托管大QLabel的{​​{1}})以及子窗口占用的所有内存( s)在结束时被释放。

QPixmap

更新2015-09-01

使用QLabel并且仅使用FigureCanvasAgg:

的示例

这是我在尝试隔离问题时生成的一个简单应用程序。单击“显示图”按钮时:

  • 如果 QSpinBox 数字等于1,则子窗口将包含 QLabel ,显示从外部图像生成的 QPixmap
  • 如果 QSpinBox 数字等于2,则子窗口将包含 QLabel ,显示从matplotlib图中生成的 QPixmap ,仅使用非GUI后端 FigureCanvasAgg

enter image description here

我还添加了一个按钮来显式调用import sys import numpy as np from PySide import QtGui, QtCore import matplotlib as mpl mpl.rcParams['backend.qt4']='PySide' from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg import gc class MyApp(QtGui.QWidget): def __init__(self): super(MyApp, self).__init__() btn_open = QtGui.QPushButton('Show Figure') btn_open.clicked.connect(self.show_a_figure) layout = QtGui.QGridLayout() layout.addWidget(btn_open, 0, 0) self.setLayout(layout) def show_a_figure(self): MyFigureManager(self).show() class MyFigureManager(QtGui.QWidget): def __init__(self, parent=None): super(MyFigureManager, self).__init__(parent) self.setWindowFlags(QtCore.Qt.Window) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) layout = QtGui.QGridLayout() layout.addWidget(MyFigureCanvas(), 0, 0) self.setLayout(layout) def closeEvent(self, event): fig_canvas = self.findChild(MyFigureCanvas) fig_canvas.figure.clear() del fig_canvas.figure fig_canvas.renderer.clear() del fig_canvas.renderer fig_canvas.mpl_disconnect(fig_canvas.scroll_pick_id) fig_canvas.mpl_disconnect(fig_canvas.button_pick_id) fig_canvas.close() del fig_canvas gc.collect() super(MyFigureManager, self).closeEvent(event) class MyFigureCanvas(FigureCanvasQTAgg): def __init__(self, parent=None): super(MyFigureCanvas, self).__init__(figure=mpl.figure.Figure()) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) #-- plot some data -- ax = self.figure.add_axes([0.1, 0.1, 0.85, 0.85]) ax.axis([0, 1, 0, 1]) N = 100000 x = np.random.rand(N) y = np.random.rand(N) colors = np.random.rand(N) area = np.pi * (5 * np.random.rand(N)) ** 2 ax.scatter(x, y, s=area, c=colors, alpha=0.5) if __name__ == '__main__': app = QtGui.QApplication(sys.argv) w = MyApp() w.show() sys.exit(app.exec_()) 。在第一种情况下(mode = 1),子窗口占用的所有内存在关闭时恢复。在“matplotlib模式”(mode = 2)中,可以在明确强制gc.collect()时恢复某些内存,但有些内存不是。未释放的内存量似乎与mpl中绘制的点数量成比例,并且与同时打开的子窗口量有些相关。

我对内存管理知之甚少。我目前正通过Ubuntu中的System Load Indicator监控应用程序使用的内存。如果我做错了,请评论。

gc.collect()

相关文档:

SO帖子:

Matplotlib文档:

0 个答案:

没有答案