show progress of a nested function triggered by ipywidget

Viewed 126

I have an input field, button and output placed in a Jupyter notebook using a function. On the button there is an on_click that will trigger a function that will plot a graph in the output. However, this function is using another function to generate the data for the graph. Now I want to show the progress of this 2nd function in the output. How would I go about doing this? I looked into using tqdm, but then I just get the initial progress bar in the log (which doesn't update), not in the notebook cell

from random import randint
from matplotlib import pyplot as plt
from tqdm.notebook import tqdm
from ipywidgets import Layout, Button, Box, FloatText, Label, Output

def form_plot_simulation():
    button = Button(
    description='Plot simulation results',
    disabled=False,
    tooltip='Plot simulation results')
    
    global simulations_field
    simulations_field = FloatText(value='10')
    
    global out_plot_sim
    out_plot_sim = Output()
    button.on_click(plot_data)
    
    form_items = [
        Box([simulations_field]),
        Box([button]),
        Box([out_plot_sim])
    ]
    form_plot_sim = Box(form_items, layout=Layout(
        display='flex',
        flex_flow='column',
        border='solid 2px',
        align_items='stretch',
        width='600px'))
    return form_plot_sim

def plot_data(b):
    simulations = int(simulations_field.value)
    data = simulate_test(simulations)
    
    plt.figure(figsize=(12,8))
    plt.plot(data)
    
    with out_plot_sim:
        out_plot_sim.clear_output()
        plt.show()

def simulate_test(simulations):
    data = []
    for i in tqdm(range(0,simulations)):
        datapoint = randint(0,10)
        data.append(datapoint)

    return data

The cell in the notebook just has this code to display the form:

display(form_plot_simulation())

EDIT: Note: I hate the usage of global, but didn't know how to pass the field value on in the functions.

1 Answers

I don't have tqdm here, but you can adapt your example. I used a functools partial to bundle another argument to the on_click function.

from random import randint
from matplotlib import pyplot as plt
# from tqdm.notebook import tqdm
from ipywidgets import Layout, Button, Box, FloatText, Label, Output
import time
import functools

def form_plot_simulation():
    button = Button(
    description='Plot simulation results',
    disabled=False,
    tooltip='Plot simulation results')
    
    global simulations_field
    simulations_field = FloatText(value='10')
    
    global out_plot_sim
    out_plot_sim = Output()
    output_progress = Output(height='20px')
    button.on_click(functools.partial(plot_data, output_progress))
    
    form_items = [
        simulations_field,
        button, 
        output_progress, 
        out_plot_sim,
    ]
    form_plot_sim = Box(form_items, layout=Layout(
        display='flex',
        flex_flow='column',
        border='solid 2px',
        align_items='stretch',
        width='600px'))
    return form_plot_sim

def plot_data(output_widget, b):
    simulations = int(simulations_field.value)
    data = simulate_test(simulations, output_widget)
    
    plt.figure(figsize=(12,8))
    plt.plot(data)
    
    with out_plot_sim:
        out_plot_sim.clear_output()
        plt.show()

def simulate_test(simulations, output_widget):
    data = []
    for i in range(0,simulations):
        datapoint = randint(0,10)
        data.append(datapoint)
        time.sleep(0.1)
        with output_widget:
            output_widget.clear_output()
            print(i)

    return data
Related