I want an object with inputs and outputs, with all outputs calculated only when needed (some outputs may depend only on a partial set of inputs, etc.), as in a Labview VI. There can be cascading relationships between outputs, e.g. the result of the calculation of output_1 depends on the result of the calculation of output_0.
Let's consider this oversimplified example (where I am conscious that output_0 and output_1 could be calculated in a single procedure, but I aim to more complex applications of course):
from traits.api import HasTraits, Int, Property, cached_property
class A(HasTraits):
input_0 = Int
output_0 = Property(depends_on='input_0')
output_1 = Property(depends_on='output_0')
@cached_property
def _get_output_0(self):
print('_get_output_0')
return self.input_0**2
@cached_property
def _get_output_1(self):
print('_get_output_1')
return self.output_0**2
a = A(input_0=3)
The problem here is that, as output_1 is declared to depend on output_0, the value of output_0 has to be calculated every time input_0 changes (which is understandable), i.e. we have '_get_output_0' printed when executing the above code.
It is possible to have a more consistent behavior using Events, as in:
from traits.api import HasTraits, Int, Property, cached_property, Event
from traitsui.api import View
class A(HasTraits):
input_0 = Int
output_0 = Property(depends_on='input_0')
output_1 = Property(depends_on='output_0_changed') # <--- depends on Event now ---
output_0_changed = Event
@cached_property
def _get_output_0(self):
print('_get_output_0')
self.output_0_changed = True # <----- Event fired ----------
return self.input_0**2
@cached_property
def _get_output_1(self):
print('_get_output_1')
return self.output_0**2
traits_view = View('input_0', 'output_0', 'output_1')
a = A(input_0=3)
Hurray! The calculation of output_0 is no more fired. Asking for output_0 or output_1 in any order will have only the relevant calculations launched. So far, so good...
Unfortunately, issuing
a.configure_traits()
and changing the value of input_0 in the dialog box will get into an infinite recursion of alternative calculations of output_0 and output_1. The traits.api doc states about on_trait_change that If the current thread is the UI thread, the notifications are executed immediately. Here we are dealing with Property's, but it should be something similar, iow the output_0_changed notification is executed before the new value is set, hence the recursive calls between output_1 and output_0
So far, I did not find any solution to get rid of this recursion. Any idea?