# Conditionals and Rounding with `st.number_input()`

I have two `st.number_inputs()` I am trying to use

``````input1 = st.number_input("x", value=5.0, step=0.5, format="%0.1f")

input2 = st.number_input("y", value=-110)
``````

They work great, except for a few specific cases. As for `input1`, Iād like for it to always be rounded to the nearest half number. The step works, but when a float such as 5.4 is given, the number is not rounded and the step behaves as normal and it goes from 5.4, 5.9, etc. when Iād like the 5.4 to be rounded to 5.5 and behave like 5.5, 6.0, etcā¦ Is there a way to always round any given number to the nearest 0.5?

As for `input2`, Iād like to start counting the opposite direction once the number gets above -100. ie, instead of -102, -101, -100, -99, Iād like for it to go -102, -101, -100, +101, +102, ā¦ etc. Is it possible to add conditionals like this to an `st.number_input()`? Furthermore, If any number between -100 and +100 is given, Iād like to round it (similar to `input1`) to either -100 or +100, whichever is closer.

So basically, my question boils down to two main topics: is it possible to round a number given to `st.number_input()` and add certain conditionals?

No, this behavior isnāt possible to add directly to number_input, but you could always add some logic after the st.number_inputās to round input1 and input2 however youād like.

You can use a callback to round on_change.

``````import streamlit as st

def forced_round():
st.session_state.half = round(st.session_state.half*2)/2

st.number_input('Half Steps',0.0,10.0, step=.5, key='half', on_change=forced_round)

def skip_100(previous):
if previous == 100 and st.session_state.zone == 99:
st.session_state.zone = -100
elif previous == -100 and st.session_state.zone == -99:
st.session_state.zone = 100
elif 0 <= st.session_state.zone < 100:
st.session_state.zone = 100
elif -100 < st.session_state.zone < 0:
st.session_state.zone = -100

if 'zone' not in st.session_state:
st.session_state.zone = 100

st.number_input('No Go Zone of 100',-1000,1000, step=1, key='zone', on_change=skip_100, args=[st.session_state.zone])
``````

The only thing is that it canāt distinguish between a manually entered change or one from clicking the increment button. As such, manually typing 99 when it currently says 100 will cause it to jump down to -100. With any other current value, entering 99 will round up to 100.

1 Like

Ugh, this seems like a great solution but I have these widgets in a form so I get the following error

``````StreamlitAPIException: With forms, callbacks can only be defined on the st.form_submit_button. Defining callbacks on other widgets inside a form is not allowed.
``````

Is there a way around this? Can I declare these callbacks within the form some other way?

If you have widgets in a form, any callback functionality would have to be tied to the submit button and would then only execute upon form submission. That creates a bit of difficulty with your second widget especially. Clicking increment button would unlikely produce the desired result since you wouldnāt be tracking the state of the widget at the time of each click.

Can you forgo the form and use other logic/caching to make sure it doesnāt impact performance? Generally, custom widget logic/widget interactivity is difficult within forms.

1 Like

I have a bunch of widgets in the form that end up filtering a data frame (these are just two of them that have very specific requirements), so it is ideal for them to all get submitted at once; it would be difficult for me to get rid of the form, Iām afraid. That is too bad, I will just set some error handling for the 100 no go zone and do the rounding after the form has been submitted, but I was hoping there was a way I could do this visually for the end user as well in real time before they submit the form just for the aesthetic appeal. Nonetheless, I appreciate your help and creative solution!

Even if you have a hundred widgets, you can usually create the logic to have a āform effectā and hold off on other processing until a button is clicked.

Imagine if have 100 widgets with keys `w_1`, `w_2`, ā¦ `w_100`. Then imagine a submit button that copies each value at `w_i` to `use_i` with your script utilizing the copied values. A user would be free to change any number of widgets without impacting the result (until they clicked submit).

This may be problematic if you have certain kinds of āheavyā objects displayed, but is often doable one way or another.

1 Like

Interestingā¦ Is there a reference for building something with this type of logic? Iād like to check it out and see if itās worth working in and using instead of a nice, clean form just to get the aesthetic changes for these two specific widgets. The data frame being filtered is not very computationally expensive, and there can be up to ~20 widgetsā¦ so nothing crazy at all.

Hereās a simple example:

``````import streamlit as st
import pandas as pd

if 'df' not in st.session_state:
st.session_state.df = pd.DataFrame({'A':[1]*5 + [2]*5 + [3]*5 + [4]*5 + [5]*5,
'B':[1,2,3,4,5]*5})
df = st.session_state.df

A = st.number_input('A',1,5,step=1, key='_A')
B = st.number_input('B',1,5,step=1, key='_B')

def submit():
st.session_state.A = st.session_state._A
st.session_state.B = st.session_state._B

st.button('Submit', on_click=submit)

if 'A' in st.session_state:
st.write(df[(df['A']==st.session_state.A)&(df['B']==st.session_state.B)])

``````
1 Like

Thanks so much! Going to give this a go.