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に埋め込んで表示させる手順を次に示します。

  1. 「tk.Tk()」によりTkinterのWindowを用意します。そのインスタンスを「master」とします。
  2. グラフが描かれたfigインスタンスを用意します。
  3. 「master」上に、「FigureCanvasTkAgg」により、canvasインスタンスとfigインスタンスを配置します。
  4. 「draw」により描画し、「update」によりTkinterを更新します。「mainloop」を使用すると描画は行われますが、ここでループ状態となり以降の処理ができなくなります。ただし、「update」ではボタンやテキストボックスの入力ができません。この場合は「mainloop」を使用する必要があります。「mainloop」から定期的に制御が移るメソッドを定義する「after」があります。

Tkinterに埋め込まれたmatplotlibのグラフを終了させる手順を次に示します。

  1. 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

この更新作業によりエラーが発生しなくなりました。