Where classic event-driven programming is about reacting to things that happen, RP is about staying up to date with changing signals. Signals are objects that have a value which changes over time. Signals are fed with either user input or other (upstream) signal values. In that way you create a pipeline that is always kept up-to date; it defines how information flows through your application.
In [1]:
from flexx import react
First, we create two input signals:
In [2]:
@react.input
def first_name(n='John'):
assert isinstance(n, str) # validation
return n.capitalize() # normalization
@react.input
def last_name(n='Doe'):
assert isinstance(n, str)
return n.capitalize()
Input signals can be called with an argument to set their value:
In [3]:
first_name() # get signal value
Out[3]:
In [4]:
first_name('jane') # set signal value (for input signals)
first_name()
Out[4]:
Now we create a new signal to react to changes in our input signals:
In [5]:
@react.connect('first_name', 'last_name')
def name(first, last):
return '%s %s' % (first, last)
Signals produce new values, thereby transforming or combining the upstream signals. Let's create another signal to react to the "name" signal:
In [6]:
@react.connect('name')
def greet(n):
print('hello %s!' % n)
So if we change either of the inputs ...
In [7]:
first_name('Guido')
In [8]:
last_name('van Rossum')
In [9]:
class Item(react.HasSignals):
@react.input
def name(n):
return str(n)
class Collection(react.HasSignals):
@react.input
def items(items):
assert all([isinstance(i, Item) for i in items])
return tuple(list(items))
@react.input
def ref(i):
assert isinstance(i, Item)
return i
In [10]:
itemA, itemB, itemC, itemD = Item(name='A'), Item(name='B'), Item(name='C'), Item(name='D')
C1 = Collection(items=(itemA, itemB))
C2 = Collection(items=(itemC, itemD))
In [11]:
itemB.name()
Out[11]:
In [12]:
C1.items()
Out[12]:
In [13]:
class Collection2(Collection):
@react.connect('ref.name')
def show_ref_name(name):
print('The ref is %s' % name)
@react.connect('items.*.name')
def show_index(*names):
print('index: '+ ', '.join(names))
In [14]:
itemA, itemB, itemC, itemD = Item(name='A'), Item(name='B'), Item(name='C'), Item(name='D')
C1 = Collection2(items=(itemA, itemB))
C2 = Collection2(items=(itemC, ))
In [15]:
C1.ref(itemA)
In [16]:
C1.ref(itemD)
In [17]:
itemD.name('D-renamed')
In [18]:
C2.items([itemC, itemD])
In [19]:
itemC.name('C-renamed')
In [20]:
@react.input
def foo(v):
return str(v)
@react.lazy('foo')
def bar(v):
print('update bar')
return v * 10 # imagine that this is an expensive operation
In [21]:
foo('hello') # Does not trigger bar
foo('heya')
foo('hi')
bar() # this is where bar gets updated
Out[21]:
In [22]:
bar() # foo has not changed; cached value is returned
Out[22]:
In [23]:
@react.input
def some_value(v=0):
return float(v)
some_value(0) # init
@react.connect('some_value')
def show_diff(s):
print('diff: ', s - some_value.last_value) # note: we might rename this to previous_value
In [24]:
some_value(10)
In [25]:
some_value(12)