Uses the file uploader widget to read in an Excel file
Gets the user to choose a worksheet they’re interested in previewing using a selectbox dropdown
Displays a preview of the worksheet
I’m using a callback from st.selectbox to display the preview, so that it only displays after the user has actively chosen a worksheet they’re interested in. But in order to do so I need to have defined my callback function before calling st.selectbox - and that’s leading the preview to appear above the file uploader and selectbox widgets in the app, rather than where I want it, below those widgets.
This is my code (Streamlit 1.3.1) and a screengrab of my output.
import streamlit as st
import pandas as pd
# CREATE APP
# Add file_uploader
uploaded_file = st.file_uploader(
label='Upload a file', type=['xls', 'xlsx', 'xlsm'],
key='file-uploader',
help='''
Upload an Excel file. The file must be
closed in order for you to upload it.
'''
)
# Define function that loads sheet preview
def load_sheet_preview():
st.dataframe(df[st.session_state.selectbox_sheet])
# Add selectbox
if uploaded_file is not None:
df = pd.read_excel(uploaded_file, sheet_name=None) # sheet_name=None needs explicitly including - it isn't the default # noqa: E501
st.selectbox(
key='selectbox_sheet',
label='Select worksheet',
options=df.keys(),
on_change=load_sheet_preview
)
I suspect I’ll kick myself when I hear the solution but I can’t think how to get around this. Wrapping the code that produces the file uploader and the selectbox in functions, ordered as I want them to appear in the app, doesn’t do it.
when you callback the load_sheet_preview function, you write to the page with st.dataframe. Since the callback function is written to the page before anything else during the streamlit reload, it means that the dataframe is written to the page before moving to the if uploaded_file is not None: statement.
I would need to thing about the solution some more, but maybe this helps you get there first.
Yeah, I think that’s right. I can’t figure out how to change my code to get around the issue though - I can’t move where the function is defined, and I’m not sure what other changes I can make to my code. If you do have any ideas, I’d be very grateful to hear them!
I’ve now tried this two ways. Including the code that displays the preview in the container gives a ‘Bad message format’ error (’‘setIn’ cannot be called on an ElementNode’"). I think it might be related to the points raised here - callbacks aren’t designed to change the display of the app.
Including the file_uploader and selectbox elements within the container instead doesn’t give an error, but the order of elements remains the same (i.e. the preview above the other elements), presumably because functions are evaluated first.
Happy to share my code on any of this if anyone would like to see it.
I have come up with a different workaround though which I’ll post below.
I’ve come up with a solution to this which involves (explicitly) using st.session_state rather than a callback and adding a default value to the dropdown options. The preview isn’t loaded while the default option is selected.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import pandas as pd
import streamlit as st
# CREATE APP
# Add file_uploader
uploaded_file = st.file_uploader(
label='Upload a file', type=['xls', 'xlsx', 'xlsm'],
key='file-uploader',
help='''
Upload an Excel file. The file must be
closed in order for you to upload it.
'''
)
# Add selectbox
if 'selectbox_sheet' not in st.session_state: # Initialise variable
st.session_state['selectbox_sheet'] = '--'
if uploaded_file is not None:
sheets_dict = pd.read_excel(uploaded_file, sheet_name=None) # This creates a dictionary of dataframes. sheet_name=None needs explicitly including - it isn't the default # noqa: E501
default_option = {'--': ''}
selectbox_options = dict(**default_option, **sheets_dict) # Join dictionaries # noqa: E501
st.selectbox(
key='selectbox_sheet',
label='Select worksheet',
options=selectbox_options.keys()
)
# Load sheet preview
if st.session_state.selectbox_sheet != '--':
st.dataframe(sheets_dict[st.session_state.selectbox_sheet])
If there’s a more elegant solution I’d still be interested to hear it - I would have thought it would be possible to do something using st.container().
Thanks for stopping by! We use cookies to help us understand how you interact with our website.
By clicking “Accept all”, you consent to our use of cookies. For more information, please see our privacy policy.
Cookie settings
Strictly necessary cookies
These cookies are necessary for the website to function and cannot be switched off. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms.
Performance cookies
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us understand how visitors move around the site and which pages are most frequently visited.
Functional cookies
These cookies are used to record your choices and settings, maintain your preferences over time and recognize you when you return to our website. These cookies help us to personalize our content for you and remember your preferences.
Targeting cookies
These cookies may be deployed to our site by our advertising partners to build a profile of your interest and provide you with content that is relevant to you, including showing you relevant ads on other websites.