深入理解Streamlit中的按钮行为(四):示例与最佳实践

文章目录

  • 1 前言🚀
  • 2 Streamlit中if st.button()的使用时机 🎯💡
  • 3 按钮的逻辑
    • 3.1 通过按钮显示临时消息的常用逻辑 📢💡
    • 3.2 状态保留按钮 🪄🔘
    • 3.3 切换按钮 🔄🔘
    • 3.4 控制流程的按钮 🕹️🔘
    • 3.5 动态添加小部件的按钮 ✔️💡💻🔄
    • 3.6 使用按钮处理耗时或写入文件的过程 🔄💾⏱️💡
  • 4 常见的按钮反模式 🚫⚠️
    • 4.1 按钮嵌套在按钮内部
    • 4.2 将其他小部件嵌套在按钮内部
    • 4.3 在按钮内部嵌套处理过程,但未保存到会话状态中
  • 5 结语

1 前言🚀

欢迎来到我们关于Streamlit按钮行为的全面指南!按钮在Streamlit应用程序中是用户交互的基本组件。无论你是刚开始接触Streamlit还是已经有了一些经验,本指南都将为你提供如何在应用程序中有效使用按钮的坚实基础。在本文中,我们还将参考我之前写的三篇相关文章:

  • Streamlit初识(一)——安装以及初步应用:
    引导你从安装开始,快速了解Streamlit的基本使用方法。

  • Streamlit的第一个应用(二): 展示了如何使用Streamlit创建一个简单的应用程序,让你感受到Streamlit的便捷和效果。

  • Streamlit应用中构建多页面(三):两种方案: 分享了构建多页面应用程序的两种方案,帮助你进一步扩展并优化你的应用。

本篇指南将深入探讨按钮行为的细微差别,探索各种示例,并解释常见的误解。
使用 st.button 函数创建的按钮具有独特的特点,这些特点对于理解至关重要。具体而言,按钮在点击后不会保留状态。相反,当按钮被点击后,脚本会重新运行,并且按钮在下一次脚本重新运行时立即返回到原始状态。如果在st.button函数中嵌套了可见元素,则该元素将在按钮被点击时显示,并在用户进行下一次操作后立即消失。这是因为脚本重新运行,按钮的返回值在按钮点击事件中变为True,并在下一次脚本重新运行时返回为False。

在本章中,我们将展示多个示例,以说明在利用Streamlit的st.session_state时如何实际使用按钮。建议您已经安装了Streamlit并熟悉其主要概念。

2 Streamlit中if st.button()的使用时机 🎯💡

这个函数的使用可以根据按钮的值来决定执行某段代码,点击按钮后只执行一次,直到再次点击按钮。

适合将if st.button()嵌套在里面的场景包括:

✅ 瞬时消失的消息:一旦按钮被点击,显示消息但立即消失。

✅ 每次点击后执行的过程:将数据保存到会话状态、文件或数据库。

而不适合将if st.button()嵌套在里面的场景包括:

❌ 需要在用户继续操作时保留的显示项。

❌ 引起脚本重新运行的其他小部件使用。

❌ 既不修改会话状态也不写入文件/数据库的过程*。(*当需要一次性结果时,这种情况也可以接受。例如,如果你有一个“验证”按钮,可以将它作为直接依赖于按钮的流程,用于创建一个弹出消息,告诉用户’有效’或’无效’,而不需要保留这些信息。)

3 按钮的逻辑

3.1 通过按钮显示临时消息的常用逻辑 📢💡

如果你想给用户一个快速的按钮,让他们检查一个条目是否有效,但不想在用户继续操作时继续显示该检查结果。

在这个示例中,用户可以点击一个按钮来检查他们的字符串是否在列表中。当用户点击“检查可用性”按钮时,他们将看到“我们有这个动物!”或“我们没有这个动物。”如果他们在st.text_input中更改了动物的输入,脚本将重新运行,并且消息会在他们再次点击“检查可用性”按钮之前消失。

import streamlit as stanimal_shelter = ['cat', 'dog', 'rabbit', 'bird']animal = st.text_input('输入一个动物')if st.button('检查可用性'):have_it = animal.lower() in animal_shelter'我们有这个动物!' if have_it else '我们没有这个动物。'

在这里插入图片描述
在这里插入图片描述

3.2 状态保留按钮 🪄🔘

在这个例子中,我们可以使用Streamlit的session_state功能来实现这个目标。我将为你展示如何创建一个变量,然后使用按钮点击事件将该变量的值设置为True。
首先,我们需要导入Streamlit库:

import streamlit as st

在使用session_state之前,我们需要确认该变量是否已经存在于session_state中,如果不存在,我们可以设置它的初始值为False:

if 'clicked' not in st.session_state:st.session_state.clicked = False

接下来,我们定义一个回调函数click_button(),在按钮点击事件发生时,我们将clicked变量的值设置为True:

def click_button():st.session_state.clicked = True

现在,我们可以创建一个按钮,并使用on_click参数将回调函数与按钮关联起来:

st.button('点击我', on_click=click_button)

最后,我们可以通过检查clicked变量的值来确定按钮是否被点击了。如果点击了按钮,我们可以在页面上保留相应的消息和嵌套小部件:

if st.session_state.clicked:# 消息和嵌套小部件将保留在页面上st.write('按钮已点击!')st.slider('选择一个值')

在这里插入图片描述

3.3 切换按钮 🔄🔘

如果你想让按钮像一个切换开关一样工作,那么可以考虑使用st.checkbox。否则,你可以使用一个带有回调函数的按钮来反转保存在st.session_state中的布尔值。

在这个例子中,我们使用一个按钮来控制另一个小部件的显示与隐藏。通过根据button变量的值在条件语句中显示或隐藏st.slider,用户可以在不让它消失的情况下与滑动条进行交互。

首先,我们需要导入Streamlit库:

import streamlit as st

在使用session_state之前,我们需要确认该变量是否已经存在于session_state中,如果不存在,我们可以设置它的初始值为False:

if 'button' not in st.session_state:st.session_state.button = False

接下来,我们定义一个回调函数click_button(),在按钮点击事件发生时,我们将button变量的值取反:

def click_button():st.session_state.button = not st.session_state.button

现在,我们可以创建一个按钮,并使用on_click参数将回调函数与按钮关联起来:

st.button('点击我', on_click=click_button)

然后,我们可以通过检查button变量的值来确定按钮是否处于打开状态。如果按钮处于打开状态,我们将保留相应的消息和嵌套小部件:

if st.session_state.button:# 消息和嵌套小部件将保留在页面上st.write('按钮已打开!')st.slider('选择一个值')
else:st.write('按钮已关闭!')

在这里插入图片描述

3.4 控制流程的按钮 🕹️🔘

与将内容嵌套在按钮中不同,另一种方法是使用st.session_state中的值来指定流程的"步骤"或"阶段"。在这个例子中,我们的脚本有四个阶段:

  1. 用户开始之前。
  2. 用户输入姓名。
  3. 用户选择颜色。
  4. 用户收到感谢消息。

在开始阶段,一个按钮可以将阶段从0增加到1。在结束阶段,一个按钮可以将阶段从3重置为0。在阶段1和2中使用的其他小部件具有回调函数来设置阶段。如果你的流程有依赖步骤,并且想保持之前的阶段可见,那么这样的回调函数会强制用户在更改之前的小部件时重新跟踪后续阶段。

首先,我们需要导入Streamlit库:

import streamlit as st

在使用session_state之前,我们需要确认该变量是否已经存在于session_state中,如果不存在,我们可以设置它的初始值为0:

if 'stage' not in st.session_state:st.session_state.stage = 0

接下来,我们定义一个函数set_state(i),用于设置阶段变量的值:

def set_state(i):st.session_state.stage = i

然后,根据当前阶段的值,我们可以显示不同的内容。在阶段0中,我们可以创建一个按钮来将阶段增加到1:

if st.session_state.stage == 0:st.button('开始', on_click=set_state, args=[1])

在阶段1和2中,我们可以使用回调函数来设置阶段的值。例如,在阶段1中,我们可以使用text_input小部件来获取用户的姓名,并在值更改时将阶段设置为2:

if st.session_state.stage >= 1:name = st.text_input('姓名', on_change=set_state, args=[2])

在阶段2中,我们可以显示根据用户输入的姓名显示的消息,并通过使用selectbox小部件来让用户选择颜色。如果颜色选择框的值为None,我们可以将阶段设置回2:

if st.session_state.stage >= 2:st.write(f'你好,{name}!')color = st.selectbox('选择一种颜色',[None, '红色', '橙色', '绿色', '蓝色', '紫色'],on_change=set_state, args=[3])if color is None:set_state(2)

在阶段3中,我们可以显示一个带有感谢消息的消息,并创建一个按钮来将阶段重置为0:

if st.session_state.stage >= 3:st.write(f'😊谢谢你,{color}!')st.button('重新开始', on_click=set_state, args=[0])

在这里插入图片描述

3.5 动态添加小部件的按钮 ✔️💡💻🔄

在开始之前,我们需要确保使用唯一的索引来保持键的唯一性并避免错误。在本示例中,我们定义了一个display_input_row函数,用于渲染一行小部件。该函数接受一个索引作为参数。小部件的键(key)使用了传入的索引,以便可以在单个脚本重新运行时多次执行而不会重复任何小部件的键。

import streamlit as stdef display_input_row(index):left, middle, right = st.columns(3)left.text_input('First', key=f'first_{index}')middle.text_input('Middle', key=f'middle_{index}')right.text_input('Last', key=f'last_{index}')

接下来,我们检查st.session_state中是否存在名为’rows’的键。如果不存在,我们将其初始值设置为0。

if 'rows' not in st.session_state:st.session_state['rows'] = 0

然后,我们定义一个increase_rows函数,在点击按钮时增加st.session_state[‘rows’]的值。

def increase_rows():st.session_state['rows'] += 1

我们创建一个"Add person"按钮,并将它与increase_rows函数进行关联。

st.button('Add person', on_click=increase_rows)

接下来,我们使用一个循环来根据st.session_state[‘rows’]的值动态地调用display_input_row函数。

for i in range(st.session_state['rows']):display_input_row(i)

最后,我们展示添加结果的部分。在一个循环中,我们根据st.session_state中的键和索引来获取相应的小部件的值,并将其展示在页面上。

st.subheader('People')
for i in range(st.session_state['rows']):st.write(f'Person {i+1}:',st.session_state[f'first_{i}'],st.session_state[f'middle_{i}'],st.session_state[f'last_{i}'])

通过点击"Add person"按钮,我们可以动态地添加小部件,并在页面上显示。在循环中,每个小部件都有一个唯一的键,因此我们可以轻松地获取和展示它们的值。
在这里插入图片描述

3.6 使用按钮处理耗时或写入文件的过程 🔄💾⏱️💡

介绍如何使用按钮来处理耗时或写入文件的过程,并将结果保存起来,以便在不必要重新执行的情况下继续访问。这对于保存到磁盘或写入数据库的过程尤其有帮助。
在本示例中,我们有一个expensive_process函数,它依赖于两个参数:option和add。函数执行相应的过程,并返回一个结果。值得注意的是,更改option会改变输出结果,而更改add只是提供了一个参数。

import streamlit as st
import pandas as pd
import timedef expensive_process(option, add):with st.spinner('Processing...'):time.sleep(5)df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C':[7, 8, 9]}) + addreturn (df, add)

我们创建了两个列(cols)来显示输入选项和添加数值的小部件。使用selectbox和number_input分别接收用户的输入。在代码中,我们使用了st.columns以便将小部件在页面上水平排列。

cols = st.columns(2)
option = cols[0].selectbox('Select a number', options=['1', '2', '3'])
add = cols[1].number_input('Add a number', min_value=0, max_value=10)

接下来,我们检查st.session_state中是否存在名为’processed’的键。如果不存在,我们将其初始化为空字典。

if 'processed' not in st.session_state:st.session_state.processed = {}

当用户点击"Process"按钮时,我们执行expensive_process函数,并将结果保存在st.session_state.processed中。

if st.button('Process'):result = expensive_process(option, add)st.session_state.processed[option] = result

最后,我们检查option是否在st.session_state.processed中,如果是的话,我们展示处理结果并打印出数据框。

if option in st.session_state.processed:st.write(f'Option {option} processed with add {add}')st.write(st.session_state.processed[option][0])

通过点击"Process"按钮,我们可以处理耗时的过程并将结果保存下来。如果用户改变了option,我们会重新运行相应的过程并更新结果。

需要注意的是,保存在st.session_state中的结果仅对当前用户的当前会话可见。如果使用st.cache_data代替,结果将对所有用户和所有会话可见。此外,如果要更新已保存的结果,必须清除该函数的所有保存结果。
在这里插入图片描述

4 常见的按钮反模式 🚫⚠️

在使用按钮时,以下是一些常见的错误做法,需要格外注意。

4.1 按钮嵌套在按钮内部

在下面的示例中,我们可以看到按钮2被嵌套在按钮1的内部。这种嵌套将导致按钮2永远不会被执行。因为当按钮1被点击时,按钮2的代码块将永远不会执行。

import streamlit as stif st.button('Button 1'):st.write('Button 1 was clicked')if st.button('Button 2'):# This will never be executed.st.write('Button 2 was clicked')

4.2 将其他小部件嵌套在按钮内部

在下面的示例中,我们可以看到当用户点击"Sign up"按钮时,会显示一个用于输入姓名的文本框。然而,当用户输入姓名后,欢迎消息不会被执行。这是因为欢迎消息的代码块被嵌套在"Sign up"按钮的代码块内部,而不是与之并列。

import streamlit as stif st.button('Sign up'):name = st.text_input('Name')if name:# This will never be executed.st.success(f'Welcome {name}')

4.3 在按钮内部嵌套处理过程,但未保存到会话状态中

在下面的示例中,我们可以看到"Get data"按钮被点击时,我们会读取一个CSV文件,并在屏幕上展示结果。然而,当用户点击"Save"按钮时,将会遇到一个错误。这是因为我们在"Save"按钮的代码块内部尝试将数据保存为CSV文件,但是没有将数据保存到会话状态(session state)中,所以在"Save"按钮点击后,数据丢失了。

import streamlit as st
import pandas as pdfile = st.file_uploader("Upload a file", type="csv")if st.button('Get data'):df = pd.read_csv(file)# This display will go away with the user's next action.st.write(df)if st.button('Save'):# This will always error.df.to_csv('data.csv')

5 结语

感谢您阅读本篇博客!我们希望这篇博客对您理解如何使用按钮处理耗时或写入文件的过程有所帮助。如果您有任何问题或需要进一步的帮助,请随时留言。如果您觉得这篇博客对您有用,请点赞并关注我们的博客,以获取更多有用的信息和教程。谢谢您的支持!🙏❤️📚
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/23159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tensorrt 原生Activate 算子讲解

Tensorrt operators docs: Activation Apply an activation function on an input tensor A and produce an output tensor B with the same dimensions. import numpy as np from cuda import cudart import tensorrt as trt # 输入张量 NCHW nIn, cIn, hIn, wI…

Scrum敏捷开发流程图怎么画?

1. 什么是Scrum敏捷开发流程图? Scrum敏捷开发流程图是一种可视化工具,用于形象地描述Scrum敏捷开发方法中的工作流程和活动。Scrum敏捷开发流程图展示了项目从需求收集到产品交付的整个开发过程,帮助团队理解和跟踪项目进展,促…

02.Redis实现添加缓存功能

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis实现添加缓存功能 学习产出&#xff1a; 流程图 1. 准备pom环境 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId&g…

EXCEL,多条件查询数字/文本内容的4种方法

目录 1 问题&#xff1a;如何根据多条件查询到想要的内容 2 方法总结 2.1 方法1&#xff1a; sumif() 和sumifs() 适合查找符合条件的多个数值之和 2.2 方法2&#xff1a;使用lookup(1,0/((区域1条件1)*(区域2条件2)*....),结果查询区域) 2.3 方法3&#xff1a;使用 ind…

广西茶叶元宇宙 武隆以茶为媒 推动茶文旅产业融合发展

8月4日&#xff0c;重庆市武隆区启动为期3天的“武隆首届玩茶荟”。本次活动以“中国最美玩茶地——武隆”为主题&#xff0c;吸引众多国内知名专家、茶企和茶馆相关负责人&#xff0c;共同探索武隆茶文旅融合发展新路径和新业态。 广西茶叶元宇宙&#xff1a;广西茶叶元宇宙 …

React安装ant design组件库,并使用

ant design是一个很棒的组件库&#xff0c;官方地址&#xff1a;快速上手 - Ant Design 但是如何在React里面用起来&#xff0c;好像并不是很顺畅&#xff0c;没有像Vue里面那么友好&#xff0c;因为我踩过这个坑&#xff0c;虽然安装很简单&#xff0c;但是想要出样式&#x…

合并两个有序链表(leetcode)

题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]思路 每次递归都会比较当前两个节点的值&#xff0c;选择较小的节点作为合并后的链…

edge://settings/defaultbrowser default ie

Microsoft Edge 中的 Internet Explorer 模式 有些网站专为与 Internet Explorer 一起使用&#xff0c;它们具有 Microsoft Edge 等新式浏览器不支持的功能。 如果你需要查看其中的某个网站&#xff0c;可使用 Microsoft Edge 中的 Internet Explorer 模式。 大多数网站在新…

Java:如何破坏类加载器的双亲委派机制?

本文重点 我们前面分析过loadClass方法,我们可以发现,这个方法的逻辑就是双亲委派机制,也就是说只要不破坏这个方法,那么就不会破坏双亲委派机制。如果要想破坏双亲委派机制,我们需要在类中重写loadClass方法,只要这样,那么就不会走双亲委派机制了。 破坏还是不破坏双…

2023-08-05力扣今日五题-好题

链接&#xff1a; 剑指 Offer 52. 两个链表的第一个公共节点 题意&#xff1a; 如题 解&#xff1a; 非常有趣的双指针 首先我们不管他们是否有公共段啊&#xff0c;我们要知道一个指针从A出发走到A结尾&#xff0c;再从B出发走到B结尾&#xff0c;和从B出发最终到A结尾是…

麦肯锡战略思维四大原则

麦肯锡战略思维四大原则 曾任职麦肯锡、安永等国家国际知名咨询机构的周正元&#xff0c;在其著作《麦肯锡结构化战略思维》将其系统的整理呈现出来&#xff0c;便于学习和使用。 模型介绍 工作中的你&#xff0c;是不是经常遇到复杂问题&#xff0c;六神无主&#xff1f; 专业…

JAVA语言:如何自定义类加载器?

本文重点 前面的课程中&#xff0c;我们已经学习了双亲委派机制&#xff0c;如果想要自定义一个类加载器&#xff0c;那么我们只需要继承ClassLoader&#xff0c;并且定义好自己的findClass就可以了&#xff0c;也就是自己的类加载器是如何进行工作的&#xff0c;而loadClass就…

Java thymeleaf bug排查记录

刚学Java 做项目时报了一个错误 一时间看的莫名其妙 EL1008E: Property or field createTime cannot be found on object of type java.util.HashMap - maybe not public or not valid? 随即向上排查至第一个报错&#xff0c;发现是thymeleaf渲染时报错。 Exception proces…

【Python从入门到进阶】31、使用JSONPath解析淘票票网站地区接口数据

接上篇《30、JSONPath的介绍和使用》 上一篇我们介绍了JSONPath的基础和具体使用&#xff0c;本篇我们来具体使用JSONPath&#xff0c;来解析淘票票网站的地区接口数据。 一、引言 1、JsonPath的作用和用途&#xff1f; JsonPath是一种用于在JSON数据中进行查询和提取的表达…

【Jmeter】压测mysql数据库中间件mycat

目录 背景 环境准备 1、下载Jmeter 2、下载mysql数据库的驱动包 3、要进行测试的数据库 Jmeter配置 1、启动Jmeter图形界面 2、加载mysql驱动包 3、新建一个线程组&#xff0c;然后如下图所示添加 JDBC Connection Configuration 4、配置JDBC Connection Configurati…

【GEMM预备工作】行主序和列主序矩阵的内存中的连续性,解决理解问题

在内存存储中&#xff0c;默认矩阵是按照行优先储存的&#xff0c;即矩阵的每一列在内存中是连续的。行优先矩阵储存中行数据是不连续的。 而对于列主序的矩阵&#xff0c;是按照列优先储存的&#xff0c;即矩阵的每一行在内存中是连续的。列优先矩阵储存中列数据是不连续的&am…

OPENCV C++(一) 二进制和灰度原理 处理每个像素点值的方法

#include <opencv2/opencv.hpp> using namespace std; using namespace cv;必须包含的头文件&#xff01; 才能开始编写代码 读取相片 一般来说加个保护程序 不至于出error和卡死 Mat image imread("test.webp"); //存放自己图像的路径 if (image.empty()){p…

SOLIDWORKS软件如何批量加图号

当我们在做模型设计时&#xff0c;不管是新研发还是改型设计&#xff0c;都需要规范模型的属性信息&#xff0c;其中必不可少的就是图号或代号。但是一套产品是由很多个零件组成的&#xff0c;每个零件的属性都需要去规范&#xff0c;一个一个改其实工作量也不小&#xff0c;今…

替代LT8711龙讯替代RTD2172 CS5265中文规格书4K60HZ转接线 设计Type-C转HDMI2.0高清投屏方案

龙迅LT8711是一款Type-C/DP1.2 to HDMI2.0方案芯片&#xff0c;北京集睿致远&#xff08;ASL&#xff09;推出的CS5265可以完全代替LT8711UX&#xff0c;封装尺寸比LT8711UX小的同时&#xff0c;CS5265的芯片集成度高&#xff0c;内置MCU&#xff0c;内置lLDO等&#xff0c;CS5…

【react】react生命周期钩子函数:

文章目录 一、生命周期概念:二、生命周期:三、挂载阶段&#xff08;constructor > render > componentDidMount&#xff09;&#xff1a;四、更新阶段&#xff08;render > componentDidUpdate&#xff09;&#xff1a;五、卸载阶段&#xff08;componentWillUnmount …