Julia InteractによるUI


JuliaにはInteractというUIライブラリがある。このライブラリはJupyter Notebookを含むいくつかの環境で動作する。Jupyterで使ってみた。

Observable

InteractのベースにはObservableというオブジェクトがある。これはJavaの同名オブジェクトとほぼ同じで、アップデートされたときにアップデートを観測者に告知する役割をもつ。ただ、JavaのObservableのように能動的に登録された観測者のメソッドを呼びに行くわけではなく、コールバック関数を登録しておくフックのような受動的な役割しかないようだ。

using Interact
o = Observable(0)
on(v -> println("updated $v"), o)

on関数で、Observableがアップデートされた際に呼び出される関数を登録する。このような関数は複数登録可能だ。Observableのアップデートはo[] = ...のように[]をつけて行う。oを更新すると登録したコールバック関数が呼び出される。

> o[] = 1
updated 1

Widget

InteractのWidgetはObservableとなっている。例えばButtonであれば、クリックされた回数を内部に保持していてそれをアップデートするようになっている。

using Interact

btn = button("push me!")
on(times -> println("pushed $times times!"), btn)
display(btn)

このようにすると、ボタンを押すたびにメッセージが表示される。

@map マクロ

複数のObservableのいずれかが更新された場合に呼び出される関数を登録することができる。式の中に&をつけたものをObservableと解釈する。上の例ではObservableは1つしか出てこないけどこれを@mapマクロで書くと次の様になる。

using Interact

btn = button("push me!")
Interact.@map println("pushed ", &btn, "times!")
display(btn)

Plots の利用

Plots ライブラリを使ってグラフをアップデートする事もできる。次のコードはObservable変数pに値を順次足していき、それに応じてグラフをredrawしている。値を追加する関数update@asyncを使って別タスクで実行しているところがポイント。

using Interact
using Plots

p = Observable([])

function update()
    for i in 0:100
        p[] = append!(p[], sin(i * 2π /100))
        sleep(0.1)
    end
end

@async update()
Interact.@map plot(&p)

アップデートは別タスクで実行されるので、これを実行している間も、別のセルでは全く別の作業ができるところもポイント。

WidgetとPlotsの併用

WidgetとPlotsと別タスクを併用する例として、グラフのY軸のスケールを動的に変更する例を示す。あまり意味は無いが。

using Interact
using Plots

p = Observable([0.0])
height = slider(1:0.1:2)

function update()
    for i in 0:100
        p[] = append!(p[], sin(i * 2π /100))
        sleep(0.2)
    end
end

@async update()
display(height)
Interact.@map plot(&p, ylims=(-&height, &height))

最後の行に&のついた変数が複数あるのがみそ。

おわりに

Intractは結構バージョンアップがはげしいようで、現在は0.10.3が最新。まだ1にもなってなくて使うのが怖いけど、Juliaはだいたいこんな感じだからなあ。。