You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Streamlit应用中点击下载按钮触发表单的实现方案问询

解决Streamlit点击下载弹出邮箱表单的问题

先唠唠你原代码的问题:

  • 表单一打开就显示,不是点下载才弹出来
  • download_flag是局部变量,Streamlit每次重刷都会重置成False,导致下载按钮根本留不住
  • 会话状态没提前初始化,第一次提交可能没反应

方法一:用会话状态+on_click实现(最通用)

核心思路就是靠会话状态控制「显示请求下载按钮→弹出表单→显示真实下载按钮」这三个阶段,用on_click触发状态切换。

修改后的完整代码

import io
import pandas as pd
import streamlit as sl
from PIL import Image
from src import create_qr_code, create_wifi_qr
from streamlit_gsheets import GSheetsConnection
import config

# 先把要用到的会话状态初始化好,避免报错
if "show_form" not in sl.session_state:
    sl.session_state.show_form = False
if "submitted" not in sl.session_state:
    sl.session_state.submitted = False
if "byte_im" not in sl.session_state:
    sl.session_state.byte_im = None
if "filename" not in sl.session_state:
    sl.session_state.filename = None

def show_email_form():
    # 点请求下载按钮时触发,把显示表单的开关打开
    sl.session_state.show_form = True

def submitted():
    # 提交表单后,标记已提交,同时隐藏表单
    sl.session_state.submitted = True
    sl.session_state.show_form = False

def reset():
    # 重置状态,允许重新申请下载
    sl.session_state.submitted = False
    sl.session_state.show_form = False

# 生成QR码的逻辑(补了你原代码里没写的输入项,可根据实际情况调整)
tab_gen = sl.tabs(["生成二维码"])[0]
with tab_gen:
    gen_content = sl.text_input("输入二维码内容")
    border = sl.number_input("边框大小", min_value=1, value=4)
    scale = sl.number_input("缩放比例", min_value=1, value=10)
    fg_color = sl.color_picker("前景色", "#000000")
    bg_color = sl.color_picker("背景色", "#ffffff")
    data_fg = sl.color_picker("数据块颜色", "#000000")
    data_bg = sl.color_picker("数据块背景色", "#ffffff")
    logo_flag = sl.checkbox("添加Logo")
    logo_image = sl.file_uploader("上传Logo", type=["png", "jpg"]) if logo_flag else None
    bg_flag = sl.checkbox("添加背景图")
    bg_image = sl.file_uploader("上传背景图", type=["png", "jpg"]) if bg_flag else None
    art_kind = sl.selectbox("风格", ["默认", "艺术化"])

    gen_btn = tab_gen.button("生成二维码")
    if gen_btn:
        byte_im, img = create_qr_code(
            content=gen_content,
            border=border,
            scale=scale,
            fg_color=fg_color,
            bg_color=bg_color,
            data_fg=data_fg,
            data_bg=data_bg,
            logo_flag=logo_flag,
            logo=logo_image,
            bg_flag=bg_flag,
            bg_image=bg_image,
            bg_url=None,
            art_kind=art_kind
        )
        tab_gen.image(img)
        # 把生成的图片数据存在会话状态里,不然刷新就没了
        sl.session_state.byte_im = byte_im
        sl.session_state.filename = "二维码.jpg"

    # 第一步:显示「申请下载」按钮,点了就弹表单
    if sl.session_state.byte_im is not None and not sl.session_state.show_form and not sl.session_state.submitted:
        tab_gen.button("申请下载", on_click=show_email_form)

    # 第二步:弹出邮箱收集表单(只有show_form为True时才显示)
    if sl.session_state.show_form:
        with tab_gen.form("邮箱表单"):
            sl.write("请留下工作邮箱以下载文件")
            email = sl.text_input("工作邮箱", key='email')
            name = sl.text_input("全名", key='name')
            sl.form_submit_button(label="提交", on_click=submitted)

    # 第三步:提交成功后,存数据+显示下载按钮
    if sl.session_state.submitted:
        # 把用户信息写到Google Sheets
        conn = sl.connection("gsheets", type=GSheetsConnection)
        data = {'email': [sl.session_state.email], 'name': [sl.session_state.name]}
        original = conn.read(spreadsheet=config.EMAIL_SHEET)
        df = pd.concat([original, pd.DataFrame(data)], ignore_index=True)
        conn.update(spreadsheet=config.EMAIL_SHEET, data=df)
        
        # 显示真实的下载按钮
        tab_gen.download_button(
            "下载文件",
            data=sl.session_state.byte_im,
            file_name=sl.session_state.filename
        )
        # 加个重置按钮,方便用户重新下载
        tab_gen.button("重新申请下载", on_click=reset)

关键改动点

  1. 会话状态存生成结果:把生成的byte_im和文件名存在session_state里,避免刷新后丢失。
  2. 分阶段控制元素显示:
    • 先显示「申请下载」按钮,点击通过on_click触发表单显示。
    • 用户提交表单后,标记submitted=True,隐藏表单,显示下载按钮。
  3. 提前初始化会话状态:避免首次运行时出现状态不存在的错误。

方法二:用Streamlit弹窗组件(版本≥1.21可用)

如果你的Streamlit是1.21及以上版本,用st.modal做弹窗表单体验更好,不用来回切换元素显示状态。

核心代码片段

# 替换方法一里的「申请下载」按钮和表单逻辑
if sl.session_state.byte_im is not None and not sl.session_state.submitted:
    if tab_gen.button("申请下载"):
        with sl.modal("填写下载信息"):
            sl.write("请留下工作邮箱以下载文件")
            email = sl.text_input("工作邮箱", key='弹窗邮箱')
            name = sl.text_input("全名", key='弹窗姓名')
            if sl.button("提交并下载"):
                # 同样把数据写到Google Sheets
                conn = sl.connection("gsheets", type=GSheetsConnection)
                data = {'email': [email], 'name': [name]}
                original = conn.read(spreadsheet=config.EMAIL_SHEET)
                df = pd.concat([original, pd.DataFrame(data)], ignore_index=True)
                conn.update(spreadsheet=config.EMAIL_SHEET, data=df)
                
                # 直接在弹窗里显示下载按钮
                sl.download_button(
                    "下载文件",
                    data=sl.session_state.byte_im,
                    file_name=sl.session_state.filename,
                    key="弹窗下载"
                )
                sl.session_state.submitted = True
                sl.rerun()  # 关闭弹窗并刷新页面

为啥推荐这个?

弹窗形式更符合用户习惯,不用在页面上切换元素,逻辑也更简单,不用管show_form的状态。

内容的提问来源于stack exchange,提问作者Sushant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:30:20