🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

2176閲覧

tkinter上のグラフの定期更新

grana

総合スコア5

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/10/19 11:37

前提・実現したいこと

Pythonにて、一定時間毎に外部から取得したデータを処理して(疑似的ではありますが、以下のコードの'worker'に相当)、現在の値と、処理の結果得られた値の経時変化を表すグラフを得たいと考えております。GUI上で上記を表示し、加えてスタート・ストップ機能が求められます。

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

以下のコードで試みたのですが、エラーメッセージは出ないものの、グラフが更新されません。現在の値、並びに経時変化のtable(以下のコードのv_table)の更新は問題ないようです。
御助言の程、何卒よろしくお願い申し上げます。

該当のソースコード

import tkinter as tk
import pandas as pd
import time
from matplotlib import pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

a = 1
b = 2

def worker(a, b):
c = a + b
d = time.time()
return c, d

class Mainframe(tk.Frame):

def __init__(self, master, *args, **kwargs): tk.Frame.__init__(self, master, bg="black", bd=2, relief="raise", width=800, height=800, *args, **kwargs) self.fig, ax = plt.subplots(2, 2, figsize=(6, 6)) self.fig.suptitle('Trend', fontsize=12) self.canvas = FigureCanvasTkAgg(self.fig, master=master) self.canvas.get_tk_widget().pack(side=tk.TOP, expand=1) self.counter_message = tk.IntVar() self.Label = tk.Label(self, textvariable=self.counter_message, fg='white', height=1, width=20, bg='black') self.Label.pack(side=tk.LEFT, expand=1) self.Button = tk.Button(self, text="Start", fg='white', height=1, width=20, command=self.do_start, bg='black') self.Button.pack(side=tk.LEFT) self.Button_2 = tk.Button(self, text="Stop", fg='white', height=1, width=20, command=self.do_stop, bg='black') self.Button_2.pack(side=tk.LEFT) self.count = 0 self.output = 0 self.results = [] self.delay = 5000 self.max_count = 2000 self.stop_process = False self.v_table = pd.DataFrame([], columns=['ID', 'Num']) self.Row_Number = 0 self.new_data = pd.Series([]) def line_plot(self): x = self.v_table["ID"] y1 = self.v_table['Num'] self.fig, ax = plt.subplots(2, 2, figsize=(6, 6)) self.fig.suptitle('Trend', fontsize=12) ax[0, 0].plot(x, y1) ax[0, 0].set_title('Total') ax[0, 0].set_xlabel('ID') ax[0, 0].set_ylabel('x') ax[0, 1].plot(x, y1) ax[0, 1].set_title('Ave') ax[0, 1].set_xlabel('ID') ax[0, 1].set_ylabel('Ave') ax[1, 0].plot(x, y1) ax[1, 0].set_title('Total(Normalized)') ax[1, 0].set_xlabel('ID') ax[1, 0].set_ylabel('Total(Normalized)') ax[1, 1].plot(x, y1) ax[1, 1].set_title('Ave(Normalized)') ax[1, 1].set_xlabel('ID') ax[1, 1].set_ylabel('Ave(Normalized)') self.canvas.draw() def update_process(self): if self.stop_process is True: return self.count += 1 self.output = worker(a, b) self.results = worker(a, b) self.Row_Number = len(self.v_table.index) self.new_data = pd.Series([self.results[0], self.results[1]], index=self.v_table.columns, name=self.Row_Number) self.v_table = self.v_table.append(self.new_data) if self.count < self.max_count: self.counter_message.set(self.output) self.line_plot() self.after(self.delay, self.update_process) else: pass def do_start(self): self.stop_process = False self.count = 0 self.counter_message.set(self.output) self.after(self.delay, self.update_process) def do_stop(self): self.stop_process = True

class App(tk.Tk):
def init(self):
tk.Tk.init(self)

self.title('Demo') self.geometry('800x800') Mainframe(self).pack() self.mainloop()

App()

試したこと

最初に設定したグラフを消去しなければならないのかと思い、'def line_plot'内で.clear()等にて試みたのですが、解決されませんでした。

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

Python 3.7

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「画面がタイマー処理により更新されない」部分についてのみ回答しますと
Mainframe.line_plot関数の中で毎回self.fig, ax = plt.subplots(2, 2, figsize=(6, 6))を行っているのが問題です。

plt.subplotsはマニュアルによると

Create a figure and a set of subplots.

とあるので毎回、新たにプロット図を作っていることになります。また、ここで作ってもキャンバス上に表示する処理はしていないのでMainFrame.line_plotでプロット図に設定するデータを更新するつもりでも画面に反映されません。

対策はMainFrame.__init__で作成したプロット図を使い続けることです。
あとプロット図のラベル設定はMainFrame.__init__で一度行っておけば充分だと思います。
これらを踏まえて修正が必要な箇所だけ述べると

Python

1class Mainframe(tk.Frame): 2 3 def __init__(self, master, *args, **kwargs): 4 5 tk.Frame.__init__(self, master, bg="black", bd=2, relief="raise", width=800, height=800, *args, **kwargs) 6 7 #self.fig, ax = plt.subplots(2, 2, figsize=(6, 6)) 8 self.fig, self.ax = plt.subplots(2, 2, figsize=(6, 6)) 9 self.fig.suptitle('Trend', fontsize=12) 10 self.canvas = FigureCanvasTkAgg(self.fig, master=master) 11 self.canvas.get_tk_widget().pack(side=tk.TOP, expand=1) 12 13 self.ax[0, 0].set_title('Total') 14 self.ax[0, 0].set_xlabel('ID') 15 self.ax[0, 0].set_ylabel('x') 16 self.ax[0, 1].set_title('Ave') 17 self.ax[0, 1].set_xlabel('ID') 18 self.ax[0, 1].set_ylabel('Ave') 19 self.ax[1, 0].set_title('Total(Normalized)') 20 self.ax[1, 0].set_xlabel('ID') 21 self.ax[1, 0].set_ylabel('Total(Normalized)') 22 self.ax[1, 1].set_title('Ave(Normalized)') 23 self.ax[1, 1].set_xlabel('ID') 24 self.ax[1, 1].set_ylabel('Ave(Normalized)') 25 26 self.counter_message = tk.IntVar() 27 #略 28 29 def line_plot(self): 30 x = self.v_table["ID"] 31 y1 = self.v_table['Num'] 32 33 self.ax[0, 0].plot(x, y1) 34 self.ax[0, 1].plot(x, y1) 35 self.ax[1, 0].plot(x, y1) 36 self.ax[1, 1].plot(x, y1) 37 38 self.canvas.draw() 39 40 def update_process(self): 41 #略

投稿2019/10/20 02:57

nomuken

総合スコア1627

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

grana

2019/10/20 13:14

問題なく動作いたしました。 簡潔で理解しやすいご回答、感謝いたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問