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.@layout
— Macro.@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
Widgets.@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