简体   繁体   English

在PyQt5 GUI中嵌入matplotlib时发生内存泄漏

[英]Memory leak when embedding matplotlib in PyQt5 GUI

I built a Matplotlib GUI with Qt Designer basically following this tutorial . 基本上按照本教程,我使用Qt Designer构建了Matplotlib GUI。 I am using python 3.5.2 and pyqt 5.6.0 . 我正在使用python 3.5.2和pyqt 5.6.0。 The code you can see below is working. 您可以在下面看到的代码正常工作。 However when changing the plots the memory used by my system increases, at least according to Windows 10 Task Manager. 但是,更改绘图时,至少根据Windows 10 Task Manager,我的系统使用的内存增加了。 To recreate this a little better one can increase the number of random values used in the plot commands. 为了更好地重新创建它,可以增加plot命令中使用的随机值的数量。 It seems like the self.canvas.close() command inside the rmmppl function is not enough to actually free the used memory. 似乎rmmppl函数中的self.canvas.close()命令不足以实际释放已使用的内存。

How can I prevent the increasing memory usage? 如何防止内存使用量增加?

edit: here is a Screenshot of the GUI 编辑:这是GUI的屏幕截图

from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
import window
import numpy as np


class Plotter(QtWidgets.QMainWindow, window.Ui_MainWindow):
    def __init__(self):
        super(Plotter, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}    
        self.mplfigs.itemClicked.connect(self.changefig)

    def addmpl(self, fig):
        self.canvas = FigureCanvas(fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()
        self.toolbar = NavigationToolbar(self.canvas,self.mplwindow, coordinates = True)
        self.mplvl.addWidget(self.toolbar)

    def rmmppl(self):
        self.mplvl.removeWidget(self.canvas)
        self.canvas.close()
        self.mplvl.removeWidget(self.toolbar)
        self.toolbar.close()

    def addfig(self, name, fig):
        self.fig_dict[name]=fig
        self.mplfigs.addItem(name)

    def changefig(self,item):
        text = item.text()
        self.rmmppl()
        self.addmpl(self.fig_dict[text])



def main():
    import sys

    fig1 = Figure()
    ax1f1= fig1.add_subplot(111)
    ax1f1.plot(np.random.rand(5))

    fig2 = Figure()
    ax1f2 = fig2.add_subplot(121)
    ax1f2.plot(np.random.rand(5))
    ax1f2 = fig2.add_subplot(122)
    ax1f2.plot(np.random.rand(10))    

    app=QtWidgets.QApplication(sys.argv)
    main=Plotter()
    main.addmpl(fig1)
    main.addfig('Figure 1', fig1)
    main.addfig('Figure 2', fig2)
    main.show()

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

The window.py which is the Basic GUI structure: window.py是基本的GUI结构:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(640, 432)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.mplwindow = QtWidgets.QWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.mplwindow.sizePolicy().hasHeightForWidth())
        self.mplwindow.setSizePolicy(sizePolicy)
        self.mplwindow.setObjectName("mplwindow")
        self.mplvl = QtWidgets.QVBoxLayout(self.mplwindow)
        self.mplvl.setContentsMargins(0, 0, 0, 0)
        self.mplvl.setObjectName("mplvl")
        self.horizontalLayout.addWidget(self.mplwindow)
        self.mplfigs = QtWidgets.QListWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.mplfigs.sizePolicy().hasHeightForWidth())
        self.mplfigs.setSizePolicy(sizePolicy)
        self.mplfigs.setMaximumSize(QtCore.QSize(200, 16777215))
        self.mplfigs.setMinimumSize(QtCore.QSize(200, 0))
        self.mplfigs.setObjectName("mplfigs")
        self.horizontalLayout.addWidget(self.mplfigs)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 31))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

I have executed your code on Linux and I have observed the same memory leak. 我已经在Linux上执行了您的代码,并且发现了相同的内存泄漏。 Try with this: 试试这个:

Import the garbage collector with: 使用以下命令导入垃圾收集器:

import gc

Then modify your rmmppl method: 然后修改您的rmmppl方法:

def rmmppl(self):
    self.canvas.close()
    self.canvas.deleteLater()
    self.toolbar.close()
    self.toolbar.deleteLater()
    gc.collect()

Here is the documentation for deleteLater QObject Class Documentation 这是deleteLater QObject类文档的文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM