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

st.protect or st.lock : Option to override all widgets to disabled=True during a process (and later discard override) #6756

Open
MathCatsAnd opened this issue May 27, 2023 · 4 comments
Labels
area:utilities type:enhancement Requests for feature enhancements or new features

Comments

@MathCatsAnd
Copy link
Contributor

MathCatsAnd commented May 27, 2023

Problem

When expensive processes are initiated, a lot of care must be taken to disable widgets to prevent a user taking action during the process. This can apply to the widget initiating the process or even other widgets on the page, including "shadow widgets."

Solution

It would be nice to have some function like st.lock() that makes Streamlit ignore all click events. This could be in the form of iterating through all widgets it has in memory to override their disabled argument to True (directly or via some hidden, dominant attribute) or a higher-level act in JavaScript ignore click events in general. The locked state should end when the page completely loads (including via st.stop) or when a companion method is called (e.g. st.unlock() or st.lock(end=True)).

MVP: If st.lock is required to be called before the first widget loads on the page, Streamlit could jump ahead to iterate through all widgets in memory to disable them on the front end (taking care of shadow widgets), then proceed with the script. Then with st.lock being active during a page load, all widgets could get silently passed an extra, dominant keyword argument force_disabled=True so the widgets continue to be disabled as Streamlit executes their functions. When Streamlit gets to the end of this "locked" page load (whether actually getting to the end or calling st.stop()), Streamlit would automatically rerun the page (erroring if it encountered st.lock again). With this "normal" page load the widgets would no longer be receiving the force_disabled=True kwarg and thus be restored to the proper state as specified in their functions.

It would thus be natural to call st.lock as the first thing in a callback, since a rerun after a callback-prefixed load would automatically not include the callback function containing the lock.

Alt MVP: st.lock silences all JavaScript click events within the app body. Getting to the end or calling st.unlock undoes that silence.

Possible additions: Options could be added to include/exclude naviagation links from the lock, or to lock main body widgets independently of sidebar widgets. Widgets could also all be given an kwarg lock='exempt' to flag a specific widget as exempt from an app lock.

Additional context

#156
#6415


Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.

@MathCatsAnd MathCatsAnd added the type:enhancement Requests for feature enhancements or new features label May 27, 2023
@cmayoracurzio
Copy link

Hi there @MathCatsAnd ,

First of all congrats :)

I deal with long processes -I like to believe quite gracefully- using the points below. Sharing in case it helps, but let me know if you have questions so I can clarify / cook a simple example app.

  • Add a callback function to the button that triggers the long process. The callback should simply set a session state variable st.session_state.lock_widgets to True.
  • Set the disabled parameter of all widgets to st.session_state.lock_widgets.
  • Avoid "shadow widgets" entirely by rendering widgets first, and placing any long processes / expensive calculations at the bottom.
  • At the end of your long process, set st.session_state.lock_widgets back to False and then force a rerun with st.experimental_rerun.

@MathCatsAnd
Copy link
Contributor Author

@marduk2 Yep, makes perfect sense! It's definitely possible to get the behavior I described as-is, but it seems like a common enough scenario that I thought some built-in option would be nice. One extra thing I would add for anyone wandering through, is that sometimes I have widgets being disabled for other reasons within a script so I wouldn't set disabled=st.session_state.lock_widgets directly and instead have a logical expression to treat the lock status as an override. Say you lock a widget with st.session_state.submitted, then I'd have the disabled parameter set by:

st.session_state.submitted or st.session_state.lock_widgets)

Simple, just noting for posterity. :)

@wiaa1234
Copy link

+1

@Odrec
Copy link

Odrec commented Dec 31, 2023

I tried the st.session_state.lock_widgets = True callback suggested by @cmayoracurzio but you I have to rerun the entire app while the long process is running for the widgets to be disabled which is not ideal. Or am I missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:utilities type:enhancement Requests for feature enhancements or new features
Projects
None yet
Development

No branches or pull requests

5 participants