How to apply a "lock all" function to session_state?

This follows on my from my question about applying a lock. I want to extend this by having a checkbox to lock each single number input, and another checkbox to lock all inputs.

The problem I have is that my function lock_all seems to get run always, when I only want it to run when it is clicked. Any changes to Lock 1 or Lock 2 get over-ridden - I can either have everything locked, or nothing locked! Any ideas how I could get this working?

Here is some working code to explain:

import streamlit as st

# need to declare the lock before function
if "lock_all" not in st.session_state:
    st.session_state["lock_all"] = False

# this is run by the lock all button
def lock_all():
    st.session_state["lock_1"] = st.session_state["lock_all"]
    st.session_state["lock_2"] = st.session_state["lock_all"]

# lock all checkbox
lock_all_box = st.checkbox(
    "Lock all",
    value=False,
    on_change=lock_all(),
    key="lock_all")

# lock_1 checkbox
lock_1 = st.checkbox(
    "Lock 1",
    value=False,
    key="lock_1")

# lock_2 checkbox
lock_2 = st.checkbox(
    "Lock 2",
    value=False,
    key="lock_2")

# lockable parameter 1
param_1 = st.number_input(
    "Parameter 1",
    key="param1",
    disabled=st.session_state.lock_1,)

# lockable parameter 2
param_2 = st.number_input(
    "Parameter 2",
    key="param2",
    disabled=st.session_state.lock_2,)

Thanks for any help, I really appreciate it :slight_smile:

You are executing the function here rather than passing it to the checkbox. The on_change argument expects a function object, i.e., lock_all. In your case, lock_all() just executes that function and returns None, which is then passed to the on_change argument.

2 Likes

Excellent thanks!.. sorry this is more of a python question than a streamlit question, but if lock_all takes args (in my case, I have a grid of parameters, with each row being several lockable parameters, with a lock_all per row), how do I get the arg (the row number) into the function object?

@altanner That’s a great question! Actually, st.checkbox (and most other streamlit widgets) have an args function that let you pass a tuple of arguments that get passed to the callback. See the docs for more st.checkbox - Streamlit Docs

For example:

def my_func(x):
   st.write(x)

st.checkbox("Item 1", on_change=my_func, args=(1,))

st.checkbox("Item 2", on_change=my_func, args=(2,))
1 Like

Amazing, thank you both @blackary and @edsaac, all working now! So pleased! (when this thing is closer to ready I’ll drop it in the community threads, and it’ll blow your minds! ha, well, maybe).

:magic_wand:

1 Like

This is close enough to my problem that I’m re-opening this rather than a full, new ticket. How do I prevent one widget from triggering the on_change even of another? Using the above example:


def my_func(x):
   st.write(x)

st.checkbox("Item 1")

st.text_input("Item 2", on_change=my_func, args=(2,))

Where I want the user to be able to interact with the checkbox, Item1, without triggering Item2, even if there is text in Item2.

So,
Type some text in to the input box, but instead of pressing Enter, just click on the checkbox. As it is, my_func() is triggered for Item2 when Item1 is clicked.

When the user changes the widget’s value, on_change is invoked. That’s what on_change is for.

Except it isn’t precisely
which is what I’m struggling with. It’s a combination of ‘value has changed’ and ‘any action’ is taken. Basically, I would expect (want) this to behave as an atomic action that only takes place when the widget I’m interacting with takes an action
not when any, other widget is activated.
Is there some other method that is expected to fill that use case and I’m just expecting a convenience/convention that doesn’t exist in Streamlit?

The value doesn’t change when you type but when you either press Enter or take the focus away from the widget. The callback is triggered not because you interact with the checkbox but because the text input losses the focus. AFAIK there is no way (within the streamlit framework) to discriminate beetween pressing Enter and losing the focus.

It looks like what you actually want is to detect a keystroke. That is low-level functionality that Streamlit doesn’t offer. Why do you need that?

“Need” is probably strong here. I’m just learning the whole thing, Python, UI implementation, etc. And, Streamlit was used as the web UI example in a video course. So, a) thank you for the clarification about ‘taking focus away’
that at least helps sort out the logic in my head
heh. And, b) the situation I’m working with right now that led to this line of questioning is: A basic task list editor that takes the text_input and turns that into a checklist item above. Once a checkbox item is added to the list of boxes, the user can click any checkbox to complete that task and remove it from the list.
However, a bug can be triggered in that flow by: Link is to my example app on Streamlit
should be publicly available?

  1. Place text in the text_input field
  2. Click on any available checkbox in the list above

Doing so will cause the selected task to be completed (yes) AND a new task to be created (no). Additionally, and rightly so, you’ll generate an error if you click 2, successive checkboxes with the same text sitting in the text_input widget.

So, at this point, and with your additional explanation, I can only assume that I need to use additional functionality to get autonomous operation out of the ability to enter text
such as a single element Form wrapping just that text_input box and setting the on_change for the submit button. I just find the default behavior of triggering solely off of loss of focus instead of a more concrete action like on_click to be moderately confusing.

That looks to me like the right thing to do. Google Tasks works the same way.

Google Tasks also do not show a prompt to the user to “Press Enter to submit”. This statement creates an expectation, to me at least, that it is the act of pressing the Enter key that performs the action/update.

None the less
I think I have my answer(s) at this point whether I like 'em or not. :stuck_out_tongue:

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.