[Python] Looking for a proper way of resolving ghost / shadow / mirage left by widget after...

Discussão em 'Python' iniciado por Stack, Maio 16, 2025.

  1. Stack

    Stack Membro Participativo

    Crossposting this from streamlit.io

    I have been having issues with ghost / shadow / mirage (or whatever it may be called) that is left by a widget, generally after a change of session_state and with a corresponding change of widgets showing on page.

    I do have a hacky workaround and a somewhat deeper insight into what is causing the issue. However, I am really dissatisfied with the workaround, and hope that this discussion can lead to a better solution.

    Code to replicate

    I created a simple application that can replicate the issue and show the workaround, available here.

    EDIT: Posting code of simple_app.py from the above linked repo here as requested by DarkBee:

    """Simple application to demonstrate issue
    Run main.py to load this file locally."""

    import streamlit as st
    from datetime import datetime
    from time import sleep

    sessions = {"show_hint": False}
    for state, value in sessions.items():
    if state not in st.session_state.keys():
    st.session_state[state] = value

    # Some widget on top, to make this feel more like a real application
    st.text_input("Type your answer here:")

    # Toggling session_state for "show_hint"
    if st.button("Show hint"):
    if st.session_state.show_hint:
    print("Hint hidden!")
    st.session_state.show_hint = False
    else:
    print("Hint revealed!")
    st.session_state.show_hint = True

    # draws widget only if session_state is True:
    if st.session_state.show_hint:
    st.write("Trust yourself!")

    display = st.empty()
    print(display)

    # workaround: move next row "fake_display" from comment to code
    # fake_display = st.empty()
    while True:
    current_time = datetime.now().strftime("%H:%M:%S")
    display.metric("Current time", current_time)
    sleep(1)


    The app consist off:

    1. Input box on top (not linked to anything)
    2. Button that toggles change in session state (“Show hint”)
    3. A text output that shows up depending on session state
    4. A timer that shows current time (placed in a loop)

    Description of issue with screenshots

    Default state after kicking off: [​IMG]

    State after clicking on button and changing session state (showing printout “Trust yourself”): [​IMG]

    State after clicking on button again (hiding “Trust yourself”). Now shadow of the timer is visible at the bottom: [​IMG]

    Root cause

    I placed two print statements in the code. One is to just show what I clicked, the other is to print out information about the st.empty element that is being used for the time display. [​IMG]

    What is apparent here is that in the hidden state (“Trust yourself” not show), the _index of the widget is 2, but if I change session and show the “Trust yourself” hint, the _index is 3.

    Inferring from above: Streamlit shows widgets and assigns each an index. In the initial state I only have 3 widgets total:

    index 0: text input
    index 1: button
    index 2: timer

    In the next session state, I have 4 widgets:
    index 0: text input
    index 1: button
    index 2: text output
    index 3: timer

    However, upon hiding the text output, the next page refresh will only replace/overwrite the indexes with the widgets that should be visible this time, so any additional elements still remain:

    index 0: text input
    index 1: button
    index 2: timer
    index 3: timer (shadow from previous session)

    Workaround

    Place another element on the page after the last element, that is otherwise invisible. Then in the next rerun, the indexes will be:

    index 0: text input
    index 1: button
    index 2: timer
    index 3: invisible element
    index 4: invisible element (likely shadowing from previous session, but user won't see it)

    E.g., in this case a simple st.empty: [​IMG]

    Edit: posting code version

    # workaround: move next row "fake_display" from comment to code
    fake_display = st.empty()


    This might work from the end-users perspective, but is bad practice.

    I would be happy for any insight about how to properly work around the issue (e.g. without placing as many unused invisible elements in the code as I need to adjust the index for the temporarily shown widgets)

    Continue reading...

Compartilhe esta Página