Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Widget output is different than widget state #6146

Closed
3 of 5 tasks
MathCatsAnd opened this issue Feb 16, 2023 · 17 comments
Closed
3 of 5 tasks

Widget output is different than widget state #6146

MathCatsAnd opened this issue Feb 16, 2023 · 17 comments
Assignees
Labels
feature:multipage-apps feature:state priority:P2 status:confirmed Bug has been confirmed by the Streamlit team type:bug Something isn't working

Comments

@MathCatsAnd
Copy link
Contributor

Checklist

  • I have searched the existing issues for similar issues.
  • I added a very descriptive title to this issue.
  • I have provided sufficient information below to help reproduce this issue.

Summary

This is likely deeply tied to many existing issues regarding the widget cleanup process and confusion over expected behavior. However, I believe it is distinct in that it is a true bug.

As of current, it is expected behavior for widgets with the same key on different pages to not sync. However, I am observing a case where widgets with the same specification (with or without a manually assigned key), will retain the output of a previously rendered instance (unexpected) while forgetting the state (expected).

Related issues regarding widget states:
#6074 [Request for input], considering changes
#5813
#5591
#4989
#4458

I tried this in 1.18.0 and also jumped back to 1.13.0: same bug in both.

Reproducible Code Example

import streamlit as st

name = st.text_input('Name:')
if name != '':
    st.write(f'Hi, {name}! Nice to meet you.')

st.button('Refresh Page')

Steps To Reproduce

  1. Create two pages with this same code
  2. On page 1, enter some text into the widget.
  3. Observe the widget correctly outputs your entry.
  4. Go to the second page.
  5. Observe the widget is empty (expected).
  6. Observe that the message is still written as if the widget had reconnected.
  7. Click the button on the page to see the output update and match its empty state.

Expected Behavior

The output should match the widget state. If state is forgotten, then the output needs to revert to the default value as well.

Current Behavior

Widgets are only half forgetful of their twin's state on a previous page: widget state resets to default, but output does not.

Is this a regression?

  • Yes, this used to work in a previous version.

Debug info

  • Streamlit version: 1.18.0 and 1.13.0
  • Python version: 3.10 and 3.9
  • Operating System: Windows 10
  • Browser: Firefox
  • Virtual environment: yes

Additional Information

No response

Are you willing to submit a PR?

  • Yes, I am willing to submit a PR!
@MathCatsAnd MathCatsAnd added status:needs-triage Has not been triaged by the Streamlit team type:bug Something isn't working labels Feb 16, 2023
@carolinedlu
Copy link
Collaborator

Thanks for sharing these findings, @MathCatsAnd!

@AnOctopus
Copy link
Collaborator

I was able to reproduce this. The widget having the same return value after switching pages isn't surprising, with how widget identity works, so the mystery is why the component in the frontend is reset. Which I should be able to dig into this week.

@MathCatsAnd
Copy link
Contributor Author

I understood the reverse to be true: a widget should reset if you switch pages (unless you do something to manually copy the data stored in the session state key). At least, that's how I've understood the staff responses in the linked issues above.

@sfc-gh-jcarroll
Copy link
Collaborator

I just went through and closed a few other issues that were having confusion around this underlying behavior.

I think this is a pretty gnarly bug. I am wondering with #7003 merged if it's any easier to address now. We should discuss!

@AnOctopus
Copy link
Collaborator

It is not directly easier to address with the widget stability work.

The frontend clears all its state when a new session is started, which happens when switching to a new page. The backend preserves session state (including widget state), so the identical widgets on different pages are seen as the same and the state transfers.

There are some potential uses for sharing the widget state across pages like this, but widgets on different pages being distinct is what I think most people expect, and usecases for sharing would make sense to address as part of potential MPA widget persistence work.

The work required for this is pretty reasonable, so I expect to have a fix soon.

@AnOctopus
Copy link
Collaborator

Should be fixed in the next release

@Asaurus1
Copy link
Contributor

Asaurus1 commented Sep 14, 2023

@AnOctopus What is the behavior of the merged fix in the following scenario

  • Have Page 1 and Page 2 with identical code as specified by the OP
  • User switches from Page 1 -> Page 2 -> Page 1

In this scenario, when going back from Page 2 to Page 1 the displayed value of the widget in the browser will have been reset, but, assuming that the Page 1 hash has not changed, the value returned by the st.text_input call would be the value from the FIRST run of Page 1, since that widget's ID (with the page hash included) has persisted in session_state?

There might be a way out by relying on the functionality where widgets that have not been seen on a given run are removed from state (thus, when Page 2 runs, the Page 1 widget's data is cleared), but I'm not sure if that works as expected for widgets with user keys, and I'm having trouble getting the master branch of streamlit to build on my workstation right now (it's an ENV thing) so I cannot actively test this hypothesis.

@MathCatsAnd
Copy link
Contributor Author

MathCatsAnd commented Sep 23, 2023

@Asaurus1 By switching from Page 1 to Page 2, the widget from Page 1 gets deleted from memory and now you have the widget from Page 2 in memory. Switching back to Page 1 is the same. Since Streamlit deletes widget information as soon as the widget is not rendered, you will observe resetting behavior without other steps to keep that information between pages. This should be the same whether or not a user-defined key is passed.

PS In case you didn't know, pip install streamlit-nightly will let you easily work with the latest from the develop branch in streamlit/streamlit without having to build the library manually. It's updated every night. 😁 Though at this point, version 1.27.0 has been released so the PR is in the main library now.

@heitormarcal
Copy link

Hi, everyone!

Do we have any update on this issue?

Thanks in advance

@sfc-gh-jcarroll
Copy link
Collaborator

It was fixed in Streamlit 1.27

@heitormarcal
Copy link

Hi, Joshua! Thanks for the answer!

Additionally, I'am still having a problem regarding the session state of the widgets when changing pages.
I've seen in other issues here in Github that it's a common problem, but didn't find a way to fix it.
Basically, if i set a widget named st.session_state['example'] to 'test', then change the page and come back to it, it resets the widget.

How can I solve it?

Thanks again!

@heitormarcal
Copy link

I forgot to mention that the widget I'm talking about is st.selectbox

@Asaurus1
Copy link
Contributor

Asaurus1 commented Oct 4, 2023

It was fixed in Streamlit 1.27

I haven't tested this since 1.27.1 but will check it when I get home 😊

EDIT: still able to create this issue in 1.27.1
image

@sfc-gh-jcarroll
Copy link
Collaborator

Additionally, I'am still having a problem regarding the session state of the widgets when changing pages.
I've seen in other issues here in Github that it's a common problem, but didn't find a way to fix it.
Basically, if i set a widget named st.session_state['example'] to 'test', then change the page and come back to it, it resets the widget. How can I solve it?

@heitormarcal ah, this is intended behavior currently. We're considering a change or a better way to support this, see #6074. In the meantime, the best way to do it today is documented here.

@heitormarcal
Copy link

heitormarcal commented Oct 5, 2023

Hi, Joshua!

I'm trying to do what is documented as below:
def salvar_valor_widget():
# Copy the value to the permanent key
st.session_state['_celula_atendimento'] = st.session_state["celula_atendimento"]

st.session_state["celula_atendimento"] = st.session_state["_celula_atendimento"]

and my widget is:
st.session_state['celula_atendimento'] = st.selectbox("OPTIONS", ['A', 'B', 'C', 'D', 'E', 'F'], on_change=salvar_valor_widget)

However, when I choose 'B', change the page and then return to the original page, the value is back to 'A'.

Do you know what might be happening?

Thanks a lot in advance!

@lavint
Copy link

lavint commented Oct 11, 2023

@sfc-gh-jcarroll

I used the suggested best way to do it. It works well when dealing with 1 multi-select on the side bar. However, when I do it for 2 multi-select and clear out one of the multi-select boxes to re-select, both of the multi-select disappeared and I got an error. Any idea why?

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Current code:

def save_value(key):
    st.session_state[key] = st.session_state["_"+key]

def get_value(key):
    st.session_state["_"+key] = st.session_state[key]



def selected_values(df):

    cities = np.sort(df['city'].unique())
    ptypes = np.sort(df['ptype'].unique())

    if "multi_select_cities" not in st.session_state:
        st.session_state["multi_select_cities"] = cities

    city_container = st.container()
    get_value("multi_select_cities")
    selected_cities = city_container.multiselect("Select one or more city:",
                                                cities, 
                                                key="_multi_select_cities",
                                                on_change=save_value,
                                                args=['multi_select_cities'])

    
    if "multi_select_ptypes" not in st.session_state:
        st.session_state["multi_select_ptypes"] = ptypes

    ptype_container = st.container()
    get_value("multi_select_ptypes")
    selected_ptypes = ptype_container.multiselect("Select one or more pytpes:",
                                                ptypes, 
                                                key="_multi_select_ptypes",
                                                on_change=save_value,
                                                args=['multi_select_ptypes'])


    # all_ptypes = st.checkbox("Select all", key=2)
    # if all_ptypes:
    #     selected_ptypes = ptype_container.multiselect("Select one or more property type:", ptypes, ptypes)
    # else:
    #     selected_ptypes =  ptype_container.multiselect("Select one or more property type:", ptypes, default=['SF'])


    return selected_cities, selected_ptypes

@sfc-gh-jcarroll
Copy link
Collaborator

These questions with code examples will be better answered on https://discuss.streamlit.io/

The code examples in the prior two comments don't seem to match the one in the docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature:multipage-apps feature:state priority:P2 status:confirmed Bug has been confirmed by the Streamlit team type:bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants