What about type hints? The ITK pythonic api is generated dynamically and IDEs (vscode) have difficulty doing auto completion. This makes it nice to read but hard to write.
Do you see the properties/named arguments to the constructor in the help/dir? I mean if you print dir(some_algorithm) or help(some_algorithm).
Yes. VTK has a script to auto-generate interface files that help IDEs and language servers with type hints. The .pyi files are packaged in the python wheels, so you should already have them. The type hints are auto generated for the new property names too!
Done, I like it! It also looks much better and makes more sense, see here: PythonicAPI on my test site. Iâll do a MR anyway, see MR 322. This means everything should be in place for upgrading/creating examples when it happens.
@ben.boeckel I was uneasy about using Python1 - it had occurred to me that one day there may be a Python 4!
Itâs too bad that you canât do source.filtera().filter(), as that feels the most natural and similar to other âchainingâ Python APIs.
While I love the idea of a function that calls .Update() and then returns the output (.GetOutput()), the word âexecuteâ doesnât convey that concept to me. It sounds like the filter is being run, but thereâs no connotation of anything being returned by an âexecuteâ method. What about .generate()? Or maybe .get_result()?
Itâs too bad that you canât do source.filtera().filter() , as that feels the most natural and similar to other âchainingâ Python APIs.
Not feasible with > 1000 algorithms Also, chaining is not as old as people might think. numpy doesnât let you chain ufuncs for example (while PyTorch lets you chain functions).
While I love the idea of a function that calls .Update() and then returns the output (.GetOutput() ), the word âexecuteâ doesnât convey that concept to me. It sounds like the filter is being run, but thereâs no connotation of anything being returned by an âexecuteâ method. What about .generate() ? Or maybe .get_result() ?
I hear you. I like generate(). Another option would be to use the __call__() method. That would look like vtkContourFilter(input=foo, contour=[10, 20])(). We could also go a more functional route, like:
result = apply(vtkContour(contours=[10, 20]) >> vtkShrink(), input_data)
For full pipelines, this could look like
result = apply(vtkSomeReader(file_name="...") >> vtkContour(contours=[10, 20]) >> vtkShrink())
The rshift operator overload, when combined with initializing properties through constructor allows for very readable code!
If you had to reproduce testCaseManyAlgorithmsWithUserInput in TestAlgorithmNumberProtocol in VTK python right now, oneâd have to write a function, instantiate every one of those filters, name the instances and setup connections. In the new code, itâs much more readable and easier to code and customize.
Just for the sake of a visual comparison, here they are:
Future
Lines of code: 12
# A pipeline object can be reused with different input data objects.
pipeline = (
vtkElevationFilter()
>> vtkShrinkFilter()
>> vtkGeometryFilter()
>> vtkPolyDataConnectivityFilter(color_regions=True, extraction_mode=VTK_EXTRACT_ALL_REGIONS)
>> vtkPolyDataNormals()
)
cone = vtkConeSource(radius=5, resolution=8, height=2).execute()
print(pipeline.execute(cone))
cylinder = vtkCylinderSource(radius=6, resolution=9, height=3).execute()
print(pipeline.execute(cylinder))
Another benefit is that vtkAlgorithm(s) are constructed only the first time when the pipeline object is defined. Compare this with the below code snippet, where they are recreated. Of course, you could write a class and store references to the algorithms, but thatâs just more complicated and annoying to do in the interpreter.
It should also be way easier to mix and match different pipelines. I expect it would be less exhausting to prototype and develop custom modeling applications on top of VTK.
Doesnât that need to be read from right-to-left? I see your point of view because itâs easier to associate pipeline with the last filter in the chain i.e, normals filter.
You can still read left to right.
I read the vtkPolyDataNormals filter input depends on vtkPolyDataConnectivityFilter output. If Iâm interested in where the poly data filter gets its input I read further to the right.
Iâm not a Python expert but it seems more logical to me.
I prefer left to right: â>>â since we read left to right and pipes â|â in bash work left to right. For me: âa >> bâ means do âaâ first then feed the result into âbâ, it also makes it easier to just add " >> c" at the end, if âcâ is a new filter.
We made some good progress with the wrapped properties and the pipeline connection with >>. See this gist for some examples:
If there are any suggestions for an alternative to execute(), I am all ears. My preference is execute since I have bee using that term for >20 years with VTK.