FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

remove image with colorbar won't release the memory consumption? · Issue #22040 · matplotlib/matplotlib · GitHub

Repository navigation

remove image with colorbar won't release the memory consumption? #22040

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

  1. tacaswell commented on Dec 23, 2021

    Member

    Does adding explict calls to gc.collect() help?

    The del calls in removeImage are 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_events as you are in side of a qapp so the event loop is already running.

    Can you reproduce the leak not in a Qt application?

  2. bactone commented on Dec 23, 2021

    Author

    @tacaswell
    thanks, i have tried your suggection.

    1. add gc.collect() works, but i have to keep del color_bar and del image in removeImage
  3. tacaswell commented on Dec 23, 2021

    Member

    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?

  4. anntzer commented on Aug 31, 2023

    Contributor

    Seems reasonable to close, per the above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL