目录
- 简介
- 不携带信息的触发
- 隐藏指定st.button(label, key)
- 触发button的html
- 代码汇总
- 携带信息的触发
- 为什么需要携带信息
- 前端JavaScript修改
- flask处理
- 总代码
简介
由于streamlit可以同时在实现前后端结合,非常方便,但是这也造成了user难以方便的对页面的组件进行位置控制,本文将使用streamlit+flask,实现普通按钮触发与携带信息
不携带信息的触发
隐藏指定st.button(label, key)
这里给出隐藏指定button的方法
hidden_button_class = 'div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"]'
hide_button_style = f"""
<style>
{hidden_button_class} {{display: none;}}
</style>
""" # 如果已经使用f方法,想使用花括号就必须使用{{代替{
st.markdown(hide_button_style, unsafe_allow_html=True)
hidden_button中的st-key-hidden_trigger可以根据你的button的key进行修改
如果key = “test”, 这里就改为st-key-test,就可以实现对某个指定button的隐藏!
当然,把.st-key-{key_name}删掉,就可以直接隐藏所有button
触发button的html
通过在页面插入iframe,在iframe内进行JavaScript操作,实现对隐藏button的模拟点击
all_html = f"""<script>function handleButtonClick(event) {{// 查找隐藏的streamlit按钮按钮const hiddenButton = parent.document.querySelector('div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"]');if (hiddenButton) {{// 触发点击hiddenButton.click();}}}}// 事件委托处理点击document.addEventListener('click', function(event) {{if (event.target.matches('.detail-btn')) {{handleButtonClick(event);}}}});</script><button class="detail-btn" data-title="test">点我</button> // 这里button的样式可以随便修改,根据需求"""
st.components.v1.html(all_html)
但是需要注意:外部的button样式对v1.html的样式不生效,内部的button样式对v1.html同样不生效
所以所有内部样式css必须写入all_html中
代码汇总
如果想要实现
import streamlit as sthide_button_style = """
<style>
div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"] {display: none;}
</style>
"""
st.markdown(hide_button_style, unsafe_allow_html=True)def generate_button():all_html = f"""<script>function handleButtonClick(event) {{// 查找隐藏按钮const hiddenButton = parent.document.querySelector('div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"]');if (hiddenButton) {{// 触发点击hiddenButton.click();}}}}// 事件委托处理点击document.addEventListener('click', function(event) {{if (event.target.matches('.detail-btn')) {{handleButtonClick(event);}}}});</script><button class="detail-btn" data-title="test">点我</button>"""st.components.v1.html(all_html)returngenerate_button()
if st.button("hidden_trigger", key="hidden_trigger"):st.success("可以修改为你想实现的功能~")
st.button("no_hidden_trigger", key="no_hidden_trigger")
css样式指定隐藏的button
我们在点击自己设定的button后,触发了隐藏的button,没有触发不隐藏的button
携带信息的触发
为什么需要携带信息
假设有这么一个股票信息管理界面,很明显,如果使用streamlit原生的组件,将难以实现查看详情的按钮效果(嵌入在内容卡片中),而一支股票有很多信息(意味着更多的查看详情),为每一个查看详情都提供一个隐藏button,会导致无意义的内存占用,所以可以通过设置一个隐藏按钮,并通过flask后端处理数据,接收并传递信息
前端JavaScript修改
def generate_button():all_html = f"""<script>function handleButtonClick(event) {{const title = event.target.dataset.title;// 查找隐藏按钮const hiddenButton = parent.document.querySelector('div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"]');if (hiddenButton) {{// 触发点击hiddenButton.click();// 发送数据到Flaskfetch('http://localhost:8000/api/data', {{method: 'POST',headers: {{'Content-Type': 'application/json',}},body: JSON.stringify({{title, stock}}),}}).then(response => response.json()).then(data => console.log('Success:', data)).catch(error => console.error('Error:', error));}}}}// 事件委托处理点击document.addEventListener('click', function(event) {{if (event.target.matches('.detail-btn')) {{handleButtonClick(event);}}}});</script><button class="detail-btn" data-title="test">点我</button>"""st.components.v1.html(all_html)return
关键在于向flask发送一个post请求,携带我们需要传递的信息(发送json串,解析为dict)
event.target.dataset是(<button class=“detail-btn” data-title=“test”>点我</button>)中,data-title的值
所以:event.target.dataset.title = “test”
利用这个属性来传递信息
flask处理
from flask import Flask, request, jsonify
from flask_cors import CORS
import requestsapp = Flask(__name__)
CORS(app)@app.route('/api/data', methods=['POST'])
def receive_data():data = request.get_json()title = process_data(data["title"])with open("data.txt", "w", encoding="utf-8") as f:f.write(title)return jsonify({200: "成功"})def process_data(data):# 数据处理逻辑return data.upper()if __name__ == '__main__':app.run(port=8000, debug=True)
一定需要CORS(),这意味着能不能实现跨域接收信息,只有加上这个才能实现网页发送信息,而flask后端能够接收网页发送的信息
同时在接收信息之后,能够将信息写入data.txt供其他文件调用(信息传递)
总代码
import timeimport streamlit as stdef generate_button():all_html = f"""<script>function handleButtonClick(event) {{const title = event.target.dataset.title;const stock = event.target.dataset.stock;// 查找隐藏按钮const hiddenButton = parent.document.querySelector('div[data-testid="stElementContainer"].st-key-hidden_trigger button[data-testid="stBaseButton-secondary"]');if (hiddenButton) {{// 触发点击hiddenButton.click();// 发送数据到Flaskfetch('http://localhost:8000/api/data', {{method: 'POST',headers: {{'Content-Type': 'application/json',}},body: JSON.stringify({{title, stock}}),}}).then(response => response.json()).then(data => console.log('Success:', data)).catch(error => console.error('Error:', error));}}}}// 事件委托处理点击document.addEventListener('click', function(event) {{if (event.target.matches('.detail-btn')) {{handleButtonClick(event);}}}});</script><button class="detail-btn" data-title="test">点我</button>"""st.components.v1.html(all_html)returngenerate_button()
if st.button("hidden_trigger", key="hidden_trigger"):time.sleep(0.05) # 让flask能够写入信息,设置一小段时间延时with open("data.txt", "r", encoding="utf-8") as fr:content = fr.read() # 读取携带的信息st.success("接收到信息:" + content)