Data transfer between Source and python

Dear Devs,

My source is defined by a set of parameters x, but also contains an inner scalar q=f(x). I want to modify the x and dynamically get the q within python. The end goal is to have a python script that loops over multiple x values, makes pictures of a source, but also gets and writes the q value into a file as follows:

GetActiveSource().x = [1,2,3,4,5]
GetActiveSource().q  # <--expecting new q value

Currently, if apply button is pressed, the correct q value is produced, but Render() + Update() in a python console do not have the same effect.

What is the correct python command for an apply button?

I’m currently trying to understand how the python interface for a source works. Particularly, what functions are called and when? Are data transferred only upon apply? Is it possible to transfer the data before apply is pressed?

Could you give a general direction towards how the python interface in PV is implemented, what classes are used etc.

Thank you very much,
Art

Have you looked through the ParaView Guide? There are numerous Python examples throughout, and a skim of the guide should answer a number of questions you’ve posed here.

Hi Cory,

Thanks a lot for the reply. I am reading it write now. =)

Sincerely,
Art

I am back. Reading the manual, though always useful, did not bring me any closer to the answer.

Let me reformulate the question a bit. I have a custom c++ plugin for a source, which has two widgets, A and B, which are linked. If A is modified, B will be modified as well. For simplicity, if A is set to 5, B will be set to 25 (i.e. A**2). I have achieved this already.

However, I would like to do the same in a python script. Namely,

mySource.A = 6 # <- new value
mySource.B #  <- fetching B, expecting 36 (= 6**2)

Currently, this combination works

mySource.A = 6 # <- new value
# pressing apply with my mouse
mySource.B
# getting 36, which is correct

However this combination does not work

mySource.A = 6 # <- new value
UpdatePipeline()
Render()
mySource.B
# getting 25, which is the old value of B, which is wrong

So obviously, the connection between two widgets and the python interface is working. And yet, UpdatePipeline() + Render() is not equivalent to pressing the Apply button, and I cannot figure out what I am doing wrong.

Possibly a more precise question, what signals are used to inform python about the changes in the widget values? Q_PROPERTY’s NOTIFY signal didn’t do the trick.

Thanks a ton,
Art

Also, as an afterthought, can one get the pqPropertiesPanel from within a plugin, and call the apply() method manually?

Dirty, but would do the trick.

Best,
Art

How are they linked ? By Qt signals ? Then it breaks the ParaView server/manager mechanism.

what signals are used to inform python about the changes in the widget values? Q_PROPERTY’s NOTIFY signal didn’t do the trick.

None, it is all managed by properties as python can be used without Qt when using pvpython alone.

can one get the pqPropertiesPanel from within a plugin, and call the apply() method manually?

Yes, using pythonQt, but definitely a code that is not supported by paraview framework and can break at any update.

If you want a property to influence another property, then it is not supported by ParaView property mechanism.

Hi, Mathieu. Thank you very much for your excellent answer.

I see the problem now. The source/client semantics of paraview is still quite new to me (we use PV only locally and never think of it as a source/client application), but I’m getting a better grasp of it now.

Are the following statements correct?

  1. If I want one property to influence another, they should belong to two different filters (or sources) and be connected via a pipeline.

  2. The only way to get some property ‘x’ exposed to python (i.e. be able to call GetActiveSource().x) is to have a Q_PROPERTY on it in its c++ class.

Thank you very much,
Art

  1. If I want one property to influence another, they should belong to two different filters (or sources) and be connected via a pipeline.

One property cannot influence another, except to compute its default value. Being in two filters is not related to that.

The only way to get some property ‘x’ exposed to python (i.e. be able to call GetActiveSource().x ) is to have a Q_PROPERTY on it in its c++ class.

Not at all. To have a property exposed in python for a proxy, the property only needs to be declared in the xml of the proxy.

I am properly confused. :slightly_smiling_face:

Indeed, but doesn’t Q_PROPERTY (and it’s READ WRITE fields) tell us which set/get methods to use for python calls (among other things)? Otherwise the xml property will return null and cannot be updated.

To summarize, if the source is defined with a parameter x and we define a dependent variable y=f(x), there is no way to updated y dynamically for every time when x is modified (programmatically or otherwise), since it violates the server/client paradigm of PV.

Q_PROPERTY

Q_PROPERTY is only use to create the link between the property and the Qt widget. unrelated to python.
ParaView can be compiled with only Qt and not python, or with only python and not Qt.

To summarize , if the source is defined with a parameter x and we define a dependent variable y =f( x ), there is no way to updated y dynamically for every time when x is modified (programmatically or otherwise), since it violates the server/client paradigm of PV.

Yes ! However, there is a few way around this to trick the user, using multiple properties and information property. I could explain in more details if you are interested.

Yes, absolutely!

Just to clarify, since my colleagues are even less skilled in paraview and programming, my hope was to have it like this:

mySource.x = 6 
mySource.y
> 36 

Or maybe to define my own function f and have it like this

mySource.x = 6
mySource.f(mySource.x)
> 36

If there is a way to achieve this or similar, I am all ears. I need to know!

Thank you very much,
Art

Using information_properties, what you can do is :

mySource.x = 6
mySource.UpdatePipelineInformation()
mySource.yInfo
> 36 
mySource.y = mySource.yInfo
mySource.Update()

how is “yInfo” different from y? Specifically in c++.

yInfo is another property XML, with a specific tag “information_only”. C++ side, it is only a call to a Getter method, which would take care of the multiplication.

See Examples/Plugins/PropertyWidgets for an information_only example. It does not contain your usecase exactly though.

So I’ve tried the “information_only” tag as follows:

xml:

<StringVectorProperty command="GetNewParameter"
                      information_only="1"
                      name="NewParameter">
    <StringArrayHelper />
</StringVectorProperty>

c++:

public:
    vtkStringArray* GetNewParameter(){
        return NewParameter;
    }
private:
    vtkNew<vtkStringArray> NewParameter;

But there is no NewParameter variable in python:

GetActiveSource().NewParameter  # <- doesn't exist

Q1: How do I make NewParameter visible in python?
Q2: What is the alternative for double? Currently, I get this error:

No SIProperty for the following information helper: DoubleArrayHelper.

Q3: In the example, “GetShrinkFactor” method is called, although the plugin has no such method. How is this possible?

Thank you very much,
Art

xml:

StringArrayHelper is deprecated. Use si_class="vtkSIDataArrayProperty" instead.

How do I make NewParameter visible in python?

  • You either set it visible in the xml with panel_visibiliy="default"
  • or you use the more complex api : GetActiveSource().SMProxy.GetProperty(“NewParameter”)

What is the alternative for double?

Same as for string if you need an array of double : si_class="vtkSIDataArrayProperty"
Or nothing if you only need a single value.

In the example, “GetShrinkFactor” method is called, although the plugin has no such method. How is this possible?

It is a macro vtkGetMacro("ShrinkFactor"); in the header.

Thank you for the detailed answers, I feel like I’m close, but I’m still having issues. My current state is

xml:

<DoubleVectorProperty command="GetNewParameter"
    information_only="1"
    name="NewParameter"
    panel_visibiliy="default"
    si_class="vtkSIDataArrayProperty" >
</DoubleVectorProperty>

c++:

public:
    vtkDoubleArray* GetNewParameter(){
        // shamelessly taken from a vtk example
        newParameter->SetNumberOfComponents(1);
        newParameter->SetNumberOfTuples(4);
        std::array<std::array<double, 1>, 4> pts = {{ {{1.}}, {{2.}}, {{3.}}, {{4.}} }};
        for (auto i = 0ul; i < pts.size(); ++i)
            newParameter->SetTuple(i, pts[i].data());
        return newParameter;
    }
private:
    vtkNew<vtkStringArray> newParameter;

This works!

GetActiveSource().SMProxy.GetProperty("NewParameter").GetNumberOfElements() # > 4
GetActiveSource().SMProxy.GetProperty("NewParameter").GetElement(0) # > 1

But this unfortunately doesn’t:

  1. NewParameter is still not visible in python:

GetActiveSource().NewParameter # doesn't exist

  1. I still have to press Apply to get an update, i.e. calling UpdatePipeline() from python CLI has no effect and GetNewParameter() is never called. How to ensure that GetNewParameter() is called every time the pipeline is updated? Example

UpdatePipeline()
GetActiveSource().SMProxy.GetProperty("NewParameter").GetElement(0)
> GetNewParameter() called, new values obtained

  1. Currently I generate the same data (newParameter = [1, 2, 3, 4]) every time. How do I fetch new python values (i.e. GetActiveSource().Parameter = [2, 3]) in c++?

Thank you so much,
Art

NewParameter is still not visible in python:

Are you using master ?

How to ensure that GetNewParameter() is called every time the pipeline is updated? Example

UpdatePipelineInformation()

How do I fetch new python values

Not sure what you mean, in your C++ class you can compute any values you want.

Are you using master ?

I’m currently using ParaView 5.7.0, compiled from source, which comes from https://www.paraview.org/download/.
I guess the error name 'UpdatePipelineInformation' is not defined is related.

in your C++ class you can compute any values you want

Yes of course, but what I mean is that upon GetActiveSource().Parameter = [2, 3] I update my widget values. I can then further parse the values around via this->proxy()->GetProperty("Parameter"), but this doesn’t seem to work for the plugin class itself.

I’m sorry if my questions are repetitive or trivial, I still have very little understanding of what I’m doing.

Thanks a lot,
Art