Customize Python View with Python plugins

This is an example of how to customize dynamically your Python View by adding parameters in ParaView linked to your Python View through the use of a custom Python Plugin (adding a Python Filter).

In this example, we will manipulate the number of bins of an histogram from our Python View. You may find the full example with the data below.
python_view_scripting.zip (72.8 KB)

Python Plugin

Creating the Python Filter

What you will want to do to add parameters to your Python View is to first create a Python Plugin that you will load in ParaView to get your custom Python Filter. For this, go ahead and create a new Python file declaring a new filter Python Filter with its base structure.

from paraview.util.vtkAlgorithm import *
from vtkmodules.numpy_interface import dataset_adapter as dsa
from vtkmodules.vtkCommonDataModel import vtkDataSet

@smproxy.filter(name="PythonViewHelperFilter")
@smproperty.input(name="Input")
@smdomain.datatype(dataTypes=["vtkDataSet"], composite_data_supported=False)
class PythonViewHelperFilter(VTKPythonAlgorithmBase):
    def __init__(self):
        super().__init__(nInputPorts=1, nOutputPorts=1, inputType="vtkDataSet", outputType="vtkDataSet")
        self._arrayName = None
        self._arrayValue = 0


    def RequestDataObject(self, request, inInfo, outInfo):
        inData = self.GetInputData(inInfo, 0, 0)
        outData = self.GetOutputData(outInfo, 0)
        assert inData is not None
        if outData is None or (not outData.IsA(inData.GetClassName())):
            outData = inData.NewInstance()
            outInfo.GetInformationObject(0).Set(outData.DATA_OBJECT(), outData)
        return super().RequestDataObject(request, inInfo, outInfo)

    def RequestData(self, request, inInfo, outInfo):
        input = dsa.WrapDataObject(vtkDataSet.GetData(inInfo[0]))
        output = dsa.WrapDataObject(self.GetOutputData(outInfo, 0))

        output.ShallowCopy(input.VTKObject)

        return 1

Here we are declaring a simple filter that takes a vtkDataSet and returns the exact same input. You can see that I added two parameters that we will want to manipulate when applying this filter. But for now, they don’t appear in ParaView.

python_filter_no_params

Let’s add the necessary to make them editable from ParaView before to explain their use.

    @smproperty.stringvector(name="ArrayName", default_values="bins")
    def SetArrayName(self, fname):
        if self._arrayName != fname:
            self._arrayName = fname
            self.Modified()


    @smproperty.intvector(name="ArrayValue", default_values=32)
    def SetArrayValue(self, value):
        if self._arrayValue != value:
            self._arrayValue = value
            self.Modified()

Now, we have two parameters we can modify inside ParaView. The first one is optional, it’s the name of the array we will create to pass our parameters to the Python View. It can be useful to expose it though in order to allow quick modification in the Python View script. The second parameter is the actual value we want to pass to the Python View, in this case the number of bins of an histogram.

python_filter_params

The last thing needed to make it work is adding the logic in the RequestData method. Using numpy interface, it’s easy to add tables to the output. Simply add the following before the return.

        output.PointData.append(self._arrayValue, self._arrayName)

Using the plugin

To use our freshly created plugin in ParaView, we have to import it. For this, open ParaView and click on Tools / Manage Plugins... on the menu bar. From there, you can Load New... and look for your Python file.

You now have loaded you Python Plugin. You can apply it on your data. Here, I will apply it on the blow.vtk dataset that you can find in ParaView Examples, or in the archive available at the top of the topic. After applying the filter, you should still see your dataset unchanged. Go in the Information panel and see your new Data Array being displayed.

python_filter_applied

Python View

The next step is to create our Python View. After applying your custom filter on your data, you may split the render screen and create a new Python View. Then, you can add your own script to the view. Here is an example of a simple Python View displaying an histogram of the displacement6 data array available on the given dataset.

from paraview import python_view
import paraview.vtk.util.numpy_support as ns
from vtkmodules.numpy_interface import dataset_adapter as dsa
from vtkmodules.vtkCommonDataModel import vtkDataSet

def setup_data(view):
  for i in range(view.GetNumberOfVisibleDataObjects()):
    view.DisableAllAttributeArrays()
    view.SetAttributeArrayStatus(i, dsa.ArrayAssociation.POINT, "displacement6", 1)


def render(view, width, height):
  figure = python_view.matplotlib_figure(width, height)
  figure.set_facecolor('white')

  plot = figure.add_subplot(2,1,1)
  plot.set_title('Displacement histogram')

  for i in range(view.GetNumberOfVisibleDataObjects()):
    dataObject = view.GetVisibleDataObjectForRendering(i)

    if not isinstance(dataObject, vtkDataSet):
      continue

    disp = dataObject.GetPointData().GetArray("displacement6")
    npDisp = ns.vtk_to_numpy(disp)

    nbBins = 32
    plot.hist(npDisp, bins=nbBins)

  vtk_image = python_view.figure_to_image(figure)
  return vtk_image

At this point, your Python View should look like this.

python_view_unchanged

An optional thing you can do for quick modifications and a better legibility is to declare at the top of your script a variable that will keep the name of your array. This will avoid repeating your array’s name in the script. Here I declare the name of the array right after the imports to be able to see it easily in the script text area.

BINS_ARRAY_NAME = "bins"

At this point, you should be able to make match names easily.

python_view_script

Lastly, we will make use of this variable and allows passing our data array to the Python View. First, setup your array in the setup_data method in the loop.

    view.SetAttributeArrayStatus(i, dsa.ArrayAssociation.POINT, BINS_ARRAY_NAME, 1)

And then, you can retrieve your information in the variable storing the number of bins.

    bins = dataObject.GetPointData().GetArray(BINS_ARRAY_NAME)
    if bins is not None:
      nbBins = bins.GetValue(0)

Congratulations! You just customized your Python View with a simple Python Plugin =)
You can now change the value in your filter and the change should appear in your Python View when you apply it. Try setting the number of bins to 4 and here is what you should see.

python_view_changed

Do not hesitate to download the archive to get the full code explained here. It comes with comments to guide you.

2 Likes