I made a program to display and update the graph according to the input with PysimpleGui. I was able to write up to the point where the graph is displayed according to the input, but I had a hard time updating the graph contents when changing the parameters on the gui. Code posted on github I made something like that for reference, so I will publish it.
I couldn't think of any other good material, so I will use the coronavirus-infected person data released by the Ministry of Health, Labor and Welfare. https://www.mhlw.go.jp/stf/covid-19/open-data.html
The number of coronavirus positives, cumulative deaths, daily deaths, number of pcr tests, etc. are plotted on the gui according to the input period. When the parameter is changed, the graph on the gui is updated.
import PySimpleGUI as sg
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from datetime import datetime
import matplotlib.dates as mdates
class Toolbar(NavigationToolbar2Tk):
def __init__(self, *args, **kwargs):
super(Toolbar, self).__init__(*args, **kwargs)
class GuiWindow:
def __init__(self):
self.line_plot_frame = sg.Frame(layout=[
[sg.Text('start day', size=(9, 1)), sg.Text('end day', size=(10, 1))],
[sg.InputText('', size=(10, 1)), sg.InputText('', size=(10, 1))],
[sg.Text('Plot param')],
[sg.CBox('Positive', size=(10, 1), key='-positive-'),
sg.CBox('PCR', size=(10, 1), key='-pcr-')],
[sg.CBox('Death', size=(10, 1), key='-death-'),
sg.CBox('Death Per Day', size=(12, 1), key='-death_per_day-')],
[sg.B('LinePlot')]],
title='LinePlot', relief=sg.RELIEF_SUNKEN, vertical_alignment='top')
self.graph_area = sg.Column(layout=[
[sg.T('Controls:')],
[sg.Canvas(key='controls_cv')],
[sg.Canvas(key='fig_cv', size=(400 * 2, 400))]], background_color='#DAE0E6', pad=(0, 0),
)
self.controls = sg.B('Exit')
self.layout = [[self.line_plot_frame],
[self.graph_area],
[self.controls]]
self.window = sg.Window('Covid 19 Plot', self.layout)
def clear_plot(self):
if self.window['fig_cv'].TKCanvas.children:
for child in self.window['fig_cv'].TKCanvas.winfo_children():
child.destroy()
# if canvas_toolbar has child
if self.window['controls_cv'].TKCanvas.children:
for child in self.window['controls_cv'].TKCanvas.winfo_children():
child.destroy()
def plot_on_canvas(self, fig):
figure_canvas_agg = FigureCanvasTkAgg(fig, master=self.window['fig_cv'].TKCanvas)
figure_canvas_agg.draw()
toolbar = Toolbar(figure_canvas_agg, self.window['controls_cv'].TKCanvas)
toolbar.update()
figure_canvas_agg.get_tk_widget().pack(side='left', fill='both', expand=2)
def event_loop(self, df_data):
#Instantiate a class for plot
graph_generator = GraphGenerator(df_data)
while True:
event, values = self.window.read()
if event in (sg.WIN_CLOSED, 'Exit'): # always, always give a way out!
break
elif event == 'LinePlot':
#Set parameters to plot
param_list = []
if values['-pcr-']:
param_list.append('pcr')
if values['-positive-']:
param_list.append('positive')
if values['-death-']:
param_list.append('death')
if values['-death_per_day-']:
param_list.append('death_per_day')
#Display period setting
start_day = datetime.strptime(values[0], '%Y/%m/%d')
end_day = datetime.strptime(values[1], '%Y/%m/%d')
fig = graph_generator.line_plot(start_day, end_day, param_list)
self.clear_plot()
self.plot_on_canvas(fig)
class GraphGenerator:
def __init__(self, df):
self.df = df
def line_plot(self, start_day, end_day, param_list):
df_covid = self.df[self.df['published_day'] <= end_day]
df_covid = df_covid[df_covid['published_day'] >= start_day]
df_covid = df_covid.set_index('published_day')
df_covid = df_covid[param_list]
sns.set()
fig, ax = plt.subplots()
sns.lineplot(data=df_covid)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d'))
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.tight_layout()
return fig
def job():
#Load dataframe
df_positive = pd.read_csv('https://www.mhlw.go.jp/content/pcr_positive_daily.csv')
df_pcr = pd.read_csv('https://www.mhlw.go.jp/content/pcr_tested_daily.csv')
df_death = pd.read_csv('https://www.mhlw.go.jp/content/death_total.csv')
#Merge by date
df = pd.merge(df_positive, df_pcr,
on='date', how='outer')
df = pd.merge(df, df_death,
on='date', how='outer')
#rename
df = df.rename(columns={'date': 'published_day',
'Number of PCR positives(Single day)': 'positive',
'Number of PCR tests performed(Single day)': 'pcr',
'Number of deaths': 'death'})
df.fillna(0, inplace=True)
df['published_day'] = pd.to_datetime(df['published_day'])
#Convert the cumulative sum of deaths to a simple array
death_per_day = []
for i, death_total in enumerate(df['death'].values):
if i == 0:
death = death_total
else:
death = death_total - df['death'].values[i - 1]
death_per_day.append(death)
df['death_per_day'] = death_per_day
window = GuiWindow()
window.event_loop(df)
job()
Let's see the transition of the number of positive people from 2020/1/1 to 2021/1/1.
I will narrow down the period from December 1, 2020.
Press the Line Plot button to update the graph.
As I wrote at the beginning, everything went smoothly up to the point where the graph was displayed. I had a hard time updating the next value I wanted to see from there. I wondered if I should use the update method of PySimpleGui, but it seems that the update method cannot be used in the graph on the gui. It seems that one way is to erase the graph with the destroy method and then redisplay it. This function in the class initializes the graph and toolbar.
def clear_plot(self):
if self.window['fig_cv'].TKCanvas.children:
for child in self.window['fig_cv'].TKCanvas.winfo_children():
child.destroy()
# if canvas_toolbar has child
if self.window['controls_cv'].TKCanvas.children:
for child in self.window['controls_cv'].TKCanvas.winfo_children():
child.destroy()
It may be difficult to understand if it is an image, but when displaying the graph, the screen on the gui is flickering. The cause is unknown, so I want to deal with it. Try to display not only line charts but also other types of graphs, etc. Also, this time I put together PySimpleGui's Window as one class, but I think that the code will be easier to read if it is subdivided a little. This area is related to class design, and I wanted to continue studying. I would like to receive any advice.
Recommended Posts