Python Programmable Filter
The python programmable filter is a general purpose filter that the end user can program within the paraview GUI to manipulate datasets as needed. To use the filter, turn the PARAVIEW_ENABLE_PYTHON cmake option on. This causes the make process to wrap paraview's classes into python callable format.
The filter is a wrapper around VTK's vtkProgrammableFilter class and adds to it:
- a string containing the user's script for the filter to execute
- an instance of the python interpreter with the wrapped paraview libraries imported
- the ability to easily change the output dataset type.
For more examples, see the ParaView Guide, section 12.4. [1]
Transform the input
When the user selects Programmable Filter from the Filters menu, an empty programmable filter is created. The default behavior of the empty script is to create a dataset of the same type as its input and to copy through the input dataset's structure. The GUI provides a selection menu where the user can choose from the five primary VTK dataset types for the output. The GUI also provides a text entry area where the user can type, edit or paste in a python script.
The following figure shows a python script that modifies the geometry of its input dataset.
This result is produced by the following python script set as the Script
<source lang="python"> pdi = self.GetPolyDataInput() pdo = self.GetPolyDataOutput() newPoints = vtk.vtkPoints() numPoints = pdi.GetNumberOfPoints() for i in range(0, numPoints): coord = pdi.GetPoint(i) x, y, z = coord[:3] x = x * 1 y = y * 1 z = 1 + z*0.3 newPoints.InsertPoint(i, x, y, z) pdo.SetPoints(newPoints) </source> |
Dealing with Composite Datasets
Readers such as Ensight, Exodus produce multiblock datasets. Here's the above squish script modified to work with multiblock datasets.
<source lang="python"> def flatten(input, output): # Copy the cells etc. output.ShallowCopy(input) newPoints = vtk.vtkPoints() numPoints = input.GetNumberOfPoints() for i in range(0, numPoints): coord = input.GetPoint(i) x, y, z = coord[:3] x = x * 1 y = y * 1 z = 10 + 0.1*z newPoints.InsertPoint(i, x, y, z) output.SetPoints(newPoints) input = self.GetInputDataObject(0, 0) output = self.GetOutputDataObject(0) if input.IsA("vtkMultiBlockDataSet"): output.CopyStructure(input) iter = input.NewIterator() iter.UnRegister(None) iter.InitTraversal() while not iter.IsDoneWithTraversal(): curInput = iter.GetCurrentDataObject() curOutput = curInput.NewInstance() curOutput.UnRegister(None) output.SetDataSet(iter, curOutput) flatten(curInput, curOutput) iter.GoToNextItem(); else: flatten(input, output) </source> |
Changing Data Type
Here we are taking in vtkPolyData and producing a vtkImageData from it. Note that the "Output Data Set Type" combo-box in the GUI has been changed to vtkImageData before hitting the Apply button.
The following figure shows a python script that produces an image data output with one cell per point in its input polygonal dataset.
<source lang="python">
pdi = self.GetInput() numPts = pdi.GetNumberOfPoints()
ido = self.GetOutput() ido.SetDimensions(numPts+1,2,2) ido.SetOrigin(-1,-1,-1) ido.SetSpacing(.1,.1,.1)
ido.SetExtent(0,numPts,0,1,0,1) ido.AllocateScalars(vtk.VTK_FLOAT,1)
ivals = pdi.GetPointData().GetScalars() ca = vtk.vtkFloatArray() ca.SetName(ivals.GetName()) ca.SetNumberOfComponents(1) ca.SetNumberOfTuples(numPts)
ido.GetCellData().AddArray(ca)
for i in range(0, numPts): ca.SetValue(i, ivals.GetValue(i))
from paraview import util pdi = self.GetInput() numPts = pdi.GetNumberOfPoints() util.SetOutputWholeExtent(self, [0,numPts,0,1,0,1]) </source> |
Generating Data (Programmable Source)
Along with processing data, python can be used to produce data i.e. act as a data source. Under the Sources menu, we have the Programmable Source which can be used to produce data using Python.
Here's an example that generates a helix. Similar to the programmable filter, the output type must be choosen using the "Output Data Set Type" combo-box before hitting Apply.
The following is a 'Programmable Source' example that generates a Helix curve.
<source lang="python">
import math numPts = 80 # Points along Helix length = 8.0 # Length of Helix rounds = 3.0 # Number of times around
pdo = self.GetPolyDataOutput()
newPts = vtk.vtkPoints() for i in range(0, numPts): #Generate the Points along the Helix x = i*length/numPts y = math.sin(i*rounds*2*math.pi/numPts) z = math.cos(i*rounds*2*math.pi/numPts) #Insert the Points into the vtkPoints object #The first parameter indicates the reference. #value for the point. Here we add them sequentially. #Note that the first point is at index 0 (not 1). newPts.InsertPoint(i, x,y,z)
pdo.SetPoints(newPts)
aPolyLine = vtk.vtkPolyLine()
aPolyLine.GetPointIds().SetNumberOfIds(numPts) for i in range(0,numPts): #Add the points to the line. The first value indicates #the order of the point on the line. The second value #is a reference to a point in a vtkPoints object. Depends #on the order that Points were added to vtkPoints object. #Note that this will not be associated with actual points #until it is added to a vtkPolyData object which holds a #vtkPoints object. aPolyLine.GetPointIds().SetId(i, i)
pdo.Allocate(1, 1)
pdo.InsertNextCell(aPolyLine.GetCellType(), aPolyLine.GetPointIds())
</source> |
Programmable Source: Double Helix
An example that draws a double helix with connecting lines (like DNA). Provides an example of using multiple drawing objects 'cells' in the same vtkPolyData output object. The 'Output Data Set Type' should be set of 'vtkPolyData'.
<source lang="python">
import math numPts = 80 # Points along each Helix length = 8.0 # Length of each Helix rounds = 3.0 # Number of times around phase_shift = math.pi/1.5 # Phase shift between Helixes
pdo = self.GetPolyDataOutput()
newPts = vtk.vtkPoints() for i in range(0, numPts): #Generate Points for first Helix x = i*length/numPts y = math.sin(i*rounds*2*math.pi/numPts) z = math.cos(i*rounds*2*math.pi/numPts) newPts.InsertPoint(i, x,y,z) #Generate Points for second Helix. Add a phase offset to y and z. y = math.sin(i*rounds*2*math.pi/numPts+phase_shift) z = math.cos(i*rounds*2*math.pi/numPts+phase_shift) #Offset Helix 2 pts by 'numPts' to keep separate from Helix 1 Pts newPts.InsertPoint(i+numPts, x,y,z)
pdo.SetPoints(newPts)
aPolyLine1 = vtk.vtkPolyLine() aPolyLine2 = vtk.vtkPolyLine()
aPolyLine1.GetPointIds().SetNumberOfIds(numPts) aPolyLine2.GetPointIds().SetNumberOfIds(numPts) for i in range(0,numPts): #First Helix - use the first set of points aPolyLine1.GetPointIds().SetId(i, i) #Second Helix - use the second set of points #(Offset the point reference by 'numPts'). aPolyLine2.GetPointIds().SetId(i,i+numPts)
links = range(0,numPts,3) pdo.Allocate(2+len(links), 1)
pdo.InsertNextCell(aPolyLine1.GetCellType(), aPolyLine1.GetPointIds()) pdo.InsertNextCell(aPolyLine2.GetCellType(), aPolyLine2.GetPointIds()) for i in links: #Add a line connecting the two Helixes. aLine = vtk.vtkLine() aLine.GetPointIds().SetId(0, i) aLine.GetPointIds().SetId(1, i+numPts) pdo.InsertNextCell(aLine.GetCellType(), aLine.GetPointIds()) </source> |
Examples
Here are some more examples of simple ParaView 3 python filters.
Paraview is built from VTK, and the python bindings for Paraview mirror the python bindings for VTK although the package names seem to be different. For example, there is a vtk.vtkLine in VTK which seems to behave the same as paraview.vtkLine in Paraview. While the online documentation for Paraview seems to be pretty limited, there is more extensive documentation with examples on the VTK documentation site. Examples are organized by class name and often include Python examples along with c++ and TCL examples:
The examples list is a little hard to navigate. Perhaps an easier way to find examples is to go to the class list, look up the class you are interested in, and click on the class name which brings up the documentation for that class. Many of the class pages include an 'examples' section that links to code examples for the class. From the examples page you can choose the 'Python' example associated with the particular class (not every class has a Python example). The VTK class index can be found here:
NOTE: You may not be able to directly copy the examples from the VTK documentation because the package names may not match Paraview package names, but with a little modification I have been able to get examples from VTK to work in Paraview.
Add Colors to a PolyData
<source lang="python"> input = self.GetPolyDataInput(); output = self.GetPolyDataOutput();
colors = vtk.vtkUnsignedCharArray(); colors.SetNumberOfComponents(3); colors.SetName("Colors");
numPoints = input.GetNumberOfPoints() for i in xrange(0, numPoints):
colors.InsertNextTuple3(255,0,0);
output.GetPointData().AddArray(colors) del colors </source>
Notes
- don't forget to un-check the map scalars display panel properties check box.
Center Data
<source lang="python"> input = self.GetPolyDataInput() output = self.GetPolyDataOutput()
numPoints = input.GetNumberOfPoints() sumx = 0 sumy = 0 sumz = 0 for i in xrange(0, numPoints):
coord = input.GetPoint(i) x, y, z = coord[:3] sumx = sumx + x sumy = sumy + y sumz = sumz + z
tx = -sumx/numPoints ty = -sumy/numPoints tz = -sumz/numPoints
- note: thanks to pipeline improvements in VTK 6
- shallow copy of input is not needed for PV version >= 4.0
ic = input.NewInstance() ic.ShallowCopy(input)
transform = vtk.vtkTransform() transform.Translate(tx,ty,tz)
transformFilter=vtk.vtkTransformPolyDataFilter() transformFilter.SetTransform(transform) transformFilter.SetInputData(ic) transformFilter.Update()
output.ShallowCopy(transformFilter.GetOutputPort(0)) </source>
Center Data using numpy
<source lang="python"> from paraview.vtk import dataset_adapter as DA
pdi = self.GetInputDataObject(0,0) pdo = self.GetOutputDataObject(0) pdo.CopyAttributes(pdi)
old_pts = inputs[0].Points
new_pts = old_pts - mean(old_pts,axis=0)
arr = DA.numpyTovtkDataArray(new_pts, 'newpts')
pdo.GetPoints().SetData(arr)
</source>