質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

matplotlibでのグラフデータをtkinterのcanvasへ画像データとして受け渡す方法が知りたいです。

解決済

回答 1

投稿

  • 評価
  • クリップ 2
  • VIEW 663

TomoWa

score 5

前提・実現したいこと

ラズパイ4B,pyhton3で計測プログラムを作成しています。
各種ICからのデータを読み込んで画面に表示することはできたのですが
10秒毎にデータグラフを書きたくてtkinterからFigureCanvasTkAggを使用して
最初の1枚を出すことはできたのですが、いろいろなアプローチを行ったんですが
2回目の書換がうまく行かずに
(エラーがでてプログラムが止まる)
苦肉の策でエラーが出ずに動いた

fig.savefig("test_figure2.png")
img = Image.open('test_figure2.png')
img = ImageTk.PhotoImage(img)
canvas.itemconfig(img2,image=img)

というファイルを経由しての書き換えを行っています。
このファイル経由をなにか変数経由で行いたいのですが
いい方法がありますでしょうか

イメージ的には

変数A(イメージ) <= 新グラフデータ 
canvas.itemconfig(img2,image=変数A(イメージ))

という感じです。
ソースファイル添付しますが計測部は省いています。

Python歴は1ヶ月です。
稚拙なソースファイルでもう訳ありませんがよろしくおねがいします。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

PYTHON
ソースコード

import time
import datetime
import tkinter as tk
import tkinter.font as font
import threading
import RPi.GPIO as GPIO
import spidev
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import csv
import pprint
import sys
import os
from PIL import Image, ImageTk

fontsize1 = 40
#path_w = 'data/src/test_w.txt' #for csv test

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.stop_fg=True
#        self.stop_fg=True
        self.locate="    0.000" # 
        self.laptime=" 0.000000000" # Process time measure
        self.laptime2=" 0.000000000" # Process time measure
        self.laptime3=" 0.000000000" # Process time measure
        self.testcount01=0 # temp counter01
        self.testcount02=0 # temp counter02
        self.temperature01A="    0.00" # temperature01  
        self.temperature01B="    0.00" # temperature02  
        self.temperature02A="    0.00" # temperature01  
        self.temperature02B="    0.00" # temperature02  
        self.adconv01="    0.000" # ADconverterA01  
        self.adconv02="    0.000" # ADconverterA02  
        self.strain01="    0.000" # Starin converter01  
        self.strain02="    0.000" # Strain converter02  
        self.hosei_fg = False
        self.GUI()
        self.on_start()

    def GUI(self):
        self.Text001=tk.Label(text=self.locate,font=("",fontsize1), anchor='w')
        self.Text001.grid(column=0,row=0)
#
        Text001=tk.Label(text=" mm (位置",font=("",fontsize1))
        Text001.grid(column=1,row=0)

        self.Text002=tk.Label(text=self.temperature01A,font=("",fontsize1),fg ='green', anchor='w')
        self.Text002.grid(column=0,row=1)

        Text002=tk.Label(text="℃ (1",font=("",fontsize1),fg ='green')
        Text002.grid(column=1,row=1)

        self.Text004=tk.Label(text=self.temperature02A,font=("",fontsize1),fg ='darkorange', anchor='w')
        self.Text004.grid(column=0,row=2)

        Text004=tk.Label(text="℃ (2",font=("",fontsize1),fg ='darkorange')
        Text004.grid(column=1,row=2)

        self.Text006=tk.Label(text=self.adconv01,font=("",fontsize1), anchor='w')
        self.Text006.grid(column=10,row=0)

        Text006=tk.Label(text=" Volt (1 ",font=("",fontsize1))
        Text006.grid(column=11,row=0)

        self.Text007=tk.Label(text=self.adconv02,font=("",fontsize1),fg ='green', anchor='w')
        self.Text007.grid(column=10,row=1)

        Text007=tk.Label(text=" Volt (2",font=("",fontsize1),fg ='green')
        Text007.grid(column=11,row=1)

        self.Text008=tk.Label(text=self.strain01,font=("",fontsize1),fg ='darkorange', anchor='w')
        self.Text008.grid(column=10,row=2)

        Text008=tk.Label(text=" e (G1 ",font=("",fontsize1),fg ='darkorange')
        Text008.grid(column=11,row=2)

        self.Text009=tk.Label(text=self.strain02,font=("",fontsize1), anchor='w')
        self.Text009.grid(column=10,row=3)

        Text009=tk.Label(text=" e (G2 ",font=("",fontsize1))
        Text009.grid(column=11,row=3)

#グラフ描画テスト
        global x1,y1,y2,fig,ax1,canvas,h,img,img2

        x1 = np.linspace(0.0, 5.0)
        y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
        y2 = np.sin(2 * np.pi * x1) * np.exp(-x1)

        fig = plt.figure(figsize=(14, 5))
        ax1 = fig.add_subplot(111)
#        ax1.plot(x1,y1,'green')
        h,=ax1.plot(x1,y1,'green')

        fig.savefig("test_figure.png")

        img = Image.open('test_figure.png')
        img = ImageTk.PhotoImage(img)

        canvas = tk.Canvas(bg = "black", width=1400, height=490)
        canvas.place(x=100, y=300) # 左上の座標を指定
        # キャンバスに画像を表示する。第一引数と第二引数は、x, yの座標
        img2 = canvas.create_image(0, 0, image=img, anchor=tk.NW)

#        canvas = FigureCanvasTkAgg(fig, master=root)  # Generate canvas instance, Embedding fig in root
#        canvas.draw()
#        canvas.get_tk_widget().place(x=50,y=300)

#laptime 表示
        self.Text108=tk.Label(text=self.laptime,font=("",20), anchor='w')
        self.Text108.grid(column=0,row=3)

        self.Text109=tk.Label(text=self.testcount01,font=("",20), anchor='w')
        self.Text109.grid(column=0,row=4)

        self.Text110=tk.Label(text=self.testcount02,font=("",20), anchor='w')
        self.Text110.grid(column=0,row=5)

#ボタン        
        Button001=tk.Button(text="Start",font=("",20),command=self.on_start)
#        Button001.grid(column=0,row=10)
        Button001.place(x=50,y=800)
        Button002=tk.Button(text="Stop",font=("",20),command=self.on_stop)
#        Button002.grid(column=1,row=10)
        Button002.place(x=150,y=800)
        Button003=tk.Button(text="Reset",font=("",20),command=self.on_reset)
#        Button002.grid(column=2,row=10)
        Button003.place(x=250,y=800)
        Button004=tk.Button(text="Quit",font=("",20),command=self.on_quit)
#        Button003.grid(column=3,row=10)
        Button004.place(x=350,y=800)

    def run(self):
        global h
        self.stop_fg=False
        elapsed_time = 0
        elapsed_timemax = 0
        elapsed_timeave = 0
        sectimer01 = 0
        avg1_list=[] 
        avg1_cnt=0
        avg1 = 0
        logtime1 = 1000.0 # msec

        while self.stop_fg!=True:

            start = time.time()
            time.sleep(0.15) #150msec wait  

            #ここに計測処理が入ります。

#処理時間の計測
            elapsed_time = (time.time() - start) * 1000
            if elapsed_time > elapsed_timemax :
                elapsed_timemax = elapsed_time 
            avg1_list.append(elapsed_time)
            avg1_cnt = avg1_cnt + 1
            if avg1_cnt > 50 :
                avg1_cnt = 0
                avg1 = sum(avg1_list) / len(avg1_list)
                avg1_list = []

            sectimer01 = sectimer01 + elapsed_time

#10秒毎の処理           
            if sectimer01 > logtime1 :
                ax1.cla()
                h,=ax1.plot(x1,y2,'red')
                fig.savefig("test_figure2.png")
                img = Image.open('test_figure2.png')
                img = ImageTk.PhotoImage(img)
                canvas.itemconfig(img2,image=img)
#                canvas.show()

                sectimer01 = 0
                now = datetime.datetime.now()
                recordtime = '{0:%Y%m%d}'.format(now)
                recordtime = recordtime + "," + '{0:%H%M%S}'.format(now)
#コンソールに測定値を表示
#                print("recordtime = %s" % recordtime)
                data = 123456
                data = recordtime + "," + str(data)
#                data = data.split(',') 
                print("%s" % data)
 #               print(type(data))                 

#日付のファイル名のCSVに書き込み
                f = open('{0:%Y%m%d}'.format(now) + ".csv" , 'a') 
                f.write(data+"\n")
                f.close()

            self.laptime= '   処理時間(単独) ' + "{:6.2f}".format(elapsed_time)+'ミリ秒'
            self.Text108["text"]= self.laptime
            self.laptime2= '   処理時間(最大) ' + "{:6.2f}".format(elapsed_timemax)+'ミリ秒'
            self.Text109["text"]= self.laptime2
            self.laptime3= '処理時間(50回平均) ' + "{:6.2f}".format(avg1)+'ミリ秒'
            self.Text110["text"]= self.laptime3

    def on_start(self):
        if self.stop_fg!=True:
            return
        else:
            threading.Thread(target=self.run).start()

    def on_stop(self):
        self.stop_fg=True

    def on_reset(self):
        self.on_stop()
        self.testcount01=0

    def on_quit(self):
#終了処理        
        raise SystemExit

root=tk.Tk()
root.title("Measurement Value")
root.geometry("1600x900+50+50")
app=Application(master=root)

app.mainloop()

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

いい方法がありますでしょうか

FigureCanvasTkAggを使うです。

・・・が、これだけだと何の解決にもならないと思うので

解決へ向けて、エラーの内容と、
問題が再現できる最小のコードを作成できますか。

省略部分のデータ所得の部分などは乱数に置き換えても良いので
エラーが再現出来るものが望ましいです。

また、未使用の外部ライブラリも動作に影響がなければコメントアウトで。
コードを実行してみるにあたり、環境を整えやすい方が良いです。


2回目の書換がうまく行かずに

FigureCanvasTkAggを用いて (解説は下)

イメージ説明

import time
import datetime
import tkinter as tk
import tkinter.font as font
import threading
#import RPi.GPIO as GPIO
#import spidev
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import csv
import pprint
import sys
import os
from PIL import Image, ImageTk

fontsize1 = 40

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.stop_fg=True
#        self.stop_fg=True
        self.locate="    0.000" # 
        self.laptime=" 0.000000000" # Process time measure
        self.laptime2=" 0.000000000" # Process time measure
        self.laptime3=" 0.000000000" # Process time measure
        self.testcount01=0 # temp counter01
        self.testcount02=0 # temp counter02
        self.temperature01A="    0.00" # temperature01  
        self.temperature01B="    0.00" # temperature02  
        self.temperature02A="    0.00" # temperature01  
        self.temperature02B="    0.00" # temperature02  
        self.adconv01="    0.000" # ADconverterA01  
        self.adconv02="    0.000" # ADconverterA02  
        self.strain01="    0.000" # Starin converter01  
        self.strain02="    0.000" # Strain converter02  
        self.hosei_fg = False
        self.GUI()

        # 動画キャプチャの為
        # self.on_start()

    def GUI(self):
        self.Text001=tk.Label(text=self.locate,font=("",fontsize1), anchor='w')
        self.Text001.grid(column=0,row=0)
#
        Text001=tk.Label(text=" mm (位置",font=("",fontsize1))
        Text001.grid(column=1,row=0)

        self.Text002=tk.Label(text=self.temperature01A,font=("",fontsize1),fg ='green', anchor='w')
        self.Text002.grid(column=0,row=1)

        Text002=tk.Label(text="℃ (1",font=("",fontsize1),fg ='green')
        Text002.grid(column=1,row=1)

        self.Text004=tk.Label(text=self.temperature02A,font=("",fontsize1),fg ='darkorange', anchor='w')
        self.Text004.grid(column=0,row=2)

        Text004=tk.Label(text="℃ (2",font=("",fontsize1),fg ='darkorange')
        Text004.grid(column=1,row=2)

        self.Text006=tk.Label(text=self.adconv01,font=("",fontsize1), anchor='w')
        self.Text006.grid(column=10,row=0)

        Text006=tk.Label(text=" Volt (1 ",font=("",fontsize1))
        Text006.grid(column=11,row=0)

        self.Text007=tk.Label(text=self.adconv02,font=("",fontsize1),fg ='green', anchor='w')
        self.Text007.grid(column=10,row=1)

        Text007=tk.Label(text=" Volt (2",font=("",fontsize1),fg ='green')
        Text007.grid(column=11,row=1)

        self.Text008=tk.Label(text=self.strain01,font=("",fontsize1),fg ='darkorange', anchor='w')
        self.Text008.grid(column=10,row=2)

        Text008=tk.Label(text=" e (G1 ",font=("",fontsize1),fg ='darkorange')
        Text008.grid(column=11,row=2)

        self.Text009=tk.Label(text=self.strain02,font=("",fontsize1), anchor='w')
        self.Text009.grid(column=10,row=3)

        Text009=tk.Label(text=" e (G2 ",font=("",fontsize1))
        Text009.grid(column=11,row=3)

#グラフ描画テスト
        global x1,y1,y2,fig,ax1,canvas,h,img,img2

        x1 = np.linspace(0.0, 5.0)
        y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
        y2 = np.sin(2 * np.pi * x1) * np.exp(-x1)

        fig = plt.figure(figsize=(14, 5))
        ax1 = fig.add_subplot(111)
#        ax1.plot(x1,y1,'green')
        h,=ax1.plot(x1,y1,'green')

        canvas = FigureCanvasTkAgg(fig, master=root)  # Generate canvas instance, Embedding fig in root
        canvas.draw()
        canvas.get_tk_widget().place(x=50,y=300)

#laptime 表示
        self.Text108=tk.Label(text=self.laptime,font=("",20), anchor='w')
        self.Text108.grid(column=0,row=3)

        self.Text109=tk.Label(text=self.testcount01,font=("",20), anchor='w')
        self.Text109.grid(column=0,row=4)

        self.Text110=tk.Label(text=self.testcount02,font=("",20), anchor='w')
        self.Text110.grid(column=0,row=5)

#ボタン        
        Button001=tk.Button(text="Start",font=("",20),command=self.on_start)
#        Button001.grid(column=0,row=10)
        Button001.place(x=50,y=800)
        Button002=tk.Button(text="Stop",font=("",20),command=self.on_stop)
#        Button002.grid(column=1,row=10)
        Button002.place(x=150,y=800)
        Button003=tk.Button(text="Reset",font=("",20),command=self.on_reset)
#        Button002.grid(column=2,row=10)
        Button003.place(x=250,y=800)
        Button004=tk.Button(text="Quit",font=("",20),command=self.on_quit)
#        Button003.grid(column=3,row=10)
        Button004.place(x=350,y=800)

    def run(self):
        global h
        self.stop_fg=False
        elapsed_time = 0
        elapsed_timemax = 0
        elapsed_timeave = 0
        sectimer01 = 0
        avg1_list=[] 
        avg1_cnt=0
        avg1 = 0
        logtime1 = 1000.0 # msec

        while self.stop_fg!=True:

            start = time.time()
            time.sleep(0.15) #150msec wait  

            #ここに計測処理が入ります。

#処理時間の計測
            elapsed_time = (time.time() - start) * 1000
            if elapsed_time > elapsed_timemax :
                elapsed_timemax = elapsed_time 
            avg1_list.append(elapsed_time)
            avg1_cnt = avg1_cnt + 1
            if avg1_cnt > 50 :
                avg1_cnt = 0
                avg1 = sum(avg1_list) / len(avg1_list)
                avg1_list = []

            sectimer01 = sectimer01 + elapsed_time

#10秒毎の処理           
            if sectimer01 > logtime1 :
                ax1.cla()
                h,=ax1.plot(x1,y2,'red')

                ## メインスレッド側で呼ぶ
                root.after(10, canvas.draw)

                sectimer01 = 0
                now = datetime.datetime.now()
                recordtime = '{0:%Y%m%d}'.format(now)
                recordtime = recordtime + "," + '{0:%H%M%S}'.format(now)
#コンソールに測定値を表示
#                print("recordtime = %s" % recordtime)
                data = 123456
                data = recordtime + "," + str(data)
                print("%s" % data)

            self.laptime= '   処理時間(単独) ' + "{:6.2f}".format(elapsed_time)+'ミリ秒'
            self.Text108["text"]= self.laptime
            self.laptime2= '   処理時間(最大) ' + "{:6.2f}".format(elapsed_timemax)+'ミリ秒'
            self.Text109["text"]= self.laptime2
            self.laptime3= '処理時間(50回平均) ' + "{:6.2f}".format(avg1)+'ミリ秒'
            self.Text110["text"]= self.laptime3

    def on_start(self):
        if self.stop_fg!=True:
            return
        else:
            threading.Thread(target=self.run, daemon=True).start()

    def on_stop(self):
        self.stop_fg=True

    def on_reset(self):
        self.on_stop()
        self.testcount01=0

    def on_quit(self):
#終了処理        
        raise SystemExit

root=tk.Tk()
root.title("Measurement Value")
root.geometry("1600x900+50+50")
app=Application(master=root)

app.mainloop()

要点は一か所のみ

## メインスレッド側で呼ぶ
root.after(10, canvas.draw)

tkinerに限らず、mainloopを実行してる以外のスレッドから
データ更新以外のGUI操作をするとうまくいきません。
(スレッドセーフな操作ではない為)

スレッドを使ったタイマー処理は tkinter では
after()で呼び出す方法がよく用いられますが、
今回は最小限の変更で済むように、canvas.drawのみを
mainloop側(メインスレッド)で呼んでもらうように修正。

エラー内容がないので確定ではありませんが、
元コードは canvas.show() でコメントアウトしていた箇所、
恐らくここで詰まってたのではないかなと予想です。


他の変更点: (問題には影響なし、コードを掲載するにあたり変更した箇所)

  • 起動時の on_start() 録画の為にコメントアウト。
  • threading.Thread(target=self.run, daemon=True).start()
    daemon=メインスレッド終了時にスレッドも終了。不要であれば削ってください。
  • 注意: csv出力や画像ファイル出力など、
    デバッグで何度も実行する時に不要だった部分はざっくり削ってあります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/04/25 09:31

    ご指摘の通り、canvas.draw()で詰まっていました。エラー表示が
    ↓ここから
    Backend terminated or disconnected.Fatal Python error: Segmentation fault

    Current thread 0xb229d460 (most recent call first):
    File "/usr/lib/python3/dist-packages/matplotlib/backends/_backend_tk.py", line 91 in blit
    File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 10 in draw
    File "/home/pi/Documents/wada_python01/testgraph02T.py", line 174 in run
    File "/usr/lib/python3.7/threading.py", line 865 in run
    File "/usr/lib/python3.7/threading.py", line 917 in _bootstrap_inner
    File "/usr/lib/python3.7/threading.py", line 885 in _bootstrap

    Thread 0xb6f91ad0 (most recent call first):
    File "/usr/lib/python3.7/tkinter/__init__.py", line 1283 in mainloop
    File "/home/pi/Documents/wada_python01/testgraph02T.py", line 224 in <module>
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 1272 in _execute_prepared_user_code
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 1200 in wrapper
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 1213 in wrapper
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 1259 in execute_source
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 815 in _execute_source
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 801 in _execute_file
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 403 in _cmd_Run
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 204 in handle_command
    File "/usr/lib/python3/dist-packages/thonny/backend.py", line 146 in mainloop
    File "/usr/lib/python3/dist-packages/thonny/backend_launcher.py", line 87 in <module> Use 'Stop/Restart' to restart.
    ↑ここまで
    といった感じでWEBで調べても具体例がなく解決方法がわかりませんでした。

    FigureCanvasTkAggを使ってグラフの更新ができるようになりました。
    本当にありがとうございました、感謝です。
    まだまだ学習が足りていないですね、もっと勉強したいと思います。

    キャンセル

  • 2020/04/25 11:38

    確かにこのエラー表示は原因の特定が難しいですね。

    これはマルチスレッド + GUI ライブラリでの共通する問題なので、
    他にもメイン以外のスレッドから直接GUIの操作をする場合は、
    同様のエラーになる可能性があるので注意です。

    一点だけ訂正。待ち時間は特に指定する必要がないので、root.after_idle(canvas.draw) の方が適切でした。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る