In [1]:
using React, Interact
Interact.jl provides interactive widgets for IJulia. Interaction relies on React.jl reactive programming package. React provides the type Signal
which represent time-varying values. For example, a Slider widget can be turned into a "signal of numbers". Execute the following two cells, and then move the slider. You will see that the value of signal(slider)
changes accordingly.
In [2]:
s = slider(0:0.01:1, label="Slider X:")
Out[2]:
In [3]:
signal(s)
Out[3]:
Let us now inspect the types of these entities.
In [4]:
display(typeof(s));
isa(s, Widget)
Out[4]:
In [5]:
display(typeof(signal(s)));
isa(signal(s), Signal{Float64})
Out[5]:
You can have many instances of the same widget in a notebook, and they stay in sync:
In [6]:
s
Out[6]:
A slider is useless if you cannot do more with it than just watch its value. Thankfully we can transform one signal into another, which means we can transform the signal of values that the slider takes into, say a signal of it's squares:
In [7]:
xsquared = lift(x -> x*x, signal(s))
Out[7]:
Go ahead and vary the slider to see this in action.
You can transform a signal into pretty much anything else. Let's use the Color package to produce different saturations of red:
In [8]:
using Color
lift(x -> RGB(x, 0.5, 0.5), signal(s))
Out[8]:
You can of course use several inputs as arguments to lift
enabling you to combine many signals. Let's create a full color-picker.
In [9]:
r = slider(0:0.01:1, label="R")
g = slider(0:0.01:1, label="G")
b = slider(0:0.01:1, label="B")
map(display, [r,g,b]);
In [10]:
color = lift((x, y, z) -> RGB(x, y, z), r, g, b)
Out[10]:
the @lift
macro provides useful syntactic sugar to do this:
In [11]:
color = @lift RGB(r, g, b)
Out[11]:
We can use the HTML widget to write some text you can change the color of.
In [12]:
@lift html(string("<div style='color:#", hex(color), "'>Hello, World!</div>"))
Out[12]:
The @manipulate
macro lets you play with any expression using widgets. We could have, for example, used @manipulate
to make a color picker along with our HTML output in one line of code:
In [13]:
@manipulate for r = 0:.05:1, g = 0:.05:1, b = 0:.05:1
html(string("<div style='color:#", hex(RGB(r,g,b)), "'>Color me concise</div>"))
end
Out[13]:
You can in fact create signal of other widgets to update them reactively. We have seen one case with HTML
above. Let us now create a signal of Slider:
In [14]:
x = slider(0:.1:2pi, label="x")
s = @lift slider(-1:.05:1, value=sin(2x), label="sin(2x)")
c = @lift slider(-1:.05:1, value=cos(2x), label="cos(2x)")
map(display, [x,s,c]);
Now vary the x slider to see sin(2x) and cos(2x) get set to their appropriate values.
But in the above case, you cannot also use sin(2x) and cos(2x) sliders as input values. To do this, we will have to create a separate Input signal and pass it as argument to lift. Unfortunaltely, we cannot use the @lift
macro here because of ambiguity in parsing. Example:
In [15]:
fx = Input(0.0) # A float input
Out[15]:
In [16]:
x = slider(0:.1:2pi, label="x")
y = lift(v -> slider(-1:.05:1, value=sin(v), input=fx, label="f(x)"), x)
map(display, (x,y));
f(x) will update as x changes. But if the user slides f(x) then the fx
signal takes the value chosen by the user.