なにがしたいか

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()

こんなかんじになる。

ezgif-6-56efb62c7cb7.gif

所感

まあいいんだけど、CPUの負荷が結構たかくなる。バックエンドでimagemagik使っているっぽいので
そのへんが重いのだろうか。

bannerAds