なにがしたいか
JupyterでGUIを書くために、ユーザの入力に応じて出力するようにしなければならない。入力を受け取る方法として、Widgetが使えるということなので調べてみた。
データの生成
描画データとしては、サインカーブを生成する。その際の、周波数と振幅をwidgetで調整できるようにする。next
メソッドを呼ぶと次のデータを返す。
import math
class Oscillator(object):
def __init__(self):
self.amp = 1.0
self.freq = 1.0
self.angle = 0
def next(self):
self.angle += self.freq / 100.0
return math.sin(self.angle * 2 * np.pi) * self.amp
周波数と振幅のコントロール
コントロールにはWidgetのFloatSlider
を用いる。スライダをいじったら、その値がOscillator
に反映されるようにするためにコールバック関数を登録する。
import ipywidgets as widgets
class OscillatorController(object):
def __init__(self, o):
self.o = o
self.fw = widgets.FloatSlider(description='Freq:', min=1.0, max=5.0, value=o.freq)
self.aw = widgets.FloatSlider(description='Ampl:', min=0.0, max=2.5, value=o.amp)
self.fw.observe(self._assign_freq, names='value')
self.aw.observe(self._assign_amp, names='value')
def _assign_freq(self, change):
self.o.freq = change['new']
def _assign_amp(self, change):
self.o.amp = change['new']
def display(self):
display(self.fw)
display(self.aw)
アニメーション
アニメーションはmatplotlib.animation
を使う。インターバルごとに指定した関数がよびだされるので、そこからOscillator
のnext
を呼び出して、データを作る。
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class AnimationPlot(object):
def __init__(self, o):
self.fig, self.ax = plt.subplots()
self.ax.set_xlim((0,100))
self.ax.set_ylim((-2, 2))
self.x = np.linspace(0, 100, 100)
self.y = np.zeros(100)
self.line, = self.ax.plot(self.x, self.y, lw=2)
self.yl = list(self.y)
def animate(self, i):
self.yl.append(o.next())
self.yl = self.yl[1:]
y = np.array(self.yl)
self.line.set_data(self.x, y)
return (self.line,)
def display(self):
self.anim = animation.FuncAnimation(self.fig, self.animate, frames=100,
interval=200, blit=True)
実行
o = Oscillator()
oc = OscillatorController(o)
aplot = AnimationPlot(o)
aplot.display()
oc.display()
こんなかんじになる。
所感
まあいいんだけど、CPUの負荷が結構たかくなる。バックエンドでimagemagik使っているっぽいので
そのへんが重いのだろうか。