Custom widget to connect many applications in Streamlit


Initially I was using a radio button or a selectbox dropdown to list all the applications. But the UI is not so appealing. I am searching for other possible options to better display the applications but I couldn’t find one. So then I tried to implement using button options each for an application as similar to the one shown in the screenshot attached. Each application is a button option and the css for it is modified for width option alone. And the buttons are grouped under beta_expander based on the category. And I add items for the applications based on the button select.

But unfortunately this option also doesn’t work for me. Once I select the button specific to the application the file_uploader() appears on it. But once I upload the file the page again refresh and goes back to the initial state of blank screen.

Is there a way we can bring similar functionality using any options? Code for the screenshot here.

import streamlit as st

def local_css(file_name):
    with open(file_name) as f:
        st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

local_css("../css/style.css")
# Title of the applcation
st.sidebar.title("Title")

# Define the Highlevel Expanders
cm_expander = st.sidebar.beta_expander("Application Cat - 1 ",expanded=True)
sa_button_clicked = cm_expander.button("Application - 1")
sr_button_clicked = cm_expander.button("Application - 2")
sb_button_clicked = cm_expander.button("Application - 3")


ie_expander = st.sidebar.beta_expander("Application Cat - 2 ",expanded=True)
ner_button_clicked = ie_expander.button("Application - 4")
kp_button_clicked = ie_expander.button("Application - 5")
kw_button_clicked = ie_expander.button("Application - 6")

us_expander = st.sidebar.beta_expander("Application Cat - 3 ",expanded=True)
tm_button_clicked = us_expander.button("Application - 7")
wc_button_clicked = us_expander.button("Application - 8")

about_button_clicked = st.sidebar.button("About")


if ner_button_clicked:
    st.header("Application 4")
    uploaded_file = st.file_uploader("Choose a file", type="txt", key="123")
    print(uploaded_file, type(uploaded_file))
    if uploaded_file is not None:
        bytes_data = uploaded_file.read()
        str_data = bytes_data.decode("utf-8")
        if str_data:
            ner_doc_text = st.text_area("Document Text",str_data, height=150, key='rdsta1')
            st.write(ner_doc_text)

Hey @Chakra!

Welcome to the Streamlit Community! :tada: :tada: :tada: :tada: :tada: :tada: :tada: :partying_face:

The issue your running into here is because on each button click Streamlit reruns the script from top to bottom, thus it resets your application choice click. There are ways around this if you use Session State, or if you do a bit of trickery with the order that you call your functions in along with pickling some info.

I have attached links to some other discussion that may help!



Hope this is helpful!
Happy Streamlit-ing!
Marisa

Thanks @Marisa_Smith for sharing this. But still I am finding little hard to understand the concept on Session State. Is there any simple example to understand it better. Thanks.

Hey @Chakra, welcome to the community :slight_smile:

I wrote a bit about buttons and state here, it should apply to your need if you translate to your problem by a SessionState check to if ner_button_clicked.
The thing to understand is when you interact with the FileUploader, the script reruns with the ner_button_clicked button state set to None since you didn’t click on it at the last interaction, so we must somehow preserve this info. For now SessionState is the way to go to preserve info between two runs. You can store which button was last pressed in the SessionState and display the corresponding app.

BTW I like this proposal :slight_smile: those buttons are appealing.

Fanilo

Thanks @andfanilo. I understood the problem now. I will work on it.

As I am using many buttons here, I need to create that many arguments (to the number of buttons in the application right)?

Also, Is there a way to clear all the session arguments at once? or should I manually write state clearing logic based on the conditions in the app.?

Thanks.

Yeah. There may be a way to optimize this better but first try by writing everything down, with all of the if/else :wink:

If you force reload your browser (with “CTRL + F5” on Windows) the SessionState gets reset. Otherwise you should do this manually.

Cheers,
Fanilo


The following is a draft of optimization that ended up not working though XD just to understand I put all apps inside a Dict and reference the latest clicked button in the state as a key of this Dict to retrieve the app to run
import streamlit as st
import SessionState

session_state = SessionState.get(last_button_checked=None)

def define_button(name: str):
    if st.button(name):
        session_state.last_button_checked = name
        return True
    session_state.last_button_checked = None
    return None

def app1():
    st.write("Hello")

def app2():
    st.header("Application 4")
    uploaded_file = st.file_uploader("Choose a file", type="txt", key="123")
    print(uploaded_file, type(uploaded_file))
    if uploaded_file is not None:
        bytes_data = uploaded_file.read()
        str_data = bytes_data.decode("utf-8")
        if str_data:
            ner_doc_text = st.text_area("Document Text",str_data, height=150, key='rdsta1')
            st.write(ner_doc_text)

def app3():
    st.write("World")

mapping = {
    "app1": app1,
    "app2": app2,
    "app3": app3
}

ie_expander = st.sidebar.beta_expander("Category",expanded=True)
with ie_expander:
    ner_button_clicked = define_button("app1")
    kp_button_clicked = define_button("app2")
    kw_button_clicked = define_button("app3")

st.write(session_state.last_button_checked)
if session_state.last_button_checked is not None:
    mapping[session_state.last_button_checked]()

Thank a lot @andfanilo.

The code snippet you had shared helps a lot. Thanks.

2 Likes