Pythonで数値積分 〜フーリエ級数展開を例に、てかscipy.integrate.quad()かわいいよscipy.integrate.quad()に先を越された気がしたので、PyplotにGUIをつけてみました。とっても簡単とは行きませんが、Tkinterと組み合わせることでグラフのパラメータをインターラクティブに変更することができます。
どうもaxis()が効いていないようで、グラフに追加するtextが微妙に変なところにでてしまいます。
ソースはこちらです。
# -*- coding: utf-8 -*- from pylab import * from scipy.integrate import quad from numpy import sin, pi from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg from matplotlib.figure import Figure import Tkinter as Tk from Pmw import ComboBox # global variable x_min = -4.0 x_max = 8.0 xs = linspace(x_min, x_max, 256) space = (x_max - x_min) / 80 class func_info(): def __init__(self, fn, name, text, prefix): self.fn = fn self.name = name self.text = text self.prefix = prefix def fourier(fun, n_max): a = [] b = [] for n in xrange(0, 1+n_max): res, err = quad(lambda x:fun(x)*cos(n*x), -pi, pi) a.append(res/pi) res, err = quad(lambda x:fun(x)*sin(n*x), -pi, pi) b.append(res/pi) def fn(x): sum = a[0] / 2 for n in xrange(1, 1+n_max): sum += a[n]*cos(n*x) + b[n]*sin(n*x) return sum return fn def yrange(fn): y_min = 100000.0 y_max = -100000.0 for x in xs: y = float(fn(x)) if y < y_min: y_min = y if y > y_max: y_max = y return (y_min, y_max) def draw(fn, txt, prefix): y_min, y_max = yrange(fn) baselineskip = (y_max - y_min) / 18 y_max += baselineskip*2.75 y_min -= baselineskip*2.75 for til in xrange(0,20): axis([x_min, x_max, y_min, y_max]) text(x_max-space, y_max-baselineskip*1.25, '$f(x)='+txt+'$', fontsize=13, horizontalalignment='right') txt_n = '$n\leq %d$' % til text(x_max-space, y_max-baselineskip*2.25, txt_n, fontsize=13, horizontalalignment='right') f_fn = fourier(fn, til) plot(xs, amap(fn,xs), 'b:', lw=1) plot(xs, amap(f_fn,xs), 'r-', lw=1) savefig("%s%02d" % (prefix,til)) clf() def display(window, fi, til): """ display graph """ fn = fi.fn txt = fi.text prefix = fi.prefix y_min, y_max = yrange(fn) baselineskip = (y_max - y_min) / 18 y_max += baselineskip*2.75 y_min -= baselineskip*2.75 axis([x_min, x_max, y_min, y_max]) # axis 効いてない window.text(x_max-space, y_max-baselineskip*1.25, '$f(x)='+txt+'$', fontsize=13, horizontalalignment='right') txt_n = '$n\leq %d$' % til window.text(x_max-space, y_max-baselineskip*2.25, txt_n, fontsize=13, horizontalalignment='right') f_fn = fourier(fn, til) window.plot(xs, amap(fn,xs), 'b:', lw=1) window.plot(xs, amap(f_fn,xs), 'r-', lw=1) clf() def regularize(x): return (x + pi) % (pi * 2) - pi def f1(x): # 1 | 0 x = regularize(x) if x >= 0: return 1 else: return 0 def f2(x): # x x = regularize(x) return x def f3(x): # |x| x = regularize(x) return abs(x) def f4(x): # x^2 x = regularize(x) return x*x def f5(x): # x | 0 x = regularize(x) if x >= 0: return x else: return 0 def f6(x): # {3,0,4,1,2} x = regularize(x) ix = int(floor((pi + x) / (pi*2) * 5)) ys = [3,0,4,1,2] return ys[ix] def init_functions(): """ initialize function list """ funcs = [] # 2つめの引数の2文字目を、indexの代わりにに使っているので、fn・・・の順番や文字を変えると死ぬ。 funcs.append(func_info(f1, 'f1', '1 (0\leq x<\pi), 0 (-\pi\leq x<0)', 'f1_')) funcs.append(func_info(f2, 'f2', 'x (-\pi\leq x<\pi)', 'f2_')) funcs.append(func_info(f3, 'f3', '|x| (-\pi\leq x<\pi)', 'f3_')) funcs.append(func_info(f4, 'f4', 'x^2 (-\pi\leq x<\pi)', 'f4_')) funcs.append(func_info(f5, 'f5', 'x (0\leq x<\pi), 0 (-\pi\leq x<0)', 'f5_')) funcs.append(func_info(f6, 'f6', '{3,0,4,1,2}', 'f6_')) return funcs # ここから root = Tk.Tk() root.option_add('*font', 'FixedSys 14') root.wm_title("Graph") fig = Figure(figsize=(5,4), dpi=100) win = fig.add_subplot(111) functions = init_functions() selected = 0 # default canvas = FigureCanvasTkAgg(fig, master=root) canvas.show() canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) toolbar = NavigationToolbar2TkAgg( canvas, root ) toolbar.update() canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) def quit_cb(): """ callback function for quit button. """ root.quit() # stops mainloop root.destroy() # this is necessary on Windows to prevent # Fatal Python Error: PyEval_RestoreThread: NULL tstate def scale_cb(h): """ callback function for scale, It draw the graph with new parameter. """ win.clear() display(win, functions[selected], int(h)) canvas.draw() def select_function_cb(entry): """ callback function for combobox """ global selected # indexが欲しかった # see init_functions() index = int(entry[1]) -1 selected = index n = scale_n.get() scale_cb(n) # GUI用フレーム frame = Tk.Frame() cb_sel = ComboBox(frame, label_text = 'function', labelpos = 'w', # labepos が無いとエラー scrolledlist_items = map((lambda x:x.name), functions), selectioncommand = select_function_cb ) cb_sel.selectitem(0) cb_sel.pack(side = Tk.LEFT, padx = 0) label_scale = Tk.Label(frame, text = "n") label_scale.pack(side = Tk.LEFT, padx = 10) scale_n = Tk.Scale(frame, orient = Tk.HORIZONTAL, from_ = 1, to = 20, command = scale_cb) scale_n.pack(side = Tk.LEFT) button_quit = Tk.Button(frame, text='Quit', command = quit_cb) button_quit.pack(side = Tk.LEFT, padx = 20) frame.pack(side = Tk.BOTTOM) Tk.mainloop()