How to pause a simulation app and resume from where you stopped

I am trying to add full functionality to program. M app runs a simulation. I would like to be to pause the simulation and resume the simulation from the last point.

I stumbled on this post: Stop or Cancel button

How can I implement a pause button that pauses my simulation at the last point and resumes from th last point when I click the resume button?

Additionally, I get the following error: ModuleNotFoundError: No module named 'SessionState'
when using he SessionState. I tried to install it but no successs

Kindly advise please

Thanks

Hi @JohnAnih,

Do you have SessionState module in your path ?

I usually put the session state in a utils module and import it from there. You can take reference from this repo, https://github.com/ash2shukla/streamlit-heroku
Here the _SessionState class resides in utils.py and I import it from there in main.py.

Hope it helps !

PS. the repo is compatible with streamlit 0.63.1 for latest version change the import in utils.py like following,

from streamlit.ReportThread import get_report_ctx
# change that πŸ‘† to this πŸ‘‡
from streamlit.report_thread import get_report_ctx

from streamlit.server.Server import Server
# change that πŸ‘† to this πŸ‘‡
from streamlit.server.server import Server

Thank you very much. How do you recommend I pause the simulation?

Can you share a minimal example of what you are trying to do ?

Based on some assumptions like you want to run your simulation with some termination condition and want a button to start/stop and resume from the last state, I came up with this,

Not the most elegant solution by far but gets the job done.

import streamlit as st
from time import sleep
from state import provide_state

import numpy as np


@provide_state
def main(state):
    # initial conditions
    state.iter = 0 if state.iter is None else state.iter
    state.data = [] if state.data is None else state.data

    # repopulate chart on next run with last data
    chart = st.line_chart()
    for data in state.data:
        chart.add_rows(data)

    stop = st.checkbox("Stop Update")

    # iteration for simulation
    while state.iter < 100:
        if stop:
            break
        # update the simulated chart
        data = np.random.randn(10, 2)
        chart.add_rows(data)
        state.data.append(data)
        sleep(1)
        # keep last iteration of simulation in state
        state.iter += 1

main()

This code produces this,

1 Like

I am getting error: **ModuleNotFoundError** : No module named 'state'
will share a minal working example shortly

I have the SessionState in my path FYI

provide_state is a decorator that I am using from this file, https://github.com/ash2shukla/streamlit-heroku/blob/be0fd199cd772c7241f0aac1f59c626923c0df11/src/utils.py#L95

You can just save this file locally and change imports accordingly.

1 Like

okay thanks. Is it a must the functionality is wrapped into a function?

Here is a snipnet of my code:

st.sidebar.title("Controls")

pause_simulation = st.sidebar.checkbox('Pause Simulation')

state = SessionState.get(pid=None)

    

if pause_simulation:

    while pause_simulation== True:

        p = psutil.Process(state.pid)

        p.suspend()

        state.pid = p.pid

        st.write("paused process with pid: ", state.pid)

    else:

        p.resume(state.pid)

        st.write("resumed state with pid:", state.pid)

This actually pauses the program but fails to resume the program.

I got this error when running the code: TypeError: inner() missing 1 required positional argument: β€˜func’

Which parameter should I put within Main() function ?

Thanks.

Streamlit has a feature called SessionState and no need of 3rd party implementation of session state in this case. See code below

import streamlit as st
from time import sleep
import numpy as np

def main():
    # initial conditions
    st.session_state.iter = 0 if 'iter' not in st.session_state else st.session_state.iter
    st.session_state.data = [] if 'data' not in st.session_state else st.session_state.data

    # repopulate chart on next run with last data
    chart = st.line_chart()
    for data in st.session_state.data:
        chart.add_rows(data)

    stop = st.checkbox("Stop Update")

    # iteration for simulation
    while st.session_state.iter < 100:
        if stop:
            break
        # update the simulated chart
        data = np.random.randn(10, 2)
        chart.add_rows(data)
        st.session_state.data.append(data)
        sleep(1)
        # keep last iteration of simulation in state
        st.session_state.iter += 1

main()

I have a big simulation with 100+ variables and I do not want to change all my variables to session.state.variable.

A easy solution which I developed is to loop through pythons globals() dict. Here are always all variables stored in (also in your script right now).
When the user clicks on Pause, I save variables in session.state with a simple callback function in the button, which is executed before the streamlits rerun.

When streamlit reruns my simulation I simply load all variables from session.state to globals(), to have all variables back.

import streamlit as st
from time import sleep


def save_session_state(var_to_save):
    for var in var_to_save:
        st.session_state[var] = globals()[var]

def delete_session_state():
    st.session_state.clear()
        
def load_session_state():
    for var in st.session_state:
        globals()[var] = st.session_state[var]
    
def pause_simulation(variables_to_save):
    global pause
    pause = not pause
    save_session_state(variables_to_save)
    print(f'saved  {count} Pause: {pause}')
    

# Variables to save in session state    
count = 0
other_var = 'blabla'

if 'pause' not in st.session_state:
    # initialize pause to False
    pause = False   
else:
    # load state after e.g. user clicked a button
    load_session_state()

# specify which variables to save in session state
variables_to_save = ['count', 'other_var', 'pause']

btn_text = "Pause" if not pause else "Continue"
pause_bt = st.button(btn_text, on_click=pause_simulation, args=(variables_to_save,))
rerun_bt = st.button("Rerun", on_click=delete_session_state)


# simulation loop
while count < 100:
    # display everything
    count += 1
    st.write(f"count: {count} Pause: {pause}")
    sleep(1)
    
    
    # pause simulation until next reload
    if pause:
        print('clicked')
        st.write(f"Simulation paused at iteration: {count}")
        break

If you really want to store all variables, you don’t need variables_to_save, but you can simply iterate over globals() to store all, and exclude some unnecessary stuff like:

def save_session_state(skip_names=[], skip_types=[]):
    print('----------------------------------')
    print('save_session_state')

    for var, value in globals().items():
        if var in skip_names:
            continue
        if var.startswith('__'):
            continue

        if isinstance(value, (tuple(skip_types))):
            print('skipped', var, type(value))
            continue
        
        print('saved', var, type(value))
        st.session_state[var] = value

def load_session_state(skip_names=[], skip_types=[]):
    print('----------------------------------')
    print('load_session_state')
    for var in st.session_state:
        if var in skip_names:
            continue
        if var.startswith('__'):
            continue
        if isinstance(st.session_state[var], (tuple(skip_types))):
            print('skipped', var, type(st.session_state[var]))
            continue
        print('loaded', var, type(st.session_state[var]))
        globals()[var] = st.session_state[var]
    
skip_types = [types.FunctionType, matplotlib.figure.Figure, Axes, types.ModuleType,  types.BuiltinFunctionType, st.delta_generator.DeltaGenerator]
skip_variables = ["pause_bt", "_", "rerun_bt", "uploaded_file", "download-csv", "csv", "Normalize", "fileUploaderField", "AxesSubplot"]