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)
关键改动点
- 会话状态存生成结果:把生成的
byte_im和文件名存在session_state里,避免刷新后丢失。 - 分阶段控制元素显示:
- 先显示「申请下载」按钮,点击通过
on_click触发表单显示。 - 用户提交表单后,标记
submitted=True,隐藏表单,显示下载按钮。
- 先显示「申请下载」按钮,点击通过
- 提前初始化会话状态:避免首次运行时出现状态不存在的错误。
方法二:用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
相关产品推荐
相关产品推荐

