Mayavi integrated into a PyQt not freeing up Memory when displaying multiple Plots after one another

49 views Asked by At

Im building an application to display relatively simple mechanical models mostly consisting of points using mayavi integrated into a PyQt application. Data is input into QTables and on a button press handed over to do mlab calls.

The problem arises when drawing a different model, as it seems the memory used by Mayavi is not being freed up. The problem does not arise when i call the function without using the mlab part. I've built a working example, structured similarly to my project. Simply press the button several times and watch RAM usage rise using the Task Manager or something similar. The effect also shows when using memory profilers.

from PyQt5.QtWidgets import *

from mayavi import mlab
import numpy as np

import sys
import os

os.environ['ETS_TOOLKIT'] = 'qt4'

from pyface.qt import QtGui

from traits.api import HasTraits, Instance
from traitsui.api import View, Item
from mayavi.core.ui.api import MayaviScene, MlabSceneModel, \
        SceneEditor


class Visualization(HasTraits):
    scene = Instance(MlabSceneModel, ())
    # the layout of the dialog screated
    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=250, width=300, show_label=False),
                resizable=True # We need this to resize with the parent widget
                )


class MayaviQWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        layout = QtGui.QVBoxLayout(self)
        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)
        self.visualization = Visualization()

        # The edit_traits call will generate the widget to embed.
        self.ui = self.visualization.edit_traits(parent=self,
                                                 kind='subpanel').control
        layout.addWidget(self.ui)
        self.ui.setParent(self)


class modell(object):
    def __init__(self,pointsIN):
        self.MakePoints(pointsIN)

    def MakePoints(self,pointsIN):
        self.points = [None] * len(np.array(pointsIN))
        for i in range(np.shape(pointsIN)[0]):
            self.points[i]=Point(pointsIN[i,0],
                                 pointsIN[i,1],
                                 pointsIN[i,2])


class Point(object):
    def __init__(self,ID, x, y):
        self.ID = ID
        self.x = x
        self.y = y


class GUI(object):
    def __init__(self,m):
        self.modell = m

    def draw_points(self):
        m = self.modell
        for k in m.points:
            id = k.ID
            x = k.x
            y = k.y
            z=0
            mlab.points3d(x,y,z,scale_factor=0.5,color=(1,1,1))
            mlab.text3d(x,y,z,str(id),scale=0.5)


class MyButtonWidget(QWidget):
    def __init__(self):
        super().__init__()

        # Create a QPushButton
        self.button = QPushButton('Make Random Points appear!')

        # Connect the button click event to a custom method
        self.button.clicked.connect(self.on_button_click)

        # Create a layout and set it for the button widget
        layout = QVBoxLayout()
        layout.addWidget(self.button)
        self.setLayout(layout)

    def on_button_click(self):
        # This method is called when the button is clicked
        mlab.clf()
        rows=5
        columns=3
        random_matrix = np.random.randint(0, 10, size=(rows, columns))
        self.mod=modell(random_matrix)
        gui = GUI(self.mod)
        gui.draw_points()


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        # Create an instance of the MyButtonWidget
        self.button_widget = MyButtonWidget()
        self.mayavi_widget = MayaviQWidget()

        # Create a layout for the main window and add the button widget to it
        layout = QVBoxLayout()
        layout.addWidget(self.button_widget)
        layout.addWidget(self.mayavi_widget)
        self.setLayout(layout)

        # Set up the main window
        self.setWindowTitle('Mayavi Memory Leak Testing')
        self.setGeometry(100, 100, 400, 200)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


I tried using gc.collect() or mlab.close(), but even closing the figure doesn't free up memory and leads to other unintended behaviour.

0

There are 0 answers