Using InteractiveLine widget in Python plugin

I’m trying to write a Python plugin with a line, defined by two points, as one of its inputs. If possible, I would like to use ParaView’s InteractiveLine widget to select the points so the user can interactively pick the points and see the line drawn on the screen.

One way I thought to do this was as follows:

import numpy as np
import vtk
from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
from vtk.util.vtkAlgorithm import VTKPythonAlgorithmBase
from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain

@smproxy.filter()
@smproperty.input(name='LineLoads', port_index=0)
class LineLoads(VTKPythonAlgorithmBase):
    def __init__(self):
        VTKPythonAlgorithmBase.__init__(self, nInputPorts=1, nOutputPorts=1, outputType='vtkPolyData')
        self.AxisBegin = [0.0] * 3
        self.AxisEnd   = [0.0] * 3

    @smproperty.xml("""
      <DoubleVectorProperty name="AxisBegin"
        number_of_elements="3"
        default_values="0 0 0"
        command="SetAxisBegin" />
      <PropertyGroup panel_widget="InteractiveLine">
        <Property function="Point1WorldPosition" name="AxisBegin" />
        <Property function="Point2WorldPosition" name="AxisEnd" />
      </PropertyGroup>""")
    def SetAxisBegin(self, *val):
        if val != self.AxisBegin:
            self.AxisBegin = np.array(val[:3])
            self.Modified()
    def SetAxisEnd(self, *val):
        if val != self.AxisEnd:
            self.AxisEnd = np.array(val[:3])
            self.Modified()

    def RequestData(self, request, inVec, outVec):
        return 1

If I do that, I get a warning saying:

vtkSMPropertyGroup.cxx:194   WARN| vtkSMPropertyGroup (0x107d9b00): Failed to locate property 'AxisEnd' for PropertyGroup. Skipping.
critical: In unknown, line 0
critical: Missing required property for function 'Point2WorldPosition'.

So then I added the smproperty.xml decorator separately to the SetAxisBegin and SetAxisEnd functions:

    @smproperty.xml("""
      <DoubleVectorProperty name="AxisBegin"
        number_of_elements="3"
        default_values="0 0 0"
        command="SetAxisBegin" />
      <PropertyGroup panel_widget="InteractiveLine">
        <Property function="Point1WorldPosition" name="AxisBegin" />
      </PropertyGroup>""")
    def SetAxisBegin(self, *val):
        if val != self.AxisBegin:
            self.AxisBegin = np.array(val[:3])
            self.Modified()

    @smproperty.xml("""
      <DoubleVectorProperty name="AxisEnd"
        number_of_elements="3"
        default_values="0 0 0"
        command="SetAxisEnd" />
      <PropertyGroup panel_widget="InteractiveLine">
        <Property function="Point2WorldPosition" name="AxisEnd" />
      </PropertyGroup>""")
    def SetAxisEnd(self, *val):
        if val != self.AxisEnd:
            self.AxisEnd = np.array(val[:3])
            self.Modified()

This gives me a message that looks like:

critical: In unknown, line 0
critical: Missing required property for function 'Point2WorldPosition'.
critical: In unknown, line 0
critical: Missing required property for function 'Point1WorldPosition'.

Unsurprisingly, I also get two InteractiveLine widgets in the GUI. What’s the magic recipe here so that I get one line widget with Point1WorldPosition connected to my plugin’s AxisBegin and Point2WorldPosition connected to AxisEnd?

I figured it out. The key was to put the PropertyGroup XML stuff in the decorator on the SetAxisEnd method so it would be after the properties for AxisBegin and AxisEnd had already been defined.

    @smproperty.xml("""
      <DoubleVectorProperty name="AxisBegin"
        number_of_elements="3"
        default_values="0 0 0"
        command="SetAxisBegin" />""")
    def SetAxisBegin(self, *val):
        if any(val != self.AxisBegin):
            self.AxisBegin[:3] = val[:3]
            self.Modified()

    @smproperty.xml("""
      <DoubleVectorProperty name="AxisEnd"
        number_of_elements="3"
        default_values="0 0 0"
        command="SetAxisEnd" />
      <PropertyGroup panel_widget="InteractiveLine">
        <Property function="Point1WorldPosition" name="AxisBegin" />
        <Property function="Point2WorldPosition" name="AxisEnd" />
      </PropertyGroup>""")
    def SetAxisEnd(self, *val):
        if any(val != self.AxisEnd):
            self.AxisEnd[:3] = val[:3]
            self.Modified()
1 Like