Introduction to Interact.jl


In [2]:
using Reactive, Interact


Interact.jl provides interactive widgets for IJulia. Interaction relies on Reactive.jl reactive programming package. Reactive 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 [3]:
s = slider(0:.1:1,label="Slider X:")


Out[3]:

In [5]:
signal(s)


Out[5]:
0.8

Let us now inspect the types of these entities.


In [6]:
display(typeof(s));
isa(s, Widget)


Interact.Slider{Float64}
Out[6]:
true

In [7]:
display(typeof(signal(s)));
isa(signal(s), Signal{Float64})


Reactive.Signal{Float64}
Out[7]:
true

You can have many instances of the same widget in a notebook, and they stay in sync:


In [8]:
s


Out[8]:

Using Widget Signals

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]:
0.25

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 [12]:
using Colors
map(x -> RGB(x, 0.5, 0.5), signal(s))


Out[12]:

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 [13]:
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 [16]:
color = map((x, y, z) -> RGB(x, y, z), signal(r), signal(g), signal(b))


Out[16]:

We can use the HTML widget to write some text you can change the color of.


In [19]:
map(color -> html(string("<div style='color:#", hex(color), "'>Hello, World!</div>")), signal(color))


Out[19]:

The @manipulate Macro

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 [20]:
@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</div>"))
end


Out[20]:

Signal of Widgets

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 [23]:
x = slider(0:.1:2pi, label="x")
s = map(a -> slider(-1:.05:1, value=sin(2a), label="sin(2x)"), signal(x))
c = map(a -> slider(-1:.05:1, value=cos(2a), label="cos(2x)"), signal(x))
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 [25]:
fx = Signal(0.0) # A float input


Out[25]:
0.0

In [28]:
x = slider(0:.1:2pi, label="x")
y = map(v -> slider(-1:.05:1, value=sin(v), signal=fx, label="f(x)"), signal(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.