Custom widgets

Custom widgets

Besides the standard widgets, Interact provides a framework to define custom GUIs. This is currently possible with two approaches, the full featured Widget type and the simple to use but more basic @manipulate macro.

The Widget type

The Widget type can be used to create custom widgets. The types is parametric, with the parameter being the name of the widget and it takes as argument a OrderedDict of children.

For example:

d = OrderedDict(:label => "My label", :button => button("My button"))
w = Widget{:mywidget}(d)

Children can be accessed and modified using getindex and setindex! on the Widget object:

println(w[:label])
w[:label] = "A new label"

Optionally, the Widget can have some output, which should be an Observable:

d = OrderedDict(:label => "My label", :button => button("My button"))
output = map(t -> t > 5 ? "You pressed me many times" : "You didn't press me enough", d[:button])
w = Interact.Widget{:mywidget}(d, output = output)

Finally the @layout! macro allows us to set the layout of the widget:

@layout! w hbox(vbox(:label, :button), observe(_)) # observe(_) refers to the output of the widget
Widgets.@layout!Macro.

@layout!(d, x)

Set d.layout to match the result of Widgets.@layout(x). See Widgets.@layout for more information.

Examples

julia> using Interact

julia> t = Widget{:test}(OrderedDict(:b => slider(1:100), :c => button()));

julia> @layout! t hbox(:b, CSSUtil.hskip(1em), :c);
Widgets.@layoutMacro.

@layout(d, x)

Apply the expression x to the widget d, replacing e.g. symbol :s with the corresponding subwidget d[:s] In this context, _ refers to the whole widget. To use actual symbols, escape them with ^, as in ^(:a). @layout can be combined with @map to have the layout update interactively as function of some widget.

Examples

julia> using Interact

julia> cpt = OrderedDict(:vertical => Observable(true), :b => slider(1:100), :c => button());

julia> t = Widget{:test}(cpt, output = observe(cpt[:b]));

julia> Widgets.@layout t vbox(:b, CSSUtil.vskip(1em), :c);

julia> Widgets.@layout t Widgets.@map &(:vertical) ? vbox(:b, CSSUtil.vskip(1em), :c) : hbox(:b, CSSUtil.hskip(1em), :c);

Use @layout! to set the widget layout in place:

julia> @layout! t Widgets.@map &(:vertical) ? vbox(:b, CSSUtil.vskip(1em), :c) : hbox(:b, CSSUtil.hskip(1em), :c);

@layout(x)

Curried version of @layout(d, x): anonymous function mapping d to @layout(d, x).

Defining custom widgets without depending on Interact

This is only relevant for package authors: it is not necessary to depend on Interact to define custom widgets. One can instead use the low-dependency package Widgets that defines (but does not export) all the widgets. For example:

# in the package MyPackage defining the recipe:
using Widgets
function myrecipe(i)
    label = "My recipe"
    wdg = Widgets.dropdown(i)
    Widget(["label" => label, "dropdown" => wdg])
end

# The user would then do:
using MyPackage, Interact

myrecipe(["a", "b", "c"])

A simpler approach: the manipulate macro

@manipulate expr

The @manipulate macro lets you play with any expression using widgets. expr needs to be a for loop. The for loop variable are converted to widgets using the widget function (ranges become slider, lists of options become togglebuttons, etc...). The for loop body is displayed beneath the widgets and automatically updated as soon as the widgets change value.

Use throttle = df to only update the output after a small time interval dt (useful if the update is costly as it prevents multiple updates when moving for example a slider).

Examples

using Colors

@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

@manipulate throttle = 0.1 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

@layout! can be used to adjust the layout of a manipulate block:

using Interact

ui = @manipulate throttle = 0.1 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
@layout! ui dom"div"(observe(_), vskip(2em), :r, :g, :b)
ui