Проблема с кнопкой в приложении Streamlit (Python): отображение предыдущего числа после нажатия кнопки вместо нового случайного числа

Вопрос или проблема

Я создаю простое приложение на Streamlit в качестве демонстрации более крупного проекта, над которым я работаю. Цель состоит в том, чтобы отображать случайное число от 0 до 100 и позволить пользователю выбрать, нравится ли ему это число или нет. После того как пользователь нажимает “Да” или “Нет”, приложение должно сохранять число и их ответ в формате JSON и затем немедленно показывать новое случайное число.

Однако после нажатия кнопки старое число продолжает отображать до следующего выбора, вместо того чтобы показывать следующее случайное число. Новое число появляется только после повторного нажатия кнопки, но в итоге ответ ассоциируется с новым числом, а не со старым. Ниже приведен упрощенный пример кода, который я использую:

import streamlit as st
import random
import json

# Инициализация состояния сессии для хранения чисел и ответов пользователей в формате JSON
if "data" not in st.session_state:
    st.session_state.data = []

# Функция для генерации следующего случайного числа
def get_next_number():
    return random.randint(0, 100)

# Инициализация первого числа при запуске приложения
if "current_number" not in st.session_state:
    st.session_state.current_number = get_next_number()

# Функция для обработки ответа пользователя
def store_response(response):
    current_number = st.session_state.current_number
    st.session_state.data.append({"Number": current_number, "Response": response})
    # Генерация следующего случайного числа сразу после ответа
    st.session_state.current_number = get_next_number()

st.title("Приложение предпочтений случайных чисел")

# Отображение текущего числа
st.write(f"Вам нравится число **{st.session_state.current_number}**?")

# Разметка для кнопок ответа
col1, col2 = st.columns(2)

# Обработка клика по кнопке "Да"
with col1:
    if st.button("Да"):
        store_response("Да")

# Обработка клика по кнопке "Нет"
with col2:
    if st.button("Нет"):
        store_response("Нет")

# Отображение сохраненных ответов в формате JSON
if len(st.session_state.data) > 0:
    st.subheader("Ответы пользователей (формат JSON)")
    st.json(st.session_state.data)

    # Позволить пользователю загрузить результаты в виде JSON-файла
    json_data = json.dumps(st.session_state.data)
    st.download_button(label="Скачать как JSON", data=json_data, file_name="responses.json", mime="application/json")

Проблема:

  • После отображения первого числа, если пользователь выбирает “Да” или “Нет”, то же самое число отображается снова.
    • Когда следующая случайная числа в конечном итоге отображается, ранее зафиксированный ответ (например, “Да” или “Нет”) применяется к вновь сгенерированному числу, а не к тому числу, которое пользователь видел, когда делал выбор.

Шаги для воспроизведения проблемы (с экранами):

  1. Начальный экран:
    Когда приложение запускается, оно сразу же показывает случайное число (например, 65). Пользователь видит две кнопки, “Да” и “Нет”, чтобы выбрать, нравится ли ему число.
    введите описание изображения здесь

  2. После выбора “Да”:
    После нажатия кнопки “Да” приложение по-прежнему показывает то же число (65). Пользователь может снова нажать “Да” или “Нет”, но кажется, что новое случайное число еще не появилось.
    ![То же число после клика](ссылка на вложение)

  3. Следующее случайное число появляется с неправильным ответом:
    После повторного нажатия “Да” новое случайное число наконец отображается (в данном случае, 44), но ответ (Да) из предыдущего выбора теперь ассоциируется с новым числом, что не является ожидаемым поведением.
    ![Новое число с неправильным ответом](ссылка на вложение)

Что я ожидаю:
– Когда пользователь нажимает кнопку (либо “Да”, либо “Нет”), следующее число должно немедленно появиться, а ответ должен быть записан для текущего числа, а не для следующего.

Я пытался управлять состоянием с помощью st.session_state и экспериментировал с st.experimental_rerun() (хотя моя версия Streamlit не поддерживает его), но я не могу заставить приложение отображать новое число сразу после нажатия кнопки.

Вопрос:
– Как мне заставить приложение показывать следующее случайное число сразу после того, как пользователь выберет свой ответ, при этом корректно ассоциируя записанный ответ с отображаемым числом?

Буду признателен за любые идеи о том, что я пропускаю, или альтернативные подходы!

Ответ или решение

Проблема, с которой вы столкнулись, связана с тем, как работает состояние сессии в Streamlit и логикой управления отображением текущего числа. Когда пользователь нажимает кнопку, приложение не обновляет состояние до следующего цикла, что приводит к тому, что прежний номер все еще отображается.

Для решения этой проблемы, вам нужно изменить способ, которым вы обрабатываете нажатия кнопок, и добавить механизм, который позволит отображать новый номер сразу после того, как пользователь сделает выбор. Вместо того чтобы обновлять число после обработки ответа, давайте обновим логику так, чтобы новое число генерировалось и отображалось сразу после нажатия кнопки.

Вот исправленный код:

import streamlit as st
import random
import json

# Инициализируем состояние сессии для хранения данных
if "data" not in st.session_state:
    st.session_state.data = []

# Функция для генерации следующего случайного числа
def get_next_number():
    return random.randint(0, 100)

# Начальное значение для текущего числа
if "current_number" not in st.session_state:
    st.session_state.current_number = get_next_number()

# Функция для обработки ответа пользователя
def store_response(response):
    current_number = st.session_state.current_number
    st.session_state.data.append({"Number": current_number, "Response": response})

    # Генерируем следующее число сразу после сохранения ответа
    st.session_state.current_number = get_next_number()

st.title("Приложение Предпочтения Случайного Числа")

# Отображаем текущее число
st.write(f"Нравится ли вам число **{st.session_state.current_number}**?")

# Обработка нажатий кнопок "Да" и "Нет"
col1, col2 = st.columns(2)

# Обработка нажатия кнопки "Да"
with col1:
    if st.button("Да"):
        store_response("Да")
        # Проверяем, чтобы обновить номер сразу после выбора
        st.experimental_rerun()  # Это приведёт к обновлению приложения и отображению нового числа

# Обработка нажатия кнопки "Нет"
with col2:
    if st.button("Нет"):
        store_response("Нет")
        # Проверяем, чтобы обновить номер сразу после выбора
        st.experimental_rerun()  # Аналогично, обновляем приложение

# Отображаем сохранённые ответы в формате JSON
if len(st.session_state.data) > 0:
    st.subheader("Ответы пользователей (формат JSON)")
    st.json(st.session_state.data)

    # Позволяем пользователям скачать результаты как JSON файл
    json_data = json.dumps(st.session_state.data)
    st.download_button(label="Скачать как JSON", data=json_data, file_name="responses.json", mime="application/json")

Объяснения изменений:

  1. st.experimental_rerun(): После того, как пользователь нажимает одну из кнопок, добавьте st.experimental_rerun(), чтобы установить состояние приложения в его исходное состояние и отобразить новое случайное число сразу после обработки выбора. Обратите внимание, что это нужно делать только в том случае, если у вас установлен Streamlit версии 0.88.0 или выше.

  2. Логика обработки ответов: Логика обработки ответов не изменилась, но теперь, когда вы вызываете st.experimental_rerun(), новое число отображается сразу после того, как будет обработан ответ.

Теперь, когда пользователь нажимает "Да" или "Нет", будет записан ответ, и новое случайное число отобразится немедленно, как вы и ожидали. Если у вас возникнут дополнительные вопросы или проблемы с реализацией, пожалуйста, дайте знать!

Оцените материал
Добавить комментарий

Капча загружается...