python3のtkinterとmatplotlibを使って多項式ベースラインを視覚的に引く

動機

研究で測定したデータのベースラインをエクセルで引いていると手作業によるミスが生じるが、データが少し汚いので全自動でベースラインを引くと望ましくないベースラインが引かれてしまうので、手作業を支援するツールを作ろうと思った。

今回行ったことの概要

後で書く

Matplotlibについて

オブジェクトとか意識しない書き方

まずはmatplotlib.pyplotをインポートします。以下のどれでも良いのですが、どのようにインポートするかによって後の記述がちょっとだけ変わってきます。

import matplotlib.pyplot as plt
from matplotlib import pyplot as plt
from matplotlib import *

この記事では、一番上のものを使っていきます。また、この記事で使うデータを定義しておきます。

x = [0,1,2,3,4,5]
y = [0,1,4,9,16,25]

それではコードを書いてみましょう。まずは直線のグラフです。インポートやデータの定義は先ほどしましたが、以下のまとまりだけをコピペすれば動くようにしたいのでもう一度書いておきます。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25] #折線グラフを描く 
plt.plot(x,y)
plt.show()

これで折れ線グラフを描けます。plt.show()をしないと、描いたグラフが現れてくれません。以下に、色々とパラメータを足したものを書きました。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25] #折線グラフを描く 、パラメータたくさん
plt.plot(x,y,color = "blue", linewidth = 2, scalex = True, scaley = True, alpha = 1, linestyle = "-")
plt.show()

最初の2つのパラメータはデータです。言語によってはy,xの順のこともありますが、matplotlibではx,yの順です。x,yがなかったら何をプロットすればいいのか分からないので必要ですが、それより後のlinewidthとかは省略してもデフォルトの値が入ってくれます。後でわかりましたが、**kwargsと呼ばれるパラメータがあり、**kwargsは他のグラフでも共通です。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25] #矢印を描く 
plt.plot(x,y)
plt.arrow(3,3,0.3,0.3)
plt.show()

arrowで矢印を描けます。arrow(x,y,dx,dy)のように書けて、(x,y)から(x+dx,y+dy)までの直線が引かれます。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25] #矢印を描く 、パラメータたくさん
plt.plot(x,y)
plt.arrow(3,3,1,0,width = 0.1,length_includes_head = True, head_width = 5, head_length = 0.5, shape = "right", overhang = 0.5, head_starts_at_zero = True, agg_filter = (1,1,3))
plt.show()

値はてきとうです。
widthは線の太さで、0.001がデフォです。
length_includes_headは線の長さにheadの部分(矢印の尖った部分)を含むかどうかです。Trueで含み、Falseで含みません。Falseがデフォです。
head_widthはheadの部分の太さです。3*width、つまり、widthの3倍がデフォです。
head_lengthはheadの長さです。1.5*head_width、つまり、head_widthの1.5倍がデフォです。length_includes_head = Trueにしてhead_length < sqrt(dx^2+dy^2)にすると、矢印が変な形になります。
shapeは形状です。"full"で両側にheadが付き、"right"と"left"はそれぞれ右か左の片方にしかheadがつきません。"full"がデフォです。
overhangは矢印の返しの部分がどれくらい反り返っているかです。0がデフォです。
head_starts_at_zeroはよく分かりません。

これらの他に、**kwargsと呼ばれるパラメータを設定できます。これは、keyword argumentsの略です。

.bar(x, height, width=0.8, bottom=None, *, align='center', data=None, **kwargs)
.pie(x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=0, radius=1, counterclock=True, wedgeprops=None, textprops=None, center=(0, 0), frame=False, rotatelabels=False, *, normalize=True, data=None)
title, twinx, twiny, 


import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25] #オートスケール 
plt.plot(x,y)
plt.autoscale(enable=True, axis='both', tight=None)
plt.show()

オートスケールとは、幅や高さをデータに合わせることです。今はxが0から5まで、yが0から25までなので、オートスケールするとグラフがそれくらいの範囲になります。
enalbeはTrueとFalseがあり、Trueだとオートスケールしますが、Falseだとしません。Trueがデフォです。
axisは"both", "x", "y"があり、"both"だと両方、"x"だとx軸だけ、"y"だとy軸だけをオートスケールします。"both"がデフォです。
tightはTrue, False, Noneがあり、あまり仕様がよく分かりませんでした。Noneがデフォです。


**kwargs

いつか調べてまとめたいなあ

オブジェクトとか意識した書き方

https://www.yutaka-note.com/entry/matplotlib_figure

上記のサイトが分かりやすいと思います。まずplt.figure()を書き、グラフを描くエリア(Figureオブジェクト)を定義します。次に、このfigの中にfig.add_subplot()もしくはfig.subplots()でグラフ(Axesオブジェクト)を追加していきます。

上記のサイトのコピペを載せようと思いましたが、良くないと思うのでちょっと変えて載せることにします。

add_subplot()の()は空っぽでも良いですし、(L,M,N)とすると、figを縦にL、横にM分割して左上から順に数えた時のN番目というようにaxesオブジェクトを追加する場所を選ぶこともできます。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25]

# add_subplotを使う

fig = plt.figure() #Figureオブジェクトを定義 
ax = fig.add_subplot() #Figureオブジェクトの中にaxというAxesオブジェクトを定義 
ax.plot(x,y) #Figureオブジェクトの中のAxesオブジェクトにグラフを描く 
plt.show()
import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25]

# add_subplotを使う

fig = plt.figure() #Figureオブジェクトを定義 
ax = fig.add_subplot(1,1,1) #Figureオブジェクトの中にaxというAxesオブジェクトを定義 
ax.plot(x,y) #Figureオブジェクトの中のAxesオブジェクトにグラフを描く 
plt.show()

subplots(M,N)を使うと、縦にM,横にN個のaxesオブジェクトを一気に作ることができます。

import matplotlib.pyplot as plt
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25]

# subplotsを使う

fig = plt.figure() #Figureオブジェクトを定義 
ax = fig.subplots(3,3) #Figureオブジェクトの中にaxというAxesオブジェクトを定義 
ax[1,1].plot(x,y) #Figureオブジェクトの中のAxesオブジェクトにグラフを描く 
plt.show()

tkinter

概要

tkinterは画面にwindowを表示し、ボタンとかマウスクリックとかのイベントを扱ったりできるなんか良い感じのやつです。python2だとTkiner、python3だとtkinterで、多くのウェブサイトの説明でTkinerが使われていたりします。この記事ではtkinterを使います。新しい方が良いですからね。あと、tkinterの解説のさまざまなサイトでclassが使われていますが、ここでは使いません。

tkinerにはwindow、Frame、whidgetの3つのオブジェクトがあります。以下の画像は、このサイトから引っ張ってきたものです。https://kuroro.blog/python/3IA9Mk6O9oBAniXsvSWU/

windowオブジェクト

root = tk.Tk()
root.mainloop()

windowオブジェクトは、上記のようにtk.Tk()で定義します。

import tkinter as tk
root = tk.Tk(Tk(screenName="ほげ", baseName="ほげほげ", className='Tk', useTk=True, sync=False, use=None))
root.mainloop()

多くの場合、Tk()の()の中身は空っぽのまま使いますが、いくつかのパラメータを設定することもできます。
screenNameは文字列で与えます。X11でのみ使える、とのことですが、X11がなんなのか僕は知りません。
baseNameはプロファイル ファイルの名前です。プロファイル ファイルがなんなのかよく分からないのですが、多分、これを他のプログラムから読み込んで実行するときに必要になってくる名前です。デフォだとプログラムネームと同じになります。
classNameは、なにこれ。
useTk、これも分からない。
syncもuseも、分からんぞ。

colorchooserはユーザーが色を選ぶ

 #ユーザーに色を選んでもらう 

import tkinter as tk
from tkinter import colorchooser

root2 = colorchooser.askcolor()

colorchooser、謎の多い機能である。import tkinter as tkとしてroot2 = tk.colorchooser.askcolor()とするとエラーが出るようで、明示的にfrom tkinter import colorchooserとインポートしなければいけないようである。
これを使うと、実行時にユーザーが色を選ぶためのwindowが出てくる。選んだらroot2はタプルとなる。((255, 149, 227), '#ff95e3')のように、1つ目の要素はRGBの3つの値を持つタプルであり、2つ目の要素はカラーコードである。多分あまり使われてない。少なくとも初心者が使うものではない。

simpledialogはユーザーが値を入力する

 #選択してもらうwindow 
import tkinter as tk #from  tkinter import colorchooser
from tkinter import simpledialog

root1 = tk.Tk()
root1.geometry("1000x1000") #root2  = colorchooser.askcolor(master = root1)

root = simpledialog.Dialog(parent = root1, title=None)
root3 = simpledialog.askfloat(title = "Hoge", prompt = "HogeHoge", parent  = root1)
root4 = simpledialog.askinteger(title = "Hoge", prompt = "HogeHoge", parent  = root1)
root5 = simpledialog.askstring(title = "Hoge", prompt = "HogeHoge", parent  = root1)
root1.mainloop()

ここではsimpledialogというものを使ってみた。これもさっきのcolorchooserと同様に、明示的にインポートしないと使えない。
simpledialog.askfloatはその名の通り、float(少数)を訊くwindowを出すものである。askintegerはinteger(整数)、askstringはstring(文字列)を訊く。
パラメータについて紹介する。
titleはタイトルである。そのwindowの上の方に出てくる。
promptは、説明みたいなものである。入力欄のすぐ上に出てくる。
parentは親である。設定しなくても動くが、他のwindowを設定すればそのwindowの真ん中に出てくる。

filedialogはファイルを選択できる

import tkinter as tk
from tkinter import colorchooser
from tkinter import simpledialog
from tkinter import filedialog
root1 = tk.Tk()
root1.geometry("1000x1000")
root7 = filedialog.asksaveasfile(title = "Hoge", parent = root1)
root8 = filedialog.askopenfilename(title = "Hoge", parent = root1)
root9 = filedialog.askdirectory(title = "Hoge", parent = root1)
root1.mainloop()

例によって、filedialogも明示的にインポートしないといけない。
asksaveasfileはask save as fileの名前の通り、保存するディレクトリを

messageboxではメッセージボックスを表示できる

import tkinter as tk
from tkinter import colorchooser
from tkinter import simpledialog
from tkinter import filedialog
from tkinter import messagebox

root1 = tk.Tk()
root1.geometry("1000x1000")

root10 = messagebox.showinfo(title="Hoge", message="HogeHoge")
root11 = messagebox.showwarning(title="Hoge", message="HogeHoge")
root12 = messagebox.showerror(title="Hoge", message="HogeHoge")
root13 = messagebox.askquestion(title="Hoge", message="HogeHoge")
root14 = messagebox.askokcancel(title="Hoge", message="HogeHoge")
root15 = messagebox.askretrycancel(title="Hoge", message="HogeHoge")
root16 = messagebox.askyesno(title="Hoge", message="HogeHoge")
root17 = messagebox.askyesnocancel(title="Hoge", message="HogeHoge")

root1.mainloop()

messageboxにはたくさんの種類があるが、showinfoとaskyesnoくらいが使えれば問題ないのではないかと思う。titleはタイトル、messageはメッセージを指定するパラメータで、それぞれ文字列で指定する。

dndではドラッグアンドドロップを扱える

ドラッグアンドドロップを扱えるようになると、随分とアプリケーションっぽいものが作れそうな予感がしてくる。でもよく分からないのでそのうち書くことにしよう。

Frameオブジェクト

import tkinter as tk
root1 = tk.Frame()
root1.mainloop()

上記の3行を書くことで、Frameオブジェクトを定義して表示することができる。これだけではしょうもないので色々な要素を加えていくことになる。

matplotlibとtkinterを組み合わせよう

matplotのオブジェクト指向的な書き方をする。まずfig = plt.figure()とか書く。実はmatplotlibの機能でFigureオブジェクトをCanvasオブジェクトに直すことができる。from matplotlib.backends.backend_tkagg import FigureCanvasTkAggというのを使えば良いのである。

import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
x, y = [0,1,2,3,4,5], [0,1,4,9,16,25]
 #以下でいろんなオブジェクトを定義するroot1  = tk.Tk() #windowオブジェクトのroot1を定義 
root1.geometry("1000x1000")

root2 = tk.Frame(root1, bd=4) #Frameオブジェクトのroot2を定義

fig = plt.figure(figsize=(3, 3)) #Figureオブジェクトのfigを定義 
ax = fig.add_subplot()
ax.plot(x,y)

canvas = FigureCanvasTkAgg(fig, root2) #FigureオブジェクトのfigをFrameオブジェクトのroot2に入れてCanvasオブジェクトにする 
canvas.draw()
canvas.get_tk_widget().grid()

root2.place(x=20, y=20)
root1.mainloop()

とりあえず上記のコードがあればmatplotのグラフをtkinterの画面に表示できる。




この記事が気に入ったらサポートをしてみませんか?