@st.experimental_dialog problem with st.rerun

Hi everyone,
I’m using the @st.experiment_dialog decorator for a function that shows me a text input widget to set a name of a map’s area already selected. The function update_session_state() sets multiple values that are useful for another part of the project. The problem is that sometimes, after the click of the button to confirm the name of the area, the dialog closes correctly and some other times the st.rerun() take me back to the top of my page. I think that the problem is caused by st.rerun(), but can somebody help me?

1 Like

Hello @a.guerrisi2, welcome to the forum!

When you post code like this, that’s very helpful, but only if it’s posted as text instead of an image.

Could you post an example script that is fully complete, and runs without any extra required functions or data, and shows the issue you are facing?

This is my best attempt to reproduce what your script might be trying to do, but it seems to work fine for me. Please post an example that is complete and as simplified as possible that still shows the issue.

import streamlit as st

if "drawings" not in st.session_state:
    st.session_state.drawings = [
        {
            "type": "Feature",
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [0, 0],
                        [0, 1],
                        [1, 1],
                        [1, 0],
                    ]
                ],
            },
            "properties": {},
        }
    ]

if "bounds_toggle" not in st.session_state:
    st.session_state.bounds_toggle = False


def calculate_bounds(drawings):
    return [
        [
            min(
                drawing["geometry"]["coordinates"][0][0][0],
                drawing["geometry"]["coordinates"][0][0][1],
            )
            for drawing in drawings
        ],
        [
            max(
                drawing["geometry"]["coordinates"][0][0][0],
                drawing["geometry"]["coordinates"][0][0][1],
            )
            for drawing in drawings
        ],
    ]


@st.experimental_dialog("Inserisci le informazioni per l'area selezionata")
def set_info_area(last_drawing, st_component):
    name = st.text_input("Inserisci il nome dell'area selezionata")
    if st.button("Salva Informazioni"):
        last_drawing["properties"]["name"] = name
        update_session_state(last_drawing, st_component)
        st.rerun()


# Funzione per aggiornare lo stato della sessione
def update_session_state(last_drawing, st_component):
    if "drawings" not in st.session_state:
        st.session_state.drawings = []
    st.session_state.drawings.append(last_drawing)

    if st.session_state.bounds_toggle:
        st.session_state.bounds = calculate_bounds(st.session_state.drawings)
    else:
        if "bounds" in st.session_state:
            del st.session_state["bounds"]

    st.session_state.lat = st_component["center"]["lat"]
    st.session_state.lon = st_component["center"]["lng"]
    st.session_state.zoom = st_component["zoom"]


last_drawing = st.session_state.drawings[-1]
st_component = {"center": {"lat": 0, "lng": 0}, "zoom": 0}

if st.button("Aggiorna"):
    set_info_area(last_drawing, st_component)

st.write(st.session_state)

Sure, but I can’t show too much because it’s a University project. I’m using an interactive map with the leafmap.foliumap module to create the map and the streamlit_folium library to show it on the screen. The st_component is the Json object representing the map and I extract his “last_active_drawing” field to get the informations about the area that the user selected. After that, the dialog appears, the user chooses the name and now the area has a tooltip showing the name. The problem shows after the click of the button in the dialog, which sometimes takes the user on top of the page and if he’s drawing on the map it could be inconvenient. I don’t understand the fact that sometimes this happens and sometimes not. I also changed a little bit the experimental dialog but I only use a selectbox instead of a text_input.

Thank you for your help!

----------------- Functions -------------------

def initialize_session_state():
    if 'lat' not in st.session_state:
        st.session_state.lat = 45.523840041350965
    if 'lon' not in st.session_state:
        st.session_state.lon = 9.21977690041348
    if 'zoom' not in st.session_state:
        st.session_state.zoom = 17
    if 'drawings' not in st.session_state:
        st.session_state.drawings = [  ]
    if 'bounds_toggle' not in st.session_state:
        st.session_state.bounds_toggle = False

def create_map():
    m = leafmap.Map(
        locate_control=True, 
        scale_control=True,
        plugin_LatLngPopup=False,
        center=(st.session_state.lat, st.session_state.lon), 
        zoom=st.session_state.zoom,
        draw_control=False,  # Disattiviamo il draw_control
    )
    m.add_basemap("SATELLITE")

    draw = Draw(
        draw_options={
            'polyline': False,
            'polygon': True,
            'circle': False,
            'rectangle': True,
            'marker': False,
            'circlemarker': False,
        },
        edit_options={
        'edit': False,
        'remove': False
    }
    )
    draw.add_to(m)
    return m

@st.experimental_dialog("Inserisci le informazioni per l'area selezionata")
def set_info_area(last_drawing, st_component):
    name = st.selectbox("Seleziona nome area selezionata", options=["Edificio", "Campo Agricolo", "Vegetazione", "Acqua", "Strada"], index=None)
    if st.button("Salva Informazioni"):
        if not name: 
            name = "Area Sconosciuta"  # Nome di default
        last_drawing['properties']['name'] = name.lower()
        update_session_state(last_drawing, st_component)
        st.rerun()

def update_session_state(last_drawing, st_component):
    if 'drawings' not in st.session_state:
        st.session_state.drawings = []
    st.session_state.drawings.append(last_drawing)
    
    if st.session_state.bounds_toggle:
        st.session_state.bounds = calculate_bounds(st.session_state.drawings)
    else:
        if 'bounds' in st.session_state:
            del st.session_state['bounds']

    st.session_state.lat = st_component['center']['lat']
    st.session_state.lon = st_component['center']['lng']
    st.session_state.zoom = st_component['zoom']

----------- Main code simplified -----------

initialize_session_state()

m = create_map()

if 'drawings' in st.session_state:
            for drawing in st.session_state.drawings:
                if 'properties' in drawing:
                    properties = drawing['properties']
                    if properties:
                        popup_text = "<br>".join([f"{key}: {value}" for key, value in properties.items()])
                        folium.GeoJson(
                            drawing,
                            tooltip=popup_text  # Usa il testo formattato qui
                        ).add_to(m)
                    else:
                        folium.GeoJson(drawing).add_to(m)
                else:
                    folium.GeoJson(drawing).add_to(m)

if 'bounds' in st.session_state:
      m.fit_bounds(st.session_state.bounds)
else:
      m.set_center(st.session_state.lon, st.session_state.lat, st.session_state.zoom)
        
st_component = st_folium(m, use_container_width=True)

if st_component.get('last_active_drawing') is not None:
   last_drawing = st_component['last_active_drawing']
   
   if 'drawings' in st.session_state:
       existing_drawings = [d['geometry'] for d in st.session_state.drawings]
   else:
       existing_drawings = []

   if last_drawing['geometry'] not in existing_drawings:
       set_info_area(last_drawing, st_component)

Thanks, that’s super helpful. I had to tweak your code slightly to get it to run (I couldn’t make it work with a leafmap map, and had to use a folium map instead), but I don’t ever seem to see the “go to the top of the page” issue that you mentioned.

Here’s the code I used

import streamlit as st
from streamlit_folium import st_folium
import leafmap
from folium.plugins import Draw
import folium


def initialize_session_state():
    if "lat" not in st.session_state:
        st.session_state.lat = 45.523840041350965
    if "lon" not in st.session_state:
        st.session_state.lon = 9.21977690041348
    if "zoom" not in st.session_state:
        st.session_state.zoom = 17
    if "drawings" not in st.session_state:
        st.session_state.drawings = []
    if "bounds_toggle" not in st.session_state:
        st.session_state.bounds_toggle = False


def create_map():
    m = leafmap.Map(
        locate_control=True,
        scale_control=True,
        plugin_LatLngPopup=False,
        center=(st.session_state.lat, st.session_state.lon),
        zoom=st.session_state.zoom,
        draw_control=False,  # Disattiviamo il draw_control
    )
    m.add_basemap("SATELLITE")

    m = folium.Map(
        location=[st.session_state.lat, st.session_state.lon],
        zoom_start=st.session_state.zoom,
    )

    draw = Draw(
        draw_options={
            "polyline": False,
            "polygon": True,
            "circle": False,
            "rectangle": True,
            "marker": False,
            "circlemarker": False,
        },
        edit_options={"edit": False, "remove": False},
    )
    draw.add_to(m)
    return m


def calculate_bounds(drawings):
    bounds = []
    for drawing in drawings:
        bounds.extend(drawing["geometry"]["coordinates"][0])
    return bounds


@st.experimental_dialog("Inserisci le informazioni per l'area selezionata")
def set_info_area(last_drawing, st_component):
    name = st.selectbox(
        "Seleziona nome area selezionata",
        options=["Edificio", "Campo Agricolo", "Vegetazione", "Acqua", "Strada"],
        index=None,
    )
    if st.button("Salva Informazioni"):
        if not name:
            name = "Area Sconosciuta"  # Nome di default
        last_drawing["properties"]["name"] = name.lower()
        update_session_state(last_drawing, st_component)
        st.rerun()


def update_session_state(last_drawing, st_component):
    if "drawings" not in st.session_state:
        st.session_state.drawings = []
    st.session_state.drawings.append(last_drawing)

    if st.session_state.bounds_toggle:
        st.session_state.bounds = calculate_bounds(st.session_state.drawings)
    else:
        if "bounds" in st.session_state:
            del st.session_state["bounds"]

    st.session_state.lat = st_component["center"]["lat"]
    st.session_state.lon = st_component["center"]["lng"]
    st.session_state.zoom = st_component["zoom"]


for _ in range(30):
    st.title("Filler text")

initialize_session_state()

m = create_map()

if "drawings" in st.session_state:
    for drawing in st.session_state.drawings:
        if "properties" in drawing:
            properties = drawing["properties"]
            if properties:
                popup_text = "<br>".join(
                    [f"{key}: {value}" for key, value in properties.items()]
                )
                folium.GeoJson(
                    drawing,
                    tooltip=popup_text,  # Usa il testo formattato qui
                ).add_to(m)
            else:
                folium.GeoJson(drawing).add_to(m)
        else:
            folium.GeoJson(drawing).add_to(m)

if "bounds" in st.session_state:
    m.fit_bounds(st.session_state.bounds)
else:
    # m.set_center(st.session_state.lon, st.session_state.lat, st.session_state.zoom)
    pass

st_component = st_folium(m, use_container_width=True)

if st_component.get("last_active_drawing") is not None:
    last_drawing = st_component["last_active_drawing"]

    if "drawings" in st.session_state:
        existing_drawings = [d["geometry"] for d in st.session_state.drawings]
    else:
        existing_drawings = []

    if last_drawing["geometry"] not in existing_drawings:
        set_info_area(last_drawing, st_component)

st.write(st.session_state)

Thanks for helping me with your code, I tried it and I can see that it doesn’t have the problem I’m talking about. In my code it doesn’t appear so often now but maybe it depends on the network connection? Thank you very much anyway!!!

1 Like