Where event-driven programming is about reacting to things that happen, RP is about staying up to date with changing signals.
In [ ]:
# The action to take upon a certain event is usually specified at the "source"
b = Button()
b.mouse_down.connect(some_callback)
...
def some_callback(event):
...
A signal can be created by decorating a function:
In [ ]:
from flexx import react
@react.connect('name')
def greet(n):
print('hello %s!' % n)
Signals yield new values, thereby transforming or combining the upstream signals. Also, you can connect to as many signals as necessary:
In [ ]:
@react.connect('first_name', 'last_name')
def name(first, last):
return '%s %s' % (first, last)
Input signals can be called with an argument to set their value:
In [ ]:
@react.input
def first_name(n='John'):
assert isinstance(n, str)
return n.capitalize()
@react.input
def last_name(n='Doe'):
assert isinstance(n, str)
return n.capitalize()
In [ ]:
# For the sake of the story, we defined the signals out of order, so we need to connect them
name.connect(); greet.connect()
In [ ]:
first_name() # get signal value
In [ ]:
first_name('jane') # set signal value (for input signals)
In [ ]:
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 [ ]:
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 [ ]:
itemB.name()
In [ ]:
C1.items()
In [ ]:
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 [ ]:
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 [ ]:
C1.ref(itemA)
In [ ]:
C1.ref(itemD)
In [ ]:
itemD.name('D-renamed')
In [ ]:
C2.items([itemC, itemD])
In [ ]:
itemC.name('C-renamed')
In [ ]:
@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 [ ]:
foo('hello') # Does not trigger bar
foo('heya')
foo('hi')
bar() # this is where bar gets updated
In [ ]:
bar() # foo has not changed; cached value is returned
In [ ]:
@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 [ ]:
some_value(10)
In [ ]:
some_value(12)