1

Trying to update the grid data with a new scalar I am not able to update the plot

I have reported the issues to PyVista support as well: https://github.com/pyvista/pyvista-support/issues/501

I am able to have a figure update by using this example: https://github.com/pyvista/pyvista-support/issues/68

My example though does not work

Example code below:

import pyvista as pv
import pyvistaqt as pvqt
import numpy as np
import time

cmap='viridis'    
nx,ny,nz = 60, 40, 42
nc = nx*ny*nz

np.random.seed(0)
inidata = np.random.randint(1, 100, nc)

# generate random data for updating
data = [np.random.randint(1, 1000, nc), 
        np.random.randint(1, 1e4, nc),
        np.random.randint(1, 1e5, nc),
        np.random.randint(1, 1e6, nc)]

gridata = np.ones((nx,ny,nz))

mesh = pv.UniformGrid()
mesh.dimensions = np.array(gridata.shape) + 1 


mesh.origin = (0, 0, 0)  # The bottom left corner of the data set
mesh.spacing = (30, 30, 2.5)  # These are the cell sizes along each axis

mesh.cell_arrays["Data"] = inidata

plotter = pvqt.BackgroundPlotter()

# Add slices
xslice = mesh.slice(normal='x')
yslice = mesh.slice(normal='y')
zslice = mesh.slice(normal='z')
rslice = mesh.slice(normal=[1,1,0])

# Plot
plotter.add_mesh(mesh.outline(), color="k")
plotter.add_mesh(xslice, cmap=cmap)
plotter.add_mesh(yslice, cmap=cmap)
plotter.add_mesh(zslice, cmap=cmap)
plotter.add_mesh(rslice, cmap=cmap)

def update():
    for dat in data:
        plotter.update_scalars(dat, mesh=mesh)
        time.sleep(1)
        plotter.update()
        
plotter.add_callback(update, interval=100)
Red Sparrow
  • 387
  • 1
  • 5
  • 17
  • I've seen your [issue](https://github.com/pyvista/pyvista-support/issues/501) on GitHub. Did you manage to find anything useful? – MrCheatak Sep 15 '21 at 21:29
  • Nothing more than what you see there. For now this seems stuck for me. I can update the scalars but this does not behave as expected. Some update takes place but it is not consistent. Some times I have the last dataset loaded, sometimes one of the middle ones. – Red Sparrow Sep 17 '21 at 15:05
  • Same problem, so far im using a workaround: clearing the scene, adding updated mesh and redrawing. Looks more like a slideshow, but it's updating. – MrCheatak Sep 17 '21 at 19:14
  • @MrCheatak do you mind posting your example code here? Would be curious to try out your solution – Red Sparrow Oct 01 '21 at 13:47

2 Answers2

3

PyVista allows updating scene via setting new scalars to the loaded mesh. To do that, new data should be passed to the plotter:

import numpy as np
import pyvista as pv

# Creating random data
N = 100
data = np.zeros((1, N, N))
data[:] = np.random.randint(0, 10000, data.shape)

# Creating a mesh from our data
g = pv.UniformGrid()
g.dimensions = np.array(data.shape) + 1
g.spacing = (10, 10, 10)
g.cell_data['data'] = data.flatten()
#Careful with threshold as it will turn your data into UnstructuredGrid
#g = g.threshold([0.0001, int(data.max())])

# Creating scene and loading the mesh
p = pv.Plotter()
p.add_mesh(g, opacity=0.5, name='data', cmap='gist_ncar')
p.show(interactive_update=True)

# Animation
for i in range(5, 1000):
    # Updating our data
    data[:] = np.random.randint(0, 10000, data.shape)
    # Updating scalars
    p.update_scalars(data.flatten())
    #p.mesh['data'] = data.flatten() # setting data to the specified mesh is also possible
    # Redrawing
    p.update()

Although, the shape (essentially the number of cells or points) of the data must stay the same. This means that if data array size changes or data filtered through threshold changes it's number of cells, the loaded mesh will reject it.

A workaround is basically to load a new mesh into the Plotter every time your data is updated.

Swap #Animation section with this snippet and the plane will grow some volume:

# Animation
for i in range(5, 1000):
    # Updating our data
    data = np.full((i, N, N),0)
    data[:] = np.random.randint(0,1000000, data.shape)
    
    # Recreating the mesh
    g = pv.UniformGrid()
    g.dimensions = np.array(data.shape) + 1
    g.spacing = (10, 10, 10)
    g.cell_data['data'] = data.flatten()

    # Reloading the mesh to the scene
    p.clear()
    p.add_mesh(g, opacity=0.5, name='data')

    # Redrawing
    p.update()

Note that scene is interactive only when its updating, so lowering update frequency will make scene pretty laggy

MrCheatak
  • 159
  • 7
0

In my opinion, we should place the sections of #"Add Slices" and "#Plot" inside of the function update() to refresh the slices.

However, the scalar_bar_range will not update if the data range is smaller than the scalar_bar_range even though we force to update by plotter.update_scalar_bar_range.

Therefore we have to refresh scalar_bar_range manually.

def update():
    for dat in data:
        # Add slices
        xslice = mesh.slice(normal='x')
        yslice = mesh.slice(normal='y')
        zslice = mesh.slice(normal='z')
        rslice = mesh.slice(normal=[1,1,0])

        # Plot
        plotter.add_mesh(mesh.outline(), color="k")
        plotter.add_mesh(xslice, cmap=cmap)
        plotter.add_mesh(yslice, cmap=cmap)
        plotter.add_mesh(zslice, cmap=cmap)
        plotter.add_mesh(rslice, cmap=cmap)
        time.sleep(1)
    plotter.scalar_bars._scalar_bar_ranges = {mesh.active_scalars_name: [0, 0]}
  • Welcome to Stack Overflow. Please edit your answer and explain how it answers the specific question being asked. See How to Answer. https://stackoverflow.com/help/how-to-answer – cards Sep 26 '21 at 12:17
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 26 '21 at 12:19