PythonでGUIを作る② グラフ編

1.はじめに

今回はGUI作成の②で、主にグラフについて触れていきたいと思います。第2回になりますので、GUIをまったく知らない場合は①から読むことをお勧めします。

2.GUI上にグラフを描写する

早速GUI上にグラフを描写してみましょう。

import tkinter as tk
import matplotlib.backends.backend_tkagg as tkagg
import matplotlib.pyplot as plt
import numpy as np

#=============================
#浅水係数算出式
#=============================

#グラフ作成
def graph(H0_1, L0_1, h_1):    
    fig,ax = plt.subplots(figsize=[4,2.5])
    plt.grid(which='major',color='black',linestyle='--')
    plt.grid(which='minor',color='black',linestyle='--')
    ax.set_xscale('log')
    ax.set_xlabel("$h/L_0$")
    ax.set_ylabel("Ks")
    ax.set_xlim(4.e-3,0.1)
    ax.set_ylim(0.8,3.)

#=============================
#浅水係数算出式
#=============================

    canvas = tkagg.FigureCanvasTkAgg(fig, master=rcanvas)
    canvas.get_tk_widget().grid(row=0, column=0)

#GUI作成
root = tk.Tk()

root.title("不規則波の浅水変形")
root.geometry('500x500')
rcanvas = tk.Canvas(root, width = 400, height = 250, background = "white")


label_1 = tk.Label(root,text='換算沖波波高 Ho´')
label_2 = tk.Label(root,text='m')
label_3 = tk.Label(root,text='沖波周期 To')
label_4 = tk.Label(root,text='s')
label_5 = tk.Label(root,text='計算水深 h')
label_6 = tk.Label(root,text='m')
label_7 = tk.Label(root,text='計算結果')
label_8 = tk.Label(root,text='波形勾配 Ho´/L')
label_9 = tk.Label(root,text='')
label_10 = tk.Label(root,text='相対水深 h/Ho´')
label_11 = tk.Label(root,text='')
label_12 = tk.Label(root,text='水深波長比 h/Lo')
label_13 = tk.Label(root,text='')
label_14 = tk.Label(root,text='波高 H')
label_15 = tk.Label(root,text='')
label_16 = tk.Label(root,text='浅水係数 Ks')
label_17 = tk.Label(root,text='')

Ho = tk.Entry(root, width = 20)
To = tk.Entry(root, width = 20)
hs = tk.Entry(root, width = 20)

button1 = tk.Button(root,text='計算実行',command=judgement)


for i in range(3):
    root.columnconfigure(i,weight=1)

for n in range(20):
    root.rowconfigure(n,weight=1)

label_1.grid(column = 0, row = 0, sticky="e")
Ho.grid(column = 1, row = 0)
label_2.grid(column = 2, row = 0, sticky="w")
label_3.grid(column = 0, row = 1, sticky="e")
To.grid(column = 1, row = 1)
label_4.grid(column = 2, row = 1, sticky="w")
label_5.grid(column = 0, row = 2, sticky="e")
hs.grid(column = 1, row = 2)
label_6.grid(column = 2, row = 2, sticky="w")

button1.grid(column = 1, row = 3)
label_7.grid(column = 1, row = 5)

label_8.grid(column = 0, row = 6, sticky="e")
label_9.grid(column = 1, row = 6)
label_10.grid(column = 0, row = 7, sticky="e")
label_11.grid(column = 1, row = 7)
label_12.grid(column = 0, row = 8, sticky="e")
label_13.grid(column = 1, row = 8)
label_14.grid(column = 0, row = 9, sticky="e")
label_15.grid(column = 1, row = 9)
label_16.grid(column = 0, row = 10, sticky="e")
label_17.grid(column = 1, row = 10)

rcanvas.grid(column = 0, row = 12, columnspan = 3, rowspan = 9)

root.mainloop()

このようなGUIが表示されるはずです。GUIは①で作成したものをベースにしていますが、ところどころ変わっているのでご注意ください。

実際に使ってみます。

このように計算結果とグラフが出るようになりました。また、計算から算出された浅水係数がグラフにオレンジ色の点で示されています。

解説をしていきます。まず、今回で作成したもの「GUI上にcanvasを作成してグラフを描写する」方法ですが、実はcanvas上に貼り付けているというよりかはcanvasとグラフを置換しているような形になります。確実に置換とは言い切れませんが、今回は置換しているということにします…
このコードのポイントは、canvasの大きさとグラフの大きさを合わせることです。コードでいうと下記の場所にあたります。

#グラフの大きさ
fig,ax = plt.subplots(figsize=[4,2.5])

#canvasの大きさ
rcanvas = tk.Canvas(root, width = 400, height = 250, background = "white")

単位は違いますが、横400ピクセル、縦250ピクセルになっています(たぶん)。ですので、置換しても全く同じ大きさになります。このサイズが違うと、計算実行前はcanvasの大きさ、計算実行後はグラフの大きさになります。グラフの大きさ変える場合は注意してください。
このコードのメリットとして、canvasの色を変えることでグラフの場所を確保しているように見えて見栄えがいいです。事前にcanvasに別の図形や文字を描くこともできます。また、視覚的にGUIの配置を調整しやすいです。デメリットして、グラフの大きさを変えるときに2か所直す必要があるのでミスしやすいです。

3.GUIに直接描写する

次にcavasを使わずにグラフを表示してみます。この方法は、いろいろ試行錯誤しているときに見つけました。ですので、ほとんど前項のコードと同じになります。

import tkinter as tk
import matplotlib.backends.backend_tkagg as tkagg
import matplotlib.pyplot as plt
import numpy as np

#=============================
#浅水係数算出式
#=============================

#グラフ作成
def graph(H0_1, L0_1, h_1):    
    fig,ax = plt.subplots(figsize=[4,2.5])
    plt.grid(which='major',color='black',linestyle='--')
    plt.grid(which='minor',color='black',linestyle='--')
    ax.set_xscale('log')
    ax.set_xlabel("$h/L_0$")
    ax.set_ylabel("Ks")
    ax.set_xlim(4.e-3,0.1)
    ax.set_ylim(0.8,3.)

#=============================
#浅水係数算出式
#=============================

    canvas = tkagg.FigureCanvasTkAgg(fig, master=rcanvas)
    canvas.get_tk_widget().grid(row=0, column=0)

#GUI作成
root = tk.Tk()
root.title("不規則波の浅水変形")
root.geometry('500x500')

label_1 = tk.Label(root,text='換算沖波波高 Ho´')
label_2 = tk.Label(root,text='m')
label_3 = tk.Label(root,text='沖波周期 To')
label_4 = tk.Label(root,text='s')
label_5 = tk.Label(root,text='計算水深 h')
label_6 = tk.Label(root,text='m')
label_7 = tk.Label(root,text='計算結果')
label_8 = tk.Label(root,text='波形勾配 Ho´/L')
label_9 = tk.Label(root,text='')
label_10 = tk.Label(root,text='相対水深 h/Ho´')
label_11 = tk.Label(root,text='')
label_12 = tk.Label(root,text='水深波長比 h/Lo')
label_13 = tk.Label(root,text='')
label_14 = tk.Label(root,text='波高 H')
label_15 = tk.Label(root,text='')
label_16 = tk.Label(root,text='浅水係数 Ks')
label_17 = tk.Label(root,text='')

Ho = tk.Entry(root, width = 20)
To = tk.Entry(root, width = 20)
hs = tk.Entry(root, width = 20)

button1 = tk.Button(root,text='計算実行',command=judgement)

for i in range(3):
    root.columnconfigure(i,weight=1)

for n in range(20):
    root.rowconfigure(n,weight=1)

label_1.grid(column = 0, row = 0, sticky="e")
Ho.grid(column = 1, row = 0)
label_2.grid(column = 2, row = 0, sticky="w")
label_3.grid(column = 0, row = 1, sticky="e")
To.grid(column = 1, row = 1)
label_4.grid(column = 2, row = 1, sticky="w")
label_5.grid(column = 0, row = 2, sticky="e")
hs.grid(column = 1, row = 2)
label_6.grid(column = 2, row = 2, sticky="w")
button1.grid(column = 1, row = 3)
label_7.grid(column = 1, row = 5)
label_8.grid(column = 0, row = 6, sticky="e")
label_9.grid(column = 1, row = 6)
label_10.grid(column = 0, row = 7, sticky="e")
label_11.grid(column = 1, row = 7)
label_12.grid(column = 0, row = 8, sticky="e")
label_13.grid(column = 1, row = 8)
label_14.grid(column = 0, row = 9, sticky="e")
label_15.grid(column = 1, row = 9)
label_16.grid(column = 0, row = 10, sticky="e")
label_17.grid(column = 1, row = 10)

root.mainloop()

ほぼ同じですが違いとしては、コードが少し簡単になる、GUI起動時の見栄えがちょっと変くらいだと思います。最終的には同じになりますが、考え方が少し違います。前項のコードは、canvasで描写予定場所を確保していましたが、このコードは、計算時にグラフをグリッドに配置する処理をしています。したがって、GUIの初期位置がグラフがない分等間隔で配置されてしまいます。このコード事態はあまり使わないのですが、この処理ができるということはサブウィンドウへの描写や、画像出力も同様の方法でできるのではないかなと考えられます。

4.サブウィンドウにグラフを描写

前項で少し話しましたが、次はサブウィンドウにグラフを描写しています。サブウィンドウはメインウィンドウに連動した別のウィンドウです。連動いるので、メインウィンドウに変化があればサブウィンドウもそれに応じて変化します。

コードは下記のようになります。

import tkinter as tk
import matplotlib.backends.backend_tkagg as tkagg
import matplotlib.pyplot as plt
import numpy as np

#=============================
#浅水係数算出式
#=============================

#グラフ作成
def graph(H0_1, L0_1, h_1):    
    fig,ax = plt.subplots(figsize=[4,2.5])
    plt.grid(which='major',color='black',linestyle='--')
    plt.grid(which='minor',color='black',linestyle='--')
    ax.set_xscale('log')
    ax.set_xlabel("$h/L_0$")
    ax.set_ylabel("Ks")
    ax.set_xlim(4.e-3,0.1)
    ax.set_ylim(0.8,3.)

#=============================
#浅水係数算出式
#=============================

    canvas = tkagg.FigureCanvasTkAgg(fig, master=rcanvas)
    canvas.get_tk_widget().grid(row=0, column=0)

#GUI作成
root = tk.Tk()
root.title("不規則波の浅水変形")
root.geometry('500x500')

label_1 = tk.Label(root,text='換算沖波波高 Ho´')
label_2 = tk.Label(root,text='m')
label_3 = tk.Label(root,text='沖波周期 To')
label_4 = tk.Label(root,text='s')
label_5 = tk.Label(root,text='計算水深 h')
label_6 = tk.Label(root,text='m')
label_7 = tk.Label(root,text='計算結果')
label_8 = tk.Label(root,text='波形勾配 Ho´/L')
label_9 = tk.Label(root,text='')
label_10 = tk.Label(root,text='相対水深 h/Ho´')
label_11 = tk.Label(root,text='')
label_12 = tk.Label(root,text='水深波長比 h/Lo')
label_13 = tk.Label(root,text='')
label_14 = tk.Label(root,text='波高 H')
label_15 = tk.Label(root,text='')
label_16 = tk.Label(root,text='浅水係数 Ks')
label_17 = tk.Label(root,text='')

Ho = tk.Entry(root, width = 20)
To = tk.Entry(root, width = 20)
hs = tk.Entry(root, width = 20)

button1 = tk.Button(root,text='計算実行',command=judgement)
#button2 = tk.Button(root,text='グラフ',command=graph)

for i in range(3):
    root.columnconfigure(i,weight=1)

for n in range(20):
    root.rowconfigure(n,weight=1)

label_1.grid(column = 0, row = 0, sticky="e")
Ho.grid(column = 1, row = 0)
label_2.grid(column = 2, row = 0, sticky="w")
label_3.grid(column = 0, row = 1, sticky="e")
To.grid(column = 1, row = 1)
label_4.grid(column = 2, row = 1, sticky="w")
label_5.grid(column = 0, row = 2, sticky="e")
hs.grid(column = 1, row = 2)
label_6.grid(column = 2, row = 2, sticky="w")
button1.grid(column = 1, row = 3)
label_7.grid(column = 1, row = 5)
label_8.grid(column = 0, row = 6, sticky="e")
label_9.grid(column = 1, row = 6)
label_10.grid(column = 0, row = 7, sticky="e")
label_11.grid(column = 1, row = 7)
label_12.grid(column = 0, row = 8, sticky="e")
label_13.grid(column = 1, row = 8)
label_14.grid(column = 0, row = 9, sticky="e")
label_15.grid(column = 1, row = 9)
label_16.grid(column = 0, row = 10, sticky="e")
label_17.grid(column = 1, row = 10)

root.mainloop()

こんな感じになります。サブウィンドウは「toplevel」を使います。あまり使いこなせてるとは言えないので、気になる方は自分で調べてみてください。
今回は、計算実行ボタンを押したときにサブウィンドウが出てきて、グラフを描写するようになっています。「GUI作成」の方でtoplevelを使用すれば最初からサブウィンドウが表示されます。個人的にはこの方法が一番お気に入りです。

5.おわりに

今回はGUIでグラフを作成して描写してみました。前提条件としてPythonでグラフが作れる必要がありますが、そこは個人で頑張ってもらえればと思います。自分も人に教えれるほどグラフを理解できていないので、勉強不足を感じています。

今回は3パターン紹介させていただきましたが、どれもいろいろ応用できそうな考え方なので少しでも問題解決の手助けになれば幸いです。

次回は入力と出力を解説できればと思います。

コメント