Atit Bimali
Atit Bimali

Reputation: 73

Plotting matplotlib plots in pyscript when a button is clicked

I am trying to plot a graph in Html using Pyscript and matplotlib. I am trying to create a button which when clicked calls the function('main' in my case) and displays the graph. My code:

<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>

    <py-env>
        - matplotlib
    </py-env>
</head>

<body>
    <h1>Matplotlib</h1>
    <py-script> print("My Lineplot")</py-script>
    <div id="lineplot"></div>
    <button psy-onClick="main" type="submit" id="submit" value="submit">Submit</button>

    <py-script output="lineplot">
        # Python Code Goes Here ...

        import matplotlib.pyplot as plt
        def main(*args, **kwargs):
            fig, ax = plt.subplots()
            year_1 = [2016, 2017, 2018, 2019, 2020, 2021]
            population_1 = [42, 43, 45, 47, 48, 50]
            year_2 = [2016, 2017, 2018, 2019, 2020, 2021]
            population_2 = [43, 43, 44, 44, 45, 45]
            plt.plot(year_1, population_1, marker='o', linestyle='--', color='g', label='Country 1')
            plt.plot(year_2, population_2, marker='d', linestyle='-', color='r', label='Country 2')
            plt.xlabel('Year')
            plt.ylabel('Population (M)')
            plt.title('Year vs Population')
            plt.legend(loc='lower right')
            fig
            return fig


    </py-script>
</body>

</html>

When I add psy-onClick="main" on the button it does call the function, I have tried adding a console.log("function called") inside the function which gets displayed when the submit button is clicked but the graph is not displayed.

However when the function is called directly without the use of button the graph gets displayed. Like this:

def main(*args, **kwargs):
            fig, ax = plt.subplots()
            year_1 = [2016, 2017, 2018, 2019, 2020, 2021]
            population_1 = [42, 43, 45, 47, 48, 50]
            year_2 = [2016, 2017, 2018, 2019, 2020, 2021]
            population_2 = [43, 43, 44, 44, 45, 45]
            plt.plot(year_1, population_1, marker='o', linestyle='--', color='g', label='Country 1')
            plt.plot(year_2, population_2, marker='d', linestyle='-', color='r', label='Country 2')
            plt.xlabel('Year')
            plt.ylabel('Population (M)')
            plt.title('Year vs Population')
            plt.legend(loc='lower right')
            fig
            return fig
main() #calling the function, graph gets displayed on the page.

The graph looks like : Image when calling the function directly

How can I display the graph (call the function and return fig) after the submit button is clicked? Any help would be much much appreciated.

Upvotes: 0

Views: 1129

Answers (1)

d r
d r

Reputation: 7776

To make it work you should have a button listener defined to ensure the call back. To do that use create_proxy function and then process the button. It looks like this:
1. index.html
I moved your python code to main.py file to have html more readable. Added - paths: in py-env node and src="./main.py" in py-script node. There are some stylings for the button and a hidden div that I use if there are more buttons to control all of them with the same button processing function.

<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>

    <py-env>
- matplotlib
- paths:
    ./main.py
    </py-env>
    
    <style>
        .button:hover {
            background: #d9d9d9;
            }
        .button {
            text-decoration: none;
            display: inline;
            padding: 5px;
            width: 120px;
            font-size: 1em;
            background: #afc7b5;            
            color: darkgreen;
            font-weight: bold; 
            }
    </style>
</head>

<body>
    <h1>Matplotlib</h1>
    <div hidden id="evtMsg">0</div>
    
    <py-script> print("My Lineplot")</py-script>
    <div id="lineplot"></div>
    <button id="plot-button" onClick="document.getElementById('evtMsg').innerHTML=100" class="button" style="">Plot</button>

    <py-script src="./main.py"></py-script>
</body>

</html>

2. main.py
Your code is here along with imports and listener definition and button processing code. I changed the name of your function to plot_it and declared it asynchronous.

import matplotlib.pyplot as plt

import asyncio
from pyodide import create_proxy

def Setup_Button_Listeners():
    btnList = document.querySelectorAll(".button")
    for i in range(len(btnList)):
        e = document.getElementById(btnList[i].id)
        btn_event = create_proxy(Process_Button)
        e.addEventListener("click", btn_event)
#
#        
async def Process_Button(event):
    if document.getElementById("evtMsg").innerHTML == '100':        #   button plot_it
        fig = await plot_it()
        pyscript.write('lineplot', fig)

async def plot_it(*args, **kwargs):
    fig, ax = plt.subplots()
    year_1 = [2016, 2017, 2018, 2019, 2020, 2021]
    population_1 = [42, 43, 45, 47, 48, 50]
    year_2 = [2016, 2017, 2018, 2019, 2020, 2021]
    population_2 = [43, 43, 44, 44, 45, 45]
    plt.plot(year_1, population_1, marker='o', linestyle='--', color='g', label='Country 1')
    plt.plot(year_2, population_2, marker='d', linestyle='-', color='r', label='Country 2')
    plt.xlabel('Year')
    plt.ylabel('Population (M)')
    plt.title('Year vs Population')
    plt.legend(loc='lower right')
    fig
    return fig
    
Setup_Button_Listeners()

This way, when page loaded, the button listener will be established waiting for you to click it. When you do - the proxy will call Process_Button function where you can do whatever you want to do with that button clicked. Regards...

Upvotes: 1

Related Questions