Flicker problem in first run due to st.columns?

Summary

I have a very strange behaviour. Only on the first run of the code and with the first change in the st.data_editor - e.g. in column B, you recognize., that the page flickers exactly one time. After the first run, it never appears again.

Steps to reproduce

Code snippet:

import pandas as pd
import streamlit as st

st.set_page_config(layout="wide", page_title="Test")

# Cache data from mysql database
@st.cache_data
def run_query():
    # Here is normally my SQL query 
    df_sql=pd.DataFrame({"A":['1','2','3'],"B":['2','3','4'],"C":['3','4','5']})
    return df_sql

# Initialize session states for further needs
if "df" not in st.session_state:
    st.session_state['df'] = run_query()

if "df_ed" not in st.session_state:
    st.session_state["df_ed"] = st.session_state["df"]

# Create columns
col1, col2 = st.columns([8,2])

# Create data_editor @ column 1
st.session_state["df_ed"]=col1.data_editor(st.session_state['df'],hide_index=True, use_container_width=True)

With this code, everything works:

import pandas as pd
import streamlit as st

st.set_page_config(layout="wide", page_title="Test")

# Cache data from mysql database
@st.cache_data
def run_query():
    # Here is normally my SQL query 
    df_sql=pd.DataFrame({"A":['1','2','3'],"B":['2','3','4'],"C":['3','4','5']})
    return df_sql

# Initialize session states for further needs
if "df" not in st.session_state:
    st.session_state['df'] = run_query()

if "df_ed" not in st.session_state:
    st.session_state["df_ed"] = st.session_state["df"]

# Create data_editor @ column 1
st.session_state["df_ed"]=st.data_editor(st.session_state['df'],hide_index=True, use_container_width=True)

Expected behavior:

No flicker with first run.

Actual behavior:

The window flickers one time in the first run and with the first edit in the table. The problem has something to do with st.columns. If I don´t use st.columns (col1) and call st.data_editor instead of col1.data_editor, everything is fine. But I need these columns both for other purposes.
What is the problem in the first example?

Debug info

  • Streamlit version: 1.27.2
  • Python version:3.9.17
  • OS version: Win10
  • Browser version: Mozilla 115.3.1esr

Any ideas or could anybody repoduce this behaviour on his client?
It seems to be a bug, but I only want to raise one, if someone else confirms this behaviour.

Can you make a screen recording to show how this flicker looks?

Could only provide in mp4-format, but isn´t accepted here. Any other ideas, where you could pick it?

Hmmm, I thought MP4s were accepted… Is it a size issue or is it not accepting the file format? There are conversion services to reformat, resize, or compress videos. Alternatively, can you upload it to YouTube as an unlisted video to share?

Hi @mathcatsand,

sorry, I found no solution. Youtube also not possible because no account.
But - have you tried my code? You must only start it, click on one of the cells, type any value in it and press return. You see, that the app will be rendered again. After the next change, this doesn’t appear any more. The flicker appears 50-100 ms … not longer. But I found, it´s disturbing and looks like a not appropriate programmed app.

I did execute your code, but I didn’t see anything unexpected. I was on Python 3.11 before, so I just retried it with Python 3.9. I repeated it several times over and it’s usually barely perceptible if at all, but occasionally I see it. Here’s the worst instance out of a dozen-plus attempts to recreate it. It might be some tiny little optimization bug?

2023-10-13_15-47-54 (3) copy

Edit: I’m noticing it more often in a narrow view when the columns are stacked than I am in a full-width screen. I also got it to do something completely different that is definitely an error. So I’m working on reproducing it into a reportable bug.

@teq508

Try setting show_spinner=False in your cache_data function:

# Cache data from mysql database
@st.cache_data(show_spinner=False)
def run_query():
    # Here is normally my SQL query 
    df_sql=pd.DataFrame({"A":['1','2','3'],"B":['2','3','4'],"C":['3','4','5']})
    return df_sql

This will prevent Streamlit from parsing the instantaneously small difference of a spinner that appears and disappears on one run, and then doesn’t appear at all on successive runs. (Thanks @lukasmasuch!)

Hi @mathcatsand,

thanks for checking this issue. With this code snippet, it really works with setting the spinner to off.

But two things:

  1. Nevertheless I don’t understand the solution - the flicker appears, when this part of code is not running any more.
  2. Testing in my bigger code, the solution still doesn’t work. But I recognized, that I have some other widgets, e.g. from pyplot, where not a spinner appears, but therefore loading field for a short moment. This could be lead to a similar problem. Is it possible to globally set, that no loading bars shall appear?

Between script runs, if there is any difference in what Streamlit encounters on the page, elements will reload from the point of difference and beyond. Streamlit tries to be efficient and not reconstruct elements if it can, but any difference means that Streamlit can’t align the elements from the previous run to the next.

In the case of the cached function, the query is executed on first run (whether already cached or not) to load the data into Session State, then doesn’t run again. That’s what’s creating the difference between the first run and all successive runs. There is a “frontend component” associated to the conditionally executed function unless the spinner is set to be nonexistent.

In general, conditional elements will cause reloading of other elements between script runs where there existence changes. A way to get around this when you do have conditional elements is to use containers. If you have heavier frontend elements, put your conditional elements inside a container that always renders. Streamlit will then be able to keep track everything that comes after that container, even if the elements within the container are being conditionally displayed. This will prevent elements from being reconstructed after your conditional ones.