Tkinterにmatplotlibで作成したグラフを埋め込み任意の場所にグラフを配置します。
Tkinterとmatplotlibについて
Tkinterにmatplotlibを組み合わせることによりグラフ描画GUIが作成できます。
Tkinterは、PythonでGUIを組むことのできるツールキット(ウィジェット)で、単に指定した順にウィジェットを詰め込んでゆくpack方式、column番号とrow番号で指定したマス目に、ウィジェットを配置してゆくgrid方式、フォーム上のx, y座標でドット単位に位置を指定するplace方式により、ウィジェットを配置します。
matplotlibは、科学計算用ライブラリNumPyのためのグラフ描画ライブラリで、オブジェクト指向のやり方により、複数のグラフを並べて表示したり、各グラフに別々な設定が可能になります。
matplotlibのFigureとAxes の違い
Figure と Axesの違いは、Figureはグラフの部品を全て格納する コンテナ 、Axesはグラフをプロットしたり、その他の装飾を行います。
matplotlibの階層構造は、基本的に下から順番にFigure、Axes、Axis、Tickの4層のオブジェクトから構成されています。一つのFigureに対して2つの 座標軸を持つ場合、次のような構造になります。
Figure ┣ Axes1(座標軸 ) ┃ ┣ XAxis1(x軸) ┃ ┃ ┗ XTick1(x目盛) ┃ ┗ YAxis1(y軸) ┃ ┗ YTick1(y目盛) ┗ Axes2 ┣ XAxis2 ┃ ┗ XTick2 ┗ YAxis2 ┗ YTick2
Artist について
図やx軸、y軸、線、点、矩形、文字など、matplotlib で描画される 全ての要素(オブジェクト) のことをArtistと言います。
Artistは、 次のようにcontainers(コンテナ:容器) と primitives(プリミティブ:複雑な構造を形作る際の要素) の2つに分類されます。
containers
- Figure
- Axes
- Axis(XAxis、YAxis)
- Tick(XTick、YTick)
primitives
- Line2D
- Rectangle
- PolyCollection
- Annotation
- Spine
- Legendなど
axesを使用した自由なグラフの配置
自由にグラフを配置する場合axesを使います。各値は 0 から 1 の小数で指定し、左下(0.0,0.0)が原点になります。定義した、axes内にText等のprimitivesを配置する場合、定義された幅, 高さによって実際に移動するピクセル値は変化します。
axes([左, 下, 幅, 高さ])
Tkinterへmatplotlibの埋め込み
matplotlibで作成したグラフをTkinterに埋め込んで表示させる手順を次に示します。
- 「tk.Tk()」によりTkinterのWindowを用意します。そのインスタンスを「master」とします。
- グラフが描かれたfigインスタンスを用意します。
- 「master」上に、「FigureCanvasTkAgg」により、canvasインスタンスとfigインスタンスを配置します。
- 「draw」により描画し、「update」によりTkinterを更新します。「mainloop」を使用すると描画は行われますが、ここでループ状態となり以降の処理ができなくなります。ただし、「update」ではボタンやテキストボックスの入力ができません。この場合は「mainloop」を使用する必要があります。「mainloop」から定期的に制御が移るメソッドを定義する「after」があります。
Tkinterに埋め込まれたmatplotlibのグラフを終了させる手順を次に示します。
- matplotlibに対して「close」、Tkinterに対して「destroy」することにより、Tkinterへmatplotlibの埋め込み処理が終了します。
axesを使った自由にグラフを配置
matplotlibのaxesを使ってグラフの配置を行います。
- 26行目の「ax」は中心の領域を確保して32行目でsinカーブを描画し、27行目の「ax2」は左側に領域を確保して40行目で長方形を描画し、28行目の「ax3」は右上に領域を確保して38行目で日時を描画します。
- 49行目の条件「xxxx」によって、53,54行目でTkinterへのmatplotlibの埋め込みを終了します。
- 44行目をコメント、46,47行目のコメントを外すと、コンポボックスやボタン操作が可能となります。ただし、mainloopメソッドの中でループ状態となります。一定時間後にループ状態から抜け出るために、46行目のafterメソッドを使用します。設定では100ms後に「5」の引数でfuncメソッドを呼び出します。
TestAxes.py
# -*- coding: utf-8 -*- import tkinter as tk import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import numpy as np from datetime import datetime import time def main(): master = tk.Tk() master.title('Test Layout ') # widgetの設定 fig = plt.figure() fig_canvas = FigureCanvasTkAgg(fig, master) fig_canvas.draw() fig_canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) # 座標軸の作成 #ax = plt.axes([0.1, 0.1, 0.8, 0.8]) #ax2 = plt.axes([0.1, 0.0, 0.8, 0.1]) #ax3 = plt.axes([0.65, 0.95, 0.3, 0.2]) ax = plt.axes([0.1, 0.1, 0.8, 0.8]) ax2 = plt.axes([0.0, 0.0, 0.1, 0.9]) ax3 = plt.axes([0.65, 0.95, 0.3, 0.2]) x = np.linspace(-np.pi, np.pi, num=100, endpoint=True) y = np.sin(x) ax.plot(x, y) while True: ax3.clear() # ax3.axis("off") now = datetime.today() ax3.text(0.0, 0.0, now.isoformat(' ', timespec='seconds'), size=14) rect = plt.Rectangle(xy=(0.5, 0.1), width=0.12, height=0.75, fill=False, linewidth=1) ax2.add_patch(rect) fig_canvas.draw() master.update() #self.master.after(100, self.func, 5) #self.master.mainloop() if xxxx: break time.sleep(1) plt.close() master.destroy() if __name__ == "__main__": main()
次のコマンドで実行するとグラフが表示されます。
$ python3 TestAxes.py
22-24行目のコメントを外し、26-28行目にコメントを付けて実行すると次のグラフが表示されます。
実行時のエラー「__init__() got an unexpected keyword argument」
このエラーは、ネットで調べるとライブラリが古い場合に発生するようで、バージョンを最新にします。今回は、matplotlibの「legend」を実行するとき、「labelcolor」に対してユーザ環境で実行する場合はOKで、管理者環境「sudo」で実行するとこのエラーが発生しました。
次のコマンドでユーザ環境で使用されるライブラリのバージョンを調べます。「matplotlib 3.4.3」でした。
$ pip3 list
次のコマンドで管理者環境で使用されるライブラリのバージョンを調べます。「matplotlib 3.0.2 」でした。
$ sudo pip3 list
管理者環境のバージョンが古いので次のコマンドで最新にバージョンアップします。
$ sudo pip3 install -U matplotlib
同様に、次のコマンドでnumpyのバージョンを最新にします。
$ sudo pip3 install -U numpy
この更新作業によりエラーが発生しなくなりました。