Update slider value

Hi Everyone, Love the concept! I’m really enjoying kicking the tires on this!

I’m trying to write a small app that allows a user to click forward and back to click through a defined number of images and click on buttons to record their impressions of it. I think this is possible to do quite easily with streamlit.

Get number of items, use slider to increment through the items. Use buttons to trigger user inputs to record their clicks to an sqlitedb.

I have two questions however:

  1. How can I modify the value of the slider widget?
  2. How can I clear the button push after it has been pushed?

Is this even feasible with the streamlit architecture or am I barking up completely the wrong tree?

Best,

Jorick

1 Like

Hi @jorick!

  1. How can I modify the value of the slider widget?

I’m having trouble understanding what exactly you’re trying to build. Can you post a mockup?

Answering your question as best I can without the mockup, I think what you’re looking for is support for “session state”. We have a session state feature request here, and a prototype implementation here.

This is how your code would look with the prototype implementation:

import streamlit as st

# SessionState module from https://gist.github.com/tvst/036da038ab3e999a64497f42de966a92
import SessionState

state = SessionState.get(position=0)

# The slider needs to come after the button, to make sure the first increment
# works correctly. So we create a placeholder for it here first, and fill it in
# later.
widget = st.empty()

if st.button('Increment position'):
    state.position += 1

state.position = widget.slider('Position', 0, 100, state.position)
  1. How can I clear the button push after it has been pushed?

Buttons get cleared automatically next time the script runs. So if you have x = st.button("Click me!"), then:

  1. The first time your app runs, x is False.
  2. When you interact with any other widget in your app, x is False
  3. When you click the button, x is True.
  4. Next time you interact with any other widget in your app, x is False again

Hi Thiago,

Thanks for the help this is exactly what I was looking for!

I’ll be able to get a better example of what I was saying when I can get to my laptop, but you understood it. I’m essentially using streamlit to help with gathering user. Classifications so that I can build a training set easily.

Best,

Jorick

@thiago I’m absolutely loving Streamlit so far. Building some really cool stuff in no time at all. My biggest issue is getting changes to persist throughout the application. So for example, if I use a slider to choose a value that then multiplies a column in a dataframe when I press a button to submit that calculation, if I go to submit using a button somewhere else that gets reset. I would like the results of that button press to persist. In other words, I want that button value to stay True.

I’m assuming what you’re working on with SessionState fixes this issue?

Or is there already a way to keep the widget value True? I.e. when I interact with another widget is it possible to keep the other widgets set to True or is that solved by SessionState.

Sorry for the confusing word spaghetti, been trying to fix this problem for a few days now haha.

I might be just misunderstanding how streamlit works but to further explain my question…

if st.button(‘addition’):
x+=1

If I hit the button 5 times x will always be 1 if it started by 0.

Is the ability to have x = 5 at the end of 5 button presses something handled by SessionState?

For now I will just output this value to a file and read that value then update that value, etc. However, that’s not a very elegant solution.

Agree - especially because you have to initialize x to something at the top of your app. I’ve thought of it two ways, either the initialization step could be performed once, or the updated variable could be cached. I too am looking for this functionality.

Hey @posey22

I’m having a hard time understanding your question, but I totally get that UI behavior is really hard to describe in words :laughing:

So for example, if I use a slider to choose a value that then multiplies a column in a dataframe when I press a button to submit that calculation, if I go to submit using a button somewhere else that gets reset. I would like the results of that button press to persist. In other words, I want that button value to stay True.

We’ve actually been thinking about the “submit button” scenario lately, and considering whether we should provide a new API just for this case. For example, maybe there should be a way to specify a “widget group” whose values only get set to the server when “submit” is pressed.

But to better design this API, it would be great to get a bunch of “toy examples” of what people would like to do with it. So if you can come up with a tiny script that exemplifies what you need, that would be amazing!

I’m assuming what you’re working on with SessionState fixes this issue?

Streamlit reruns your script fresh from the top every time you change the value of a widget. But sometimes you want to keep some values around in between runs (for example, when incrementing a variable, when doing incremental manipulations of a dataset, etc.).

That’s where SessionState comes in. It lets you persist data across reruns.

If I hit the button 5 times x will always be 1 if it started by 0.

Hmmm… that’s not supposed to happen with the code snippet I pasted above. Did you try running it? Or perhaps you’re reacting to the API used for the SessionState prototype? In case it’s the latter, here’s a quick explanation of how the prototype API works:

When you do this:

state = SessionState.get(x = 100)

if st.button('Increment x'):
    state.x += 1

st.write("Current value of x:", state.x)

Here’s what happens behind the scenes:

  1. When a user opens a new browser tab with your Streamlit app, Streamlit runs the script above from top to bottom. When it reaches the SessionState.get() statement, it checks whether that given user already has a “session” object associated with it. If not, then it initializes the state value of state.x to 100. Note that this only happens once per user!
  2. When the user clicks the “Increment x” button, Streamlit reruns your script from top to bottom. This time, the SessionState.get() statement will return that user’s existing SessionState object.
  3. Since the user clicked the button, the if condition will be True, so state.x will increment by 1 and st.write() will write “Current value of x: 1” to the app.
  4. If the user clicks “Increment x” again, Streamlit will rerun your script from top to bottom. Now SessionState.get() will return a state object where state.x is set to 1. So this time the code inside the if condition will increment it to 2.
    And so on!

I’ve thought of it two ways, either the initialization step could be performed once, or the updated variable could be cached. I too am looking for this functionality.

As described above, your first idea is exactly what we do: the initialization step is only performed once :smiley:


This discussion is super valuable, by the way! It makes it clear that API of the SessionState prototype is not very clear. One way to improve it would be to rename the get() method to something like get_or_set or get_existing or something like that.

Also: with respect to buttons in general, in addition to the “widget group” API I mentioned at the top, we’re also considering another API where buttons are allowed to run small callback functions before a script executes — among other ideas. For more on this, check out this thread: How to evolve complex state (e.g., annotate data)?

Thanks for your excellent reply. You have answered very clearly. Will need to dive deeper into SessionState, been using all sorts of messy hacks to do what pretty much is performed by SessionState!

Appreciate it!

1 Like

Hej Thiago,

I was wondering whether there have been any changes to the code. When I run your example code from above I get the following error message:
“‘SessionState’ object has no attribute ‘position’”

What am I missing here?

Best wishes,
Matthias

I would still appreciate your response. In the meantime I found something that worked for me here: Alternative implementation of session state

1 Like

Hi @MatthiasPilz,

Thanks for giving this a try! Apologies for the inconvenience.

These prototypes we put out for people to try are just that: prototypes. As such it’s hard for us to keep them properly supported – we can’t really promise that the code and our discussion about the code will really stay in sync. These prototypes are therefore provided “as is” and aren’t even guaranteed to work between Streamlit versions.

One of the reasons we haven’t put out an official State or SessionState object in Streamlit is that we’re still getting parts of the infrastructure to gel that could affect how a “state” object works. It turns out it’s not as simple as it appears on the surface, so we’re putting it behind a couple other infrastructure improvements to make sure that when we do release it, it really works.

In the meantime, I’d recommend doing what you’re already doing – finding what looks like the latest community revision of a “state” object and playing with that. We’re definitely watching how people use and talk about our product as a way to make sure our feature development matches what people actually want to do.

Thanks for commenting!

Hello there, I have a similar use case where I would like to “save” the state of a button and use another interactive widget after without the state button resetting to False.

My failed attempt looks like this:

if st.button('Save data as a csv file?'):
    filename = st.text_input('Enter file name', 'default_name.csv')
    data.to_csv(filename)

That is, I want to display the text input field for the file name only if the user actually wants to save the file.

I have read the above about a future SessionState class. Is there any update on this feature ?

I have also read the question about complex state linked by @thiago where he did a little API brainstorming. For my application, I was thinking that a context manager approach could be nice, something like:

with st.context_button('Download data as a csv file?'):
    filename = st.text_input('Enter file name', 'default_name.csv')
    data.to_csv(filename)

where you execute the code in the context only if the button is clicked, and even interactive widgets inside do not reset the button. Is this feasible or even desirable?

In the meantime, is there a hacky way to achieve my goal through a clever usage of st.cache?