【はじめてのPython】マンデルブロ集合を動き回る①

ITスキル
  • マンデルブロ集合を使って、Pythonを使ったGUIの実装例を解説していきます
  • PyplotとTkinterを組み合わせた処理の基礎について学ぶことができます
スポンサーリンク

前回までのおさらいと今回の目標

以前、Pythonを使ってマンデルブロ集合の実装について考えてきました。

これによって、指定した箇所のマンデルブロ集合を、指定した解像度で表示することができるようになりました。

def set_mandelbrot(left, right, bottom, top, resolution, max):
    if (right - left) > (top - bottom):
        width = resolution
        height = round(resolution / (right - left) * (top - bottom))
    elif (right - left) < (top - bottom):
        width = round(resolution / (top - bottom) * (right - left))
        height = resolution
    else:
        width = resolution
        height = resolution
    #返却用の配列
    mandelbrot_array = np.zeros((height, width))
    #縦横を解像度に分解
    re_array = np.linspace(left, right, width)
    im_array = np.linspace(top, bottom, height)
    #縦横のポイントごとにfunc_mandelbrotを適用
    for w_num, re_num in enumerate(re_array):
        for h_num, im_num in enumerate(im_array):
            mandelbrot_array[h_num][w_num] = func_mandelbrot(max, re_num, im_num)
    return mandelbrot_array

今回はこれを応用して、GUIを使ってマンデルブロ集合を動かせるようにします。

上下左右に移動

ボタンの配置

動かすといっても、Tkinterを使って、Pyplotで描き出すマンデルブロ集合の描写場所をずらすというだけです。
ボタンを押すごとに、「top = 1.2、bottom = -1.2、left = -2.4、right = 0.8」の範囲で表示していたマンデルブロ集合を、例えば10%ずつずらすようなイメージです。

まずはTkinterを使って、前回作成したマンデルブロ集合を表示し、その下に上下左右に動かす用のボタンを配置します。

class Application(tk.Frame):
    def __init__(self, left, right, bottom, top, resolution, max, master=None):
        #set definition
        self.left, self.right, self.bottom, self.top, self.resolution = left, right, bottom, top, resolution
        self.ver_mid, self.hor_mid = round((self.top + self.bottom) / 2, 8), round((self.left + self.right) / 2, self.round_digit)
        self.ver_lng, self.hor_lng = round((self.top - self.bottom), 8), round((self.right - self.left), self.round_digit)
        if (right - left) > (top - bottom):
            self.res_width = resolution
            self.res_height = round(resolution / (right - left) * (top - bottom))
        elif (right - left) < (top - bottom):
            self.res_width = round(resolution / (top - bottom) * (right - left))
            self.res_height = resolution
        else:
            self.res_width = resolution
            self.res_height = resolution
        self.magnific = 1.0
        self.max = max
        #set figure
        self.fig = plt.figure()
        self.fig.set_size_inches(16, 12) 
        super().__init__(master)
        self.master = master
        self.master.title('Matplotlib in tkinter')
        self.pack()
        #set Mandelbrot
        self.Mandelbrot = set_mandelbrot(self.left, self.right, self.bottom, self.top, self.resolution, self.max)
        plt.imshow(self.Mandelbrot, cmap="jet")
        #func canvas, button
        self.create_widgets()

    def create_widgets(self):
        self.canvas_frame = tk.Frame(self.master)
        self.canvas_frame.pack(side=tk.TOP)
        self.canvas = FigureCanvasTkAgg(self.fig, self.canvas_frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.TOP)
        
        self.control_frame = tk.Frame(self.master)
        self.control_frame.pack(side=tk.BOTTOM)        
        self.button = tk.Button(self.control_frame, text = "Move Left", command = self.move_left, width = 16)
        self.button.pack(side = tk.LEFT)
        self.button = tk.Button(self.control_frame, text = "Move Right", command = self.move_right, width = 16)
        self.button.pack(side = tk.RIGHT)
        self.button = tk.Button(self.control_frame, text = "Move Up", command = self.move_up, width = 16)
        self.button.pack(side = tk.TOP)
        self.button = tk.Button(self.control_frame, text = "Move Down", command = self.move_down, width = 16)
        self.button.pack(side = tk.TOP)

Tkinterクラスをセットし、「#set definition」以下では実際に動かすときに計算しやすいように、中心線の場所を把握し、また、縦横の解像度を把握しています。
「#set figure」の中の「#set Mandelbrot」でマンデルブロ集合をTkinterの中に描画し、「#func canvas, button」でset_widgets関数に処理を私、その中でボタンをセットしていきます。

ボタンが押されると、例えばMove Upのボタンなら「self.move_up」のコマンドが処理されます。
試しにそれぞれのコマンドに「print(“flag”)」と入れて処理すると、次のように、ボタンとともに描画されます。

ボタンの処理

あとは、ボタンを押されるごとにどのような処理をするかを実装すれば、実際にマンデルブロ集合を動かすことができます。

    def move_up(self):
        self.ver_mid = round(self.ver_mid + (self.ver_lng) / 10, self.round_digit)
        self.top_res = self.top
        self.top, self.bottom = round(self.ver_mid + (self.ver_lng) / 2, self.round_digit), round(self.ver_mid - (self.ver_lng) / 2, self.round_digit)
        self.buf_Mandelbrot = set_mandelbrot(self.left, self.right, self.top_res, self.top, self.res_width, self.max)
        self.Mandelbrot = self.Mandelbrot[:round(-1 * self.res_height / 10), :]
        self.Mandelbrot = np.concatenate([self.buf_Mandelbrot, self.Mandelbrot], 0)
        plt.imshow(self.Mandelbrot, cmap="jet")
        self.canvas.draw()

上へ動かすときの実装は上の通りです。
初期化のときに設定した中心線を使って、実際にtopとbottomの位置を設定します。

この時、改めてマンデルブロ集合全体を描きなおしてもいいのですが、それだと処理が重くなりすぎるので、上部分10%だけを描きなおして、それを上にくっつけるという処理をおこなっています。

こうすることで、描きなおすより10倍の速度で動かすことができます。
実際にボタンを押してみると、次の通り上に少し動きました。

さいごに

これを左右と下に対しても実装すれば、自由にマンデルブロ集合を動かせます。
ただ、これだと拡大縮小ができないため、まだあまり面白くありません。
次回以降で、拡大と縮小についても、実装していきたいと思います。

コメント

タイトルとURLをコピーしました