Repository navigation
Closed
Labels
Description
Problem
I am trying to remove an image from a figure and release the memory. when colorbar is not added for the image, memory is released when image is removed, however, if colorbar is added, it fails, please try the follow demo-code:
import numpy as np
from PyQt5 import QtWidgets
import matplotlib
from matplotlib.figure import Figure
import matplotlib.cm as cm
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.axes._axes import Axes
matplotlib.use("Qt5Agg")
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axe = self.fig.add_subplot(1, 1, 1, label='good')
super().__init__(self.fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
self.axe = self.canvas.axe
layout.addWidget(self.canvas)
self.pushButton_addColorBar = QtWidgets.QPushButton('Add ColorBars')
layout.addWidget(self.pushButton_addColorBar)
self.pushButton_remove = QtWidgets.QPushButton('remove')
layout.addWidget(self.pushButton_remove)
# Create a placeholder widget to hold our toolbar and canvas.
widget = QtWidgets.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.pushButton_remove.clicked.connect(self.removeImage)
self.pushButton_addColorBar.clicked.connect(self.createColorBars)
self.pcolormesh_test()
def pcolormesh_test(self):
"""add two images"""
delta = 0.01
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
im = self.axe.pcolormesh(X, Y, Z, cmap=cm.viridis, shading='auto') # , extent=[-3, 3, -3, 3]
im.set_clim(vmax=np.amax(Z), vmin=np.amin(Z))
Zx = (Z1 + Z2) * 2
imx = self.axe.pcolormesh(X, Y, Zx, cmap=cm.Blues, shading='auto') # , extent=[-3, 3, -3, 3], , cmap=mpl.cm.Blues
imx.set_clim(vmax=np.amax(Zx), vmin=np.amin(Zx))
def createColorBars(self):
""" to create a color bar for an image. """
axe = self.axe
fig = axe.get_figure()
images = self.getImages(axe)
for image in images:
if not image.colorbar: # color bar doesn't exist
inset_axe = axe.inset_axes([1.115, 0, 0.05, 1], transform=axe.transAxes) # [x0, y0, width, height]
fig.colorbar(image, ax=axe, cax=inset_axe)
break # each trigger create one colorbar for one image
self.reDraw()
def removeImage(self):
"""
Usage:
* each trigger remove one image
"""
images = self.getImages(self.axe)
print(f'images={images}')
if images:
image = images[-1]
color_bar = image.colorbar
if color_bar:
color_bar.remove()
del color_bar
# remove image
image.remove() # remove line from the list of ax.lines
del image # remove the line from the memory if no other variable reference to the line
self.reDraw()
def getImages(self, axe: Axes):
"""to obtain the image list in the axe"""
images = []
images.extend(axe.images)
images.extend(axe.collections)
return images
def reDraw(self):
self.canvas.draw_idle()
self.canvas.flush_events()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())Proposed solution
No response
Activity
tacaswell commentedon Dec 23, 2021
Does adding explict calls to
gc.collect()help?The
delcalls inremoveImageare mostly redundant (they drop the reference in the local namespace, but that would also happen on return from the method.You should also not need
flush_eventsas you are in side of a qapp so the event loop is already running.Can you reproduce the leak not in a Qt application?
bactone commentedon Dec 23, 2021
@tacaswell
thanks, i have tried your suggection.
gc.collect()works, but i have to keepdel color_baranddel imageinremoveImagetacaswell commentedon Dec 23, 2021
That makes sense, otherwise the local name will be the hard-ref that keeps it alive!
Can we close this as I think the issues is the timing of when the cyclic gc runs, not an actual memory leak?
anntzer commentedon Aug 31, 2023
Seems reasonable to close, per the above.