创建一个多页面APP
在 附加功能 部分,我们介绍了多页面应用程序的相关内容,包括如何定义页面、构建以及运行多页面应用程序,以及在用户界面中进行页面之间的导航操作。您可以在我们的 多页面应用程序指南 中获取更多详细信息。
在本指南中,让我们将对多页面应用程序的理解付诸实践,将我们之前版本的 streamlit hello
应用程序转换为一个多页面应用程序!
动机
在 Streamlit 1.10.0 版本之前,streamlit hello 命令是一个包含大量内容的单页应用程序。由于不支持多页面功能,我们只好在侧边栏使用 st.selectbox
来选择要运行的内容。这些内容包括三个用于绘图、地图绘制和数据框展示的演示示例。
这就是代码和单页应用程序的 原始样子。
import streamlit as st
def intro():
import streamlit as st
st.write("# Welcome to Streamlit! 👋")
st.sidebar.success("Select a demo above.")
st.markdown(
"""
Streamlit is an open-source app framework built specifically for
Machine Learning and Data Science projects.
**👈 Select a demo from the dropdown on the left** to see some examples
of what Streamlit can do!
### Want to learn more?
- Check out [streamlit.io](https://streamlit.io)
- Jump into our [documentation](https://docs.streamlit.io)
- Ask a question in our [community
forums](https://discuss.streamlit.io)
### See more complex demos
- Use a neural net to [analyze the Udacity Self-driving Car Image
Dataset](https://github.com/streamlit/demo-self-driving)
- Explore a [New York City rideshare dataset](https://github.com/streamlit/demo-uber-nyc-pickups)
"""
)
def mapping_demo():
import streamlit as st
import pandas as pd
import pydeck as pdk
from urllib.error import URLError
st.markdown(f"# {list(page_names_to_funcs.keys())[2]}")
st.write(
"""
This demo shows how to use
[`st.pydeck_chart`](https://docs.streamlit.io/develop/api-reference/charts/st.pydeck_chart)
to display geospatial data.
"""
)
@st.cache_data
def from_data_file(filename):
url = (
"http://raw.githubusercontent.com/streamlit/"
"example-data/master/hello/v1/%s" % filename
)
return pd.read_json(url)
try:
ALL_LAYERS = {
"Bike Rentals": pdk.Layer(
"HexagonLayer",
data=from_data_file("bike_rental_stats.json"),
get_position=["lon", "lat"],
radius=200,
elevation_scale=4,
elevation_range=[0, 1000],
extruded=True,
),
"Bart Stop Exits": pdk.Layer(
"ScatterplotLayer",
data=from_data_file("bart_stop_stats.json"),
get_position=["lon", "lat"],
get_color=[200, 30, 0, 160],
get_radius="[exits]",
radius_scale=0.05,
),
"Bart Stop Names": pdk.Layer(
"TextLayer",
data=from_data_file("bart_stop_stats.json"),
get_position=["lon", "lat"],
get_text="name",
get_color=[0, 0, 0, 200],
get_size=15,
get_alignment_baseline="'bottom'",
),
"Outbound Flow": pdk.Layer(
"ArcLayer",
data=from_data_file("bart_path_stats.json"),
get_source_position=["lon", "lat"],
get_target_position=["lon2", "lat2"],
get_source_color=[200, 30, 0, 160],
get_target_color=[200, 30, 0, 160],
auto_highlight=True,
width_scale=0.0001,
get_width="outbound",
width_min_pixels=3,
width_max_pixels=30,
),
}
st.sidebar.markdown("### Map Layers")
selected_layers = [
layer
for layer_name, layer in ALL_LAYERS.items()
if st.sidebar.checkbox(layer_name, True)
]
if selected_layers:
st.pydeck_chart(
pdk.Deck(
map_style="mapbox://styles/mapbox/light-v9",
initial_view_state={
"latitude": 37.76,
"longitude": -122.4,
"zoom": 11,
"pitch": 50,
},
layers=selected_layers,
)
)
else:
st.error("Please choose at least one layer above.")
except URLError as e:
st.error(
"""
**This demo requires internet access.**
Connection error: %s
"""
% e.reason
)
def plotting_demo():
import streamlit as st
import time
import numpy as np
st.markdown(f'# {list(page_names_to_funcs.keys())[1]}')
st.write(
"""
This demo illustrates a combination of plotting and animation with
Streamlit. We're generating a bunch of random numbers in a loop for around
5 seconds. Enjoy!
"""
)
progress_bar = st.sidebar.progress(0)
status_text = st.sidebar.empty()
last_rows = np.random.randn(1, 1)
chart = st.line_chart(last_rows)
for i in range(1, 101):
new_rows = last_rows[-1, :] + np.random.randn(5, 1).cumsum(axis=0)
status_text.text("%i%% Complete" % i)
chart.add_rows(new_rows)
progress_bar.progress(i)
last_rows = new_rows
time.sleep(0.05)
progress_bar.empty()
# Streamlit widgets automatically run the script from top to bottom. Since
# this button is not connected to any other logic, it just causes a plain
# rerun.
st.button("Re-run")
def data_frame_demo():
import streamlit as st
import pandas as pd
import altair as alt
from urllib.error import URLError
st.markdown(f"# {list(page_names_to_funcs.keys())[3]}")
st.write(
"""
This demo shows how to use `st.write` to visualize Pandas DataFrames.
(Data courtesy of the [UN Data Explorer](http://data.un.org/Explorer.aspx).)
"""
)
@st.cache_data
def get_UN_data():
AWS_BUCKET_URL = "http://streamlit-demo-data.s3-us-west-2.amazonaws.com"
df = pd.read_csv(AWS_BUCKET_URL + "/agri.csv.gz")
return df.set_index("Region")
try:
df = get_UN_data()
countries = st.multiselect(
"Choose countries", list(df.index), ["China", "United States of America"]
)
if not countries:
st.error("Please select at least one country.")
else:
data = df.loc[countries]
data /= 1000000.0
st.write("### Gross Agricultural Production ($B)", data.sort_index())
data = data.T.reset_index()
data = pd.melt(data, id_vars=["index"]).rename(
columns={"index": "year", "value": "Gross Agricultural Product ($B)"}
)
chart = (
alt.Chart(data)
.mark_area(opacity=0.3)
.encode(
x="year:T",
y=alt.Y("Gross Agricultural Product ($B):Q", stack=None),
color="Region:N",
)
)
st.altair_chart(chart, use_container_width=True)
except URLError as e:
st.error(
"""
**This demo requires internet access.**
Connection error: %s
"""
% e.reason
)
page_names_to_funcs = {
"—": intro,
"Plotting Demo": plotting_demo,
"Mapping Demo": mapping_demo,
"DataFrame Demo": data_frame_demo
}
demo_name = st.sidebar.selectbox("Choose a demo", page_names_to_funcs.keys())
page_names_to_funcs[demo_name]()
请注意这个文件的大小是多么之大!每个应用程序的“页面”都是以函数的形式编写,而选择框则用于选择要显示的页面。随着我们应用程序的不断发展,维护代码需要付出大量的额外工作。此外,由于 st.selectbox
用户界面的限制,我们只能选择要运行的“页面”,无法使用 st.set_page_config
自定义每个页面的标题,并且也无法通过 URL 在页面之间进行导航。
将现有应用程序转换为多页面应用程序
既然我们已经发现了单页面应用程序的局限性,那么我们该如何解决这个问题呢?凭借上一节所获得的知识,我们当然可以将现有的应用程序转换为多页面应用程序!从总体上看,我们需要执行以下步骤:
-
在“入口文件”(
hello.py
)所在的同一文件夹中创建一个新的pages
文件夹 -
将我们的入口文件重命名为
hello.py
,以便侧边栏中的标题为大写形式 -
在
pages
文件夹内创建三个新文件:-
pages/1_📈_Plotting_Demo.py
-
pages/2_🌍_Mapping_Demo.py
-
pages/3_📊_DataFrame_Demo.py
-
-
将
plotting_demo
、mapping_demo
和data_frame_demo
的内容移动到步骤 3 中创建的相应新文件中 -
运行
streamlit run Hello.py
以查看您新转换的多页面应用程序!
现在,让我们逐步查看整个过程中的每个步骤,并查看代码中相应的更改。
创建入口文件
# Hello.py
import streamlit as st
st.set_page_config(
page_title="Hello",
page_icon="👋",
)
st.write("# Welcome to Streamlit! 👋")
st.sidebar.success("Select a demo above.")
st.markdown(
"""
Streamlit is an open-source app framework built specifically for
Machine Learning and Data Science projects.
**👈 Select a demo from the sidebar** to see some examples
of what Streamlit can do!
### Want to learn more?
- Check out [streamlit.io](https://streamlit.io)
- Jump into our [documentation](https://docs.streamlit.io)
- Ask a question in our [community
forums](https://discuss.streamlit.io)
### See more complex demos
- Use a neural net to [analyze the Udacity Self-driving Car Image
Dataset](https://github.com/streamlit/demo-self-driving)
- Explore a [New York City rideshare dataset](https://github.com/streamlit/demo-uber-nyc-pickups)
"""
)
我们将入口文件重命名为hello.py
,这样侧边栏中的标题就会首字母大写,并且只包含介绍页面的代码。此外,我们还可以通过调用 st.set_page_config
来自定义页面标题和网站图标——它会在浏览器标签页中显示出来。我们还可以为每个页面都进行这样的设置!
请注意,侧边栏中并未显示页面标签,因为我们尚未创建任何页面。
创建多个页面
在此需要注意以下几点:
-
我们可以通过在每个 Python 文件的开头添加数字来更改 MPA 中页面的排列顺序。如果我们在文件名前添加一个 1,Streamlit 将会把该文件放在列表中的首位。
-
每个 Streamlit 应用的名称由文件名决定,所以要更改应用名称,就需要更改文件名!
-
我们还可以在文件名中添加表情符号,这些表情符号会在我们的 Streamlit 应用中显示出来。
-
每个页面都有其自己的 URL,由文件名定义。
下面来看看我们是如何完成这一切的!对于每个新页面,我们在“pages”文件夹中创建一个新的文件,并在其中添加相应的演示代码。
# pages/1_📈_Plotting_Demo.py
import streamlit as st
import time
import numpy as np
st.set_page_config(page_title="Plotting Demo", page_icon="📈")
st.markdown("# Plotting Demo")
st.sidebar.header("Plotting Demo")
st.write(
"""This demo illustrates a combination of plotting and animation with
Streamlit. We're generating a bunch of random numbers in a loop for around
5 seconds. Enjoy!"""
)
progress_bar = st.sidebar.progress(0)
status_text = st.sidebar.empty()
last_rows = np.random.randn(1, 1)
chart = st.line_chart(last_rows)
for i in range(1, 101):
new_rows = last_rows[-1, :] + np.random.randn(5, 1).cumsum(axis=0)
status_text.text("%i%% Complete" % i)
chart.add_rows(new_rows)
progress_bar.progress(i)
last_rows = new_rows
time.sleep(0.05)
progress_bar.empty()
# Streamlit widgets automatically run the script from top to bottom. Since
# this button is not connected to any other logic, it just causes a plain
# rerun.
st.button("Re-run")
# pages/2_🌍_Mapping_Demo.py
import streamlit as st
import pandas as pd
import pydeck as pdk
from urllib.error import URLError
st.set_page_config(page_title="Mapping Demo", page_icon="🌍")
st.markdown("# Mapping Demo")
st.sidebar.header("Mapping Demo")
st.write(
"""This demo shows how to use
[`st.pydeck_chart`](https://docs.streamlit.io/develop/api-reference/charts/st.pydeck_chart)
to display geospatial data."""
)
@st.cache_data
def from_data_file(filename):
url = (
"http://raw.githubusercontent.com/streamlit/"
"example-data/master/hello/v1/%s" % filename
)
return pd.read_json(url)
try:
ALL_LAYERS = {
"Bike Rentals": pdk.Layer(
"HexagonLayer",
data=from_data_file("bike_rental_stats.json"),
get_position=["lon", "lat"],
radius=200,
elevation_scale=4,
elevation_range=[0, 1000],
extruded=True,
),
"Bart Stop Exits": pdk.Layer(
"ScatterplotLayer",
data=from_data_file("bart_stop_stats.json"),
get_position=["lon", "lat"],
get_color=[200, 30, 0, 160],
get_radius="[exits]",
radius_scale=0.05,
),
"Bart Stop Names": pdk.Layer(
"TextLayer",
data=from_data_file("bart_stop_stats.json"),
get_position=["lon", "lat"],
get_text="name",
get_color=[0, 0, 0, 200],
get_size=15,
get_alignment_baseline="'bottom'",
),
"Outbound Flow": pdk.Layer(
"ArcLayer",
data=from_data_file("bart_path_stats.json"),
get_source_position=["lon", "lat"],
get_target_position=["lon2", "lat2"],
get_source_color=[200, 30, 0, 160],
get_target_color=[200, 30, 0, 160],
auto_highlight=True,
width_scale=0.0001,
get_width="outbound",
width_min_pixels=3,
width_max_pixels=30,
),
}
st.sidebar.markdown("### Map Layers")
selected_layers = [
layer
for layer_name, layer in ALL_LAYERS.items()
if st.sidebar.checkbox(layer_name, True)
]
if selected_layers:
st.pydeck_chart(
pdk.Deck(
map_style="mapbox://styles/mapbox/light-v9",
initial_view_state={
"latitude": 37.76,
"longitude": -122.4,
"zoom": 11,
"pitch": 50,
},
layers=selected_layers,
)
)
else:
st.error("Please choose at least one layer above.")
except URLError as e:
st.error(
"""
**This demo requires internet access.**
Connection error: %s
"""
% e.reason
)
# pages/3_📊_DataFrame_Demo.py
import streamlit as st
import pandas as pd
import altair as alt
from urllib.error import URLError
st.set_page_config(page_title="DataFrame Demo", page_icon="📊")
st.markdown("# DataFrame Demo")
st.sidebar.header("DataFrame Demo")
st.write(
"""This demo shows how to use `st.write` to visualize Pandas DataFrames.
(Data courtesy of the [UN Data Explorer](http://data.un.org/Explorer.aspx).)"""
)
@st.cache_data
def get_UN_data():
AWS_BUCKET_URL = "http://streamlit-demo-data.s3-us-west-2.amazonaws.com"
df = pd.read_csv(AWS_BUCKET_URL + "/agri.csv.gz")
return df.set_index("Region")
try:
df = get_UN_data()
countries = st.multiselect(
"Choose countries", list(df.index), ["China", "United States of America"]
)
if not countries:
st.error("Please select at least one country.")
else:
data = df.loc[countries]
data /= 1000000.0
st.write("### Gross Agricultural Production ($B)", data.sort_index())
data = data.T.reset_index()
data = pd.melt(data, id_vars=["index"]).rename(
columns={"index": "year", "value": "Gross Agricultural Product ($B)"}
)
chart = (
alt.Chart(data)
.mark_area(opacity=0.3)
.encode(
x="year:T",
y=alt.Y("Gross Agricultural Product ($B):Q", stack=None),
color="Region:N",
)
)
st.altair_chart(chart, use_container_width=True)
except URLError as e:
st.error(
"""
**This demo requires internet access.**
Connection error: %s
"""
% e.reason
)
在我们新增了更多页面之后,现在就可以在下面的最后一步中将所有内容整合在一起了。
运行多页面应用程序
要运行您刚刚转换完成的多页面应用程序,请执行以下操作:
运行以下命令:streamlit run Hello.py
!现在,hello.py
脚本就相当于你的应用程序的主页面了,而 Streamlit 在pages
文件夹中找到的其他脚本也会出现在侧边栏出现的新页面选择器中。
下一步
恭喜! 如果您已经读到这里,那么您很可能已经学会了如何创建单页和多页应用程序。接下来您要走的路完全取决于您的创造力!我们非常期待看到您现在所构建的作品,因为现在为您的应用程序添加更多页面比以往任何时候都更加容易。试着为刚刚构建的应用程序添加更多页面作为练习。另外,也请访问论坛,与 Streamlit 社区一起展示您的多页应用程序吧! 🎈
以下是一些能帮助您入门的资源:
-
在 Streamlit 的社区云平台上免费部署您的应用程序。
-
在我们的社区论坛上提出问题或分享您的多页面应用程序。
-
查看关于多页面应用程序的文档。
-
阅读有关缓存、主题设置以及为应用程序添加状态性等内容的说明。
-
浏览我们的 API 参考,以获取每个 Streamlit 命令的示例。