Python API Comments¶
Introduction¶
Warning
These examples only work with VTK Version: 9.3.20240428 or greater.
VTK documentation regarding the new interface can be found here:
- Add properties for python wrappers
- Accept keyword arguments in constructor for python wrapped VTK classes
- Added new Python API for connecting pipelines
The following sections provide you with further comments and links to examples illustrating the new interface.
Initializing a VTK Class¶
You can initialize the properties of a wrapped VTK class by specifying keyword arguments in the constructor.
Example Name | Comments | Image |
---|---|---|
WarpCombustor | Note that in the initialization of vtkMultiBlockPLOT3DReader , SetFileName is an alias for SetXYZFileName so you can use file_name instead of xyz_file_name . | |
ParametricKuenDemo | Very useful in regards to the intitialization of vtkSliderRepresentation2D in make_slider_widget(...) . | |
Set/Get Properties of a VTK Class¶
Instead of SetSomeProperty()
or GetSomeProperty()
you can just just drop the Set or Get prefix and use the snake case version of the suffix: some_property
.
print(
f'Cylinder properties:\n height: {cylinder.height}, radius: {cylinder.radius},'
f' center: {cylinder.center} resolution: {cylinder.resolution} capping: {cylinder.capping == 1}')
Generally if a VTK class has a Set or Get method then converting the name to snake case will give you the relevant property e.g. for SetResolution()
or GetResolution()
the wrapped property will be resolution
.
However:
ca.GetProperty().SetColor(colors.GetColor3d('Tomato'))
becomes:
ca.property.color=colors.GetColor3d('Tomato')
In the case of GetColor3d()
a vtkColor3d
class is returned not a vector, tuple or a simple variable.
Multiple connections in a pipeline¶
A pipeline can have multiple connections.
(a, b, c) >> d >> e >> f >> g
E.g.
[vtk.vtkSphereSource(), vtk.vtkSphereSource()] >> vtk.vtkAppendFilter()
or
a = vtk.vtkAppendFilter()
vtk.vtkSphereSource() >> a
vtk.vtkSphereSource() >> a
To reset the append filter, you can use any one of these commands:
a.RemoveAllInputConnections(0)
None >> a
[] >> a
() >> a
Note: None >> a
can also be used to clear any inputs on the filter a
, whether they are multiple connections or not.
Example Name | Comments | Image |
---|---|---|
WarpCombustor | Three planes are added to a vtkAppendPolyData filter. | |
Multiple outputs from a pipeline¶
A pipeline can produce multiple outputs. Here, p is a tuple of vtkDataObjects, for example: p: tuple(vtkDataObject, ..., vtkDataObject)
. Subsequent pipelines can access the individual elements of the tuple.
p = (a >> b >> c).update().output
p1 = vtkSomeClass(input_data=p[0]) >> e >> f
p1 = vtkAnotherClass(input_data=p[1]) >> g >> h
Example Name | Comments | Image |
---|---|---|
SolidClip | The tuple clipper contains the clipped output as the first element and clipped away output as the second element. | |
Selecting ports in a pipeline¶
Ports in a pipeline can be selected using the function select_ports()
. See: vtkmodules.util.execution_model
This function is accessed as follows:
from vtkmodules.util.execution_model import select_ports
The possibilities are:
- select_ports(input_port, algorithm)
- select_ports(algorithm, output_port)
- select_ports(input_port, algorithm, output_port)
So in VisualizeDirectedGraph instead of writing:
arrow_glyph.SetInputConnection(0, graph_to_poly.GetOutputPort(1))
arrow_glyph.SetInputConnection(1, arrow_source.GetOutputPort())
you can write:
select_ports(graph_to_poly, 1) >> arrow_glyph
arrow_source >> select_ports(1, arrow_glyph)
In PBR_Skybox_Anisotropy instead of writing:
cube_map.SetInputConnection(i, flipped_images[i].GetOutputPort())
you can write:
flipped_images[i] >> select_ports(i, cube_map)
Example Name | Comments | Image |
---|---|---|
BoxClipStructuredPoints | Here, port 0 of box_clip is connected to mapper_in and port 1 of box_clip is connected to mapper_out . | |
PBR_Skybox_Anisotropy | Here we use select_ports() in a loop to add images to a cube map as documented above. | |
VisualizeDirectedGraph | In this case, as documented above, select_ports() is used for both input and output. | |
Updating part of a pipeline¶
Sometimes we need to update part of a pipeline so that output can be used in other pipelines.
a >> b >> c >> d
c.update() # At this point, a and b will also be updated.
# Use some data from c to build a new object, say v.
...
# Then:
v >> w >> x
Example Name | Comments | Image |
---|---|---|
LineOnMesh | One pipeline creates a smoothed dataset. However we need to update smooth_loop in the pipeline so that vtkCellLocator finds cells in order to create the spline. | |
MeshLabelImageColor | We need the smoother error for the scalar range in the mapper. So we create the pipeline and update smoother to get the needed scalar range. Of course, all other pipeline elements feeding into smoother will be updated also. | |
PineRootDecimation | We update the pipeline in this line: (reader >> deci >> connect >> iso_mapper).update() . This allows us to output counts of triangles all in one place. | |
Reusing a pipeline¶
Pipelines can be reused.
# The pipeline to reuse.
p = (a >> b >> c)
# Sources for the pipeline.
s1 = d
s2 = e
# Use the pipeline in a functional way.
p1 = p(s1())
p2 = p(s2())
Example Name | Comments | Image |
---|---|---|
PipelineReuse | Here we use the pipeline in a functional way. This allows us to reuse the pipeline, here, p(cone()) returns a data object so any changes to the pipeline afterward would not be automatically propagated to the rendering pipeline. Finally, we use an append filter to combine the cone and cylinder. | |
Grouping pipelines¶
Grouping the pipelines into code blocks may improve the readability of the code.
Example Name | Comments | Image |
---|---|---|
EnhanceEdges | The pipelines are grouped into a single code block. This may make understanding the code easier. | |
How to handle #defines using data classes¶
This example, CurvaturesNormalsElevations, is relatively complex in that a single source feeds into two functions generate_gaussian_curvatures(...)
and generate_mean_curvatures(...)
returning filters, scalar ranges of curvatures and elevation along with the lookup tables. Additionally a text widget and scalar bar widgets are positioned into two viewports. It nicely demonstrates the usage of data classes.
We can initialize nearly all properties of a wrapped VTK class by specifying keyword arguments in the constructor. There are no issues if the properties are True or False or an existing variable or an enum (which is wrapped in Python) e.g.:
color_series = vtkColorSeries(color_scheme=vtkColorSeries.BREWER_QUALITATIVE_SET3)
However, a lot of Set/Get functions in the VTK classes use values defined as #define VTK_SOME_CONSTANT x
. Many of these are defined in the vtkCommonCore module, where they were defined as preprocessor macros. So you can usually access most of them this way or use a data class in Python 3.7 or later.
The real advantage of the data class approach is that the defined VTK constants are used instead of meaningless integers or other values and you don't need to search through the various header files to find the definitions.
Example Name | Comments | Image |
---|---|---|
VTKDataClasses | This snippet defines immutable data classes that can be used in the initialization of VTK classes or to replace the Set/Get functions that set and get these constants. | |
CurvaturesNormalsElevations | A lot of immutable data classes are used in this example | |
Python functions and pipelines¶
A Python function returning a VTK object can be used as the first element of a pipeline.
- If the function returns
None
, then the pipeline will never be implemented.
read_poly_data(pth) >> mapper
Try this example with a .csv
file to see what happens.
Example Name | Comments | Image |
---|---|---|
ReadAllPolyDataTypesDemo | read_poly_data(pth) returns polydata that is fed directly into the mapper. | |
Pass by Reference¶
Some VTK methods methods use pass-by-reference to return values back to the caller. Calling these methods from Python requires special consideration, since Python’s str
, tuple
, int
, and float
types are immutable.
A reference type is provided via:
from vtkmodules.vtkCommonCore import reference
This is a simple container allowing pass-by-reference.
Example Name | Comments | Image |
---|---|---|
DistancePointToLine | Here, we need to pass t as a reference to [vtkLine](https://www.vtk.org/doc/nightly/html/classvtkLine.html).DistanceToLine . |
Snippets¶
Here are some code snippets that can be directly dropped into your code.
Python hints¶
Make a tuple if you have a starred expression¶
In MultiplePlots we had an expression:
points.SetColor(*colors.GetColor4ub('Black'))
This can be rewritten as a tuple, using brackets and a comma:
points.color = (*colors.GetColor4ub('Black'),)
or, even better, by using a tuple constructor to make it more obvious:
points.color = tuple(colors.GetColor4ub('Black'))