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.