Streamlit 使用介绍
- 安装和运行
- 参考资料
- 公共组件
- 页面设置
- 显示代码并运行
- st.help 查询帮助文档
- 多页渲染
- 对象渲染
- 直接渲染对象
- 文本渲染
- st.markdown
- 代码渲染
- 公式渲染
- 展示图表
- 显示表格
- 趋势小卡片
- 显示图表
- 显示媒体
- 画迷宫
- 显示本地图片
- 播放视频/音频
- 显示状态
- 彩蛋-放气球
- 进度条
- 互动组件
- 单选框
- 按钮
- 复选框
- 单选框
- 下拉选择框
- 多项选择框
- 多行文本框
- 滑杆、选项滑杆
- 滑杆
- 选项滑杆
- 信息输入
- 下载按钮
- 文件上传
- 拾色器
- 布局、控制流和工具
- st.sidebar 侧边栏
- st.expander 折叠栏
- st.columns 多列布局
- st.container 容器
- st.empty 单一元素空容器
- st.stop 立即结束程序
- 表单与表单提交按钮
- 访问数据库
- 通过odbc驱动访问SQLite3数据库
安装和运行
#安装
pip install streamlit
#运行:
streamlit run xxx.py
参考资料
官方帮助文档
Streamlit 体验 作者:刘志昱 (1.1.0.20211105)
本文用到的所有代码及资源包下载
公共组件
页面设置
通过st.set_page_config可以设置页面的部分属性。注意 本句是初始化语句,只能出现在程序的第一句,且只能出现一次。
import streamlit as st
#" page_title:页面标题,str or None。page_icon:页面图标,s.image or Emoji "
#" layout:可以为centered或wide。如果是wide则为宽屏模式。建议在分辨率较低的情况下使用centered,并尽量减少复杂布局。"
#" initial_sidebar_state:在auto模式下,电脑端会自动显示sidebar,而移动端会隐藏sidebar。一般不需要修改。"
#" menu_items:应用右上角的功能框,可以加入你的自定义内容。"
st.set_page_config(
page_title="Ex-stream-ly Cool App",
page_icon="🧊",
layout="wide",
initial_sidebar_state="expanded",
menu_items={
'Get Help': 'https://www.extremelycoolapp.com/help',
'Report a bug': "https://www.extremelycoolapp.com/bug",
'About': "# This is a header. This is an *extremely* cool app!"
}
)
效果:
显示代码并运行
with st.echo():
st.write('This code will be printed')
效果:
st.help 查询帮助文档
st.help(list())
效果:
多页渲染
如需渲染多个网页,只需编写主页面+每个页面的py文件,子页面统一放在主页面py文件所在路径的 **“pages”**目录即可,效果如下:
对象渲染
直接渲染对象
var_demo = [123,456,789]
var_demo
var_demo1 = "HelloWorld!"
var_demo1
14.7456
效果:
文本渲染
st.markdown
st.markdown("##### 123456")
st.markdown("> markdown文字")
st.caption("注释文字")
效果:
代码渲染
st.code("import streamlit\nprint(123)\n ", language='python')
效果:
公式渲染
st.latex(r"a + ar + a r^2 + a r^3 + \cdots + a r^{n-1} = \sum_{k=0}^{n-1} ar^k = a \left(\frac{1-r^{n}}{1-r}\right)")
效果:
展示图表
显示表格
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randn(5,5),columns=(str(i) for i in range(5)))
"### This is dataframe"
st.dataframe(df.style.highlight_max(axis=1),1000,200) #后面2个参数是表格宽高
趋势小卡片
st.metric是用来展示数据变化趋势的小卡片。文字会显示为三行,分别对应label(标签)、value(当前值)、delta(变化值)
r12c1,r12c2,r12c3,r12c4 = st.columns([1,2,3,3])
with r12c1: # a simple example
st.metric(label="Temperature", value="70 °F", delta="1.2 °F")
with r12c2: # render a line when value is None
st.metric(label="Number1", value=None, delta="-15")
with r12c3: # red up and green down
st.metric(label="Number2", value=666,delta=166,delta_color="inverse")
with r12c4: # color off
st.metric(label="HelloWorld",value="111",delta="222",delta_color="off")
st.metric(label="HelloWorld1",value="222",delta="-111",delta_color="off")
效果:
显示图表
这三个图表是Streamlit内置的,参数相同。width和height为宽高的像素值,use_container_width为使用容器宽度,优先级高于width和height。通常不需要设置参数。
st.code("\
st.line_chart(data=None, width:int=0, height:int=0, use_container_width:bool=True)\n\
st.area_chart(data=None, width:int=0, height:int=0, use_container_width:bool=True)\n\
st.bar_chart(data=None, width:int=0, height:int=0, use_container_width:bool=True)\n\
", language='python')
df = pd.DataFrame(np.random.randn(20,3),columns=["A","B","C"])
"###### 这是折线图 This is line chart"
st.line_chart(df)
"###### 这是面积图 This is area chart"
st.area_chart(df)
"###### 这是棒棒图 This is bar chart"
st.bar_chart(df)
"###### 显示地图"
"""这个内置方法仅支持根据经纬度在地图上做标记。如果需要专业地图功能,请使用第三方API。要求传入的数据data为经纬度,并且列名必须为lat(纬度latitude)和lon(经度longitude)。zoom参数是地图缩放等级。use_container_width为使用容器宽度。"""
st.map(data=None, zoom=None, use_container_width=True)
df = pd.DataFrame(np.random.randn(200,2) / [50,50] + [39.81819,119.48165],columns=["lat","lon"])
st.map(df,zoom=11) # Welcome to Beidaihe!Try zoom in and out (by mouse wheel up and down)!
Streamlit目前支持的第三方图表库包括matplotlib、altair、vega_lite、plotly、bokeh、pydeck、graphviz。详细API请参考官方文档。因第三方依赖占用资源,本文档不再逐一展示。
显示媒体
画迷宫
import random
import numpy as np
r15c1,r15c3= st.columns([3,3])
with r15c1:
r15form = st.form("r15form")
num_rows = int(r15form.number_input("请输入行数 Input Rows:",1,100,20,1))
num_cols = int(r15form.number_input("请输入列数 Input Columns:",1,100,20,1))
if r15form.form_submit_button("画迷宫 Draw Maze"):
M = np.zeros((num_rows,num_cols,5), dtype=np.uint8)
image = np.zeros((num_rows*10,num_cols*10), dtype=np.uint8)
r = 0
c = 0
history = [(r,c)]
while history:
M[r,c,4] = 1
check = []
if c > 0 and M[r,c-1,4] == 0:
check.append('L')
if r > 0 and M[r-1,c,4] == 0:
check.append('U')
if c < num_cols-1 and M[r,c+1,4] == 0:
check.append('R')
if r < num_rows-1 and M[r+1,c,4] == 0:
check.append('D')
if len(check):
history.append([r,c])
move_direction = random.choice(check)
if move_direction == 'L':
M[r,c,0] = 1
c = c-1
M[r,c,2] = 1
if move_direction == 'U':
M[r,c,1] = 1
r = r-1
M[r,c,3] = 1
if move_direction == 'R':
M[r,c,2] = 1
c = c+1
M[r,c,0] = 1
if move_direction == 'D':
M[r,c,3] = 1
r = r+1
M[r,c,1] = 1
else:
r,c = history.pop()
M[0,0,0] = 1
M[num_rows-1,num_cols-1,2] = 1
for row in range(0,num_rows):
for col in range(0,num_cols):
cell_data = M[row,col]
for i in range(10*row+2,10*row+8):
image[i,range(10*col+2,10*col+8)] = 255
if cell_data[0] == 1:
image[range(10*row+2,10*row+8),10*col] = 255
image[range(10*row+2,10*row+8),10*col+1] = 255
if cell_data[1] == 1:
image[10*row,range(10*col+2,10*col+8)] = 255
image[10*row+1,range(10*col+2,10*col+8)] = 255
if cell_data[2] == 1:
image[range(10*row+2,10*row+8),10*col+9] = 255
image[range(10*row+2,10*row+8),10*col+8] = 255
if cell_data[3] == 1:
image[10*row+9,range(10*col+2,10*col+8)] = 255
image[10*row+8,range(10*col+2,10*col+8)] = 255
with r15c3:
st.image(image,caption=f"{num_rows} x {num_cols} 迷宫")
效果:
显示本地图片
from PIL import Image
st.image(Image.open("pic.jpeg"))#该图片存在本地py文件所在路径
播放视频/音频
播放所用视频/音频文件均存储在py文件所在本地路径
# videos
st.video(open('video.mp4', 'rb').read())
# audios
st.audio(open('花好月圆.mp3', 'rb').read(),format='audio/mp3')
效果:
显示状态
st.info("On ne pomnit slova 'da' i slova 'net' 他不记得“是”和“否”两个词")
st.success("On ne pomnit ni chinov, ni imen 他不记得军衔和荣誉")
st.warning("I spasoben datjanut'snja da zvezd 他本以为可以摘到星星")
st.exception(NameError("Ne schitaja, chto eta son 不曾想,这只是个梦"))
st.error("I upast, opalennim zvezdoy pa imeni solntse 他坠落,被焚尽,被那颗,名为太阳的星辰")
效果:
彩蛋-放气球
st.balloons()
进度条
"Progress 动态进度条"
import time
bar = st.progress(0.0)
for percentage in range(20):
time.sleep(0.1)
bar.progress(percentage * 0.05 + 0.05)
"Spinner 静态进度条"
with st.spinner('Wait for it...'):
time.sleep(1)
st.success('Done!')
互动组件
Streamlit提供了大量交互组件。这些组件有如下特性:
(1)每个组件自身都有返回值,因此不需要回调函数(当然如果有需要也可以从参数设置)。
(2)每当组件的状态值发生变化(例如按钮被点击、输入值被修改等等),会触发整个应用的重新运行(rerun)。通常来说很方便,但是对于大型应用、特别是组件存在组合、嵌套时,就需要使用缓存机制(session_state和cache,下文会讲解)。
(3)组件易于理解和使用,有一些公共的含义相同或相似的标准参数,下文会讲解。大部分参数通常无需修改,使用方便。
单选框
con = st.container()
def callback():
st.balloons()
pass
myradio = st.radio(label = "这是单选框",
options = ("A","B","C"), # 选项
index = 1, # 初始化选项
format_func = lambda x : f"这是选项{x}", #这是格式化函数,注意我把原始选项ABC修改了。一般地,跟字典或dataframe结合也很好用。
key = "radio_demo",
help = "我是帮助文字", # 看到组件右上角的问号了没?上去悬停一下。
on_change = callback, args = None, kwargs = None)
if myradio:
con.write(f"你选择了{myradio} 它的session_state是{st.session_state.radio_demo}")
按钮
#st.button(bool)
if st.button("点我!"):
"你点击了按钮!"
复选框
if st.checkbox("复选框1号"):
"选中啦!"
else:
"没选中。"
单选框
my_radio = st.radio("你最喜欢哪首歌?",
("pachka sigaret","gruppa krovi","zvezda pa imeni solntse"))
if my_radio:
f"我最喜欢{my_radio}"
下拉选择框
my_selectbox = st.selectbox("Which song do you like most?",
("pachka sigaret","gruppa krovi","zvezda pa imeni solntse"))
if my_selectbox:
f"I like {my_selectbox} most."
多项选择框
my_multiselect = st.multiselect("选择你喜欢的歌",
options=("《一包香烟》","《血型》","《名为太阳的星辰》"),
default=("《一包香烟》","《血型》"))
if my_multiselect:
f"我喜欢 {'、'.join(i for i in my_multiselect)}。"
多行文本框
st.text_area("Text Area Height = 200 pixels",
f"{' '.join(str(i) for i in range(200))}",
height=200)
滑杆、选项滑杆
滑杆
my_slider = st.slider("计算平方数",0,100,50,1)
if my_slider:
f"{my_slider}的平方是{my_slider ** 2}"
选项滑杆
transdict = {"Red":"红","Orange":"橙","Yellow":"黄",
"Green":"绿","Blue":"蓝","Purple":"紫"}
my_select_slider = st.select_slider("Choose Your Favorite Color!",
("Red","Orange","Yellow","Green","Blue","Purple"),"Red")
if my_select_slider:
f"你选择了{transdict[my_select_slider]}色!"
信息输入
st.text_input("这是TEXT_INPUT")
st.number_input("这是NUMBER_INPUT",value=10)
st.date_input("这是DATE_INPUT")
st.time_input("这是TIME_INPUT")
下载按钮
# 1
#@st.cache
def convert_df(df):
return df.to_csv().encode('utf-8')
d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']),'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
df_one = pd.DataFrame(d)
csv = convert_df(df_one)
st.download_button(label="Download data as CSV",
data=csv, file_name='large_df.csv')
# 2
text_contents = '''This is some text'''
st.download_button('Download some text', text_contents)
# 3
binary_contents = b'example content'
# MIME default as 'application/octet-stream'
st.download_button('Download binary file', binary_contents)
# 4
with open("pic.jpeg", "rb") as file:
btn = st.download_button(label="Download image",
data=file,file_name="pic.jpeg",mime="image/png")
文件上传
from io import StringIO
# 上传单个文件 Upload single file
uploaded_file = st.file_uploader("Choose a file")
if uploaded_file is not None:
# To read file as bytes:
bytes_data = uploaded_file.getvalue()
st.write(bytes_data)
# To convert to a string based IO:
stringio = StringIO(uploaded_file.getvalue().decode("utf-8"))
st.write(stringio)
# To read file as string:
string_data = stringio.read()
st.write(string_data)
# Can be used wherever a "file-like" object is accepted:
dataframe = pd.read_csv(uploaded_file)
st.write(dataframe)
#上传多个文件 Upload multi files
uploaded_files = st.file_uploader("Choose a CSV file", accept_multiple_files=True)
for uploaded_file in uploaded_files:
bytes_data = uploaded_file.read()
st.write("filename:", uploaded_file.name)
st.write(bytes_data)
拾色器
color = st.color_picker('选择颜色', '#00f900')
st.write('当前的颜色值是', color)
布局、控制流和工具
st.sidebar 侧边栏
“创建组件时,只需加上st.sidebar就可以将组件显示在侧栏。例如st.radio改为st.sidebar.radio。除了st.echo和st.spinner都支持。”
st.sidebar.radio(
"Choose a shipping method",
("Standard (5-15 days)", "Express (2-5 days)")
)
st.expander 折叠栏
你可以使用st.expander来隐藏不需要立刻展示的部分,以优化布局空间。请注意,即使st.expander在折叠状态下,其内部内容也会被渲染。所以,如果内容较多,还是建议更多地利用st.cache等功能分页处理。
with st.expander("点击查看内部内容",False):
st.success("这是内部的内容")
st.columns 多列布局
如果是整数n,则将该行平均分成n列;如果是序列,例如[3,2,1],则将该行分为三列,各列的宽度分别为最大宽度的3/6、2/6、1/6。
注意:
- 移动终端会自动使用自适应模式,导致本功能失效。
- 在目前版本中st.columns不能嵌套,即不能在某一列中嵌套使用st.columns。
r24c1,r24c2,r24c3 = st.columns(3)
with r24c1:
st.info("这是第一列")
with r24c2:
st.info("这是第二列")
with r24c3:
st.info("这是第三列")
r25c1,r25c2,r25c3,r25c4 = st.columns([5,2,2,1])
with r25c1:
st.success("第一列")
with r25c2:
st.warning("第二列")
with r25c3:
st.error("第三列")
with r25c4:
st.info("第4列")
st.container 容器
st.container是一个占位容器。Streamlit是自上而下渲染的。如果你需要调整对象显示的顺序,就可以先设置一个容器,然后将渲染后的内容写入容器里。
将一个不可见的容器插入到您的应用程序中,该容器可用于容纳多个元素。例如,这允许您将多个元素乱序插入到您的应用程序中。
要将元素添加到返回的容器中,您可以使用 with 表示法(首选)或直接在返回的对象上调用方法。请参阅下面的示例。
container = st.container()
container.write("This is inside the container")
st.write("This is outside the container")
container.write("This is inside too")
st.empty 单一元素空容器
st.empty也是一个占位容器,不同的是,st.empty只接收“一个”对象的写入,再次写入会覆盖此前的内容。
import time
with st.empty():
for seconds in range(2):
st.write(f"⏳ {seconds} seconds have passed")
time.sleep(1)
st.write("✔️ Done!")
st.stop 立即结束程序
name = st.text_input('Name')
if not name:
st.warning('Please input a name.')
# st.stop() # quit
st.success('Thank you for inputting a name.')
表单与表单提交按钮
- st.form
- st.form_submit_button
参数:key:表单键名(注意,必须指定)。clear_on_submit:是否在提交后清除表单内容。
前面说过,Streamlit的组件状态变化会触发rerun。如果想同时提交一组数据时,普通的组件就不能用了。
st.form也是一个容器,用法与st.container类似,可以用with语句,也可以链式调用。
注意,在st.form中,只有st.form_submit_button允许有回调函数。
with st.form("calc"):
num_1 = st.number_input("请输入第一个数",value=10)
num_2 = st.number_input("请输入第二个数",value=10)
if st.form_submit_button("计算乘积"):
f"两个数的乘积是{num_1 * num_2}"
访问数据库
通过odbc驱动访问SQLite3数据库
代码:
import streamlit as st
import pyodbc
# Initialize connection.
# Uses st.cache_resource to only run once.
@st.cache_resource
def init_connection():
return pyodbc.connect(
"DRIVER={SQLite3 ODBC Driver};SERVER="
+ st.secrets["localhost"]
+ ";DATABASE="
+ st.secrets["123"]
+ ";UID="
#+ st.secrets[""]
+ ";PWD=;"
#+ st.secrets[""]
)
conn = pyodbc.connect("Driver=SQLite3 ODBC Driver;Database=d://123")
# Perform query.
# Uses st.cache_data to only rerun when the query changes or after 10 min.
#@st.cache_data(ttl=600)
def run_query(query):
with conn.cursor() as cur:
cur.execute(query)
return cur.fetchall()
rows = run_query("SELECT * from '2222';")
# Print results.
for row in rows:
st.write(f"{row[0]} :{row[1]}:{row[2]}")
用SQLiteStudio创建表如下:
效果: