☰
如何用 Streamlit 的 st.pagination 为大结果集、搜索页和向导实现分页导航
2026/10/11 4:22:10 网站建设 项目流程

如何用 Streamlit 的 st.pagination 为大结果集、搜索页和向导实现分页导航

【免费下载链接】streamlitStreamlit — A faster way to build and share data apps.项目地址: https://gitcode.com/gh_mirrors/st/streamlit

如果你的 Streamlit 应用里有一个几百行的数据表、一份搜索结果,或者一个分步填写的表单,st.pagination提供了一个带上一页/下一页箭头和页码按钮的分页控件,直接返回当前选中的页码(从 1 开始),你只需要用它做数据切片。它是带状态的控件:一次始终选中一页,选中页会在 rerun 之间保留,返回值在下一次 rerun 时更新。本文基于仓库中的产品规格 specs/2026-03-22-st-pagination/product-spec.md、组件实现 lib/streamlit/elements/widgets/pagination.py 和 e2e 演示应用 e2e_playwright/st_pagination.py,给出三种典型场景的完整写法和验证方式。

组件签名与参数

以组件实现 lib/streamlit/elements/widgets/pagination.py 中的签名和 docstring 为准:

st.pagination( num_pages: int, *, default: int = 1, max_visible_pages: int | None = 7, width: Literal["content", "stretch"] | int = "content", key: Key | None = None, on_change: WidgetCallback | None = None, args: WidgetArgs | None = None, kwargs: WidgetKwargs | None = None, disabled: bool = False, bind: BindOption = None, persist_state: PersistStateOption = None, ) -> int

关键参数(含义取自实现文件 docstring):

参数说明
num_pages总页数,必须 ≥ 1
default初始选中页(1-indexed),必须在 1 到num_pages之间,默认1
max_visible_pages最多显示的页码按钮数(不含箭头),默认7;0只显示前后箭头,1只显示当前页,None去掉数量上限(窄容器下仍可能自动隐藏部分页码)
width"content"随内容、"stretch"撑满父容器宽度(按钮保持居中)、int为固定像素宽度
key控件唯一 key;提供后可以通过st.session_state[key]读取和改写当前页
on_change选中页变化时的回调,args/kwargs传给回调
disabled为True时整个控件禁用
bind设为"query-params"时把页码同步到 URL 查询串,要求同时设置key,得到可分享、能保留页码状态的链接
persist_state"page"表示页码只在当前页保留,"session"表示整个会话保留(跨页面切换也保留,要求有key);同时设置bind="query-params"时以 URL 绑定为准

返回值是当前选中页的int。注意分页控件本身不负责切片数据,切片由你自己根据返回的页码计算——这也是产品规格中选择"低层控件"而非自动分页迭代器的原因:它和数据源(数据库、API、本地数据)解耦。

场景一:为大结果集做分页数据表

docstring 中给出的 dataframe 示例(lib/streamlit/elements/widgets/pagination.py)展示了完整做法:先用st.empty()占位,把分页控件放在数据表下方右对齐,再按页码切片:

import streamlit as st import pandas as pd df = pd.DataFrame({"A": range(100), "B": range(100, 200)}) rows_per_page = 10 total_pages = (len(df) + rows_per_page - 1) // rows_per_page # Use placeholders to show dataframe above pagination dataframe_slot = st.empty() with st.container(horizontal_alignment="right"): page = st.pagination(num_pages=total_pages) start_idx = (page - 1) * rows_per_page end_idx = start_idx + rows_per_page dataframe_slot.dataframe(df.iloc[start_idx:end_idx])

这里的要点:

  • total_pages = (len(df) + rows_per_page - 1) // rows_per_page是向上取整的页数计算,50 行、每页 10 行得到 5 页。
  • 切片区间是(page - 1) * rows_per_page到start_idx + rows_per_page,因为页码从 1 开始。
  • 用st.empty()占位是为了让数据表渲染在分页控件上方;如果直接先写分页再写数据表,布局会反过来。

把df换成pd.read_csv(...)或数据库查询结果即可,规格中的 示例 用的是pd.read_csv("large_dataset.csv")、每页 25 行的写法。

场景二:搜索页——回调、程序化跳转与 URL 同步

搜索结果页通常需要一个稳定的key,因为要支持程序化改页和监听页码变化。

带回调的写法(来自 e2e_playwright/st_pagination.py,回调内通过st.session_state[key]读当前页):

def on_change(): st.write(f"callback-page: {st.session_state.callback_pagination}") st.pagination(10, key="callback_pagination", on_change=on_change)

on_change在"有效页码相对上一次 rerun 发生变化"时触发,包括用户点击、通过st.session_state[key]的编程式修改,以及num_pages变小导致页码回落到default的情况(仅当回落后的页码与之前不同时)。

程序化跳转(来自 产品规格 的示例):

import streamlit as st # Jump to a specific page programmatically if st.button("Go to page 5"): st.session_state.my_page = 5 # Reset to first page if st.button("Reset"): st.session_state.my_page = 1 page = st.pagination(num_pages=10, key="my_page")

e2e 应用 e2e_playwright/st_pagination.py 里还演示了"三个按钮跳到第 1/5/10 页"的搜索页常见模式,写法相同:按钮回调里给st.session_state[key]赋值,控件用同一个key声明。

可选分支:让搜索结果页的页码可分享。设置bind="query-params"并给出key后,页码会读写到 URL 查询串(key即查询参数名),用户把链接发给别人时页码状态得以保留:

page = st.pagination(10, key="page", bind="query-params")

没有key时设置bind="query-params"会抛出要求提供唯一 key 的异常。

场景三:多步骤向导

把每一步映射为一个页码,用width="stretch"让控件撑满宽度,再按step - 1取步骤标题(页码是 1-indexed):

import streamlit as st steps = ["Personal Info", "Address", "Payment", "Review"] step = st.pagination(num_pages=len(steps), width="stretch") st.header(steps[step - 1]) # Render step content based on current step

这是 产品规格 给出的向导示例。注意规格同时说明:用自定义标签(而不是数字)标注向导步骤、输入框直接跳页、每页条数选择器、"第 3 / 10 页"之类的总数展示,目前都在 Out of Scope 清单里,尚不支持,向导步骤只能用数字页码表达。

页码截断、宽度与响应式行为

当num_pages超过max_visible_pages时,控件按既定模式截断(规格中的布局示意):

< | 1 | 2 | 3 | ... | 10 | > (when on page 1-3) < | 1 | ... | 5 | 6 | 7 | ... | 10 | > (when on page 6) < | 1 | ... | 8 | 9 | 10 | > (when on page 8-10)
  • 首尾页和当前页始终保留,当前页周围附带 1–2 页上下文,省略号表示被隐藏的区段;
  • max_visible_pages=0只剩< | >,=1显示< | 5 | >,=2显示当前页加末页(当前页在边缘时改为首页加末页);
  • 当前选中页用主色高亮,...不可点击。

响应式方面:控件根据容器可用宽度自动隐藏页码,页码按钮永远不会折行;空间不足时按"箭头 > 当前页 > 末页 > 首页 > 相邻上下文页"的优先级递减,极窄容器下会退化为< | 5 | >甚至只有箭头。键盘可访问性:Tab 在箭头和页码按钮间移动,Enter/Space 激活,焦点环只在键盘导航时可见。

验证运行结果

把上面任一示例保存为streamlit_app.py,运行streamlit run streamlit_app.py,然后按仓库 e2e 测试 e2e_playwright/st_pagination_test.py 里的断言核对行为,这些断言就是分页行为的验收标准:

  • 初始渲染显示Current page: 1(对应st.write(f"Current page: {page}")),第 1 页时上一页箭头为 disabled,下一页可用;
  • 点击下一页,rerun 后显示Current page: 2;点击页码按钮 5,rerun 后显示Current page: 5;
  • 设置default=5时初始显示Default page: 5,点上一页变为 4;
  • disabled=True时前后箭头和所有页码按钮都是 disabled;num_pages=1时只显示页码 1,两个箭头均 disabled。

单元测试 lib/tests/streamlit/elements/pagination_test.py 覆盖了另一侧的可执行断言,即参数校验错误。出现以下报错时对照检查参数:

触发条件异常
num_pages < 1(或非 int/bool)StreamlitAPIException,信息含`num_pages` must be an integer of at least 1
default < 1或> num_pagesStreamlitValueOutOfRangeError,信息含required range [1, num_pages]
default非 int(True也被拒绝,因为 bool 是 int 子类)StreamlitInvalidParameterTypeError
max_visible_pages < 0(或非 int/bool)StreamlitAPIException,信息含`max_visible_pages` must be a non-negative integer or None
设置bind="query-params"但没有key要求提供唯一 key 的StreamlitAPIException
同一页内重复声明同一无 key 控件提示重复 ID 的StreamlitAPIException

状态语义与限制

写代码前需要确认几条来自规格与实现的硬规则:

  • 控件有状态:选中页在 rerun 间保留;default只在st.session_state[key]尚无值时生效,之后再改default不会改变当前页、也不触发on_change。
  • 运行期num_pages变小且当前页超过新的num_pages时,页码回落到default;这算作一次页码变化,仅当回落后的页码与之前不同才触发on_change。
  • 控件可以放进st.form(提交前不触发整页 rerun,取值随表单提交生效)和@st.fragment(fragment 内局部 rerun),两者都在 e2e_playwright/st_pagination.py 中有对应演示区块。
  • 目前不支持:自定义页码标签、跳页输入框、每页条数选择器、总数展示、全局方向键快捷键(产品规格 Out of Scope 一节)。

规格与实现中有一处参数描述口径不同,这里如实指出:规格中max_visible_pages是"maximum number of page buttons",而实现 docstring 表述为 "Target number of page buttons",并说明个别边界情况下实际数量可能略高,以保证首尾页始终可见。两者对常规使用的约束一致(默认 7、可设0/1/None),按上述取值使用即可。

【免费下载链接】streamlitStreamlit — A faster way to build and share data apps.项目地址: https://gitcode.com/gh_mirrors/st/streamlit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询