[PYTHON] Display / update the graph according to the input with PySimpleGui

Introduction

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

things to do

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.

code

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()

GUI display screen

image.png

plot

Let's see the transition of the number of positive people from 2020/1/1 to 2021/1/1.

image.png

I will narrow down the period from December 1, 2020.

image.png

Press the Line Plot button to update the graph.

Difficulties

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()

Future tasks

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

Display / update the graph according to the input with PySimpleGui
Display the graph while changing the parameters with PySimpleGUI + Matplotlib
[Neo4J] ④ Try to handle the graph structure with Cypher
Transit to the update screen with the Django a tag
[Don't refer to 04.02.17] Display the temperature sensor on a real-time graph with rasberry pi 3.
Dot according to the image
How to update with SQLAlchemy?
[pyqtgraph] Add region to the graph and link it with the graph region
Display the time in Django according to the user's region (UTC)
[Memo] How to use BeautifulSoup4 (2) Display the article headline with Requests
Output the call graph with PyCallGraph
I tried with the top 100 PyPI packages> I tried to graph the packages installed on Python
I tried to display the altitude value of DTM in a graph
How to display the progress bar (tqdm)
[Python] Set the graph range with matplotlib
[PyQt] Display a multi-axis graph with QtChart
Image display taken with the built-in ISIGHT
Display Python 3 in the browser with MAMP
How to manually update the AMP cache
Script example to display BoundingBox with PIL
How to display python Japanese with lolipop
Display the graph of tensorBoard on jupyter
I want to display the progress bar
Display markers above the border with matplotlib
Sample program to display video with PyQt
Match the colorbar to the figure with matplotlib
The road to compiling to Python 3 with Thrift
How to turn off the scale value display while leaving the grid with matplotlib
Let's play with Python Receive and save / display the text of the input form
How to display in the entire window when setting the background image with tkinter
Aim to improve prediction accuracy with Kaggle / MNIST (1. Create CNN according to the tutorial)
Try to solve the fizzbuzz problem with Keras
Dynamically generate sqlalchemy filter according to the conditions
Mathematics memorandum to keep up with the field # 4
Python constants like None (according to the reference)
Crop the image to rounded corners with pythonista
How to specify the NIC to scan with amazon-dash
How to draw a 2-axis graph with pyplot
[Python] How to change the date format (display format)
How to Skip Correlation Graph Depiction with pandas-profiling
I tried to save the data with discord
Increase the font size of the graph with matplotlib
I want to display multiple images with matplotlib.
Specify the Python executable to use with virtualenv
When you want to update the chrome driver.
I wanted to play with the Bezier curve
Say hello to the world with Python with IntelliJ
How to use the graph drawing library Bokeh
The easiest way to use OpenCV with python
Mathematics memorandum to keep up with the field # 1
Introduction to Python with Atom (on the way)
Save the object to a file with pickle
How to display images continuously with matplotlib Note
The basis of graph theory with matplotlib animation
Update the XSERVER website with Git push (Note)
Mathematics memorandum to keep up with the field # 2
[TF] How to specify variables to update with Optimizer
How to Learn Kaldi with the JUST Corpus
I want to display the progress in Python!
Mathematics memorandum to keep up with the field # 3
Display the image after Data Augmentation with Pytorch