I have created a class that encapsulates the creation of:
- a
vtkImageData
referred to here as “mesh
”, - a lookup table,
- a poly data set (from
vtkImageDataGeometryFilter
), - a mapper (using
vtkPolyDataMapper
), that associates the lookup table to it, with appropriate ranges, - an actor,
- a renderer,
- a render window, referred to here as “
renderWindow
”, - and a render window interactor, referred to as “
renderWindowInteractor
”,
The constructer finishes with a call to renderWindowInteractor.Initialize()
.
All of these objects are instance attributes of the class. I’ve skipped a few method calls to try to keep the question succint.
When instantiated, the user must input the initial scalar values of the cells using a numpy array referred to as “values
”. An instance attribute called (self.
) values
is generated. A shallow copy of this numpy array is then taken using numpy_to_vtk
(from vtk.utils.numpy_support
) yielding a corresponding vtk array referred to as “values_vtk
”, which is also an instance attribute of the class. The scalars of the cell data of the vtkImageData object (self.
) mesh
is set to this vtk array.
The real issue I am having is when updating the vtkImageData
of the object when new cell data scalar values
are passed.
I created a method as def update(self, values)
that:
self.values = values
- Re-assignes
self.values_vtk
to be equal to the vtk equivalent value of this newself.values
array. -
self.mesh.GetCellData().SetScalars(self.values_vtk)
to updatemesh
. -
self.renderWindow.Render()
to refresh the window.
Firstly, I would like to know if steps 1 to 3 are okay, performance-wise, in order to update the cell scalar values of an ImageData. I had initially thought that just by changing self.values_vtk
(i.e. only step 2 without step 3), the ImageData would automatically change (maybe after a call to .Modified()
), but that was not the case in my tests.
Lastly, and most importantly, step 4 is not working - the window does not refresh with the changed ImageData cell scalar values. Or rather, it does so only a couple of times and then stops. When using PyCharm, if I begin the process in debug mode and set a breakpoint anywhere in the code after calling .update()
, then the window consistently updates the visualization as desired, when the execution reaches the breakpoint. I tried using time.pause(0.001)
, but this did not work. I also tried calling self.renderWindowInteractor.ReInitialize()
after step 5 but this just crashes the program.
Edit: the written class can be found attached below.
vtkVisualizationPackage.py (11.9 KB)
Edit 2: fixed something in the attached code related to the spacing
input.
I am starting to believe the issue has something to do with Windows. In the following MWE, the first call, of quickly changing renders works fine. But the second one stops. I am in Windows 10.
import numpy as np
from vtkVisualizationPackage import VTKImageData2DTopology
import time
def main(n_iter=100, sleep_time=0.01):
nels = [20, 10]
design_domain = [1., 0.5]
types_of_els = 3
n_tot_els = nels[0]*nels[1]
values = np.zeros(n_tot_els, dtype=np.int64)
values[1::3] = 1
values[2::3] = 2
imageObject = VTKImageData2DTopology(nels=nels, number_of_vals=types_of_els,
values=values, domain_size=design_domain)
imageObject.zoom_camera(1.66)
for i in range(n_iter):
if (i % 3) == 0:
values = np.ones(n_tot_els, dtype=np.int64)*2
values[1::3] = 0
values[2::3] = 1
elif (i % 3) == 1:
values = np.ones(n_tot_els, dtype=np.int64)
values[1::3] = 2
values[2::3] = 0
else:
values = np.zeros(n_tot_els, dtype=np.int64)
values[1::3] = 1
values[2::3] = 2
imageObject.update(values)
time.sleep(sleep_time)
if __name__ == "__main__":
main(sleep_time=0.01)
print(f"Finished first animation")
main(n_iter=30, sleep_time=0.3)
print(f"Finished second animation")
Edit 3: It seems that a quick and dirty fix would be to make the window interactor .Start()
or after updating and then somehow trigger a Q
click event automatically. Which is not ideal… Or maybe a method that makes the window interactive for a milisecond, after calling the .Render()
method.
Edit 4: Edit 3 has been accomplished using renderWindowInteractor.ProcessEvents()
. But I hope that there is a better way to accomplish this.