Button to autoset values to current page using values from previous page

Summary

So I have two pages currently: one that takes in the given number inputs from “Page1” and then puts it all in a graph, and then “Page2”. I want Page2 to have their own number input, but if you press on a button, it’ll give you all of the numbers from Page1 to use for its specific function.

Can you provide an executable snippet of your code? That will help the community understand your issue. If you want to carry information between pages, you may want to read about session state.

We have also added a guide to the docs to explain how to combine buttons with session state and callbacks effectively.

1 Like

Sorry, for the late response, this is my current code for it. This is just for one variable, but I want to eventually do it for multiple at once. ‘Tfp_Data_analysis’ is from uploading a file and calculating the variable. Sadly, my file uploading page resets once I leave to a different one, but it still saves that specific variable which is all that I will worry about for now.

desired_tfp = st.number_input('Tfp', key='desired_tfp', format='%f')
st.write(st.session_state['Tfp_Data_Analysis'])
if st.button('Import tfp from Data Analysis'):
    if 'Tfp_Data_Analysis' in st.session_state:
        desired_tfp = st.session_state['Tfp_Data_Analysis']
    else:
        st.write('No data has been uploaded.')

If I’m understanding your issue correctly:

Key fact: When you assign a key to a widget, that key will get deleted along with that widget on any script run where it doesn’t appear (e.g. switching pages).

Fix: If you want a widget to remain stateful in the presence of switching pages, you need to copy that widget’s information into another key that is not directly tied to a widget.

Solutions

There are two ways to fix this. One way is a little hacky and interrupts the process by which a widget’s key is deleted when a widget doesn’t render. This solution needs to be placed on every single page in an app to work. The other solution is a few more lines of code, but only needs to be placed on the page with the widget you’re concerned about.

Solution 1: Interrupt the widget cleanup process

Solution 2: Create copies of each key you want to keep (such as with a _ prefix)

For solution one, how exactly would I assign the keys to that widget? I did it in a ‘loopy’ way that I don’t think was right. Below is what I did, but I think your first solution would be better. These variables are on my Page1, that I want to use with the button to autoset Page2.

def Default_Dict():
    if 'cantilever_dict' not in st.session_state:
        st.session_state['cantilever_dict'] = {
            'amp_invols':1.15821e-07,
            'def_invols':1.15821e-07,
            'soft_amp':0.3,
            'drive_freq':279173,
            'res_freq':279173,
            'k':20,
            'q_factor':450
            }
    if 'cantilever_dict' in st.session_state:
        st.session_state['cantilever_dict'] = st.session_state['cantilever_dict']
    return cant_dict

User_Input():
    cant_parms = default_dict()
    with st.expander('Cantilever Parameters', expanded = False):
        amp_invols = st.number_input('Amplitude (volts to meters)', value=cant_parms['amp_invols'], format='%e')
        def_invols = st.number_input('Deflection (volts to meters)', value=cant_parms['def_invols'], format='%e')
        soft_amp = st.number_input('Soft Amplitude', value=cant_parms['soft_amp'], format='%f')
        drive_freq = st.number_input('Driving Frequency', value=cant_parms['drive_freq'])
        res_freq = st.number_input('Resonance Frequency', value=cant_parms['res_freq'])
        k = st.number_input('Spring Constant', value=cant_parms['k'], format='%e')
        q_factor = st.number_input('Cantilever Quality', value=cant_parms['q_factor'])

    cant_parms['amp_invols'] = amp_invols
    cant_parms['def_invols'] = def_invols
    cant_parms['soft_amp'] = soft_amp
    cant_parms['drive_freq'] = drive_freq
    cant_parms['res_freq'] = res_freq
    cant_parms['k'] = k
    cant_parms['q_factor'] = q_factor

return cant_parms

If you only call your User_Input function once on a page, you can hard-code keys into the widgets it produces. If you intend to call this function repeatedly, you would need to pass in a string parameter that could then prefix whatever keys you use for your widgets. (In this latter case, I’d definitely recommend solution 2 instead of 1.)

Here is a simplified example. In this case, I’ve opted to initialize session state on all pages so a user could theoretically start on any page of the app without a problem.

`app.py` in your main working directory:
import streamlit as st

def defaults():
    st.session_state.x = 1
    st.session_state.y = 2
    st.session_state.a = 'a'
    st.session_state.b = 'b'
    st.session_state.KEEPERS = ['x','y','a','b']

if 'x' not in st.session_state:
    defaults()

for key in st.session_state.KEEPERS:
    st.session_state[key]=st.session_state[key]

def get_input():
    st.number_input('Enter x', key='x')
    st.number_input('Enter y', key='y')

get_input()
st.session_state.x
st.session_state.y
`pages/a.py`
import streamlit as st
from app import defaults

if 'x' not in st.session_state:
    defaults()

for key in st.session_state.KEEPERS:
    st.session_state[key]=st.session_state[key]

def get_input():
    st.text_input('Enter a', key='a')
    st.text_input('Enter b', key='b')

get_input()
st.session_state.a
st.session_state.b
`pages/b.py`
import streamlit as st
from app import defaults

if 'x' not in st.session_state:
    defaults()

for key in st.session_state.KEEPERS:
    st.session_state[key]=st.session_state[key]

st.write(f'x: {st.session_state.x}')
st.write(f'y: {st.session_state.y}')
st.write(f'a: {st.session_state.a}')
st.write(f'b: {st.session_state.b}')

2023-07-19_14-57-35 (1)

Oh, I want my app to start on any page as well! So I should use your solution 2? Also, if you decided to go back to your previous page, lets say app with the x and y inputs, would the user’s inputs still display?

  1. Change variables on page app
  2. leave to any other page
  3. Go back to page app to see if changed variables are still the same or back to default

Both solutions can be implemented to allow starting on any page. Defining a set of defaults as I’ve done can be done for both solutions. The difference is in keeping a list of keys to copy (and then copying each of them once at the top of all pages) vs solution 2 that basically copy each key twice, but only on a page which modifies them with a widget.

And yes, for both solutions I’m describing how to make widgets stateful across multiple pages. A user can jump around and always come back to see what they last entered. (If the page is refreshed or opened in another tab, a new session would start and then it would all go back to the defaults.)

Okay, I decided to still go with your first solution solely because I need to save A LOT of variables and having it as a list is more convenient (at least coding wise). I noticed with my st.number_inputs if I set the key which has their default value, it just displays it as a 0 until I updated it. I was able to fix it by instead of key=session_state key, I did value = session_state key (I only did this with the DICTIONARIES I want to save though)

Going back to the beginning of this thread, now that I have my session_state in order (thank you so much!), for setting my information of page2 from page1, how would I implement the button? I wanted the user to have the option to I guess have separate values that can differ from page 1, but if I press a button will take the values of Page1 (that is held in session state) and autofill page2. I was thinking I code in the button like so (it didn’t work):

if 'cantilever_dict' not in st.session_state:
    Defaults()

if st.button('Get parameters from Simulation'):
    for key in st.session_state.KEEPERS:
        st.session_state[key]=st.session_state[key]

Would it even be possible to implement this button feature?

In your case, it looks like you have values in a dictionary in session state. The string passed to key is going to get saved directly in st.session_state. Your dictionary of values is not at risk of being deleted from session state and isn’t naturally inclined to solution 1.

If you are going to try and control a widget both from the value parameter and the key parameter, you could end up with a conflict. You’d want to be sure what you are actually using as your source of data. (In practice, you can just use one or the other. If you use both, you may want to disable a warning that can get generated when there is a conflict.)

How about a different example, where your defaults are saved in a dictionary within session state instead of directly in session state:

app.py
import streamlit as st

def defaults():
    st.session_state.my_values = {
        'x_a': 1,
        'y_a': 2,
        'x_b': 3,
        'y_b': 4,
        'x_home': 5,
        'y_home':6
    }

def update_value(key):
    st.session_state.my_values[key]=st.session_state[key]

def copy_values(copy_from, copy_to):
    st.session_state.my_values[f'x_{copy_to}'] =\
        st.session_state.my_values[f'x_{copy_from}']
    st.session_state.my_values[f'y_{copy_to}'] =\
        st.session_state.my_values[f'y_{copy_from}']

if 'my_values' not in st.session_state:
    defaults()

def get_input():
    # Copy from your source of truth (my_values dict)
    st.session_state.x_home = st.session_state.my_values['x_home']
    st.session_state.y_home = st.session_state.my_values['y_home']
    # Use the temporary keys, then write back to your source of truth
    st.number_input('Enter x', key='x_home', on_change=update_value, args=['x_home'])
    st.number_input('Enter y', key='y_home', on_change=update_value, args=['y_home'])

get_input()

st.button('Copy from a', on_click=copy_values, args=['a','home'])
st.button('Copy from b', on_click=copy_values, args=['b','home'])
pages/a.py
import streamlit as st
from app import defaults

if 'my_values' not in st.session_state:
    defaults()

def update_value(key):
    st.session_state.my_values[key]=st.session_state[key]

def get_input():
    # Copy from your source of truth (my_values dict)
    st.session_state.x_a = st.session_state.my_values['x_a']
    st.session_state.y_a = st.session_state.my_values['y_a']
    # Use the temporary keys, then write back to your source of truth
    st.number_input('Enter x', key='x_a', on_change=update_value, args=['x_a'])
    st.number_input('Enter y', key='y_a', on_change=update_value, args=['y_a'])

get_input()
pages/b.py
import streamlit as st
from app import defaults

if 'my_values' not in st.session_state:
    defaults()

def update_value(key):
    st.session_state.my_values[key]=st.session_state[key]

def get_input():
    # Copy from your source of truth (my_values dict)
    st.session_state.x_b = st.session_state.my_values['x_b']
    st.session_state.y_b = st.session_state.my_values['y_b']
    # Use the temporary keys, then write back to your source of truth
    st.number_input('Enter x', key='x_b', on_change=update_value, args=['x_b'])
    st.number_input('Enter y', key='y_b', on_change=update_value, args=['y_b'])

get_input()

2023-07-19_17-06-36 (1)

Thank you so much!! I got it to work, I used your approach but adjusted it for multiple dictionaries held in session_state :slight_smile: The only thing I need to adjust is the copy_value function; below I just wanted to test and see if it worked so I only tested one variable, and not copying an entire dictionary. UPDATE: I was able to get it working for the entire dictionary, it’s in my functions used how I did it, and I used it with st.button on Page2!

Functions used
def defaults():
        st.session_state['cantilever_dict'] = {
            'amp_invols':1.15821e-07,
            'def_invols':1.15821e-07,
            'soft_amp':0.3,
            'drive_freq':279173,
            'res_freq':279173,
            'k':20,
            'q_factor':450
            }

        st.session_state['force_dict'] = {
            'es_force':3.72358e-08,
            'delta_freq':-170, 
            'tau':50e-6
            }
        st.session_state['simulation_dict'] = {
            'trigger':0.0004, # 0.4 ms\n",
            'total_time':0.004, # 4 ms total\n",
            'sampling_rate':1e7
            }
        st.session_state['others'] = {
            'beta':0.4,
            'tau_stretched':50e-6,
            'exponential_type':'Single Exponential',
            'tau1':50e-6,
            'tau2':500e-6
            }
        st.session_state['CC_cantilever_dict'] = {
            'amp_invols':1.15821e-07,
            'def_invols':1.15821e-07,
            'soft_amp':0.3,
            'drive_freq':279173,
            'res_freq':279173,
            'k':20,
            'q_factor':450
            }

        st.session_state['CC_force_dict'] = {
            'es_force':3.72358e-08,
            'delta_freq':-170, 
            'tau':50e-6
            }
        st.session_state['CC_simulation_dict'] = {
            'trigger':0.0004, # 0.4 ms\n",
            'total_time':0.004, # 4 ms total\n",
            'sampling_rate':1e7
            }
        st.session_state['CC_others'] = {
            'beta':0.4,
            'tau_stretched':50e-6,
            'exponential_type':'Single Exponential',
            'tau1':50e-6,
            'tau2':500e-6
            }

def update_dict(dict_name, key):    
    st.session_state[dict_name][key] = st.session_state[key]

def copy_values(ourDict, dictUsed):
    target_dict = st.session_state[ourDict]
    source_dict = st.session_state[dictUsed]
    for key in target_dict:
        if key in source_dict:
            target_dict[key] = source_dict[key]
        else:
            st.write('Didnt work babes')
            
def copy_ALL_values(dict1A, dict2A, dict1B, dict2B, dict1C, dict2C, dict1D, dict2D):
    copy_values(dict1A, dict2A)      
    copy_values(dict1B, dict2B)      
    copy_values(dict1C, dict2C)      
    copy_values(dict1D, dict2D)      
 
Page1
with st.sidebar:
    cant_parms = st.session_state['cantilever_dict']
    force_parms = st.session_state['force_dict']
    sim_parms = st.session_state['simulation_dict']
    other_parms = st.session_state['others']

    for key in cant_parms:
        st.session_state[key] = cant_parms[key]
    for key in force_parms:
        st.session_state[key] = force_parms[key]
    for key in sim_parms:
        st.session_state[key] = sim_parms[key]
    for key in other_parms:
        st.session_state[key] = other_parms[key]
        
    exponential_type = st.selectbox('', ('Single Exponential','Stretched Exponential', 'Bi Exponential'), key='exponential_type', on_change=update_dict, args=['others', 'exponential_type'])
    
    if other_parms['exponential_type'] == 'Stretched Exponential':
        beta = st.number_input('Beta', key='beta',on_change=update_dict, args=['others', 'beta'], format='%f')
        tau_stretched = st.number_input('Tau', key='tau_stretched',on_change=update_dict, args=['others', 'tau_stretched'], format='%e')
    if other_parms['exponential_type'] == 'Bi Exponential':
        tau1 = st.number_input('Tau 1', key='tau1', on_change=update_dict, args=['others', 'tau1'], format='%e')
        tau2 = st.number_input('Tau 2', key='tau2',on_change=update_dict, args=['others', 'tau2'], format='%e')
    with st.expander('Cantilever Parameters', expanded = True):
        amp_invols = st.number_input('Amplitude (volts to meters)', key='amp_invols', on_change=update_dict, args=['cantilever_dict', 'amp_invols'], format='%e')
        def_invols = st.number_input('Deflection (volts to meters)', key='def_invols', on_change=update_dict, args=['cantilever_dict', 'def_invols'], format='%e')
        soft_amp = st.number_input('Soft Amplitude', key='soft_amp', on_change=update_dict, args=['cantilever_dict', 'soft_amp'], format='%f')
        drive_freq = st.number_input('Driving Frequency', key='drive_freq', on_change=update_dict, args=['cantilever_dict', 'drive_freq'])
        res_freq = st.number_input('Resonance Frequency', key='res_freq', on_change=update_dict, args=['cantilever_dict', 'res_freq'])
        k = st.number_input('Spring Constant', key='k', on_change=update_dict, args=['cantilever_dict', 'k'])
        q_factor = st.number_input('Cantilever Quality', key='q_factor', on_change=update_dict, args=['cantilever_dict', 'q_factor'])
    with st.expander('Force Parameters', expanded = False):
        es_force = st.number_input('Electrostatic Force', key='es_force', on_change=update_dict, args=['force_dict', 'es_force'], format='%e')
        delta_freq = st.number_input('Delta Frequency', key='delta_freq', on_change=update_dict, args=['force_dict', 'delta_freq'])
        if other_parms['exponential_type'] == 'Single Exponential':
            tau = st.number_input('Tau', key='tau', on_change=update_dict, args=['force_dict', 'tau'], format='%e')
        else:
            tau = 50e-6
    with st.expander('Simulation Parameters', expanded = False):
        trigger = st.number_input('Trigger (ms)', key='trigger', on_change=update_dict, args=['simulation_dict', 'trigger'], format='%f')
        total_time = st.number_input('Total Time(ms)', key='total_time', on_change=update_dict, args=['simulation_dict', 'total_time'], format='%f')
        sampling_rate = st.number_input('Sampling Rate (ms)', key='sampling_rate', on_change=update_dict, args=['simulation_dict', 'sampling_rate'], format='%e')  
Page2 (Where the button is used)
cant_parms = st.session_state['CC_cantilever_dict']
force_parms = st.session_state['CC_force_dict']
sim_parms = st.session_state['CC_simulation_dict']
other_parms = st.session_state['CC_others']

for key in cant_parms:
    st.session_state[key] = cant_parms[key]
for key in force_parms:
    st.session_state[key] = force_parms[key]
for key in sim_parms:
    st.session_state[key] = sim_parms[key]
for key in other_parms:
    st.session_state[key] = other_parms[key]


exponential_type = st.selectbox('', ('Single Exponential','Stretched Exponential', 'Bi Exponential'), key='exponential_type', on_change=update_dict, args=['CC_others', 'exponential_type'])


if other_parms['exponential_type'] == 'Stretched Exponential':
    beta = st.number_input('Beta', key='beta',on_change=update_dict, args=['CC_others', 'beta'], format='%f')
    tau_stretched = st.number_input('Tau', key='tau_stretched',on_change=update_dict, args=['others', 'tau_stretched'], format='%e')
if other_parms['exponential_type'] == 'Bi Exponential':
    tau1 = st.number_input('Tau 1', key='tau1', on_change=update_dict, args=['CC_others', 'tau1'], format='%e')
    tau2 = st.number_input('Tau 2', key='tau2',on_change=update_dict, args=['CC_others', 'tau2'], format='%e')
with st.expander('Cantilever Parameters', expanded = True):
    amp_invols = st.number_input('Amplitude (volts to meters)', key='amp_invols', on_change=update_dict, args=['CC_cantilever_dict', 'amp_invols'], format='%e')
    def_invols = st.number_input('Deflection (volts to meters)', key='def_invols', on_change=update_dict, args=['CC_cantilever_dict', 'def_invols'], format='%e')
    soft_amp = st.number_input('Soft Amplitude', key='soft_amp', on_change=update_dict, args=['CC_cantilever_dict', 'soft_amp'], format='%f')
    drive_freq = st.number_input('Driving Frequency', key='drive_freq', on_change=update_dict, args=['CC_cantilever_dict', 'drive_freq'])
    res_freq = st.number_input('Resonance Frequency', key='res_freq', on_change=update_dict, args=['CC_cantilever_dict', 'res_freq'])
    k = st.number_input('Spring Constant', key='k', on_change=update_dict, args=['CC_cantilever_dict', 'k'])
    q_factor = st.number_input('Cantilever Quality', key='q_factor', on_change=update_dict, args=['CC_cantilever_dict', 'q_factor'])
with st.expander('Force Parameters', expanded = False):
    es_force = st.number_input('Electrostatic Force', key='es_force', on_change=update_dict, args=['CC_force_dict', 'es_force'], format='%e')
    delta_freq = st.number_input('Delta Frequency', key='delta_freq', on_change=update_dict, args=['CC_force_dict', 'delta_freq'])
    if other_parms['exponential_type'] == 'Single Exponential':
        tau = st.number_input('Tau', key='tau', on_change=update_dict, args=['CC_force_dict', 'tau'], format='%e')
    else:
        tau = 50e-6
with st.expander('Simulation Parameters', expanded = False):
    trigger = st.number_input('Trigger (ms)', key='trigger', on_change=update_dict, args=['CC_simulation_dict', 'trigger'], format='%f')
    total_time = st.number_input('Total Time(ms)', key='total_time', on_change=update_dict, args=['CC_simulation_dict', 'total_time'], format='%f')
    sampling_rate = st.number_input('Sampling Rate (ms)', key='sampling_rate', on_change=update_dict, args=['CC_simulation_dict', 'sampling_rate'], format='%e') 

st.button('Copy from Sim', on_click=copy_ALL_values, args=['CC_cantilever_dict','cantilever_dict','CC_force_dict','force_dict','CC_simulation_dict','simulation_dict','CC_others','others'])   

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.