Problem in using submit button function - sending a code for email verification

My script is not working after “if submit_button:”, I cant understand what I am doing wrong the script below.

email_form = st.form(key='my_email_form',clear_on_submit=False)
email=email_form.text_input(label='Please enter your email address')
submit_e_button = email_form.form_submit_button(label='send')
   
if submit_e_button:
      with st.form(key='my_code' , clear_on_submit=True):
            code = st.text_input(label='Kodu girin')
            submit_button = st.form_submit_button(label='Confirm')
      st.write('Press submit')
      
      fixed_digits = 4
      key=random.randrange(1111, 9999, fixed_digits)
      
      sentmail2()
      
      if submit_button:
          print('e')
          if str(code)==str(key):
            st.text('succcess')

Buttons always return to False so it’s not a good idea to have nested buttons or other widgets afterwards.

1 Like

Hi @fyec - As mentioned by @edsaac buttons shouldn’t be nested. They also cause the entire script to be run from the top before their truth condition is executed. The same applies to form buttons!! This can be very confusing, but easy to verify if you use a step debugger in your IDE with breakpoints set at the top of your program and in the truth condition block (the top breakpoint will be hit before the condition block breakpoint). As a workaround, you need to use callbacks and hold state variables to effect a conventional control flow. Essentially, you’ll construct a micro state machine. It adds a bit of complexity which I think Streamlit could avoid if it did continuation runs on button interactions, rather than full reruns, but it is what it is.

Here’s my attempt at your program with adjustments made to effect a conventional control flow:

import time
import random
import streamlit as st

state = st.session_state

# State machine states
if 'READY_TO_SEND' not in state:
    state.READY_TO_SEND = False
if 'CODE_CONFIRMED' not in state:
    state.CODE_CONFIRMED = False

# Session values
if 'CODE' not in state:
    state.CODE = ''
if 'RAND_KEY' not in state:
    state.RAND_KEY = 0

message = st.empty()
def sendmail():
    message.success('Message sent')
    time.sleep(2)
    message.empty()

if not state.READY_TO_SEND:
    with st.form(key='my_email_form', clear_on_submit=False):
        email = st.text_input(label='Please enter your email address', placeholder='name@company.com')
        if st.form_submit_button(label='send'):
            state.READY_TO_SEND = True

def _code_confirm_cb():
    state.CODE_CONFIRMED = True

if state.READY_TO_SEND and not state.CODE_CONFIRMED:
    fixed_digits = 4
    state.RAND_KEY = random.randrange(1111, 9999, fixed_digits)
    message.info(f'DEBUG (code hint): {state.RAND_KEY}')

if state.READY_TO_SEND:
    with st.form(key='my_code', clear_on_submit=True):
        state.CODE = st.text_input(label='Enter code', key='code')
        if st.form_submit_button(label='Confirm', on_click=_code_confirm_cb):
            print(state.CODE, state.RAND_KEY)
            # state.CODE = state.code
            if str(state.CODE) == str(state.RAND_KEY):
                state.READY_TO_SEND = False

if state.CODE_CONFIRMED:
    state.CODE_CONFIRMED = False
    sendmail()
    st.experimental_rerun()

HTH,
Arvindra

1 Like

Thank you Arvindra for your explanations. It helped me much. I solved my problem after the first explanation and looking at session_state in streamlit.

Here is my script of the solution, (please check for any improvement):

import random
import streamlit as st
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
import pyautogui
import time

def sentmail2():
    gmailUser = '*********@gmail.com'
    gmailPassword = '********'
    recipient = str(email)
    fixed_digits = 4
    key=random.randrange(1111, 9999, fixed_digits)
    st.session_state.key=key
    message = str(st.session_state.key)
    msg = MIMEMultipart()
    msg['From'] = f'"Your Name" <{gmailUser}>'
    msg['To'] = recipient
    msg['Subject'] = str()
    msg.attach(MIMEText(message))
    try:
        mailServer = smtplib.SMTP('smtp.gmail.com', 587)
        mailServer.ehlo()
        mailServer.starttls()
        mailServer.ehlo()
        mailServer.login(gmailUser, gmailPassword)
        mailServer.sendmail(gmailUser, recipient, msg.as_string())
        mailServer.close()
    except:
        print ('Something went wrong...')
        st.text('please check your email...')

if "submit_e_button" not in st.session_state:
    st.session_state.submit_e_button=False
    st.session_state.key=0
    key=0
def callback():
    st.session_state.submit_e_button = True

if "submit_button" not in st.session_state:
    st.session_state.submit_button=False
    st.session_state.key=0
    key=0      
def callback_2():
    st.session_state.submit_e_button = True
    st.session_state.submit_button=True

if "confirm_enter_button" not in st.session_state:
    st.session_state.confirm_enter_button=False
def callback_3():
    st.session_state.submit_e_button = True
    st.session_state.submit_button=True
    st.session_state.confirm_enter_button=True
    
def ref_page():
    pyautogui.hotkey("ctrl","F5")

if st.session_state.submit_button == False and st.session_state.submit_e_button == False:
    email_text=st.text_input(label='Please enter your email address', placeholder='***@***.com', on_change=callback,disabled=False)
    submit_e_button=st.button(label='Send',on_click=callback,disabled=False)
    email=email_text
else:
    email_text=st.text_input(label='Please enter your email address', placeholder='***@***.com', on_change=callback,disabled=True)
    submit_e_button=st.button(label='Send',on_click=callback,disabled=True)
    email=email_text

if submit_e_button or st.session_state.submit_e_button:
    if st.session_state.submit_button == False and st.session_state.submit_e_button == True:
        sentmail2()

    if st.session_state.submit_button == False and st.session_state.submit_e_button == True:
        submit_button=st.text_input(label='Enter the code', placeholder='****', on_change=callback_2,disabled=False)
        confirm_enter_button=st.button(label='Onayla',on_click=callback_2,disabled=False)
        code=submit_button
    else:
        submit_button=st.text_input(label='Enter the code', placeholder='****', on_change=callback_2,disabled=True)
        confirm_enter_button=st.button(label='Onayla',on_click=callback_2,disabled=True)
        code=submit_button
    
    if submit_button or st.session_state.submit_button:
        if str(code)==str(st.session_state.key):
          st.text('succcessfully verified e-mail: ')
          str(st.text(email))          
        if str(code)!=str(st.session_state.key):
            st.text('code is not true, please wait to send another code again.')
            st.session_state.submit_e_button == True
            ph = st.empty()
            N = 1*30
            for secs in range(N,0,-1):
                mm, ss = secs//60, secs%60
                ph.metric("Countdown", f"{mm:02d}:{ss:02d}")
                time.sleep(1)
                if ss==1:
                  ph.metric("Countdown", "Time is up")
                  try_again=st.button(label='Click to try again.',on_click=ref_page,disabled=False)

Now, I am trying to integrate a countdown clock for the verification process. The part that I want to integrate into my script is:

    ph = st.empty()
    N = 1*60
    for secs in range(N,0,-1):
        mm, ss = secs//60, secs%60
        ph.metric("Countdown", f"{mm:02d}:{ss:02d}")
        time.sleep(1)
        t2=time.time()
        t=t2-t1
        if ss==1:
          ph.metric("Countdown", "Time is up")

Nice. For the timer, feel free to lift code from this Pomodoro timer script:

import streamlit as st
import time

st.set_page_config(
    page_title='Pomodoro',
    layout='centered',
    page_icon='🍅'
)

def count_down(ts):
    with st.empty():
        while True:
            mins, secs = divmod(ts, 60)
            time_now = '{:02d}:{:02d}'.format(mins, secs)
            st.header(f"{time_now}")
            time.sleep(1)
            ts -= 1
            if ts < 0:
                break
    st.write("Time Up!")
    st.balloons()

def main():
    st.title("Pomodoro")
    time_minutes = st.number_input('Enter the time in minutes ', min_value=0.1, value=25.0)
    time_in_seconds = time_minutes * 60
    if st.button("START"):
            count_down(int(time_in_seconds))

if __name__ == '__main__':
    main()
1 Like