Intro to custom components
开发Streamlit组件的第一步是决定是创建一个静态组件(即一次性渲染,由Python控制)还是创建一个可以在Python和JavaScript之间双向通信的组件。
Create a static component
如果您创建Streamlit组件的目标仅仅是显示HTML代码或从Python可视化库渲染图表,Streamlit提供了两种方法,可以大大简化这个过程:components.html()
和 components.iframe()
。
如果您不确定是否需要双向通信,请首先从这里开始!
Render an HTML string
虽然st.text
、st.markdown
和st.write
使得向Streamlit应用程序写入文本变得容易,但有时你可能更愿意实现一个自定义的HTML片段。同样,虽然Streamlit原生支持许多图表库,但你可能希望为新的图表库实现特定的HTML/JavaScript模板。components.html
通过让你能够在Streamlit应用程序中嵌入一个包含你所需输出的iframe来实现这一功能。
示例
import streamlit as st
import streamlit.components.v1 as components
# bootstrap 4 collapse example
components.html(
"""
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<div id="accordion">
<div class="card">
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</button>
</h5>
</div>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
Collapsible Group Item #1 content
</div>
</div>
</div>
<div class="card">
<div class="card-header" id="headingTwo">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Collapsible Group Item #2
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
<div class="card-body">
Collapsible Group Item #2 content
</div>
</div>
</div>
</div>
""",
height=600,
)
Render an iframe URL
components.iframe
在功能上与 components.html
类似,不同之处在于 components.iframe
接受一个 URL 作为输入。这适用于您希望在 Streamlit 应用程序中包含整个页面的情况。
示例
import streamlit as st
import streamlit.components.v1 as components
# embed streamlit docs in a streamlit app
components.iframe("https://example.com", height=500)
Create a bi-directional component
一个双向的Streamlit组件有两个部分:
- 一个前端,由HTML和您喜欢的任何其他网络技术(JavaScript、React、Vue等)构建,并通过iframe标签在Streamlit应用程序中呈现。
- 一个Python API,Streamlit 应用程序使用它来实例化并与前端进行通信
为了使创建双向Streamlit组件的过程更加容易,我们在Streamlit组件模板GitHub仓库中创建了一个React模板和一个仅TypeScript的模板。我们还在同一个仓库中提供了一些示例组件。
Development Environment Setup
要构建一个Streamlit组件,您需要在开发环境中安装以下内容:
克隆component-template GitHub 仓库,然后决定是否要使用 React.js ("template") 或纯 TypeScript ("template-reactless") 模板。
-
从终端初始化和构建组件模板前端:
# React 模板 template/my_component/frontend npm install # 初始化项目并安装 npm 依赖 npm run start # 启动 Webpack 开发服务器 # 或者 # 仅 TypeScript 模板 template-reactless/my_component/frontend npm install # 初始化项目并安装 npm 依赖 npm run start # 启动 Webpack 开发服务器
-
从另一个终端,运行声明并使用组件的Streamlit应用程序(Python):
# React模板 cd template . venv/bin/activate # 或类似命令以激活安装了Streamlit的venv/conda环境 pip install -e . # 将模板安装为可编辑包 streamlit run my_component/example.py # 运行示例 # 或者 # 仅TypeScript模板 cd template-reactless . venv/bin/activate # 或类似命令以激活安装了Streamlit的venv/conda环境 pip install -e . # 将模板安装为可编辑包 streamlit run my_component/example.py # 运行示例
运行上述步骤后,您应该在浏览器中看到一个如下所示的Streamlit应用程序:

模板中的示例应用程序展示了如何实现双向通信。Streamlit 组件显示一个按钮(Python → JavaScript
),最终用户可以点击该按钮。每次点击按钮时,JavaScript 前端会增加计数器值并将其传递回 Python(JavaScript → Python
),然后由 Streamlit 显示(Python → JavaScript
)。
Frontend
因为每个Streamlit组件都是自己的网页,会被渲染到iframe
中,所以你可以使用几乎任何你喜欢的网页技术来创建那个网页。我们在Streamlit的Components-template GitHub仓库中提供了两个模板来帮助你开始;其中一个模板使用了React,而另一个则没有。
注意
即使您还不熟悉React,您可能仍然想查看基于React的模板。它处理了从Streamlit发送和接收数据所需的大部分样板代码,您可以在使用过程中学习所需的React知识。
如果您不想使用React,也请阅读本节!它解释了Streamlit ↔ 组件通信的基础知识。
React
基于React的模板位于template/my_component/frontend/src/MyComponent.tsx
。
MyComponent.render()
当组件需要重新渲染时自动调用(就像在任何 React 应用中一样)- 从Python脚本传递的参数可以通过
this.props.args
字典获取:
# Send arguments in Python:
result = my_component(greeting="Hello", name="Streamlit")
// Receive arguments in frontend:
let greeting = this.props.args["greeting"]; // greeting = "Hello"
let name = this.props.args["name"]; // name = "Streamlit"
- 使用
Streamlit.setComponentValue()
将数据从组件返回到 Python 脚本:
// Set value in frontend:
Streamlit.setComponentValue(3.14);
# Access value in Python:
result = my_component(greeting="Hello", name="Streamlit")
st.write("result = ", result) # result = 3.14
当你调用Streamlit.setComponentValue(new_value)
时,这个新值会被发送到Streamlit,然后重新从头到尾执行Python脚本。当脚本重新执行时,调用my_component(...)
将返回新值。
从代码流程的角度来看,似乎你正在与前端同步传输数据:Python将参数发送给JavaScript,JavaScript将值返回给Python,所有这些都在一个函数调用中完成!但实际上,这一切都是异步发生的,而Python脚本的重新执行才是实现这一技巧的关键。
- 使用
Streamlit.setFrameHeight()
来控制组件的高度。默认情况下,React 模板会自动调用此方法(参见StreamlitComponentBase.componentDidUpdate()
)。如果需要更多控制,可以覆盖此行为。 - 文件的最后一行有一点小魔法:
export default withStreamlitConnection(MyComponent)
- 这与Streamlit进行了一些握手,并设置了双向数据通信的机制。
仅限TypeScript
仅限TypeScript的模板位于template-reactless/my_component/frontend/src/MyComponent.tsx
。
这个模板比它的React版本有更多的代码,因为所有握手、设置事件监听器和更新组件框架高度的机制都是手动完成的。React版本的模板自动处理了大部分这些细节。
- 在源文件的底部,模板调用
Streamlit.setComponentReady()
来告诉Streamlit它已准备好开始接收数据。(通常,您会在创建并加载组件所依赖的所有内容后执行此操作。) - 它订阅了
Streamlit.RENDER_EVENT
以在需要重新绘制时收到通知。(此事件在调用setComponentReady
之前不会触发) - 在其
onRender
事件处理程序中,它通过event.detail.args
访问Python脚本中传递的参数 - 它以与React模板相同的方式将数据发送回Python脚本——点击“Click Me!”按钮会调用
Streamlit.setComponentValue()
- 它通过
Streamlit.setFrameHeight()
通知Streamlit其高度可能已更改
使用主题
注意
自定义组件主题支持需要 streamlit-component-lib 版本 1.2.0 或更高。
除了向您的组件发送一个args
对象外,Streamlit 还会发送一个theme
对象,定义当前主题,以便您的组件可以以兼容的方式调整其样式。此对象与args
在同一消息中发送,因此可以通过this.props.theme
(使用 React 模板时)或event.detail.theme
(使用纯 TypeScript 模板时)访问。
theme
对象具有以下形状:
{
"base": "lightORdark",
"primaryColor": "someColor1",
"backgroundColor": "someColor2",
"secondaryBackgroundColor": "someColor3",
"textColor": "someColor4",
"font": "someFont"
}
base
选项允许您指定一个预设的 Streamlit 主题,您的自定义主题将继承该主题。任何未在您的主题设置中定义的主题配置选项都将使用基础主题的值。base
的有效值为 "light"
和 "dark"
。
请注意,主题对象具有与通过命令streamlit config show
打印的配置选项中的“theme”部分相同的名称和语义的字段。
使用 React 模板时,以下 CSS 变量也会自动设置。
--base
--primary-color
--background-color
--secondary-background-color
--text-color
--font
如果你不熟悉 CSS变量, 简而言之,你可以这样使用它们:
.mySelector {
color: var(--text-color);
}
这些变量与上面定义的theme
对象中的字段匹配,是否在组件中使用CSS变量或主题对象是个人偏好的问题。
其他前端细节
- 因为你是通过开发服务器(通过
npm run start
)托管你的组件,所以当你保存时,你所做的任何更改都应该自动反映在Streamlit应用中。 - 如果你想为你的组件添加更多的包,可以在组件的
frontend/
目录中运行npm add
来添加它们。
npm add baseui
- 要构建组件的静态版本,请运行
npm run export
。有关更多信息,请参阅 准备您的组件
Python API
components.declare_component()
是创建你的组件的 Python API 所需的全部内容:
import streamlit.components.v1 as components
my_component = components.declare_component(
"my_component",
url="http://localhost:3001"
)
然后,您可以使用返回的my_component
函数与您的前端代码发送和接收数据:
# Send data to the frontend using named arguments.
return_value = my_component(name="Blackbeard", ship="Queen Anne's Revenge")
# `my_component`'s return value is the data returned from the frontend.
st.write("Value = ", return_value)
虽然上述内容是从Python端定义工作组件所需的全部内容,但我们建议创建一个带有命名参数和默认值、输入验证等的“包装”函数。这将使最终用户更容易理解您的函数接受哪些数据值,并允许定义有用的文档字符串。
请参阅此示例来自组件模板,以了解创建包装函数的示例。
Data serialization
Python → 前端
你通过将关键字参数传递给你的组件的invoke函数(即从declare_component
返回的函数)来将数据从Python发送到前端。你可以从Python发送以下类型的数据到前端:
- 任何可JSON序列化的数据
numpy.array
pandas.DataFrame
任何可JSON序列化的数据都会被序列化为JSON字符串,并反序列化为其JavaScript等效对象。numpy.array
和 pandas.DataFrame
使用 Apache Arrow 进行序列化,并反序列化为 ArrowTable
的实例,这是一个自定义类型,封装了Arrow结构并在其上提供了方便的API。
查看CustomDataframe和SelectableDataTable组件的示例代码,了解更多关于如何使用ArrowTable
的上下文。
前端 → Python
你通过Streamlit.setComponentValue()
API(这是模板代码的一部分)将数据从前端发送到Python。与从Python → 前端传递参数不同,这个API只接受一个值。如果你想返回多个值,你需要将它们包装在Array
或Object
中。
自定义组件可以从前端发送可JSON序列化的数据到Python,以及Apache Arrow ArrowTable
s来表示数据框。
还有问题吗?
我们的 论坛 充满了有用的信息和Streamlit专家。